Skip to content

Commit 55d45c1

Browse files
committed
fix: AsyncAbstractResponse might loose part of send buffer
AsyncAbstractResponse::_ack could allocate temp buffer with size larger than available sock buffer (i.e. to fit headers) and eventually lossing the remainder on transfer due to not checking if the complete data was added to sock buff. Refactoring code in favor of having a dedicated std::vector object acting as accumulating buffer and more carefull control on amount of data actually copied to sockbuff Closes #315
1 parent 12fcdd8 commit 55d45c1

File tree

4 files changed

+171
-151
lines changed

4 files changed

+171
-151
lines changed

src/ESPAsyncWebServer.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1105,8 +1105,10 @@ class AsyncWebServerResponse {
11051105
bool _sendContentLength;
11061106
bool _chunked;
11071107
size_t _headLength;
1108+
// amount of data sent for content part of the response (excluding all headers)
11081109
size_t _sentLength;
11091110
size_t _ackedLength;
1111+
// amount of response bytes (including all headers) written to sockbuff for delivery
11101112
size_t _writtenLength;
11111113
WebResponseState _state;
11121114

src/WebRequest.cpp

Lines changed: 28 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -33,66 +33,66 @@ AsyncWebServerRequest::AsyncWebServerRequest(AsyncWebServer *s, AsyncClient *c)
3333
[](void *r, AsyncClient *c, int8_t error) {
3434
(void)c;
3535
// async_ws_log_e("AsyncWebServerRequest::_onError");
36-
AsyncWebServerRequest *req = (AsyncWebServerRequest *)r;
37-
req->_onError(error);
36+
static_cast<AsyncWebServerRequest*>(r)->_onError(error);
3837
},
3938
this
4039
);
4140
c->onAck(
4241
[](void *r, AsyncClient *c, size_t len, uint32_t time) {
4342
(void)c;
4443
// async_ws_log_e("AsyncWebServerRequest::_onAck");
45-
AsyncWebServerRequest *req = (AsyncWebServerRequest *)r;
46-
req->_onAck(len, time);
44+
static_cast<AsyncWebServerRequest*>(r)->_onAck(len, time);
4745
},
4846
this
4947
);
5048
c->onDisconnect(
5149
[](void *r, AsyncClient *c) {
5250
// async_ws_log_e("AsyncWebServerRequest::_onDisconnect");
53-
AsyncWebServerRequest *req = (AsyncWebServerRequest *)r;
54-
req->_onDisconnect();
55-
delete c;
51+
static_cast<AsyncWebServerRequest*>(r)->_onDisconnect();
5652
},
5753
this
5854
);
5955
c->onTimeout(
6056
[](void *r, AsyncClient *c, uint32_t time) {
6157
(void)c;
6258
// async_ws_log_e("AsyncWebServerRequest::_onTimeout");
63-
AsyncWebServerRequest *req = (AsyncWebServerRequest *)r;
64-
req->_onTimeout(time);
59+
static_cast<AsyncWebServerRequest*>(r)->_onTimeout(time);
6560
},
6661
this
6762
);
6863
c->onData(
6964
[](void *r, AsyncClient *c, void *buf, size_t len) {
7065
(void)c;
7166
// async_ws_log_e("AsyncWebServerRequest::_onData");
72-
AsyncWebServerRequest *req = (AsyncWebServerRequest *)r;
73-
req->_onData(buf, len);
67+
static_cast<AsyncWebServerRequest*>(r)->_onData(buf, len);
7468
},
7569
this
7670
);
7771
c->onPoll(
7872
[](void *r, AsyncClient *c) {
7973
(void)c;
8074
// async_ws_log_e("AsyncWebServerRequest::_onPoll");
81-
AsyncWebServerRequest *req = (AsyncWebServerRequest *)r;
82-
req->_onPoll();
75+
static_cast<AsyncWebServerRequest*>(r)->_onPoll();
8376
},
8477
this
8578
);
8679
}
8780

