AJAX in webserver not working properly

ClosedLoop
Posts: 14
Joined: Mon May 16, 2022 8:13 am
Location: Portugal

AJAX in webserver not working properly

Postby ClosedLoop » Mon May 16, 2022 9:59 am

Hello, i am trying to run a webserver on the esp32. Through tutorials online and the simple and file serving example i managed to get it working. My problem is that when trying to run AJAX i cannot get it to work and enter the js functions inside the html code. Right now, im sending an header "HTTP/1.1 200 OK\r\nContent-Length: %u\r\nContent-type: text/html\r\nConnection: keep-alive\r\n\r\n" and after i send the html code using httpd_resp_send_chunk function. Through this method the header doesn't seem to be registering and its treated like another html page. I'm posting my code below. I am experimenting with a simple function that uses ajax, that i got from w3school to try and see if through ajax i can see the GETs being done in order to try and understand the problem and why i can't enter the functions inside the html code. Below i put the code that i'm using.

Code: Select all

#define SCRATCH_BUFSIZE 8192
/* This example demonstrates how to create file server
 * using esp_http_server. This file has only startup code.
 * Look in file_server.c for the implementation */
char parsed[5][25];
char parse[100];
char size_ssid[35];
char ssid_get[30] = "ssid", pass_get[25] = "pass", conn_get[10] = "wifi";

char webpage[]=  "<!DOCTYPE html>"
        "<html>"
        "<head>"
        "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" /> "
		  "<link rel=\"stylesheet\" href=\"bootstrap.min.css\">"


        "<div style=\"padding:20px;margin-top:50px;\" class=\"container\">"
        "  <form action=\"/index_html\">"
        "<h2>Home Page</h2>"
        "<p>This part is used to change the network that gateway will connect. Select the right SSID from drop-down selections and write password for this network. </p>"




"<h1>The XMLHttpRequest Object</h1>"

"<button type=\"button\" onclick=\"loadDoc()\">Get my CD collection</button>"
"<br><br>"
"<table id=\"demo\"></table>"
"<script src=\"bootstrap.min.js\"> </script>"
"<script>"
"alert(\"Entrou\");"
"function loadDoc() {"
"var xhttp = new XMLHttpRequest();"
"xhttp.onreadystatechange = function() {"
"if (this.readyState == 4 && this.status == 200) {"
" myFunction(this);"
"}"
"};"
"xhttp.open(\"GET\", \"cd_catalog.xml\", true);"
" xhttp.send();"
"}"
"function myFunction(xml) {"
"var i;"
"var xmlDoc = xml.responseXML;"
"var table=\"<tr><th>Artist</th><th>Title</th></tr>\";"
"var x = xmlDoc.getElementsByTagName(\"CD\");"
"for (i = 0; i <x.length; i++) {"
"table += \"<tr><td>\" +"
" x[i].getElementsByTagName(\"ARTIST\")[0].childNodes[0].nodeValue +"
"</td><td> +"
"x[i].getElementsByTagName(\"TITLE\")[0].childNodes[0].nodeValue +"
"</td></tr>;"
"  }"
"document.getElementById(\"demo\").innerHTML = table;"
"}"


	      "</script>\n"

        "</body>"

        "</html>";

struct file_server_data
{
    /* Base path of file storage */
    char base_path[ESP_VFS_PATH_MAX + 1];

    /* Scratch buffer for temporary storage during file transfer */
    char scratch[SCRATCH_BUFSIZE];
};

static const char *TAG = "file_server";

static esp_err_t event_handler(void *ctx, system_event_t *event)
{
    return ESP_OK;
}
/* Function to initialize SPIFFS */
static esp_err_t init_spiffs(void)
{
    ESP_LOGI(TAG, "Initializing SPIFFS");

    esp_vfs_spiffs_conf_t conf = {
        .base_path = "/spiffs",
        .partition_label = NULL,
        .max_files = 5, // This decides the maximum number of files that can be created on the storage
        .format_if_mount_failed = true};

    esp_err_t ret = esp_vfs_spiffs_register(&conf);
    if (ret != ESP_OK)
    {
        if (ret == ESP_FAIL)
        {
            ESP_LOGE(TAG, "Failed to mount or format filesystem");
        }
        else if (ret == ESP_ERR_NOT_FOUND)
        {
            ESP_LOGE(TAG, "Failed to find SPIFFS partition");
        }
        else
        {
            ESP_LOGE(TAG, "Failed to initialize SPIFFS (%s)", esp_err_to_name(ret));
        }
        return ESP_FAIL;
    }

    size_t total = 0, used = 0;
    ret = esp_spiffs_info(NULL, &total, &used);
    if (ret != ESP_OK)
    {
        ESP_LOGE(TAG, "Failed to get SPIFFS partition information (%s)", esp_err_to_name(ret));
        return ESP_FAIL;
    }

    ESP_LOGI(TAG, "Partition size: total: %d, used: %d", total, used);
    return ESP_OK;
}

