Skip to content

Commit 1db83fd

Browse files
committed
refactor fix to be more robust and encapsulated
1 parent 2070e49 commit 1db83fd

3 files changed

Lines changed: 42 additions & 20 deletions

File tree

src/AsyncJson.cpp

Lines changed: 41 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@
55

66
#if ASYNC_JSON_SUPPORT == 1
77

8+
typedef struct {
9+
size_t length; // the size we can write into "content", not including null termination
10+
uint8_t content[1]; // this will be of size "content-length" + 1 byte to guarantee that the content is null terminated. null termination is needed for ArduinoJson 5
11+
} AsyncJsonResponseBuffer;
12+
813
#if ARDUINOJSON_VERSION_MAJOR == 5
914
AsyncJsonResponse::AsyncJsonResponse(bool isArray) : _isValid{false} {
1015
_code = 200;
@@ -119,40 +124,42 @@ void AsyncCallbackJsonWebHandler::handleRequest(AsyncWebServerRequest *request)
119124
return;
120125
}
121126
// this is not a GET
122-
// check if body is too large, if it is, don't parse
127+
// check if json body is too large, if it is, don't deserialize
123128
if (request->contentLength() > _maxContentLength)
124129
{
125130
request->send(413);
126131
return;
127132
}
128133

129134
// try to parse body as JSON
130-
if (request->_tempObject != NULL)
135+
if (request->_tempObject != NULL) // see if we succeeded allocating a buffer earlier
131136
{
132-
size_t dataSize = min(request->contentLength(), request->_tempSize); // smaller value of contentLength or the size of the buffer. normally those should match.
137+
AsyncJsonResponseBuffer * buffer = (AsyncJsonResponseBuffer*)request->_tempObject;
133138
#if ARDUINOJSON_VERSION_MAJOR == 5
134139
DynamicJsonBuffer jsonBuffer;
135-
uint8_t * p = (uint8_t *)(request->_tempObject);
136-
p[dataSize] = '\0'; // null terminate, assume we allocated one extra char
140+
buffer->content[buffer->length] = '\0'; // null terminate, assume we allocated one extra char
137141
// parse can only get null terminated strings as parameters
138-
JsonVariant json = jsonBuffer.parse(p);
142+
JsonVariant json = jsonBuffer.parse(buffer->content);
139143
if (json.success()) {
140144
#elif ARDUINOJSON_VERSION_MAJOR == 6
141-
DynamicJsonDocument jsonBuffer(this->maxJsonBufferSize);
142-
DeserializationError error = deserializeJson(jsonBuffer, (uint8_t *)(request->_tempObject), dataSize);
145+
DynamicJsonDocument jsonBuffer(this->maxJsonBufferSize); // content with length > this->maxJsonBufferSize might not get deserialized
146+
DeserializationError error = deserializeJson(jsonBuffer, buffer->content, buffer->length);
143147
if (!error) {
144148
JsonVariant json = jsonBuffer.as<JsonVariant>();
145149
#else
146150
JsonDocument jsonBuffer;
147151
// deserializeJson expects a null terminated string or a pointer plus length
148-
DeserializationError error = deserializeJson(jsonBuffer, (uint8_t *)(request->_tempObject), dataSize);
152+
DeserializationError error = deserializeJson(jsonBuffer, buffer->content, buffer->length);
149153
if (!error) {
150154
JsonVariant json = jsonBuffer.as<JsonVariant>();
151155
#endif
152156

153157
_onRequest(request, json);
154158
return;
155159
}
160+
// free buffer, we are done with it, so release memory ASAP
161+
free(request->_tempObject);
162+
request->_tempObject = NULL;
156163
}
157164
// there is no body, no buffer or we had an error parsing the body
158165
request->send(400);
@@ -163,30 +170,46 @@ void AsyncCallbackJsonWebHandler::handleRequest(AsyncWebServerRequest *request)
163170

164171
void AsyncCallbackJsonWebHandler::handleBody(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) {
165172
if (_onRequest) {
166-
if (total > 0 && request->_tempObject == NULL && total < _maxContentLength) { // if request content length is valid size and we have no content buffer yet
167-
request->_tempObject = malloc(total + 1); // allocate one additional byte so we can null terminate this buffer (needed for ArduinoJson 5)
173+
if (index == 0) // on first piece
174+
{
175+
// check nobody has already allocated the buffer
176+
if (request->_tempObject != NULL)
177+
{
178+
#ifdef ESP32
179+
log_e("Temp object already in use");
180+
#endif
181+
return; // do nothing else here, handleRequest will return a HTTP error
182+
}
183+
// check total size is valid
184+
if (total >= _maxContentLength)
185+
{
186+
return; // do nothing else here, handleRequest will return a HTTP error
187+
}
188+
// allocate buffer
189+
request->_tempObject = calloc(1, sizeof(AsyncJsonResponseBuffer) + total); // normally _tempObject will be "free"ed by the destructor of the request, but can release earlier if desired.
168190
if (request->_tempObject == NULL) { // if allocation failed
169191
#ifdef ESP32
170192
log_e("Failed to allocate");
171193
#endif
172-
request->abort();
173-
return;
194+
return; // do nothing else here, handleRequest will return a HTTP error
174195
}
175-
request->_tempSize = total; // store the size of allocation we made into _tempSize
196+
((AsyncJsonResponseBuffer*)request->_tempObject)->length = total; // store the size of allocation we made into _tempObject
176197
}
198+
199+
// add data to the buffer if the buffer exists
177200
if (request->_tempObject != NULL) {
201+
AsyncJsonResponseBuffer * buffer = (AsyncJsonResponseBuffer*)request->_tempObject;
178202
// check if the buffer is the right size so we don't write out of bounds
179-
if (request->_tempSize >= total)
203+
if (buffer->length >= total && buffer->length >= index + len)
180204
{
181-
memcpy((uint8_t *)(request->_tempObject) + index, data, len);
205+
memcpy(buffer->content + index, data, len);
182206
}
183207
else
184208
{
185209
#ifdef ESP32
186210
log_e("Bad size of temp buffer");
187211
#endif
188-
request->abort();
189-
return;
212+
return; // do nothing else here
190213
}
191214
}
192215
}

src/ESPAsyncWebServer.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,6 @@ class AsyncWebServerRequest {
261261
public:
262262
File _tempFile;
263263
void *_tempObject;
264-
size_t _tempSize;
265264

266265
AsyncWebServerRequest(AsyncWebServer *, AsyncClient *);
267266
~AsyncWebServerRequest();

src/WebRequest.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ AsyncWebServerRequest::AsyncWebServerRequest(AsyncWebServer *s, AsyncClient *c)
2525
: _client(c), _server(s), _handler(NULL), _response(NULL), _temp(), _parseState(PARSE_REQ_START), _version(0), _method(HTTP_ANY), _url(), _host(),
2626
_contentType(), _boundary(), _authorization(), _reqconntype(RCT_HTTP), _authMethod(AsyncAuthType::AUTH_NONE), _isMultipart(false), _isPlainPost(false),
2727
_expectingContinue(false), _contentLength(0), _parsedLength(0), _multiParseState(0), _boundaryPosition(0), _itemStartIndex(0), _itemSize(0), _itemName(),
28-
_itemFilename(), _itemType(), _itemValue(), _itemBuffer(0), _itemBufferIndex(0), _itemIsFile(false), _tempObject(NULL), _tempSize(0) {
28+
_itemFilename(), _itemType(), _itemValue(), _itemBuffer(0), _itemBufferIndex(0), _itemIsFile(false), _tempObject(NULL) {
2929
c->onError(
3030
[](void *r, AsyncClient *c, int8_t error) {
3131
(void)c;

0 commit comments

Comments
 (0)