From 5ab7c053b9862f28bb7a450449585b9b42786b24 Mon Sep 17 00:00:00 2001 From: Bogdan Date: Mon, 14 Nov 2022 08:25:04 +0200 Subject: [PATCH 1/6] spiffs added, http server prepare to multi fs operations --- CMakeLists.txt | 2 + include/HTTPServer.h | 9 ++ include/spifs.h | 33 +++++ src/FileServer.c | 277 ++++++++++++++++++++++++++++++++++++++ src/HTTPServer.c | 11 +- src/SystemConfiguration.c | 4 +- src/spifs.c | 67 +++++++++ 7 files changed, 394 insertions(+), 9 deletions(-) create mode 100644 include/spifs.h create mode 100644 src/FileServer.c create mode 100644 src/spifs.c diff --git a/CMakeLists.txt b/CMakeLists.txt index b833856..7aa6080 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,7 @@ idf_component_register( SRCS "src/SystemConfiguration.c" "src/romfs.c" + "src/spifs.c" "src/HTTPServer.c" "src/HTTPPrintSystem.c" "src/HTTPPostSystem.c" @@ -20,6 +21,7 @@ idf_component_register( REQUIRES nvs_flash libespfs + spiffs esp_http_server mbedtls lwip diff --git a/include/HTTPServer.h b/include/HTTPServer.h index b538c32..ad7131a 100644 --- a/include/HTTPServer.h +++ b/include/HTTPServer.h @@ -47,6 +47,15 @@ #define MAX_DYNVAR_LENGTH 256 #define MAX_INCFILE_LENGTH 1024 +/* Max length a file path can have on storage */ +#define FILE_PATH_MAX (ESP_VFS_PATH_MAX + CONFIG_SPIFFS_OBJ_NAME_LEN) +#define MAX_FILE_SIZE (200*1024) // 200 KB +#define MAX_FILE_SIZE_STR "200KB" + +/* Scratch buffer size */ +#define SCRATCH_BUFSIZE 4096 +#define AUTH_DATA_MAX_LENGTH 16 + #define HTTP_SERVER_DEBUG_LEVEL 0 typedef enum diff --git a/include/spifs.h b/include/spifs.h new file mode 100644 index 0000000..20d2c49 --- /dev/null +++ b/include/spifs.h @@ -0,0 +1,33 @@ + /*! Copyright 2022 Bogdan Pilyugin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * \file spifs.h + * \version 1.0 + * \date 2022-11-13 + * \author Bogdan Pilyugin + * \brief + * \details + * \copyright Apache License, Version 2.0 + */ + +#ifndef COMPONENTS_WEBGUIAPP_INCLUDE_SPIFS_H_ +#define COMPONENTS_WEBGUIAPP_INCLUDE_SPIFS_H_ + +#include "esp_err.h" + +esp_err_t init_spi_fs(const char *root); + + + +#endif /* COMPONENTS_WEBGUIAPP_INCLUDE_SPIFS_H_ */ diff --git a/src/FileServer.c b/src/FileServer.c new file mode 100644 index 0000000..3213230 --- /dev/null +++ b/src/FileServer.c @@ -0,0 +1,277 @@ + /*! Copyright 2022 Bogdan Pilyugin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * \file FileServer.c + * \version 1.0 + * \date 2022-11-13 + * \author Bogdan Pilyugin + * \brief + * \details + * \copyright Apache License, Version 2.0 + */ + + +#include "HTTPServer.h" + +static const char *TAG = "HTTPServer"; + +/* Send HTTP response with a run-time generated html consisting of + * a list of all files and folders under the requested path. + * In case of SPIFFS this returns empty list when path is any + * string other than '/', since SPIFFS doesn't support directories */ +static esp_err_t http_resp_dir_html(httpd_req_t *req, const char *dirpath) +{ + char entrypath[FILE_PATH_MAX]; + char entrysize[16]; + const char *entrytype; + + struct dirent *entry; + struct stat entry_stat; + + DIR *dir = opendir(dirpath); + const size_t dirpath_len = strlen(dirpath); + + /* Retrieve the base path of file storage to construct the full path */ + strlcpy(entrypath, dirpath, sizeof(entrypath)); + + if (!dir) { + ESP_LOGE(TAG, "Failed to stat dir : %s", dirpath); + /* Respond with 404 Not Found */ + httpd_resp_send_err(req, HTTPD_404_NOT_FOUND, "Directory does not exist"); + return ESP_FAIL; + } + + /* Send HTML file header */ + httpd_resp_sendstr_chunk(req, ""); + + /* Get handle to embedded file upload script */ + extern const unsigned char upload_script_start[] asm("_binary_upload_script_html_start"); + extern const unsigned char upload_script_end[] asm("_binary_upload_script_html_end"); + const size_t upload_script_size = (upload_script_end - upload_script_start); + + /* Add file upload form and script which on execution sends a POST request to /upload */ + httpd_resp_send_chunk(req, (const char *)upload_script_start, upload_script_size); + + /* Send file-list table definition and column labels */ + httpd_resp_sendstr_chunk(req, + "" + "" + "" + ""); + + /* Iterate over all files / folders and fetch their names and sizes */ + while ((entry = readdir(dir)) != NULL) { + entrytype = (entry->d_type == DT_DIR ? "directory" : "file"); + + strlcpy(entrypath + dirpath_len, entry->d_name, sizeof(entrypath) - dirpath_len); + if (stat(entrypath, &entry_stat) == -1) { + ESP_LOGE(TAG, "Failed to stat %s : %s", entrytype, entry->d_name); + continue; + } + sprintf(entrysize, "%ld", entry_stat.st_size); + ESP_LOGI(TAG, "Found %s : %s (%s bytes)", entrytype, entry->d_name, entrysize); + + /* Send chunk of HTML file containing table entries with file name and size */ + httpd_resp_sendstr_chunk(req, "\n"); + } + closedir(dir); + + /* Finish the file list table */ + httpd_resp_sendstr_chunk(req, "
NameTypeSize (Bytes)Delete
uri); + httpd_resp_sendstr_chunk(req, entry->d_name); + if (entry->d_type == DT_DIR) { + httpd_resp_sendstr_chunk(req, "/"); + } + httpd_resp_sendstr_chunk(req, "\">"); + httpd_resp_sendstr_chunk(req, entry->d_name); + httpd_resp_sendstr_chunk(req, ""); + httpd_resp_sendstr_chunk(req, entrytype); + httpd_resp_sendstr_chunk(req, ""); + httpd_resp_sendstr_chunk(req, entrysize); + httpd_resp_sendstr_chunk(req, ""); + httpd_resp_sendstr_chunk(req, "
uri); + httpd_resp_sendstr_chunk(req, entry->d_name); + httpd_resp_sendstr_chunk(req, "\">
"); + httpd_resp_sendstr_chunk(req, "
"); + + /* Send remaining chunk of HTML file to complete it */ + httpd_resp_sendstr_chunk(req, ""); + + /* Send empty chunk to signal HTTP response completion */ + httpd_resp_sendstr_chunk(req, NULL); + return ESP_OK; +} + +/* Handler to upload a file onto the server */ +static esp_err_t upload_post_handler(httpd_req_t *req) +{ + char filepath[FILE_PATH_MAX]; + FILE *fd = NULL; + struct stat file_stat; + + /* Skip leading "/upload" from URI to get filename */ + /* Note sizeof() counts NULL termination hence the -1 */ + const char *filename = get_path_from_uri(filepath, ((struct file_server_data *)req->user_ctx)->base_path, + req->uri + sizeof("/upload") - 1, sizeof(filepath)); + if (!filename) { + /* Respond with 500 Internal Server Error */ + httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Filename too long"); + return ESP_FAIL; + } + + /* Filename cannot have a trailing '/' */ + if (filename[strlen(filename) - 1] == '/') { + ESP_LOGE(TAG, "Invalid filename : %s", filename); + httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Invalid filename"); + return ESP_FAIL; + } + + if (stat(filepath, &file_stat) == 0) { + ESP_LOGE(TAG, "File already exists : %s", filepath); + /* Respond with 400 Bad Request */ + httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "File already exists"); + return ESP_FAIL; + } + + /* File cannot be larger than a limit */ + if (req->content_len > MAX_FILE_SIZE) { + ESP_LOGE(TAG, "File too large : %d bytes", req->content_len); + /* Respond with 400 Bad Request */ + httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, + "File size must be less than " + MAX_FILE_SIZE_STR "!"); + /* Return failure to close underlying connection else the + * incoming file content will keep the socket busy */ + return ESP_FAIL; + } + + fd = fopen(filepath, "w"); + if (!fd) { + ESP_LOGE(TAG, "Failed to create file : %s", filepath); + /* Respond with 500 Internal Server Error */ + httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to create file"); + return ESP_FAIL; + } + + ESP_LOGI(TAG, "Receiving file : %s...", filename); + + /* Retrieve the pointer to scratch buffer for temporary storage */ + char *buf = ((struct file_server_data *)req->user_ctx)->scratch; + int received; + + /* Content length of the request gives + * the size of the file being uploaded */ + int remaining = req->content_len; + + while (remaining > 0) { + + ESP_LOGI(TAG, "Remaining size : %d", remaining); + /* Receive the file part by part into a buffer */ + if ((received = httpd_req_recv(req, buf, MIN(remaining, SCRATCH_BUFSIZE))) <= 0) { + if (received == HTTPD_SOCK_ERR_TIMEOUT) { + /* Retry if timeout occurred */ + continue; + } + + /* In case of unrecoverable error, + * close and delete the unfinished file*/ + fclose(fd); + unlink(filepath); + + ESP_LOGE(TAG, "File reception failed!"); + /* Respond with 500 Internal Server Error */ + httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to receive file"); + return ESP_FAIL; + } + + /* Write buffer content to file on storage */ + if (received && (received != fwrite(buf, 1, received, fd))) { + /* Couldn't write everything to file! + * Storage may be full? */ + fclose(fd); + unlink(filepath); + + ESP_LOGE(TAG, "File write failed!"); + /* Respond with 500 Internal Server Error */ + httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to write file to storage"); + return ESP_FAIL; + } + + /* Keep track of remaining size of + * the file left to be uploaded */ + remaining -= received; + } + + /* Close file upon upload completion */ + fclose(fd); + ESP_LOGI(TAG, "File reception complete"); + + /* Redirect onto root to see the updated file list */ + httpd_resp_set_status(req, "303 See Other"); + httpd_resp_set_hdr(req, "Location", "/"); +#ifdef CONFIG_EXAMPLE_HTTPD_CONN_CLOSE_HEADER + httpd_resp_set_hdr(req, "Connection", "close"); +#endif + httpd_resp_sendstr(req, "File uploaded successfully"); + return ESP_OK; +} + +/* Handler to delete a file from the server */ +static esp_err_t delete_post_handler(httpd_req_t *req) +{ + char filepath[FILE_PATH_MAX]; + struct stat file_stat; + + /* Skip leading "/delete" from URI to get filename */ + /* Note sizeof() counts NULL termination hence the -1 */ + const char *filename = get_path_from_uri(filepath, ((struct file_server_data *)req->user_ctx)->base_path, + req->uri + sizeof("/delete") - 1, sizeof(filepath)); + if (!filename) { + /* Respond with 500 Internal Server Error */ + httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Filename too long"); + return ESP_FAIL; + } + + /* Filename cannot have a trailing '/' */ + if (filename[strlen(filename) - 1] == '/') { + ESP_LOGE(TAG, "Invalid filename : %s", filename); + httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Invalid filename"); + return ESP_FAIL; + } + + if (stat(filepath, &file_stat) == -1) { + ESP_LOGE(TAG, "File does not exist : %s", filename); + /* Respond with 400 Bad Request */ + httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "File does not exist"); + return ESP_FAIL; + } + + ESP_LOGI(TAG, "Deleting file : %s", filename); + /* Delete file */ + unlink(filepath); + + /* Redirect onto root to see the updated file list */ + httpd_resp_set_status(req, "303 See Other"); + httpd_resp_set_hdr(req, "Location", "/"); +#ifdef CONFIG_EXAMPLE_HTTPD_CONN_CLOSE_HEADER + httpd_resp_set_hdr(req, "Connection", "close"); +#endif + httpd_resp_sendstr(req, "File deleted successfully"); + return ESP_OK; +} + + + diff --git a/src/HTTPServer.c b/src/HTTPServer.c index d617898..582d8e0 100644 --- a/src/HTTPServer.c +++ b/src/HTTPServer.c @@ -29,15 +29,7 @@ const char GZIP_SIGN[] = { 0x1f, 0x8b, 0x08 }; static esp_err_t GETHandler(httpd_req_t *req); static esp_err_t CheckAuth(httpd_req_t *req); -/* Max length a file path can have on storage */ -#define FILE_PATH_MAX (ESP_VFS_PATH_MAX + CONFIG_SPIFFS_OBJ_NAME_LEN) -/* Max size of an individual file. Make sure this - * value is same as that set in upload_script.html */ - -/* Scratch buffer size */ -#define SCRATCH_BUFSIZE 4096 -#define AUTH_DATA_MAX_LENGTH 16 struct file_server_data { @@ -104,6 +96,7 @@ static esp_err_t CheckAuth(httpd_req_t *req) #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) @@ -460,6 +453,8 @@ file_send_error: return ESP_FAIL; } + + static httpd_handle_t start_webserver(void) { httpd_handle_t server = NULL; diff --git a/src/SystemConfiguration.c b/src/SystemConfiguration.c index 42c1fb9..18a2479 100644 --- a/src/SystemConfiguration.c +++ b/src/SystemConfiguration.c @@ -36,6 +36,7 @@ #include "SystemConfiguration.h" #include "romfs.h" +#include "spifs.h" #include "NetTransport.h" #include "Helpers.h" #include "HTTPServer.h" @@ -102,8 +103,9 @@ esp_err_t WebGuiAppInit(void) } ESP_ERROR_CHECK(InitSysConfig()); - //init rom file system + //init file systems init_rom_fs("/espfs"); + init_spi_fs("/data"); #if CONFIG_WEBGUIAPP_GPRS_ENABLE diff --git a/src/spifs.c b/src/spifs.c new file mode 100644 index 0000000..0e114ca --- /dev/null +++ b/src/spifs.c @@ -0,0 +1,67 @@ + /*! Copyright 2022 Bogdan Pilyugin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * \file spifs.c + * \version 1.0 + * \date 2022-11-13 + * \author Bogdan Pilyugin + * \brief + * \details + * \copyright Apache License, Version 2.0 + */ + +#include "spifs.h" +#include +#include +#include "esp_log.h" +#include "esp_err.h" +#include "sdkconfig.h" +#include "esp_spiffs.h" + + +static const char *TAG = "SPIFS"; + +esp_err_t init_spi_fs(const char *root) +{ + ESP_LOGI(TAG, "Initializing SPIFFS"); + + esp_vfs_spiffs_conf_t conf = { + .base_path = root, + .partition_label = NULL, + .max_files = 5, // This sets the maximum number of files that can be open at the same time + .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 ret; + } + + 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 ret; + } + + ESP_LOGI(TAG, "Partition size: total: %d, used: %d", total, used); + return ESP_OK; +} From af7911277de0e052748be19340152534c0a2ba66 Mon Sep 17 00:00:00 2001 From: Bogdan Date: Tue, 15 Nov 2022 13:49:27 +0200 Subject: [PATCH 2/6] added handlers to file server --- CMakeLists.txt | 3 ++ include/HTTPServer.h | 17 ++++++++++ src/FileServer.c | 48 ++++++++++++++++++++++---- src/HTTPServer.c | 29 ++++++++++------ upload_script.html | 80 ++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 161 insertions(+), 16 deletions(-) create mode 100644 upload_script.html diff --git a/CMakeLists.txt b/CMakeLists.txt index 7aa6080..c8a30b3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,6 +3,7 @@ idf_component_register( "src/romfs.c" "src/spifs.c" "src/HTTPServer.c" + "src/FileServer.c" "src/HTTPPrintSystem.c" "src/HTTPPostSystem.c" "src/Helpers.c" @@ -18,6 +19,8 @@ idf_component_register( INCLUDE_DIRS "." "include" "src" + + EMBED_FILES "upload_script.html" REQUIRES nvs_flash libespfs diff --git a/include/HTTPServer.h b/include/HTTPServer.h index ad7131a..2edfcab 100644 --- a/include/HTTPServer.h +++ b/include/HTTPServer.h @@ -67,6 +67,18 @@ typedef enum HTTP_IO_DONE_NOREFRESH } HTTP_IO_RESULT; + + +struct file_server_data +{ + /* Base path of file storage */ + char base_path[ESP_VFS_PATH_MAX + 1]; + char base_path2[ESP_VFS_PATH_MAX + 1]; + /* Scratch buffer for temporary storage during file transfer */ + char scratch[SCRATCH_BUFSIZE]; +/* Pointer to external POST handler*/ +}; + typedef struct { const char tag[16]; @@ -81,4 +93,9 @@ esp_err_t start_file_server(void); HTTP_IO_RESULT HTTPPostApp(httpd_req_t *req, const char *filename, char *PostData); int HTTPPrint(httpd_req_t *req, char* buf, char* var); + +esp_err_t http_resp_dir_html(httpd_req_t *req, const char *dirpath); +esp_err_t upload_post_handler(httpd_req_t *req); +esp_err_t delete_post_handler(httpd_req_t *req); + #endif /* COMPONENTS_WEB_GUI_APPLICATION_INCLUDE_HTTPSERVER_H_ */ diff --git a/src/FileServer.c b/src/FileServer.c index 3213230..4fe25c6 100644 --- a/src/FileServer.c +++ b/src/FileServer.c @@ -24,13 +24,47 @@ #include "HTTPServer.h" -static const char *TAG = "HTTPServer"; +static const char *TAG = "FileServer"; + +/* 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; +} /* Send HTTP response with a run-time generated html consisting of * a list of all files and folders under the requested path. * In case of SPIFFS this returns empty list when path is any * string other than '/', since SPIFFS doesn't support directories */ -static esp_err_t http_resp_dir_html(httpd_req_t *req, const char *dirpath) +esp_err_t http_resp_dir_html(httpd_req_t *req, const char *dirpath) { char entrypath[FILE_PATH_MAX]; char entrysize[16]; @@ -52,6 +86,8 @@ static esp_err_t http_resp_dir_html(httpd_req_t *req, const char *dirpath) return ESP_FAIL; } + + /* Send HTML file header */ httpd_resp_sendstr_chunk(req, ""); @@ -116,7 +152,7 @@ static esp_err_t http_resp_dir_html(httpd_req_t *req, const char *dirpath) } /* Handler to upload a file onto the server */ -static esp_err_t upload_post_handler(httpd_req_t *req) +esp_err_t upload_post_handler(httpd_req_t *req) { char filepath[FILE_PATH_MAX]; FILE *fd = NULL; @@ -124,7 +160,7 @@ static esp_err_t upload_post_handler(httpd_req_t *req) /* Skip leading "/upload" from URI to get filename */ /* Note sizeof() counts NULL termination hence the -1 */ - const char *filename = get_path_from_uri(filepath, ((struct file_server_data *)req->user_ctx)->base_path, + const char *filename = get_path_from_uri(filepath, ((struct file_server_data *)req->user_ctx)->base_path2, req->uri + sizeof("/upload") - 1, sizeof(filepath)); if (!filename) { /* Respond with 500 Internal Server Error */ @@ -230,14 +266,14 @@ static esp_err_t upload_post_handler(httpd_req_t *req) } /* Handler to delete a file from the server */ -static esp_err_t delete_post_handler(httpd_req_t *req) +esp_err_t delete_post_handler(httpd_req_t *req) { char filepath[FILE_PATH_MAX]; struct stat file_stat; /* Skip leading "/delete" from URI to get filename */ /* Note sizeof() counts NULL termination hence the -1 */ - const char *filename = get_path_from_uri(filepath, ((struct file_server_data *)req->user_ctx)->base_path, + const char *filename = get_path_from_uri(filepath, ((struct file_server_data *)req->user_ctx)->base_path2, req->uri + sizeof("/delete") - 1, sizeof(filepath)); if (!filename) { /* Respond with 500 Internal Server Error */ diff --git a/src/HTTPServer.c b/src/HTTPServer.c index 582d8e0..9c90587 100644 --- a/src/HTTPServer.c +++ b/src/HTTPServer.c @@ -29,16 +29,6 @@ const char GZIP_SIGN[] = { 0x1f, 0x8b, 0x08 }; static esp_err_t GETHandler(httpd_req_t *req); static esp_err_t CheckAuth(httpd_req_t *req); - - -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]; -/* Pointer to external POST handler*/ -}; struct file_server_data *server_data = NULL; httpd_handle_t server = NULL; static const char *TAG = "HTTPServer"; @@ -183,6 +173,7 @@ static esp_err_t POSTHandler(httpd_req_t *req) #if HTTP_SERVER_DEBUG_LEVEL > 0 ESP_LOGI(TAG, "POST request handle"); #endif + char *buf = ((struct file_server_data*) req->user_ctx)->scratch; int received; int remaining = req->content_len; @@ -215,10 +206,20 @@ static esp_err_t POSTHandler(httpd_req_t *req) if (received) { char filepath[FILE_PATH_MAX]; + const char *filename = get_path_from_uri(filepath, ((struct file_server_data*) req->user_ctx)->base_path, req->uri, sizeof(filepath)); + ESP_LOGW(TAG, "filepath %s", filepath); + + filename = get_path_from_uri(filepath, + ((struct file_server_data*) req->user_ctx)->base_path, + req->uri, + sizeof(filepath)); + + + if (!memcmp(filename, "/api", 4)) { @@ -295,6 +296,13 @@ static esp_err_t GETHandler(httpd_req_t *req) return ESP_OK; } + + if (!strcmp(filename, "/files.html")) + { + return http_resp_dir_html(req, "/data"); + } + + //check auth for all files except status.json if (strcmp(filename, "/status.json")) { @@ -528,6 +536,7 @@ esp_err_t start_file_server(void) return ESP_ERR_NO_MEM; } strlcpy(server_data->base_path, "/", sizeof("/")); + strlcpy(server_data->base_path2, "/data", sizeof("/data")); server = start_webserver(); return ESP_OK; } diff --git a/upload_script.html b/upload_script.html new file mode 100644 index 0000000..5513ee8 --- /dev/null +++ b/upload_script.html @@ -0,0 +1,80 @@ + + + +
+