uint16_t ap_num = MAX_APs;
wifi_ap_record_t ap_records[MAX_APs];

static esp_err_t http_resp_dir_html(httpd_req_t *req, const char *dirpath);
static esp_err_t index_html_get_handler(httpd_req_t *req)
{
    httpd_resp_set_status(req, "307 Temporary Redirect");
    httpd_resp_set_hdr(req, "Location", "/index_html");
    httpd_resp_send(req, NULL, 0); // Response body can be empty
    return ESP_OK;
}

static esp_err_t logo_get_handler(httpd_req_t *req)
{
    extern const unsigned char logo_png_start[] asm("_binary_logo_png_start");
    extern const unsigned char logo_png_end[] asm("_binary_logo_png_end");
    const size_t logo_png_size = (logo_png_end - logo_png_start);
    httpd_resp_set_type(req, "image/png");
    httpd_resp_send(req, (const char *)logo_png_start, logo_png_size);
    return ESP_OK;
}

static esp_err_t bootstrap_css_get_handler(httpd_req_t *req)
{
	extern const unsigned char bootstrap_css_start[] asm("_binary_bootstrap_min_css_start");
	extern const unsigned char bootstrap_css_end[]   asm("_binary_bootstrap_min_css_end");
	const size_t bootcss_size = ((bootstrap_css_end-1) - bootstrap_css_start);
    httpd_resp_set_hdr(req, "Location", "/bootstrap_min_css");
    httpd_resp_send(req, (const char *)bootstrap_css_start, bootcss_size);
    return ESP_OK;
}
static esp_err_t  bootstrap_js_get_handler(httpd_req_t *req)
{
	extern const unsigned char bootstrap_js_start[] asm("_binary_bootstrap_min_js_start");
	extern const unsigned char bootstrap_js_end[]   asm("_binary_bootstrap_min_js_end");
	const size_t bootjs_size = ((bootstrap_js_end-1) - bootstrap_js_start);
    httpd_resp_set_hdr(req, "Location", "/bootstrap_min_js");
    httpd_resp_send(req, (const char *)bootstrap_js_start, bootjs_size);

    return ESP_OK;
}
static esp_err_t cd_xml_get_handler(httpd_req_t *req)
{
	extern const unsigned char cd_xml_start[] asm("_binary_cd_catalog_xml_start");
	extern const unsigned char cd_xml_end[]   asm("_binary_cd_catalog_xml_end");
	const size_t cd_size = (cd_xml_end - cd_xml_start);
	httpd_resp_set_hdr(req, "Location", "/bootstrap_min_css");
	httpd_resp_send(req, (const char *)cd_xml_start, cd_size);
	return ESP_OK;
}