8881
AsyncWebServerRequest::~AsyncWebServerRequest() {
89-
// async_ws_log_e("AsyncWebServerRequest::~AsyncWebServerRequest");
82+
if (_client){
83+
// usually it is _client's disconnect triggers object destruct, but for completeness we define behavior
84+
// if for some reason *this will be desturcted while client is still connected
85+
_client->onDisconnect(nullptr);
86+
delete _client;
87+
_client = nullptr;
88+
}
9089

91-
_this.reset();
90+
if (_response){
91+
delete _response;
92+
_response = nullptr;
93+
}
9294

93-
AsyncWebServerResponse *r = _response;
94-
_response = NULL;
95-
delete r;
95+
_this.reset();
9696

9797
if (_tempObject != NULL) {
9898
free(_tempObject);
@@ -217,30 +217,20 @@ void AsyncWebServerRequest::_onData(void *buf, size_t len) {
217217

218218
void AsyncWebServerRequest::_onPoll() {
219219
// os_printf("p\n");
220-
if (_response != NULL && _client != NULL && _client->canSend()) {
221-
if (!_response->_finished()) {
222-
_response->_ack(this, 0, 0);
223-
} else {
224-
AsyncWebServerResponse *r = _response;
225-
_response = NULL;
226-
delete r;
227-
228-
_client->close();
229-
}
220+
if (_response && _client && _client->canSend()) {
221+
_response->_ack(this, 0, 0);
230222
}
231223
}
232224

233225
void AsyncWebServerRequest::_onAck(size_t len, uint32_t time) {
234226
// os_printf("a:%u:%u\n", len, time);
235-
if (_response != NULL) {
236-
if (!_response->_finished()) {
237-
_response->_ack(this, len, time);
238-
} else if (_response->_finished()) {
239-
AsyncWebServerResponse *r = _response;
240-
_response = NULL;
241-
delete r;
242-
243-
_client->close();
227+
if (!_response) return;
228+
229+
if (!_response->_finished()) {
230+
_response->_ack(this, len, time);
231+
// recheck if response has just completed, close connection
232+
if (_response->_finished()) {
233+
_client->close(); // this will trigger _onDisconnect() and object destruction
244234
}
245235
}
246236
}
@@ -709,7 +699,7 @@ void AsyncWebServerRequest::_send() {
709699
send(500, T_text_plain, "Invalid data in handler");
710700
}
711701

712-
// here, we either have a response give nfrom user or one of the two above
702+
// here, we either have a response given from user or one of the two above
713703
_client->setRxTimeout(0);
714704
_response->_respond(this);
715705
_sent = true;

src/WebResponseImpl.h

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,12 @@
1414
#include <memory>
1515
#include <vector>
1616

17+
#ifndef CONFIG_LWIP_TCP_MSS
18+
// as it is defined for ESP32's Arduino LWIP
19+
#define CONFIG_LWIP_TCP_MSS 1436
20+
#endif
21+
22+
#define ASYNC_RESPONCE_BUFF_SIZE CONFIG_LWIP_TCP_MSS * 2
1723
// It is possible to restore these defines, but one can use _min and _max instead. Or std::min, std::max.
1824

1925
class AsyncBasicResponse : public AsyncWebServerResponse {
@@ -39,12 +45,19 @@ class AsyncAbstractResponse : public AsyncWebServerResponse {
3945
// in-flight queue credits
4046
size_t _in_flight_credit{2};
4147
#endif
42-
String _head;
48+
// buffer to accumulate all response headers
49+
String _assembled_headers;
50+
// amount of headers buffer writtent to sockbuff
51+
size_t _assembled_headers_written{0};
4352
// Data is inserted into cache at begin().
4453
// This is inefficient with vector, but if we use some other container,
4554
// we won't be able to access it as contiguous array of bytes when reading from it,
4655
// so by gaining performance in one place, we'll lose it in another.
4756
std::vector<uint8_t> _cache;
57+
// intermediate buffer to copy outbound data to, also it will keep pending data between _send calls
58+
std::unique_ptr< std::array<uint8_t, ASYNC_RESPONCE_BUFF_SIZE> > _send_buffer;
59+
// buffer data size specifiers
60+
size_t _send_buffer_offset{0}, _send_buffer_len{0};
4861
size_t _readDataFromCacheOrContent(uint8_t *data, const size_t len);
4962
size_t _fillBufferAndProcessTemplates(uint8_t *buf, size_t maxLen);
5063

0 commit comments

Comments
 (0)