httpd: parse a configuration upload once the whole request has arrived

Saving the configuration works in Chrome and fails in Firefox, and the
difference is only how each browser splits the request. The handler
erased the config sector as soon as the request started and then parsed
the multipart body one TCP segment at a time, which requires every
boundary and every part header to fall inside a single segment. Firefox
splits inside a part header, so the parser lost its place and the
erased sector was left holding a truncated body or nothing at all. A
single-burst scripted post lost the whole body the same way.

The configuration is limited to two kilobytes, so the whole request body
now accumulates in xdata and is parsed only after the closing boundary
has arrived. The parts are walked in one pass, the part carrying a
filename is written to a freshly erased sector, and the client receives
a 200 instead of the previous silent close. No segmentation can confuse
this, since the parser only ever sees a complete body.

Locating the closing boundary first also bounds the walk over the parts,
since none can lie beyond it, so the length of the buffer is no longer
the bound and the test for the two trailing dashes is unnecessary.

The walk matches at offsets inside a buffer that is not terminated, so
neither existing helper fits: strcmp() goes on to compare the byte after
the match and is_word_x() demands a separator there. Add strstart() and
strstart_x() for that case, and use strlen_x() for the boundary length.

The firmware upload path still streams, since a megabyte cannot be
buffered, and is untouched.
This commit is contained in:
bloqaudio
2026-08-23 18:11:46 -05:00
parent 59d20ed9b6
commit 772e9dc526
3 changed files with 123 additions and 5 deletions
+2
View File
@@ -164,6 +164,8 @@ uint16_t strlen_x(register __xdata const char *s);
uint16_t strtox(register __xdata uint8_t *dst, register __code const char *s);
uint16_t strcpy(register __xdata uint8_t *dst, register const char *s);
char strcmp(register __xdata const uint8_t *a, register __code const uint8_t *b);
bool strstart(__xdata const uint8_t *a, __code const uint8_t *b);
bool strstart_x(__xdata const uint8_t *a, __xdata const uint8_t *b);
void tcpip_output(void);
uint8_t read_flash(uint8_t bank, __code uint8_t *addr);
void get_random_32(void);