From cfe1cee4d337ea3072ae125f0d8152f28e1cc63b Mon Sep 17 00:00:00 2001 From: jetpax Date: Thu, 10 Apr 2025 14:12:37 -0700 Subject: [PATCH] Replace brotli compressed file serving with gzip (#23274) * Replace brotli compressed file serving with gzip as it is better supported * Add HTTP_POST support --- .../berry_tasmota/src/be_httpserver_lib.c | 80 ++++++++++++++++--- .../berry_tasmota/src/be_webfiles_lib.c | 50 +++++++----- 2 files changed, 96 insertions(+), 34 deletions(-) diff --git a/lib/libesp32/berry_tasmota/src/be_httpserver_lib.c b/lib/libesp32/berry_tasmota/src/be_httpserver_lib.c index b6ac1bbd4..ba81c1b62 100644 --- a/lib/libesp32/berry_tasmota/src/be_httpserver_lib.c +++ b/lib/libesp32/berry_tasmota/src/be_httpserver_lib.c @@ -351,8 +351,52 @@ void be_httpserver_process_web_request(bvm *vm, http_queue_msg_t *msg) { // Push URI as argument be_pushstring(handler_vm, current_request->uri); + // Check if this is a POST request and handle POST data + int arg_count = 1; // Start with 1 for the URI + + if (current_request->method == HTTP_POST) { + ESP_LOGI(TAG, "Processing POST request data"); + + // Get content length + int content_len = current_request->content_len; + ESP_LOGI(TAG, "POST content length: %d", content_len); + + if (content_len > 0) { + // Allocate buffer for POST data + char *post_data = malloc(content_len + 1); + if (post_data) { + // Read POST data + int received = httpd_req_recv(current_request, post_data, content_len); + if (received > 0) { + // Null-terminate the data + post_data[received] = '\0'; + ESP_LOGI(TAG, "Received POST data: %s", post_data); + + // Push POST data as second argument + be_pushstring(handler_vm, post_data); + arg_count = 2; // Now we have 2 arguments + } else { + ESP_LOGW(TAG, "Failed to read POST data, received: %d", received); + // Push nil as second argument + be_pushnil(handler_vm); + arg_count = 2; + } + free(post_data); + } else { + ESP_LOGE(TAG, "Failed to allocate memory for POST data"); + // Push nil as second argument + be_pushnil(handler_vm); + arg_count = 2; + } + } else { + // No content, push empty string as second argument + be_pushstring(handler_vm, ""); + arg_count = 2; + } + } + // Call the Berry function - int result = be_pcall(handler_vm, 1); + int result = be_pcall(handler_vm, arg_count); // Log stack state after call ESP_LOGI(TAG, "STACK: After be_pcall, stack top = %d, result = %d", be_top(handler_vm), result); @@ -688,19 +732,36 @@ static int w_httpserver_start(bvm *vm) { // Register a URI handler static int w_httpserver_on(bvm *vm) { int top = be_top(vm); + httpd_method_t http_method = HTTP_GET; // Default method if (top < 2 || http_server == NULL) { be_raise(vm, "value_error", top < 2 ? "Missing arguments" : "Server not started"); - return 0; + be_return(vm); } if (!be_isstring(vm, 1) || !be_isfunction(vm, 2)) { be_raise(vm, "type_error", "String and function required"); - return 0; + be_return(vm); + } + + // Check for optional method argument + if (top >= 3) { + if (!be_isstring(vm, 3)) { + be_raise(vm, "type_error", "Method must be a string"); + be_return(vm); + } + const char *method_str = be_tostring(vm, 3); + if (strcasecmp(method_str, "POST") == 0) { + http_method = HTTP_POST; + } else if (strcasecmp(method_str, "GET") != 0) { + be_raise(vm, "value_error", "Method must be 'GET' or 'POST'"); + be_return(vm); + } } const char *uri = be_tostring(vm, 1); - ESP_LOGI(TAG, "Registering handler for URI: %s", uri); + ESP_LOGI(TAG, "Registering handler for URI: %s, Method: %s", uri, + http_method == HTTP_GET ? "GET" : "POST"); // Find a free handler slot int slot = -1; @@ -713,7 +774,7 @@ static int w_httpserver_on(bvm *vm) { if (slot < 0) { be_raise(vm, "runtime_error", "No more handler slots available"); - return 0; + be_return(vm); } // Store handler info @@ -729,7 +790,7 @@ static int w_httpserver_on(bvm *vm) { // Register the handler with ESP-IDF HTTP server httpd_uri_t http_uri = { .uri = uri, - .method = HTTP_GET, + .method = http_method, .handler = berry_handlers[slot], .user_ctx = NULL }; @@ -776,12 +837,6 @@ static int w_httpserver_stop(bvm *vm) { be_return (vm); } -// Get the server handle (for advanced usage) -static int w_httpserver_get_handle(bvm *vm) { - be_pushint(vm, (int)(intptr_t)http_server); - be_return (vm); -} - // Simple wrapper around httpd_resp_sendstr static int w_httpserver_send(bvm *vm) { int argc = be_top(vm); @@ -832,7 +887,6 @@ module httpserver (scope: global, strings: weak) { start, func(w_httpserver_start) on, func(w_httpserver_on) send, func(w_httpserver_send) - _handle, func(w_httpserver_get_handle) stop, func(w_httpserver_stop) process_queue, func(w_httpserver_process_queue) } diff --git a/lib/libesp32/berry_tasmota/src/be_webfiles_lib.c b/lib/libesp32/berry_tasmota/src/be_webfiles_lib.c index 921705ef0..bda127ec2 100644 --- a/lib/libesp32/berry_tasmota/src/be_webfiles_lib.c +++ b/lib/libesp32/berry_tasmota/src/be_webfiles_lib.c @@ -160,10 +160,11 @@ static void set_content_type_from_file(httpd_req_t *req, const char *filepath) { static esp_err_t webfiles_handler(httpd_req_t *req) { char filepath[FILE_PATH_MAX]; - char brotli_filepath[FILE_PATH_MAX]; + char compressed_filepath[FILE_PATH_MAX]; FILE *file = NULL; struct stat file_stat; - bool use_brotli = false; + bool use_compression = false; + const char* compression_type = NULL; // Only "gzip" for for now // Process any URL query parameters if needed char *query = strchr(req->uri, '?'); @@ -184,20 +185,24 @@ static esp_err_t webfiles_handler(httpd_req_t *req) { ESP_LOGI(TAG, "Requested file: %s", filepath); - // Check if file is .html, .css, or .js and if a .br version exists + // Check if file is .html, .css, .js, or .svg and if a compressed version exists const char *ext = strrchr(filepath, '.'); if (ext && (strcasecmp(ext, ".html") == 0 || strcasecmp(ext, ".css") == 0 || strcasecmp(ext, ".js") == 0 || strcasecmp(ext, ".svg") == 0)) { - // Check if client supports Brotli - char accept_encoding[64]; - if (httpd_req_get_hdr_value_str(req, "Accept-Encoding", accept_encoding, sizeof(accept_encoding)) == ESP_OK && - strstr(accept_encoding, "br") != NULL) { - // Construct Brotli filepath - snprintf(brotli_filepath, sizeof(brotli_filepath), "%s.br", filepath); - if (stat(brotli_filepath, &file_stat) == 0 && S_ISREG(file_stat.st_mode)) { - use_brotli = true; - strcpy(filepath, brotli_filepath); // Use the .br file - ESP_LOGI(TAG, "Found Brotli version: %s", filepath); + + // Check what compression formats the client supports + char accept_encoding[64] = {0}; + if (httpd_req_get_hdr_value_str(req, "Accept-Encoding", accept_encoding, sizeof(accept_encoding)) == ESP_OK) { + ESP_LOGI(TAG, "Client supports compression: %s", accept_encoding); + if (!use_compression && strstr(accept_encoding, "gzip") != NULL) { + // Construct Gzip filepath + snprintf(compressed_filepath, sizeof(compressed_filepath), "%s.gz", filepath); + if (stat(compressed_filepath, &file_stat) == 0 && S_ISREG(file_stat.st_mode)) { + use_compression = true; + compression_type = "gzip"; + strcpy(filepath, compressed_filepath); // Use the .gz file + ESP_LOGI(TAG, "Found Gzip version: %s", filepath); + } } } } @@ -224,20 +229,23 @@ static esp_err_t webfiles_handler(httpd_req_t *req) { return ESP_FAIL; } - // Set content type based on file extension (use original path for MIME type if Brotli) + // Set content type based on file extension (use original path for MIME type if compressed) char original_filepath[FILE_PATH_MAX]; - if (use_brotli) { - // Strip .br for MIME type detection + if (use_compression) { + // Strip compression extension for MIME type detection strcpy(original_filepath, filepath); - original_filepath[strlen(original_filepath) - 3] = '\0'; // Remove ".br" + char* dot_pos = strrchr(original_filepath, '.'); + if (dot_pos) { + *dot_pos = '\0'; // Remove compression extension + } set_content_type_from_file(req, original_filepath); } else { set_content_type_from_file(req, filepath); } - // Set Brotli headers if applicable - if (use_brotli) { - httpd_resp_set_hdr(req, "Content-Encoding", "br"); + // Set compression headers if applicable + if (use_compression) { + httpd_resp_set_hdr(req, "Content-Encoding", compression_type); httpd_resp_set_hdr(req, "Vary", "Accept-Encoding"); } @@ -260,7 +268,7 @@ static esp_err_t webfiles_handler(httpd_req_t *req) { // Finish the HTTP response httpd_resp_send_chunk(req, NULL, 0); - ESP_LOGI(TAG, "File sent successfully (%d bytes, %s)", (int)total_sent, use_brotli ? "Brotli" : "uncompressed"); + ESP_LOGI(TAG, "File sent successfully (%d bytes, %s)", (int)total_sent, use_compression ? compression_type : "uncompressed"); return ESP_OK; }