static esp_err_t connect_get_handler(httpd_req_t *req)
{
    char *token = strtok(parse, "?");
    token = strtok(NULL, "?");

    printf("after(?) token:%s \n", token);


    if (!(strncmp("home", token, 4)))
    {
        httpd_resp_set_status(req, "307 Temporary Redirect");
        httpd_resp_set_hdr(req, "Location", "/index_html");
        httpd_resp_send(req, NULL, 0);
    }
    if (!(strncmp("ssid", token, 4)))
    {

        strcpy(ssid_get, " ");
        strcpy(pass_get, " ");
        token = strtok(token, "&");
        printf(" %s \t", token);
        stpcpy(parsed[0], token + 5);
        strcpy(ssid_get, parsed[0]);
        printf("ssid detected! %s     \n", ssid_get);
        token = strtok(NULL, "&");
        printf("%s \t", token);
        stpcpy(parsed[1], token + 4);
        strcpy(pass_get, parsed[1]);
        printf("pass detected! %s     \n", pass_get);
    }
    else if (!(strncmp("conn", token, 4)))
    {
        strcpy(conn_get, "");
        token = strtok(token, "&");
        printf(" %s \t", token);
        stpcpy(parsed[0], token + 12);
        strcpy(conn_get, parsed[0]);
        printf("connection type detected! %s     \n", conn_get);
    }
    else
    {
        printf("could not find any data! \n");
        //	 httpd_resp_send_chunk(req,  " alert('error!')/*displays error message*/",-1);
        httpd_resp_set_status(req, "307 Temporary Redirect");
        httpd_resp_set_hdr(req, "Location", "/index_html");
        httpd_resp_send(req, NULL, 0); // Response body can be empty
    }
    //httpd_resp_send_chunk(req,  " SAVED!",-1);
    // httpd_resp_send_chunk(req,  " alert('changed values are saved!')/*displays error message*/",-1);
    //    printf ("new connection type : %s   %s \n",parsed[0] ,parsed[1]);
    printf("going to success\n");
    httpd_resp_set_status(req, "307 Temporary Redirect");
    httpd_resp_set_hdr(req, "Location", "/index_html");
    httpd_resp_send(req, NULL, 0); // Response body can be empty
    return ESP_OK;
}


static esp_err_t http_resp_dir_html(httpd_req_t *req, const char *dirpath)
{

	static char http_html_hdr[150];

	sprintf(http_html_hdr,"HTTP/1.1 200 OK\r\nContent-Length: %u\r\nContent-type: text/html\r\nConnection: keep-alive\r\n\r\n",sizeof(webpage)-1);
	httpd_resp_send(req,http_html_hdr,strlen(http_html_hdr));
	vTaskDelay(5);
	httpd_resp_send_chunk(req,webpage,strlen(webpage));


    return ESP_OK;
}


#define IS_FILE_EXT(filename, ext) \
    (strcasecmp(&filename[strlen(filename) - sizeof(ext) + 1], ext) == 0)

/* Set HTTP response content type according to file extension */
static esp_err_t set_content_type_from_file(httpd_req_t *req, const char *filename)
{
    if (IS_FILE_EXT(filename, ".pdf"))
    {
        return httpd_resp_set_type(req, "application/pdf");
    }
    else if (IS_FILE_EXT(filename, ".html"))
    {
        return httpd_resp_set_type(req, "text/html");
    }
    else if (IS_FILE_EXT(filename, ".png"))
    {
        return httpd_resp_set_type(req, "image/png");
    }
    else if (IS_FILE_EXT(filename, ".jpeg"))
    {
        return httpd_resp_set_type(req, "image/jpeg");
    }
    else if (IS_FILE_EXT(filename, ".xml"))
    {
        return httpd_resp_set_type(req, "text/xml");
    }
    else if (IS_FILE_EXT(filename, ".css"))
        {
            return httpd_resp_set_type(req, "text/css");
        }
    else if (IS_FILE_EXT(filename, ".js"))
        {
            return httpd_resp_set_type(req, "application/javascript");
        }
    /* This is a limited set only */
    /* For any other type always set as plain text */
    return httpd_resp_set_type(req, "text/plain");
}

/* Copies the full path into destination buffer and returns
 * pointer to path (skipping the preceding base path) */
static const char *get_path_from_uri(char *dest, const char *base_path, const char *uri, size_t destsize)
{

    const size_t base_pathlen = strlen(base_path);
    size_t pathlen = strlen(uri);

    const char *quest = strchr(uri, '?');
    if (quest)
    {
        pathlen = MIN(pathlen, quest - uri);
    }
    const char *hash = strchr(uri, '#');
    if (hash)
    {
        pathlen = MIN(pathlen, hash - uri);
    }

    if (base_pathlen + pathlen + 1 > destsize)
    {
        /* Full path string won't fit into destination buffer */
        return NULL;
    }
    /* Construct full path (base + path) */
    strcpy(dest, base_path);
    strlcpy(dest + base_pathlen, uri, pathlen + 1);

    /* Return pointer to path, skipping the base */
    return dest + base_pathlen;
}

