Skip to content

Commit 7c5196d

Browse files
authored
fix(webserver): Proposal for simplifying webserver file uploads via form POST. (#9167)
* Proposal for fixing file webserver uploads via form upload. The form parser has shown to have issues with files ending with "--\r\n". This commit replaces the form parser with the parser from ESP8266, which passes the test case. * Replace _uploadReadByte() in WebServer/Parsing.cpp with ESP8266 implementation.
1 parent 5d73dd5 commit 7c5196d

File tree

1 file changed

+55
-112
lines changed

1 file changed

+55
-112
lines changed

Diff for: libraries/WebServer/src/Parsing.cpp

+55-112
Original file line numberDiff line numberDiff line change
@@ -309,42 +309,14 @@ void WebServer::_uploadWriteByte(uint8_t b){
309309
_currentUpload->buf[_currentUpload->currentSize++] = b;
310310
}
311311

312-
int WebServer::_uploadReadByte(WiFiClient& client){
312+
int WebServer::_uploadReadByte(WiFiClient& client) {
313313
int res = client.read();
314-
if(res < 0) {
315-
// keep trying until you either read a valid byte or timeout
316-
unsigned long startMillis = millis();
317-
long timeoutIntervalMillis = client.getTimeout();
318-
boolean timedOut = false;
319-
for(;;) {
320-
if (!client.connected()) return -1;
321-
// loosely modeled after blinkWithoutDelay pattern
322-
while(!timedOut && !client.available() && client.connected()){
323-
delay(2);
324-
timedOut = millis() - startMillis >= timeoutIntervalMillis;
325-
}
326314

327-
res = client.read();
328-
if(res >= 0) {
329-
return res; // exit on a valid read
330-
}
331-
// NOTE: it is possible to get here and have all of the following
332-
// assertions hold true
333-
//
334-
// -- client.available() > 0
335-
// -- client.connected == true
336-
// -- res == -1
337-
//
338-
// a simple retry strategy overcomes this which is to say the
339-
// assertion is not permanent, but the reason that this works
340-
// is elusive, and possibly indicative of a more subtle underlying
341-
// issue
342-
343-
timedOut = millis() - startMillis >= timeoutIntervalMillis;
344-
if(timedOut) {
345-
return res; // exit on a timeout
346-
}
347-
}
315+
if (res < 0) {
316+
while(!client.available() && client.connected())
317+
delay(2);
318+
319+
res = client.read();
348320
}
349321

350322
return res;
@@ -436,88 +408,59 @@ bool WebServer::_parseForm(WiFiClient& client, String boundary, uint32_t len){
436408
if(_currentHandler && _currentHandler->canUpload(_currentUri))
437409
_currentHandler->upload(*this, _currentUri, *_currentUpload);
438410
_currentUpload->status = UPLOAD_FILE_WRITE;
439-
int argByte = _uploadReadByte(client);
440-
readfile:
441411

442-
while(argByte != 0x0D){
443-
if(argByte < 0) return _parseFormUploadAborted();
444-
_uploadWriteByte(argByte);
445-
argByte = _uploadReadByte(client);
446-
}
447-
448-
argByte = _uploadReadByte(client);
449-
if(argByte < 0) return _parseFormUploadAborted();
450-
if (argByte == 0x0A){
451-
argByte = _uploadReadByte(client);
452-
if(argByte < 0) return _parseFormUploadAborted();
453-
if ((char)argByte != '-'){
454-
//continue reading the file
455-
_uploadWriteByte(0x0D);
456-
_uploadWriteByte(0x0A);
457-
goto readfile;
458-
} else {
459-
argByte = _uploadReadByte(client);
460-
if(argByte < 0) return _parseFormUploadAborted();
461-
if ((char)argByte != '-'){
462-
//continue reading the file
463-
_uploadWriteByte(0x0D);
464-
_uploadWriteByte(0x0A);
465-
_uploadWriteByte((uint8_t)('-'));
466-
goto readfile;
467-
}
468-
}
469-
470-
uint8_t endBuf[boundary.length()];
471-
uint32_t i = 0;
472-
while(i < boundary.length()){
473-
argByte = _uploadReadByte(client);
474-
if(argByte < 0) return _parseFormUploadAborted();
475-
if ((char)argByte == 0x0D){
476-
_uploadWriteByte(0x0D);
477-
_uploadWriteByte(0x0A);
478-
_uploadWriteByte((uint8_t)('-'));
479-
_uploadWriteByte((uint8_t)('-'));
480-
uint32_t j = 0;
481-
while(j < i){
482-
_uploadWriteByte(endBuf[j++]);
483-
}
484-
goto readfile;
485-
}
486-
endBuf[i++] = (uint8_t)argByte;
487-
}
488-
489-
if (strstr((const char*)endBuf, boundary.c_str()) != NULL){
490-
if(_currentHandler && _currentHandler->canUpload(_currentUri))
491-
_currentHandler->upload(*this, _currentUri, *_currentUpload);
492-
_currentUpload->totalSize += _currentUpload->currentSize;
493-
_currentUpload->status = UPLOAD_FILE_END;
494-
if(_currentHandler && _currentHandler->canUpload(_currentUri))
495-
_currentHandler->upload(*this, _currentUri, *_currentUpload);
496-
log_v("End File: %s Type: %s Size: %d", _currentUpload->filename.c_str(), _currentUpload->type.c_str(), _currentUpload->totalSize);
497-
line = client.readStringUntil(0x0D);
498-
client.readStringUntil(0x0A);
499-
if (line == "--"){
500-
log_v("Done Parsing POST");
501-
break;
412+
int fastBoundaryLen = 4 /* \r\n-- */ + boundary.length() + 1 /* \0 */;
413+
char fastBoundary[ fastBoundaryLen ];
414+
snprintf(fastBoundary, fastBoundaryLen, "\r\n--%s", boundary.c_str());
415+
int boundaryPtr = 0;
416+
while ( true ) {
417+
int ret = _uploadReadByte(client);
418+
if (ret < 0) {
419+
// Unexpected, we should have had data available per above
420+
return _parseFormUploadAborted();
502421
}
503-
continue;
504-
} else {
505-
_uploadWriteByte(0x0D);
506-
_uploadWriteByte(0x0A);
507-
_uploadWriteByte((uint8_t)('-'));
508-
_uploadWriteByte((uint8_t)('-'));
509-
uint32_t i = 0;
510-
while(i < boundary.length()){
511-
_uploadWriteByte(endBuf[i++]);
422+
char in = (char) ret;
423+
if (in == fastBoundary[ boundaryPtr ]) {
424+
// The input matched the current expected character, advance and possibly exit this file
425+
boundaryPtr++;
426+
if (boundaryPtr == fastBoundaryLen - 1) {
427+
// We read the whole boundary line, we're done here!
428+
break;
429+
}
430+
} else {
431+
// The char doesn't match what we want, so dump whatever matches we had, the read in char, and reset ptr to start
432+
for (int i = 0; i < boundaryPtr; i++) {
433+
_uploadWriteByte( fastBoundary[ i ] );
434+
}
435+
if (in == fastBoundary[ 0 ]) {
436+
// This could be the start of the real end, mark it so and don't emit/skip it
437+
boundaryPtr = 1;
438+
} else {
439+
// Not the 1st char of our pattern, so emit and ignore
440+
_uploadWriteByte( in );
441+
boundaryPtr = 0;
442+
}
512443
}
513-
argByte = _uploadReadByte(client);
514-
goto readfile;
515-
}
516-
} else {
517-
_uploadWriteByte(0x0D);
518-
goto readfile;
519444
}
520-
break;
445+
// Found the boundary string, finish processing this file upload
446+
if (_currentHandler && _currentHandler->canUpload(_currentUri))
447+
_currentHandler->upload(*this, _currentUri, *_currentUpload);
448+
_currentUpload->totalSize += _currentUpload->currentSize;
449+
_currentUpload->status = UPLOAD_FILE_END;
450+
if (_currentHandler && _currentHandler->canUpload(_currentUri))
451+
_currentHandler->upload(*this, _currentUri, *_currentUpload);
452+
log_v("End File: %s Type: %s Size: %d",
453+
_currentUpload->filename.c_str(),
454+
_currentUpload->type.c_str(),
455+
(int)_currentUpload->totalSize);
456+
if (!client.connected()) return _parseFormUploadAborted();
457+
line = client.readStringUntil('\r');
458+
client.readStringUntil('\n');
459+
if (line == "--") { // extra two dashes mean we reached the end of all form fields
460+
log_v("Done Parsing POST");
461+
break;
462+
}
463+
continue;
521464
}
522465
}
523466
}

0 commit comments

Comments
 (0)