Page 1 of 1

Bootstrap implementation using SPIFFs

Posted: Mon May 23, 2022 10:18 am
by ClosedLoop
Hello, i am trying to create a webserver, and in my html pages i would like to use bootstrap. I make my handler function, which then gets called when bootstrap.min.css is detected

Code: Select all

esp_err_t bootstrap_css_get_handler(httpd_req_t *req)
{
	extern const uint8_t bootstrap_css_start[] asm("_binary_bootstrap_min_css_start");
	extern const uint8_t 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;
    
    
    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");
}
    
    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 home_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, "/index_html") == 0)
        {
            ESP_LOGI(TAG, "inside index_html");
            if (!(strcmp(parse, "/index_html")))
            {
                return http_resp_dir_html(req, filepath);
            }
            else
                return connect_get_handler(req);
        }
        else if (strcmp(filename, "/bootstrap.min.css") == 0)
                {
                    ESP_LOGI(TAG, "bootcss!");
                    return bootstrap_css_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;
}
}
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;
}
On my html page i have <link rel="stylesheet" href=bootstrap.min.css> and the code detects it and calls the handler function, but on the webpage bootstrap isn't running. I am using the single factory app(large), no OTA partition and i have the CMAKE and component.mk files below.

Code: Select all

set(COMPONENT_REQUIRES )
set(COMPONENT_PRIV_REQUIRES )
idf_component_register(SRCS "main.c" "webserver.c"
    INCLUDE_DIRS "."
    EMBED_FILES    spiffs_image/bootstrap.min.css spiffs_image/bootstrap.min.js)

Code: Select all

COMPONENT_SRCDIRS += . spiffs_image  ..
COMPONENT_EMBED_FILES :=  spiffs_image/logo.png spiffs_image/logo2.png spiffs_image/bootstrap.min.css spiffs_image/bootstrap.min.js
I don't really know why bootstrap isn't working, since the functions get called with no problem. If i try to load images they work fine but bootstrap doesn't seem to.

Re: Bootstrap implementation using SPIFFs

Posted: Mon May 23, 2022 11:12 am
by boarchuz
ClosedLoop wrote:
Mon May 23, 2022 10:18 am

Code: Select all

    const size_t bootcss_size = ((bootstrap_css_end-1) - bootstrap_css_start)
    httpd_resp_set_hdr(req, "Location", "/bootstrap_min_css");
Why (bootstrap_css_end-1)? EMBED_TXTFILES will null-terminate, but EMBED_FILES should embed the file as-is.

What's the intention of the Location header? At best the browser will ignore it, but it could be the problem. Is there a subsequent request to "/bootstrap_min_css"? Do you want that to happen?

If you open "/bootstrap.min.css" with your browser do you get the contents of bootstrap.min.css?

The Network tab of your browser's Developer Tools will show you what's going on.

Re: Bootstrap implementation using SPIFFs

Posted: Mon May 23, 2022 11:17 am
by Craige Hales
Use EMBED_TXTFILES for text files to get a trailing NULL character to terminate the string. Whatever parses the text may depend on the NULL, which is not part of the file's data.

Re: Bootstrap implementation using SPIFFs

Posted: Mon May 23, 2022 2:23 pm
by ClosedLoop
Hello, thank you for the responses. I changed the handler function, took out the -1 and changed the first two variables of the function from uint8_t to const unsigned char. I also changed the CMAKE file to EMBED_TXTFILES but the problem continues. On devtools it says i fail to load the .map file but i dont think that is the issue. Other than that i have no errors.

Re: Bootstrap implementation using SPIFFs

Posted: Mon May 23, 2022 3:17 pm
by Craige Hales
boarchuz last two suggestions are really good.

Re: Bootstrap implementation using SPIFFs

Posted: Mon May 23, 2022 3:40 pm
by ClosedLoop
I managed to get it working, i realized i forgot to set the type so i deleted the httpd_resp_set_hdr(req, "Location", "/bootstrap_min_css"); line and added httpd_resp_set_type(req, "text/css");
Thanks for the help.