Skip to content

Commit 4b24f82

Browse files
committed
Implemented suggestions from Copilot
1 parent 32e0df6 commit 4b24f82

3 files changed

Lines changed: 65 additions & 26 deletions

File tree

examples/ChunkRequest/ChunkRequest.ino

Lines changed: 30 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -58,44 +58,51 @@ void handleRequest(AsyncWebServerRequest *request) {
5858
}
5959

6060
// If request->_tempObject is not null, handleBody already
61-
// did the necessary work for a PUT operation
61+
// did the necessary work for a PUT operation. Otherwise,
62+
// handleBody was either not called, or did nothing, so we
63+
// handle the request later in this routine. That happens
64+
// when a non-chunked PUT has Content-Length: 0.
6265
auto state = static_cast<RequestState *>(request->_tempObject);
6366
if (state) {
67+
// If handleBody successfully opened the file, whether or not it
68+
// wrote data to it, we close it here and send the "created"
69+
// response. If handleBody did not open the file, because the
70+
// open attempt failed or because the operation was rejected,
71+
// state will be non-null but state->outFile will be false. In
72+
// that case, handleBody has already sent an appropriate
73+
// response code.
74+
6475
if (state->outFile) {
6576
// The file was already opened and written in handleBody so
66-
// we are done. We will handle PUT without body data below.
77+
// we close it here and issue the appropriate response.
6778
state->outFile.close();
6879
request->send(201); // Created
6980
}
81+
// Finally, releast the resources used by state
7082
delete state;
7183
request->_tempObject = nullptr;
7284
return;
7385
}
7486

7587
String path = request->url();
7688

77-
if (request->method() == HTTP_PUT) {
78-
// This PUT code executes if the body was empty, which
79-
// can happen if the client creates a zero-length file.
80-
// MacOS WebDAVFS does that, then later LOCKs the file
81-
// and issues a subsequent PUT with body contents.
89+
// This PUT code executes if the body was empty, which
90+
// can happen if the client creates a zero-length file.
91+
// MacOS WebDAVFS does that, then later LOCKs the file
92+
// and issues a subsequent PUT with body contents.
8293

8394
#ifdef ESP32
84-
File file = LittleFS.open(path, "w", true);
95+
File file = LittleFS.open(path, "w", true);
8596
#else
86-
File file = LittleFS.open(path, "w");
97+
File file = LittleFS.open(path, "w");
8798
#endif
8899

89-
if (file) {
90-
file.close();
91-
request->send(201); // Created
92-
return;
93-
}
94-
request->send(403);
100+
if (file) {
101+
file.close();
102+
request->send(201); // Created
95103
return;
96104
}
97-
98-
request->send(404);
105+
request->send(403);
99106
}
100107