/* Handler to download a file kept on the server */
static esp_err_t webserver_get_handler(httpd_req_t *req)
{
    char filepath[FILE_PATH_MAX];
    FILE *fd = NULL;
    struct stat file_stat;

    for (int a = 0; a < 5; a++)
    {
        strcpy(parsed[a], "");
    }
    ESP_LOGI(TAG, " storage ssid: %s     pass: %s   ", ssid_get, pass_get);

    const char *filename = get_path_from_uri(filepath, ((struct file_server_data *)req->user_ctx)->base_path,
                                             req->uri, sizeof(filepath));

    printf("uri %s \n", req->uri);
    strcpy(parse, req->uri); // calling strcpy function

    printf("parse>>>> %s ,   filename: %s\n", parse, filename);

    if (!filename)
    {
        ESP_LOGE(TAG, "Filename is too long");
        /* Respond with 500 Internal Server Error */
        httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Filename too long");
        return ESP_FAIL;
    }
    printf("\n \n %s \n \n",filename);
    /* If name has trailing '/', respond with directory contents */
    if (filename[strlen(filename) - 1] == '/')
    {
        return http_resp_dir_html(req, filepath);
    }
    if (stat(filepath, &file_stat) == -1)
    {
        /* If file not present on SPIFFS check if URI
         * corresponds to one of the hardcoded paths */


        if (strcmp(filename, "/logo.png") == 0)
        {
            ESP_LOGI(TAG, "logo!");
            return logo_get_handler(req);
        }
        else if (strcmp(filename, "/bootstrap.min.css") == 0)
               {
                   ESP_LOGI(TAG, "bootstrap.css!");
                   return bootstrap_css_get_handler(req);
               }
        else if (strcmp(filename, "/bootstrap.min.js") == 0)
                       {
                           ESP_LOGI(TAG, "bootstrap.js!");
                           return bootstrap_js_get_handler(req);
                       }
        else if (strcmp(filename, "/cd_catalog.xml") == 0)
                               {
                                   ESP_LOGI(TAG, "cd.xml!");
                                   return cd_xml_get_handler(req);
                               }
        ESP_LOGE(TAG, "Failed to stat file : %s", filepath);
        /* Respond with 404 Not Found */
        httpd_resp_send_err(req, HTTPD_404_NOT_FOUND, "File does not exist");
        return ESP_FAIL;
    }

    fd = fopen(filepath, "r");
    if (!fd)
    {
        ESP_LOGE(TAG, "Failed to read existing file : %s", filepath);
        /* Respond with 500 Internal Server Error */
        httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to read existing file");
        return ESP_FAIL;
    }

    ESP_LOGI(TAG, "Sending file : %s (%ld bytes)...", filename, file_stat.st_size);
    set_content_type_from_file(req, filename);

    /* Retrieve the pointer to scratch buffer for temporary storage */
    char *chunk = ((struct file_server_data *)req->user_ctx)->scratch;
    size_t chunksize;
    do
    {
        /* Read file in chunks into the scratch buffer */
        chunksize = fread(chunk, 1, SCRATCH_BUFSIZE, fd);

        /* Send the buffer contents as HTTP response chunk */
        if (httpd_resp_send_chunk(req, chunk, chunksize) != ESP_OK)
        {

            fclose(fd);
            ESP_LOGE(TAG, "File sending failed!");
            /* Abort sending file */
            httpd_resp_sendstr_chunk(req, NULL);
            /* Respond with 500 Internal Server Error */
            httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to send file");
            return ESP_FAIL;
        }

        /* Keep looping till the whole file is sent */
    } while (chunksize != 0);

    /* Close file after sending complete */
    fclose(fd);
    ESP_LOGI(TAG, "File sending complete");

    /* Respond with an empty chunk to signal HTTP response completion */
    httpd_resp_send_chunk(req, NULL, 0);
    return ESP_OK;
}