ESP32 File Server

+
+ + + + + + + + + + +
+ + + +
+ + + + + +
+
+ From c1a8cadda50d4cb3430c6db64f1d27d246e59dd6 Mon Sep 17 00:00:00 2001 From: Bogdan Pilyugin Date: Tue, 15 Nov 2022 16:19:46 +0200 Subject: [PATCH 3/6] debug multi fs http server --- include/HTTPServer.h | 4 +- src/FileServer.c | 229 ++++++++++++++++++++++++++++++++++++------- src/HTTPServer.c | 23 +++-- upload_script.html | 2 +- 4 files changed, 209 insertions(+), 49 deletions(-) diff --git a/include/HTTPServer.h b/include/HTTPServer.h index 2edfcab..6257e16 100644 --- a/include/HTTPServer.h +++ b/include/HTTPServer.h @@ -93,9 +93,9 @@ esp_err_t start_file_server(void); HTTP_IO_RESULT HTTPPostApp(httpd_req_t *req, const char *filename, char *PostData); int HTTPPrint(httpd_req_t *req, char* buf, char* var); - -esp_err_t http_resp_dir_html(httpd_req_t *req, const char *dirpath); +esp_err_t download_get_handler(httpd_req_t *req); esp_err_t upload_post_handler(httpd_req_t *req); esp_err_t delete_post_handler(httpd_req_t *req); + #endif /* COMPONENTS_WEB_GUI_APPLICATION_INCLUDE_HTTPSERVER_H_ */ diff --git a/src/FileServer.c b/src/FileServer.c index 4fe25c6..6984c56 100644 --- a/src/FileServer.c +++ b/src/FileServer.c @@ -1,4 +1,4 @@ - /*! Copyright 2022 Bogdan Pilyugin +/*! Copyright 2022 Bogdan Pilyugin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,11 +21,60 @@ * \copyright Apache License, Version 2.0 */ - #include "HTTPServer.h" static const char *TAG = "FileServer"; +/* Handler to redirect incoming GET request for /index.html to / + * This can be overridden by uploading file with same name */ +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", "/"); + httpd_resp_send(req, NULL, 0); // Response body can be empty + 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, ".jpeg")) + { + return httpd_resp_set_type(req, "image/jpeg"); + } + else if (IS_FILE_EXT(filename, ".png")) + { + return httpd_resp_set_type(req, "image/png"); + } + else if (IS_FILE_EXT(filename, ".ico")) + { + return httpd_resp_set_type(req, "image/x-icon"); + } + else if (IS_FILE_EXT(filename, ".css")) + { + return httpd_resp_set_type(req, "text/css"); + } + else if (IS_FILE_EXT(filename, ".woff2")) + { + return httpd_resp_set_type(req, "font/woff2"); + } + /* 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, @@ -64,7 +113,7 @@ static const char* get_path_from_uri(char *dest, const char *base_path, * a list of all files and folders under the requested path. * In case of SPIFFS this returns empty list when path is any * string other than '/', since SPIFFS doesn't support directories */ -esp_err_t http_resp_dir_html(httpd_req_t *req, const char *dirpath) +static esp_err_t http_resp_dir_html(httpd_req_t *req, const char *dirpath) { char entrypath[FILE_PATH_MAX]; char entrysize[16]; @@ -79,39 +128,41 @@ esp_err_t http_resp_dir_html(httpd_req_t *req, const char *dirpath) /* Retrieve the base path of file storage to construct the full path */ strlcpy(entrypath, dirpath, sizeof(entrypath)); - if (!dir) { + if (!dir) + { ESP_LOGE(TAG, "Failed to stat dir : %s", dirpath); /* Respond with 404 Not Found */ - httpd_resp_send_err(req, HTTPD_404_NOT_FOUND, "Directory does not exist"); + httpd_resp_send_err(req, HTTPD_404_NOT_FOUND, "Files directory does not exist"); return ESP_FAIL; } - - /* Send HTML file header */ httpd_resp_sendstr_chunk(req, ""); /* Get handle to embedded file upload script */ extern const unsigned char upload_script_start[] asm("_binary_upload_script_html_start"); - extern const unsigned char upload_script_end[] asm("_binary_upload_script_html_end"); + extern const unsigned char upload_script_end[] asm("_binary_upload_script_html_end"); const size_t upload_script_size = (upload_script_end - upload_script_start); /* Add file upload form and script which on execution sends a POST request to /upload */ - httpd_resp_send_chunk(req, (const char *)upload_script_start, upload_script_size); + httpd_resp_send_chunk(req, (const char*) upload_script_start, upload_script_size); /* Send file-list table definition and column labels */ - httpd_resp_sendstr_chunk(req, - "" - "" - "" - ""); + httpd_resp_sendstr_chunk( + req, + "
NameTypeSize (Bytes)Delete
" + "" + "" + ""); /* Iterate over all files / folders and fetch their names and sizes */ - while ((entry = readdir(dir)) != NULL) { + while ((entry = readdir(dir)) != NULL) + { entrytype = (entry->d_type == DT_DIR ? "directory" : "file"); strlcpy(entrypath + dirpath_len, entry->d_name, sizeof(entrypath) - dirpath_len); - if (stat(entrypath, &entry_stat) == -1) { + if (stat(entrypath, &entry_stat) == -1) + { ESP_LOGE(TAG, "Failed to stat %s : %s", entrytype, entry->d_name); continue; } @@ -122,7 +173,8 @@ esp_err_t http_resp_dir_html(httpd_req_t *req, const char *dirpath) httpd_resp_sendstr_chunk(req, "
NameTypeSize (Bytes)Delete
uri); httpd_resp_sendstr_chunk(req, entry->d_name); - if (entry->d_type == DT_DIR) { + if (entry->d_type == DT_DIR) + { httpd_resp_sendstr_chunk(req, "/"); } httpd_resp_sendstr_chunk(req, "\">"); @@ -151,6 +203,99 @@ esp_err_t http_resp_dir_html(httpd_req_t *req, const char *dirpath) return ESP_OK; } +/* Handler to download a file kept on the server */ +esp_err_t download_get_handler(httpd_req_t *req) +{ + char filepath[FILE_PATH_MAX]; + FILE *fd = NULL; + struct stat file_stat; + + const char *filename = get_path_from_uri(filepath, ((struct file_server_data*) req->user_ctx)->base_path, + req->uri, + sizeof(filepath)); + 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; + } + + /* If name has trailing '/', respond with directory contents */ + if (filename[strlen(filename) - 1] == '/') + { + return http_resp_dir_html(req, "/data"); + } + + 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, "/") == 0) + { + return index_html_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); + + if (chunksize > 0) + { + /* 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 */ +#ifdef CONFIG_EXAMPLE_HTTPD_CONN_CLOSE_HEADER + httpd_resp_set_hdr(req, "Connection", "close"); +#endif + httpd_resp_send_chunk(req, NULL, 0); + return ESP_OK; +} + /* Handler to upload a file onto the server */ esp_err_t upload_post_handler(httpd_req_t *req) { @@ -160,22 +305,26 @@ esp_err_t upload_post_handler(httpd_req_t *req) /* Skip leading "/upload" from URI to get filename */ /* Note sizeof() counts NULL termination hence the -1 */ - const char *filename = get_path_from_uri(filepath, ((struct file_server_data *)req->user_ctx)->base_path2, - req->uri + sizeof("/upload") - 1, sizeof(filepath)); - if (!filename) { + const char *filename = get_path_from_uri(filepath, ((struct file_server_data*) req->user_ctx)->base_path, + req->uri + sizeof("/files/upload") - 1, + sizeof(filepath)); + if (!filename) + { /* Respond with 500 Internal Server Error */ httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Filename too long"); return ESP_FAIL; } /* Filename cannot have a trailing '/' */ - if (filename[strlen(filename) - 1] == '/') { + if (filename[strlen(filename) - 1] == '/') + { ESP_LOGE(TAG, "Invalid filename : %s", filename); httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Invalid filename"); return ESP_FAIL; } - if (stat(filepath, &file_stat) == 0) { + if (stat(filepath, &file_stat) == 0) + { ESP_LOGE(TAG, "File already exists : %s", filepath); /* Respond with 400 Bad Request */ httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "File already exists"); @@ -183,7 +332,8 @@ esp_err_t upload_post_handler(httpd_req_t *req) } /* File cannot be larger than a limit */ - if (req->content_len > MAX_FILE_SIZE) { + if (req->content_len > MAX_FILE_SIZE) + { ESP_LOGE(TAG, "File too large : %d bytes", req->content_len); /* Respond with 400 Bad Request */ httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, @@ -195,7 +345,8 @@ esp_err_t upload_post_handler(httpd_req_t *req) } fd = fopen(filepath, "w"); - if (!fd) { + if (!fd) + { ESP_LOGE(TAG, "Failed to create file : %s", filepath); /* Respond with 500 Internal Server Error */ httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to create file"); @@ -205,19 +356,22 @@ esp_err_t upload_post_handler(httpd_req_t *req) ESP_LOGI(TAG, "Receiving file : %s...", filename); /* Retrieve the pointer to scratch buffer for temporary storage */ - char *buf = ((struct file_server_data *)req->user_ctx)->scratch; + char *buf = ((struct file_server_data*) req->user_ctx)->scratch; int received; /* Content length of the request gives * the size of the file being uploaded */ int remaining = req->content_len; - while (remaining > 0) { + while (remaining > 0) + { ESP_LOGI(TAG, "Remaining size : %d", remaining); /* Receive the file part by part into a buffer */ - if ((received = httpd_req_recv(req, buf, MIN(remaining, SCRATCH_BUFSIZE))) <= 0) { - if (received == HTTPD_SOCK_ERR_TIMEOUT) { + if ((received = httpd_req_recv(req, buf, MIN(remaining, SCRATCH_BUFSIZE))) <= 0) + { + if (received == HTTPD_SOCK_ERR_TIMEOUT) + { /* Retry if timeout occurred */ continue; } @@ -234,7 +388,8 @@ esp_err_t upload_post_handler(httpd_req_t *req) } /* Write buffer content to file on storage */ - if (received && (received != fwrite(buf, 1, received, fd))) { + if (received && (received != fwrite(buf, 1, received, fd))) + { /* Couldn't write everything to file! * Storage may be full? */ fclose(fd); @@ -273,22 +428,26 @@ esp_err_t delete_post_handler(httpd_req_t *req) /* Skip leading "/delete" from URI to get filename */ /* Note sizeof() counts NULL termination hence the -1 */ - const char *filename = get_path_from_uri(filepath, ((struct file_server_data *)req->user_ctx)->base_path2, - req->uri + sizeof("/delete") - 1, sizeof(filepath)); - if (!filename) { + const char *filename = get_path_from_uri(filepath, ((struct file_server_data*) req->user_ctx)->base_path2, + req->uri + sizeof("/delete") - 1, + sizeof(filepath)); + if (!filename) + { /* Respond with 500 Internal Server Error */ httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Filename too long"); return ESP_FAIL; } /* Filename cannot have a trailing '/' */ - if (filename[strlen(filename) - 1] == '/') { + if (filename[strlen(filename) - 1] == '/') + { ESP_LOGE(TAG, "Invalid filename : %s", filename); httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Invalid filename"); return ESP_FAIL; } - if (stat(filepath, &file_stat) == -1) { + if (stat(filepath, &file_stat) == -1) + { ESP_LOGE(TAG, "File does not exist : %s", filename); /* Respond with 400 Bad Request */ httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "File does not exist"); @@ -309,5 +468,3 @@ esp_err_t delete_post_handler(httpd_req_t *req) return ESP_OK; } - - diff --git a/src/HTTPServer.c b/src/HTTPServer.c index 9c90587..1ebcdd9 100644 --- a/src/HTTPServer.c +++ b/src/HTTPServer.c @@ -211,7 +211,13 @@ static esp_err_t POSTHandler(httpd_req_t *req) ((struct file_server_data*) req->user_ctx)->base_path, req->uri, sizeof(filepath)); - ESP_LOGW(TAG, "filepath %s", filepath); + + ESP_LOGW(TAG, "URI %s", req->uri); + if (memmem(filename, strlen(filename), "/files/upload/", sizeof("/files/upload/"))) + { + ESP_LOGW(TAG, "filepath %s", filepath); + return upload_post_handler(req); + } filename = get_path_from_uri(filepath, ((struct file_server_data*) req->user_ctx)->base_path, @@ -219,8 +225,6 @@ static esp_err_t POSTHandler(httpd_req_t *req) sizeof(filepath)); - - if (!memcmp(filename, "/api", 4)) { return RestApiHandler(req); @@ -284,6 +288,12 @@ static esp_err_t GETHandler(httpd_req_t *req) return ESP_FAIL; } + ESP_LOGW(TAG, "URI %s", req->uri); + if (!strcmp(filename, "/files/")) + { + return download_get_handler(req); + } + /* Redirect request to /index.html */ if (filename[strlen(filename) - 1] == '/') { @@ -296,13 +306,6 @@ static esp_err_t GETHandler(httpd_req_t *req) return ESP_OK; } - - if (!strcmp(filename, "/files.html")) - { - return http_resp_dir_html(req, "/data"); - } - - //check auth for all files except status.json if (strcmp(filename, "/status.json")) { diff --git a/upload_script.html b/upload_script.html index 5513ee8..9efe632 100644 --- a/upload_script.html +++ b/upload_script.html @@ -33,7 +33,7 @@ function setpath() { } function upload() { var filePath = document.getElementById("filepath").value; - var upload_path = "/upload/" + filePath; + var upload_path = "files/upload/" + filePath; var fileInput = document.getElementById("newfile").files; /* Max size of an individual file. Make sure this From be81499a00d687c4718195c73e2d35d87202bbfc Mon Sep 17 00:00:00 2001 From: Bogdan Date: Wed, 16 Nov 2022 08:26:09 +0200 Subject: [PATCH 4/6] upload script edited --- upload_script.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/upload_script.html b/upload_script.html index 9efe632..608f1ee 100644 --- a/upload_script.html +++ b/upload_script.html @@ -33,7 +33,7 @@ function setpath() { } function upload() { var filePath = document.getElementById("filepath").value; - var upload_path = "files/upload/" + filePath; + var upload_path = "upload/" + filePath; var fileInput = document.getElementById("newfile").files; /* Max size of an individual file. Make sure this From 00a60102b4f5811719e328abb52c06a683c10ee6 Mon Sep 17 00:00:00 2001 From: Bogdan Pilyugin Date: Wed, 16 Nov 2022 16:01:45 +0200 Subject: [PATCH 5/6] intermediate save of file server debug --- src/FileServer.c | 14 +++++++++++--- src/HTTPServer.c | 45 +++++++++++++++++++++------------------------ 2 files changed, 32 insertions(+), 27 deletions(-) diff --git a/src/FileServer.c b/src/FileServer.c index 6984c56..fc4465f 100644 --- a/src/FileServer.c +++ b/src/FileServer.c @@ -210,9 +210,13 @@ esp_err_t download_get_handler(httpd_req_t *req) FILE *fd = NULL; struct stat file_stat; - const char *filename = get_path_from_uri(filepath, ((struct file_server_data*) req->user_ctx)->base_path, + const char *filename = get_path_from_uri(filepath, ((struct file_server_data*) req->user_ctx)->base_path2, req->uri, sizeof(filepath)); + ESP_LOGW(TAG, "FILE_GET_URI %s", req->uri); + ESP_LOGW(TAG, "FILE_GET_FILEPATH %s", filepath); + ESP_LOGW(TAG, "FILE_GET_FILENAME %s", filename); + if (!filename) { ESP_LOGE(TAG, "Filename is too long"); @@ -224,7 +228,7 @@ esp_err_t download_get_handler(httpd_req_t *req) /* If name has trailing '/', respond with directory contents */ if (filename[strlen(filename) - 1] == '/') { - return http_resp_dir_html(req, "/data"); + return http_resp_dir_html(req, "/data/"); } if (stat(filepath, &file_stat) == -1) @@ -305,9 +309,13 @@ esp_err_t upload_post_handler(httpd_req_t *req) /* Skip leading "/upload" from URI to get filename */ /* Note sizeof() counts NULL termination hence the -1 */ - const char *filename = get_path_from_uri(filepath, ((struct file_server_data*) req->user_ctx)->base_path, + const char *filename = get_path_from_uri(filepath, ((struct file_server_data*) req->user_ctx)->base_path2, req->uri + sizeof("/files/upload") - 1, sizeof(filepath)); + ESP_LOGW(TAG, "FILE_POST_URI %s", req->uri); + ESP_LOGW(TAG, "FILE_POST_FILEPATH %s", filepath); + ESP_LOGW(TAG, "FILE_POST_FILENAME %s", filename); + if (!filename) { /* Respond with 500 Internal Server Error */ diff --git a/src/HTTPServer.c b/src/HTTPServer.c index 1ebcdd9..85d558f 100644 --- a/src/HTTPServer.c +++ b/src/HTTPServer.c @@ -86,7 +86,6 @@ static esp_err_t CheckAuth(httpd_req_t *req) #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) @@ -174,6 +173,11 @@ static esp_err_t POSTHandler(httpd_req_t *req) ESP_LOGI(TAG, "POST request handle"); #endif + if (true) + { + return upload_post_handler(req); + } + char *buf = ((struct file_server_data*) req->user_ctx)->scratch; int received; int remaining = req->content_len; @@ -206,24 +210,12 @@ static esp_err_t POSTHandler(httpd_req_t *req) if (received) { char filepath[FILE_PATH_MAX]; - - const char *filename = get_path_from_uri(filepath, - ((struct file_server_data*) req->user_ctx)->base_path, - req->uri, - sizeof(filepath)); - - ESP_LOGW(TAG, "URI %s", req->uri); - if (memmem(filename, strlen(filename), "/files/upload/", sizeof("/files/upload/"))) - { - ESP_LOGW(TAG, "filepath %s", filepath); - return upload_post_handler(req); - } + const char *filename; filename = get_path_from_uri(filepath, - ((struct file_server_data*) req->user_ctx)->base_path, - req->uri, - sizeof(filepath)); - + ((struct file_server_data*) req->user_ctx)->base_path, + req->uri, + sizeof(filepath)); if (!memcmp(filename, "/api", 4)) { @@ -268,6 +260,14 @@ static esp_err_t GETHandler(httpd_req_t *req) #if HTTP_SERVER_DEBUG_LEVEL > 0 ESP_LOGI(TAG, "GET request handle"); #endif + + ESP_LOGW(TAG, "URI %s", req->uri); + //if (!strcmp(req->uri, "/files/")) + if(memmem(req->uri, strlen(req->uri), "/files/", 7)) + { + return download_get_handler(req); + } + char filepath[FILE_PATH_MAX]; espfs_file_t *file; struct espfs_stat_t stat; @@ -275,6 +275,9 @@ static esp_err_t GETHandler(httpd_req_t *req) uint32_t bufSize; //size of ram buffer for chunk of data, read from file uint32_t readBytes; //number of bytes, read from file. used for information only + + + const char *filename = get_path_from_uri(filepath, ((struct file_server_data*) req->user_ctx)->base_path, req->uri, @@ -288,11 +291,7 @@ static esp_err_t GETHandler(httpd_req_t *req) return ESP_FAIL; } - ESP_LOGW(TAG, "URI %s", req->uri); - if (!strcmp(filename, "/files/")) - { - return download_get_handler(req); - } + /* Redirect request to /index.html */ if (filename[strlen(filename) - 1] == '/') @@ -464,8 +463,6 @@ file_send_error: return ESP_FAIL; } - - static httpd_handle_t start_webserver(void) { httpd_handle_t server = NULL; From c1f4678cdbb498640ca373ce723276ccc52def5b Mon Sep 17 00:00:00 2001 From: Bogdan Pilyugin Date: Thu, 17 Nov 2022 14:07:48 +0200 Subject: [PATCH 6/6] implemented spiffs with http web access --- src/FileServer.c | 20 +++++++++----------- src/HTTPServer.c | 19 ++++++------------- upload_script.html | 4 ++-- 3 files changed, 17 insertions(+), 26 deletions(-) diff --git a/src/FileServer.c b/src/FileServer.c index fc4465f..74daa88 100644 --- a/src/FileServer.c +++ b/src/FileServer.c @@ -103,7 +103,7 @@ static const char* get_path_from_uri(char *dest, const char *base_path, /* Construct full path (base + path) */ strcpy(dest, base_path); - strlcpy(dest + base_pathlen, uri, pathlen + 1); + strlcpy(dest + base_pathlen, uri + (sizeof("/files")-1), pathlen + 1 - (sizeof("/files")-1)); /* Return pointer to path, skipping the base */ return dest + base_pathlen; @@ -150,8 +150,8 @@ static esp_err_t http_resp_dir_html(httpd_req_t *req, const char *dirpath) /* Send file-list table definition and column labels */ httpd_resp_sendstr_chunk( req, - "" - "" + "
" + "" "" ""); @@ -184,8 +184,8 @@ static esp_err_t http_resp_dir_html(httpd_req_t *req, const char *dirpath) httpd_resp_sendstr_chunk(req, "\n"); @@ -228,7 +228,7 @@ esp_err_t download_get_handler(httpd_req_t *req) /* If name has trailing '/', respond with directory contents */ if (filename[strlen(filename) - 1] == '/') { - return http_resp_dir_html(req, "/data/"); + return http_resp_dir_html(req, filepath); } if (stat(filepath, &file_stat) == -1) @@ -241,8 +241,6 @@ esp_err_t download_get_handler(httpd_req_t *req) return index_html_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"); @@ -310,7 +308,7 @@ esp_err_t upload_post_handler(httpd_req_t *req) /* Skip leading "/upload" from URI to get filename */ /* Note sizeof() counts NULL termination hence the -1 */ const char *filename = get_path_from_uri(filepath, ((struct file_server_data*) req->user_ctx)->base_path2, - req->uri + sizeof("/files/upload") - 1, + req->uri + sizeof("/upload") - 1, sizeof(filepath)); ESP_LOGW(TAG, "FILE_POST_URI %s", req->uri); ESP_LOGW(TAG, "FILE_POST_FILEPATH %s", filepath); @@ -420,7 +418,7 @@ esp_err_t upload_post_handler(httpd_req_t *req) /* Redirect onto root to see the updated file list */ httpd_resp_set_status(req, "303 See Other"); - httpd_resp_set_hdr(req, "Location", "/"); + httpd_resp_set_hdr(req, "Location", "/files/"); #ifdef CONFIG_EXAMPLE_HTTPD_CONN_CLOSE_HEADER httpd_resp_set_hdr(req, "Connection", "close"); #endif @@ -468,7 +466,7 @@ esp_err_t delete_post_handler(httpd_req_t *req) /* Redirect onto root to see the updated file list */ httpd_resp_set_status(req, "303 See Other"); - httpd_resp_set_hdr(req, "Location", "/"); + httpd_resp_set_hdr(req, "Location", "/files/"); #ifdef CONFIG_EXAMPLE_HTTPD_CONN_CLOSE_HEADER httpd_resp_set_hdr(req, "Connection", "close"); #endif diff --git a/src/HTTPServer.c b/src/HTTPServer.c index 85d558f..7ac9ad5 100644 --- a/src/HTTPServer.c +++ b/src/HTTPServer.c @@ -173,10 +173,11 @@ static esp_err_t POSTHandler(httpd_req_t *req) ESP_LOGI(TAG, "POST request handle"); #endif - if (true) - { + if (memmem(req->uri, strlen(req->uri), "/files/upload/", sizeof("/files/upload/")-1)) return upload_post_handler(req); - } + + if (memmem(req->uri, strlen(req->uri), "/files/delete/", sizeof("/files/delete/")-1)) + return delete_post_handler(req); char *buf = ((struct file_server_data*) req->user_ctx)->scratch; int received; @@ -261,12 +262,9 @@ static esp_err_t GETHandler(httpd_req_t *req) ESP_LOGI(TAG, "GET request handle"); #endif - ESP_LOGW(TAG, "URI %s", req->uri); - //if (!strcmp(req->uri, "/files/")) - if(memmem(req->uri, strlen(req->uri), "/files/", 7)) - { + //Route to file server GET handler + if (memmem(req->uri, strlen(req->uri), "/files/", sizeof("/files/") - 1)) return download_get_handler(req); - } char filepath[FILE_PATH_MAX]; espfs_file_t *file; @@ -275,9 +273,6 @@ static esp_err_t GETHandler(httpd_req_t *req) uint32_t bufSize; //size of ram buffer for chunk of data, read from file uint32_t readBytes; //number of bytes, read from file. used for information only - - - const char *filename = get_path_from_uri(filepath, ((struct file_server_data*) req->user_ctx)->base_path, req->uri, @@ -291,8 +286,6 @@ static esp_err_t GETHandler(httpd_req_t *req) return ESP_FAIL; } - - /* Redirect request to /index.html */ if (filename[strlen(filename) - 1] == '/') { diff --git a/upload_script.html b/upload_script.html index 608f1ee..c3bc094 100644 --- a/upload_script.html +++ b/upload_script.html @@ -1,7 +1,7 @@
NameTypeSize (Bytes)Delete
"); httpd_resp_sendstr_chunk(req, entrysize); httpd_resp_sendstr_chunk(req, ""); - httpd_resp_sendstr_chunk(req, "
uri); + httpd_resp_sendstr_chunk(req, "uri); httpd_resp_sendstr_chunk(req, entry->d_name); httpd_resp_sendstr_chunk(req, "\">
"); httpd_resp_sendstr_chunk(req, "
- +
-

ESP32 File Server

+

ESP32 File Server