From 9c668969d490c121dd7278f27829521b4c798ac7 Mon Sep 17 00:00:00 2001 From: logicog Date: Fri, 1 May 2026 07:16:35 +0200 Subject: [PATCH] Fix POST upload request handling for Chrome browser Chrome sends upload requests using POST with multipart/form-data content type in multiple packets for the header part of the form-data. Introduce a TSTATE_MULTIPART for the httpd server states that denotes that so far only a part of the multipart header has been transmitted. Once the full header has been transmitted, we change to TSTATE_POST as for Firefox which sends all the multipart header in one piece. The main further change required then is to make sure that the parsing of the initial part of the multipart request is only parsed once and initially to distinguish between configuration and firmware uploads. --- httpd/httpd.c | 101 +++++++++++++++++++++++++++++--------------------- 1 file changed, 58 insertions(+), 43 deletions(-) diff --git a/httpd/httpd.c b/httpd/httpd.c index 38f2ec1..9e33f1a 100644 --- a/httpd/httpd.c +++ b/httpd/httpd.c @@ -60,11 +60,12 @@ __xdata uint32_t now; __xdata uint8_t *timeptr; __xdata uint32_t last_session_use; -#define TSTATE_NONE 0 -#define TSTATE_TX 1 -#define TSTATE_ACKED 2 -#define TSTATE_CLOSED 3 -#define TSTATE_POST 4 +#define TSTATE_NONE 0 +#define TSTATE_TX 1 +#define TSTATE_ACKED 2 +#define TSTATE_CLOSED 3 +#define TSTATE_POST 4 +#define TSTATE_MULTIPART 5 extern __xdata uint16_t crc_value; __xdata uint16_t crc_final; @@ -355,21 +356,46 @@ void handle_post(void) __xdata uint8_t *p = uip_appdata; __xdata uint8_t *request_path = p + 6; - dbg_string("Is POST\n"); - p += 5; // Skip post - // Find end of request path - while (*p && !is_separator(*p)) - p++; - *p++ = '\0'; + // Was the multipart header sent in multiple packets? + if (s->tstate != TSTATE_MULTIPART) { + dbg_string("Is POST\n"); + p += 5; // Skip post + // Find end of request path + while (*p && !is_separator(*p)) + p++; + *p++ = '\0'; - // Find end of request header - boundary[0] ='\0'; - p = scan_header(p); - dbg_string("Boundary: >"); dbg_string_x(boundary); dbg_string("<\n"); - if (!*p || !content_type) { - dbg_string("Bad Request!\n"); - send_not_found(); - return; + // Find end of request header + boundary[0] ='\0'; + p = scan_header(p); + dbg_string("Boundary: >"); dbg_string_x(boundary); dbg_string("<\n"); + if (!*p || !content_type) { + dbg_string("Bad Request!\n"); + send_not_found(); + return; + } + if (is_word(request_path, "upload")) { + if (flash_size < FIRMWARE_UPLOAD_START*2) + { + print_string("Flash too small for firmware upload!\n"); + send_bad_request(); + return; + } + print_string("Firmware upload started."); + uptr = FIRMWARE_UPLOAD_START; + verify_crc = 1; + max_upload = 1024576; + } else if (is_word(request_path, "config")) { + dbg_string("Configuration upload, erasing config mem!\n"); + uptr = CONFIG_START; + verify_crc = 0; + max_upload = 2048; + flash_region.addr = CONFIG_START; + flash_sector_erase(); + } + // Check for other POST requests, which are not multipart, below + } else { + dbg_string("Multipart request\n"); } if (is_word(request_path, "cmd")) { @@ -401,7 +427,7 @@ void handle_post(void) slen = strtox(outbuf, "HTTP/1.1 302 Found\r\nLocation: login.html\r\n\r\n"); } return; - } else if (is_word(request_path, "upload") || is_word(request_path, "config")) { + } else if (s->tstate == TSTATE_MULTIPART || is_word(request_path, "upload") || is_word(request_path, "config")) { dbg_string("POST upload/config request\n"); if (!authenticated) { send_unauthorized(); @@ -415,8 +441,10 @@ void handle_post(void) // We skip the intial parts as part of the header do { p = skip_boundary(p); - if (!*p) - goto bad_request; + if (!*p) { + s->tstate = TSTATE_MULTIPART; + return; + } p = scan_header(p); if (!*p) goto bad_request; @@ -426,25 +454,6 @@ void handle_post(void) dbg_string("Have content octets\n"); p += 4; // Skip \r\n\r\n sequence at end of preamble of part - if (is_word(request_path, "upload")) { - if (flash_size < FIRMWARE_UPLOAD_START*2) - { - print_string("Flash too small for firmware upload!\n"); - send_bad_request(); - return; - } - print_string("Firmware upload started."); - uptr = FIRMWARE_UPLOAD_START; - verify_crc = 1; - max_upload = 1024576; - } else { - dbg_string("Configuration upload, erasing config mem!\n"); - uptr = CONFIG_START; - verify_crc = 0; - max_upload = 2048; - flash_region.addr = CONFIG_START; - flash_sector_erase(); - } flash_init(0); // Re-initialize flash for non-DIO operation, otherwise flashing fails set_sys_led_state(SYS_LED_FAST); @@ -473,6 +482,12 @@ void httpd_appcall(void) __xdata struct httpd_state * __xdata s = &(uip_conn->appstate); dbg_char('P'); +#ifdef DEBUG + if (uip_newdata()) + write_char('N'); + print_byte(s->tstate); + write_char(' '); +#endif if(uip_connected() && s->tstate == TSTATE_CLOSED) { dbg_string("Connected...\n"); s->tstate = TSTATE_NONE; @@ -544,10 +559,10 @@ void httpd_appcall(void) dbg_char('\n'); #endif p = uip_appdata; - if (is_word(p, "POST")) { + if (is_word(p, "POST") || s->tstate == TSTATE_MULTIPART) { handle_post(); // If this is an ongoing post stream, then wait for the next packet - if (s->tstate == TSTATE_POST) { + if (s->tstate == TSTATE_POST || s->tstate == TSTATE_MULTIPART) { uip_len = 0; return; }