Replace brotli compressed file serving with gzip (#23274)

* Replace brotli compressed file serving with gzip as it is better supported

* Add HTTP_POST support
This commit is contained in:
jetpax 2025-04-10 14:12:37 -07:00 committed by GitHub
parent 2999eed8b9
commit cfe1cee4d3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 96 additions and 34 deletions

View File

@ -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)
}

View File

@ -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;
}