/* Function to start the file server */
esp_err_t start_file_server(const char *base_path)
{
    static struct file_server_data *server_data = NULL;
    /* Validate file storage base path */
    if (!base_path || strcmp(base_path, "/spiffs") != 0)
    {
        ESP_LOGE(TAG, "File server presently supports only '/spiffs' as base path");
        return ESP_ERR_INVALID_ARG;
    }
    if (server_data)
    {
        ESP_LOGE(TAG, "File server already started");
        return ESP_ERR_INVALID_STATE;
    }
    /* Allocate memory for server data */
    server_data = calloc(1, sizeof(struct file_server_data));
    if (!server_data)
    {
        ESP_LOGE(TAG, "Failed to allocate memory for server data");
        return ESP_ERR_NO_MEM;
    }
    strlcpy(server_data->base_path, base_path,
            sizeof(server_data->base_path));

    httpd_handle_t server = NULL;
    httpd_config_t config = HTTPD_DEFAULT_CONFIG();

    /* Use the URI wildcard matching function in order to
     * allow the same handler to respond to multiple different
     * target URIs which match the wildcard scheme */
    config.uri_match_fn = httpd_uri_match_wildcard;

    ESP_LOGI(TAG, "Starting HTTP Server");
    if (httpd_start(&server, &config) != ESP_OK)
    {
        ESP_LOGE(TAG, "Failed to start file server!");
        return ESP_FAIL;
    }

    /* URI handler for getting uploaded files */
    httpd_uri_t file_server = {
        .uri = "/*", // Match all URIs of type /path/to/file
        .method = HTTP_GET,
        .handler = webserver_get_handler,
        .user_ctx = server_data // Pass server data as context
    };
    httpd_register_uri_handler(server, &file_server);

    return ESP_OK;
}


void app_main(void)
{
    ESP_ERROR_CHECK(nvs_flash_init());
    tcpip_adapter_init();

    // initialize the wifi event handler
    ESP_ERROR_CHECK(esp_event_loop_init(event_handler, NULL));

    // configure, initialize and start the wifi driver
    wifi_init_config_t wifi_config = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&wifi_config));
    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_APSTA));

    wifi_config_t ap_config = {
        .ap = {
            .ssid = AP_SSID,
            .password = AP_PASSWORD,
            .ssid_hidden = 0, // Access point in ssid sini wifi scanlarda gizlemek için kullanılan parametre
        },
    };
    ap_config.ap.authmode = WIFI_AUTH_OPEN;
    ap_config.ap.ssid_len = 0;
    ap_config.ap.max_connection = AP_MAX_CONN;
    ap_config.ap.channel = AP_CHANNEL;

    ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_AP, &ap_config));

    wifi_config_t sta_config = {
        .sta = {
            .ssid = STA_SSID,
            .password = STA_PASSWORD},
    };
    ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &sta_config));

    ESP_ERROR_CHECK(esp_wifi_start()); //STA
    printf("Starting access point, SSID=%s\n", "Test_Transmitter");
    ESP_ERROR_CHECK(esp_wifi_connect()); //AP
    /* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig.
     * Read "Establishing Wi-Fi or Ethernet Connection" section in
     * examples/protocols/README.md for more information about this function.
     */

    /* Initialize file storage */
    ESP_ERROR_CHECK(init_spiffs());
    ESP_ERROR_CHECK(start_file_server("/spiffs"));
}


ClosedLoop
Posts: 14
Joined: Mon May 16, 2022 8:13 am
Location: Portugal

Re: AJAX in webserver not working properly

Postby ClosedLoop » Tue May 17, 2022 8:52 am

Update: I went back into the simple example and changed the httpd_resp_send funtion. Through that i got AJAX working, the only problem now is the URIs arent recognized and 404 not found error occurs.

User avatar
mbratch
Posts: 298
Joined: Fri Jun 11, 2021 1:51 pm

Re: AJAX in webserver not working properly

Postby mbratch » Tue May 17, 2022 10:47 am

Not much information to go on here but a couple of comments...

It looks like your code is written to process any http path sent. In the 404 response case, are you seeing any console output from your EPS32 program? You have several `printf` statements. Are you seeing the output from these in that case? And what are some examples of paths that work and some that don't work (respond with 404)? That should give you some clues as to what's going on.

ClosedLoop
Posts: 14
Joined: Mon May 16, 2022 8:13 am
Location: Portugal

Re: AJAX in webserver not working properly

Postby ClosedLoop » Wed May 18, 2022 8:55 am

Hello, I was able to make ajax work and solve my issue with the URIs. Nevertheless thank you for your response.

Who is online

Users browsing this forum: No registered users and 115 guests