mirror of
https://github.com/logicog/RTLPlayground.git
synced 2026-09-02 15:02:51 +08:00
httpd: buffer a firmware upload's part header before streaming
A firmware image cannot be buffered whole, so the upload is streamed to flash, and stream_upload() already resumes across TCP segments. The multipart preamble did not: handle_post() walked the part headers from the start of whichever segment it held, so a client that split inside the octet-stream part header lost its place, never started streaming, and the request hung. Firefox splits exactly there, right after filename=". Buffer the multipart body only until the octet-stream part header is complete, locate the payload, then stream from that point; later segments stream as before. The header reuses the configuration buffer, which is idle during a firmware upload, so no extra memory is needed.
This commit is contained in:
+36
-31
@@ -49,6 +49,7 @@ __xdata uint8_t boundary[72];
|
|||||||
__xdata uint8_t config_upload;
|
__xdata uint8_t config_upload;
|
||||||
__xdata uint8_t config_buf[CONFIG_UPLOAD_BUF];
|
__xdata uint8_t config_buf[CONFIG_UPLOAD_BUF];
|
||||||
__xdata uint16_t cfg_pos, cfg_hdr, cfg_body, cfg_end, cfg_last;
|
__xdata uint16_t cfg_pos, cfg_hdr, cfg_body, cfg_end, cfg_last;
|
||||||
|
__xdata uint16_t pre_acc;
|
||||||
__xdata uint8_t cfg_bl;
|
__xdata uint8_t cfg_bl;
|
||||||
__xdata uint8_t * __xdata content_type = 0;
|
__xdata uint8_t * __xdata content_type = 0;
|
||||||
__xdata uint8_t * __xdata session = 0;
|
__xdata uint8_t * __xdata session = 0;
|
||||||
@@ -240,17 +241,6 @@ void send_unauthorized(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
__xdata uint8_t *skip_boundary(__xdata uint8_t *p)
|
|
||||||
{
|
|
||||||
while (*p) {
|
|
||||||
if (is_word_x(p, boundary))
|
|
||||||
return p + strlen_x(boundary);
|
|
||||||
p++;
|
|
||||||
}
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
__xdata uint8_t *scan_header(__xdata uint8_t * __xdata p)
|
__xdata uint8_t *scan_header(__xdata uint8_t * __xdata p)
|
||||||
{
|
{
|
||||||
content_type = 0;
|
content_type = 0;
|
||||||
@@ -386,21 +376,38 @@ static uint8_t config_take(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// unlike scan_header(), keeps no auth state, so it may run on every buffered segment
|
||||||
|
static uint16_t preamble_payload_start(__xdata uint16_t n)
|
||||||
|
{
|
||||||
|
for (cfg_pos = 0; cfg_pos + 24 <= n; cfg_pos++) {
|
||||||
|
if (strstart(&config_buf[cfg_pos], "application/octet-stream"))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (cfg_pos + 24 > n)
|
||||||
|
return 0;
|
||||||
|
cfg_pos += 24;
|
||||||
|
while (cfg_pos + 3 < n && !strstart(&config_buf[cfg_pos], "\r\n\r\n"))
|
||||||
|
cfg_pos++;
|
||||||
|
if (cfg_pos + 3 >= n)
|
||||||
|
return 0;
|
||||||
|
return cfg_pos + 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Reads post data from the http stream and writes it into flash memory
|
* Reads post data from the http stream and writes it into flash memory
|
||||||
* Input: the current position in the TCP buffer (uip_appdata)
|
* Input: the current position in the TCP buffer (uip_appdata)
|
||||||
* Returns 1: More data to read, 0: Upload complete, all parts reads
|
* Returns 1: More data to read, 0: Upload complete, all parts reads
|
||||||
*/
|
*/
|
||||||
uint8_t stream_upload(uint16_t bptr)
|
uint8_t stream_upload(__xdata uint8_t *p, uint16_t bptr, uint16_t plen)
|
||||||
{
|
{
|
||||||
__xdata uint8_t *p = uip_appdata;
|
|
||||||
__xdata struct httpd_state * __xdata s = &(uip_conn->appstate);
|
__xdata struct httpd_state * __xdata s = &(uip_conn->appstate);
|
||||||
|
|
||||||
dbg_string("Stream_upload called: ");
|
dbg_string("Stream_upload called: ");
|
||||||
dbg_short(bptr); dbg_char('\n');
|
dbg_short(bptr); dbg_char('\n');
|
||||||
|
|
||||||
do {
|
do {
|
||||||
if (bptr >= uip_len) {
|
if (bptr >= plen) {
|
||||||
s->tstate = TSTATE_POST;
|
s->tstate = TSTATE_POST;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@@ -431,7 +438,7 @@ uint8_t stream_upload(uint16_t bptr)
|
|||||||
flash_region.addr = uptr;
|
flash_region.addr = uptr;
|
||||||
flash_region.len = 1;
|
flash_region.len = 1;
|
||||||
flash_write_bytes(flash_buf);
|
flash_write_bytes(flash_buf);
|
||||||
if (bptr >= uip_len)
|
if (bptr >= plen)
|
||||||
return 0;
|
return 0;
|
||||||
if(!verify_crc)
|
if(!verify_crc)
|
||||||
//ugly hack to signal connection finished after config upload.
|
//ugly hack to signal connection finished after config upload.
|
||||||
@@ -512,6 +519,7 @@ void handle_post(void)
|
|||||||
uptr = FIRMWARE_UPLOAD_START;
|
uptr = FIRMWARE_UPLOAD_START;
|
||||||
verify_crc = 1;
|
verify_crc = 1;
|
||||||
max_upload = 1024576;
|
max_upload = 1024576;
|
||||||
|
pre_acc = 0;
|
||||||
} else if (is_word(request_path, "config")) {
|
} else if (is_word(request_path, "config")) {
|
||||||
if (!authenticated) {
|
if (!authenticated) {
|
||||||
send_unauthorized();
|
send_unauthorized();
|
||||||
@@ -600,21 +608,21 @@ void handle_post(void)
|
|||||||
slen = strtox(outbuf, "HTTP/1.1 200 OK\r\nConnection: close\r\n\r\n");
|
slen = strtox(outbuf, "HTTP/1.1 200 OK\r\nConnection: close\r\n\r\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// We skip the intial parts as part of the header
|
cfg_pos = uip_len - (p - uip_appdata);
|
||||||
do {
|
if (pre_acc + cfg_pos >= CONFIG_UPLOAD_BUF) {
|
||||||
p = skip_boundary(p);
|
print_string("Firmware upload header too large, aborting.\n");
|
||||||
if (!*p) {
|
s->tstate = TSTATE_NONE;
|
||||||
|
send_bad_request();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
memcpy(config_buf + pre_acc, p, cfg_pos);
|
||||||
|
pre_acc += cfg_pos;
|
||||||
|
cfg_end = preamble_payload_start(pre_acc);
|
||||||
|
if (!cfg_end) {
|
||||||
s->tstate = TSTATE_MULTIPART;
|
s->tstate = TSTATE_MULTIPART;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
p = scan_header(p);
|
|
||||||
if (!*p)
|
|
||||||
goto bad_request;
|
|
||||||
if (!content_type) // We are waiting for the part with the octet stream
|
|
||||||
continue;
|
|
||||||
} while (!is_word(content_type, "application/octet-stream"));
|
|
||||||
dbg_string("Have content octets\n");
|
dbg_string("Have content octets\n");
|
||||||
p += 4; // Skip \r\n\r\n sequence at end of preamble of part
|
|
||||||
|
|
||||||
flash_init(0); // Re-initialize flash for non-DIO operation, otherwise flashing fails
|
flash_init(0); // Re-initialize flash for non-DIO operation, otherwise flashing fails
|
||||||
set_sys_led_state(SYS_LED_FAST);
|
set_sys_led_state(SYS_LED_FAST);
|
||||||
@@ -622,7 +630,7 @@ void handle_post(void)
|
|||||||
crc_value = 0;
|
crc_value = 0;
|
||||||
bindex = 0;
|
bindex = 0;
|
||||||
write_len = 0;
|
write_len = 0;
|
||||||
stream_upload(p - uip_appdata);
|
stream_upload(config_buf, cfg_end, pre_acc);
|
||||||
|
|
||||||
dbg_string("Done reading first fragment\n");
|
dbg_string("Done reading first fragment\n");
|
||||||
return;
|
return;
|
||||||
@@ -633,9 +641,6 @@ void handle_post(void)
|
|||||||
}
|
}
|
||||||
slen = strtox(outbuf, "HTTP/1.1 200 OK\r\nConnection: close\r\n\r\n");
|
slen = strtox(outbuf, "HTTP/1.1 200 OK\r\nConnection: close\r\n\r\n");
|
||||||
return;
|
return;
|
||||||
bad_request:
|
|
||||||
send_bad_request();
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -703,7 +708,7 @@ void httpd_appcall(void)
|
|||||||
} else if (uip_newdata() && s->tstate == TSTATE_POST) {
|
} else if (uip_newdata() && s->tstate == TSTATE_POST) {
|
||||||
// Check here maxupload by subtracting uip_len and close socekt if fails!
|
// Check here maxupload by subtracting uip_len and close socekt if fails!
|
||||||
if (max_upload - uip_len > 0) {
|
if (max_upload - uip_len > 0) {
|
||||||
stream_upload(0);
|
stream_upload(uip_appdata, 0, uip_len);
|
||||||
write_char('.');
|
write_char('.');
|
||||||
} else {
|
} else {
|
||||||
send_bad_request();
|
send_bad_request();
|
||||||
|
|||||||
Reference in New Issue
Block a user