101108
void handleBody(AsyncWebServerRequest *request, unsigned char *data, size_t len, size_t index, size_t total) {
@@ -141,8 +148,12 @@ void handleBody(AsyncWebServerRequest *request, unsigned char *data, size_t len,
141148
request->send(403, "text/plain", "Cannot PUT to a directory");
142149
return;
143150
}
144-
// If we already returned, the File object in request->_tempObject
145-
// is the default-contructed one. The presence of
151+
// If we already returned, the File object in
152+
// request->_tempObject is the default-constructed one. The
153+
// presence of a non-default-constructed File in state->outFile
154+
// indicates that the file was opened successfully and is ready
155+
// to receive body data. The File will be closed later when
156+
// handleRequest is called after all calls to handleBody
146157

147158
std::swap(state->outFile, file);
148159
// Now request->_tempObject contains the actual file object which owns it,

src/WebRequest.cpp

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,15 +34,16 @@ enum {
3434
CHUNK_LENGTH, // Getting chunk length - HHHH[;...] CR LF
3535
CHUNK_EXTENSION, // Getting chunk length - HHHH[;...] CR LF
3636
CHUNK_DATA, // Handling chunk data
37+
CHUNK_ERROR, // Invalid chunk header
3738
CHUNK_END, // Getting chunk end marker - CR LF
3839
};
3940

4041
AsyncWebServerRequest::AsyncWebServerRequest(AsyncWebServer *s, AsyncClient *c)
4142
: _client(c), _server(s), _handler(NULL), _response(NULL), _onDisconnectfn(NULL), _temp(), _parseState(PARSE_REQ_START), _version(0), _method(HTTP_ANY),
4243
_url(), _host(), _contentType(), _boundary(), _authorization(), _reqconntype(RCT_HTTP), _authMethod(AsyncAuthType::AUTH_NONE), _isMultipart(false),
4344
_isPlainPost(false), _expectingContinue(false), _contentLength(0), _parsedLength(0), _multiParseState(0), _boundaryPosition(0), _itemStartIndex(0),
44-
_itemSize(0), _itemName(), _itemFilename(), _itemType(), _itemValue(), _itemBuffer(0), _itemBufferIndex(0), _itemIsFile(false),
45-
_chunkedParseState(CHUNK_NONE), _tempObject(NULL) {
45+
_itemSize(0), _itemName(), _itemFilename(), _itemType(), _itemValue(), _itemBuffer(0), _itemBufferIndex(0), _itemIsFile(false), _chunkStartIndex(0),
46+
_chunkOffset(0), _chunkSize(0), _chunkedParseState(CHUNK_NONE), _tempObject(NULL) {
4647
c->onError(
4748
[](void *r, AsyncClient *c, int8_t error) {
4849
(void)c;
@@ -389,25 +390,45 @@ bool AsyncWebServerRequest::_parseChunkedBytes(uint8_t *buf, size_t len) {
389390
if (_chunkedParseState == CHUNK_LENGTH) {
390391
// Incrementally decode a hex number
391392
if (data >= '0' && data <= '9') {
392-
_chunkSize = (_chunkSize * 16) + (data - '0');
393+
if (_chunkSize >= 0x1000000) {
394+
_chunkedParseState = CHUNK_ERROR;
395+
} else {
396+
_chunkSize = (_chunkSize * 16) + (data - '0');
397+
}
393398
} else if (data >= 'A' && data <= 'F') {
394-
_chunkSize = (_chunkSize * 16) + (data - 'A' + 10);
399+
if (_chunkSize >= 0x1000000) {
400+
_chunkedParseState = CHUNK_ERROR;
401+
} else {
402+
_chunkSize = (_chunkSize * 16) + (data - 'A' + 10);
403+
}
395404
} else if (data >= 'a' && data <= 'f') {
396-
_chunkSize = (_chunkSize * 16) + (data - 'a' + 10);
405+
if (_chunkSize >= 0x1000000) {
406+
_chunkedParseState = CHUNK_ERROR;
407+
} else {
408+
_chunkSize = (_chunkSize * 16) + (data - 'a' + 10);
409+
}
397410
} else if (data == ';') {
398411
_chunkedParseState = CHUNK_EXTENSION;
412+
} else if (data == '\r') {
413+
// Ignore CR; wait for LF
399414
} else if (data == '\n') {
400415
_chunkOffset = 0;
401416
_chunkedParseState = CHUNK_DATA;
417+
} else {
418+
// Invalid hex character
419+
_chunkedParseState = CHUNK_ERROR;
402420
}
403421
} else if (_chunkedParseState == CHUNK_EXTENSION) {
422+
// Chunk extensions appear after a semicolon.
423+
// We ignore them because their use cases are
424+
// specialized and obscure.
404425
if (data == '\n') {
405-
// A zero length chunk marks the end of the chunk stream
406426
_chunkOffset = 0;
407427
_chunkedParseState = CHUNK_DATA;
408428
}
409429
} else if (_chunkedParseState == CHUNK_END) {
410430
if (data == '\n') {
431+
// A zero length chunk marks the end of the chunk stream
411432
if (_chunkSize == 0) {
412433
// If we needed to support trailers, we would switch to
413434
// TRAILER state, but since we have no use case for them,
@@ -417,6 +438,13 @@ bool AsyncWebServerRequest::_parseChunkedBytes(uint8_t *buf, size_t len) {
417438
_chunkSize = 0;
418439
_chunkedParseState = CHUNK_LENGTH;
419440
}
441+
} else if (_chunkedParseState == CHUNK_ERROR) {
442+
// If there was an error when parsing the chunk length, the
443+
// rest of the data stream is unreliable. Ideally we should
444+
// close the connection, but that risks leaving things dangling
445+
// (e.g. an open file), so it is probably best to just ignore
446+
// the rest of the data and give handleRequest a chance to
447+
// clean up.
420448
}
421449
}
422450
}

src/literals.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ DECLARE_STR(T_HTTP_CODE_502, "Bad Gateway");
216216
DECLARE_STR(T_HTTP_CODE_503, "Service Unavailable");
217217
DECLARE_STR(T_HTTP_CODE_504, "Gateway Time-out");
218218
DECLARE_STR(T_HTTP_CODE_505, "HTTP Version Not Supported");
219-
DECLARE_STR(T_HTTP_CODE_507, "Insufficient storage");
219+
DECLARE_STR(T_HTTP_CODE_507, "Insufficient Storage");
220220
DECLARE_STR(T_HTTP_CODE_ANY, "Unknown code");
221221

222222
static constexpr const char *T_only_once_headers[] = {

0 commit comments

Comments
 (0)