Skip to content

Commit 7a44202

Browse files
committed
clang and correction
1 parent 6fccad6 commit 7a44202

3 files changed

Lines changed: 32 additions & 19 deletions

File tree

README.md

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,13 @@ std::unique_ptr<AsyncHttpRequest> request(new AsyncHttpRequest(HTTP_METHOD_GET,
117117
client.request(std::move(request), onSuccess, onError);
118118
```
119119
120+
## Migration v2 → v2.1
121+
122+
- **No breaking API changes** — all public APIs remain the same.
123+
- `setMaxBodySize()` is now enforced even when `setNoStoreBody(true)` is active (streaming mode). If you relied on unlimited streaming, call `setMaxBodySize(0)` explicitly.
124+
- `Content-Type` is now auto-detected for `post()`/`put()`/`patch()`: JSON bodies (starting with `{` or `[`) get `application/json`. Set a `Content-Type` header explicitly (via `setHeader()` or per-request) to override.
125+
- `Transfer-Encoding: gzip, chunked` (multi-value) is now correctly parsed as chunked.
126+
120127
## API Reference
121128
122129
### AsyncHttpClient Class
@@ -289,10 +296,12 @@ client.get("http://api.example.com/data",
289296

290297
### POST with JSON Data
291298

299+
Since v2.1, `Content-Type` is auto-detected: bodies starting with `{` or `[` default to `application/json`, otherwise `application/x-www-form-urlencoded`. You can still override via `client.setHeader("Content-Type", ...)` or per-request headers.
300+
292301
```cpp
293-
client.setHeader("Content-Type", "application/json");
294302
String jsonData = "{\"sensor\":\"temperature\",\"value\":25.5}";
295303

304+
// Content-Type: application/json is set automatically
296305
client.post("http://api.example.com/sensor", jsonData.c_str(),
297306
[](std::shared_ptr<AsyncHttpResponse> response) {
298307
Serial.printf("Posted data, status: %d\n", response->getStatusCode());
@@ -423,7 +432,7 @@ Notes:
423432

424433
If `Content-Length` is present, the response is considered complete once that many bytes have been received. Extra bytes (if a misbehaving server sends more) are ignored. Without `Content-Length`, completion is determined by connection close.
425434

426-
Configure `client.setMaxBodySize(maxBytes)` to abort early when the announced `Content-Length` or accumulated chunk data would exceed `maxBytes`, yielding `MAX_BODY_SIZE_EXCEEDED`. Pass `0` to disable the guard (this applies only when buffering the response body in memory).
435+
Configure `client.setMaxBodySize(maxBytes)` to abort early when the announced `Content-Length` or accumulated chunk data would exceed `maxBytes`, yielding `MAX_BODY_SIZE_EXCEEDED`. Pass `0` to disable the guard. Since v2.1 the limit is enforced even in streaming mode (`setNoStoreBody(true)`) to protect against a malicious server sending unbounded data — the bytes are counted but not stored.
427436

428437
Likewise, guard against oversized or malicious header blocks via `client.setMaxHeaderBytes(limit)`. When the cumulative response headers exceed `limit` bytes before completion of `\r\n\r\n`, the request aborts with `HEADERS_TOO_LARGE`.
429438

@@ -461,9 +470,9 @@ Common HTTPS errors:
461470

462471
## Thread Safety
463472

464-
- The library is designed for single-threaded use (Arduino main loop)
465-
- Callbacks are executed in the context of the network event loop
466-
- Keep callback functions lightweight and non-blocking
473+
- AsyncTCP callbacks run on the lwIP/WiFi task while `loop()` (or the auto-loop task) runs on a different core. Since v2.1 the library guards against use-after-free by holding `RequestContext` in `std::shared_ptr` (captured by transport lambdas) and using an `std::atomic<bool> cancelled` flag that is set before cleanup erases the context.
474+
- On ESP32 with `ASYNC_HTTP_ENABLE_AUTOLOOP`, a recursive mutex protects shared containers (`_activeRequests`, `_pendingQueue`, etc.).
475+
- Callbacks are still executed in the context of the network event loop — keep them lightweight and non-blocking.
467476

468477
## Dependencies
469478

@@ -490,14 +499,13 @@ Common HTTPS errors:
490499
## Object lifecycle / Ownership
491500

492501
1. `AsyncHttpClient::makeRequest()` creates a dynamic `AsyncHttpRequest` (or you pass yours to `request()`).
493-
2. `request()` allocates a `RequestContext`, an `AsyncHttpResponse` and an `AsyncTransport`.
494-
3. Once connected the fully built HTTP request is written (`buildHttpRequest()`).
495-
4. Reception: headers buffered until `\r\n\r\n`, then body accumulation (or chunk decoding).
496-
5. On complete success: success callback invoked with `std::shared_ptr<AsyncHttpResponse>`.
497-
6. On error or after success callback returns: `cleanup()` deletes the transport, `AsyncHttpRequest`, and `RequestContext`.
498-
7. The response is freed when the last `shared_ptr` copy is released.
499-
500-
For very large bodies or future streaming options, a hook would be placed inside `handleData` after `headersComplete` before `appendBody`.
502+
2. `request()` allocates a `RequestContext` as `shared_ptr`, an `AsyncHttpResponse` and an `AsyncTransport`.
503+
3. Transport callbacks capture the `shared_ptr<RequestContext>`, keeping the context alive even after `cleanup()` erases it from `_activeRequests`.
504+
4. Once connected the fully built HTTP request is written (`buildHttpRequest()`).
505+
5. Reception: headers buffered until `\r\n\r\n`, then body accumulation (or chunk decoding).
506+
6. On complete success: success callback invoked with `std::shared_ptr<AsyncHttpResponse>`.
507+
7. On error or after success callback returns: `cleanup()` sets `cancelled = true`, releases the transport and erases the context from `_activeRequests`. The `RequestContext` is destroyed when the last `shared_ptr` reference (including those in transport lambdas) is released.
508+
8. The response is freed when the last `shared_ptr` copy is released.
501509

502510
## Error Codes
503511

src/AsyncHttpClient.cpp

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -442,28 +442,32 @@ void AsyncHttpClient::executeRequest(RequestContext* context) {
442442
context->transport->setConnectHandler(
443443
[this, ctxShared](void* /*arg*/, AsyncTransport* t) {
444444
(void)t;
445-
if (ctxShared->cancelled.load()) return;
445+
if (ctxShared->cancelled.load())
446+
return;
446447
handleConnect(ctxShared.get());
447448
},
448449
nullptr);
449450
context->transport->setDataHandler(
450451
[this, ctxShared](void* /*arg*/, AsyncTransport* t, void* data, size_t len) {
451452
(void)t;
452-
if (ctxShared->cancelled.load()) return;
453+
if (ctxShared->cancelled.load())
454+
return;
453455
handleData(ctxShared.get(), static_cast<char*>(data), len);
454456
},
455457
nullptr);
456458
context->transport->setDisconnectHandler(
457459
[this, ctxShared](void* /*arg*/, AsyncTransport* t) {
458460
(void)t;
459-
if (ctxShared->cancelled.load()) return;
461+
if (ctxShared->cancelled.load())
462+
return;
460463
handleDisconnect(ctxShared.get());
461464
},
462465
nullptr);
463466
context->transport->setErrorHandler(
464467
[this, ctxShared](void* /*arg*/, AsyncTransport* t, HttpClientError error, const char* message) {
465468
(void)t;
466-
if (ctxShared->cancelled.load()) return;
469+
if (ctxShared->cancelled.load())
470+
return;
467471
handleTransportError(ctxShared.get(), error, message);
468472
},
469473
nullptr);
@@ -474,7 +478,8 @@ void AsyncHttpClient::executeRequest(RequestContext* context) {
474478
[this, ctxShared](void* /*arg*/, AsyncTransport* transport, uint32_t t) {
475479
(void)transport;
476480
(void)t;
477-
if (ctxShared->cancelled.load()) return;
481+
if (ctxShared->cancelled.load())
482+
return;
478483
triggerError(ctxShared.get(), REQUEST_TIMEOUT, "Request timeout");
479484
},
480485
nullptr);

src/HttpCommon.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818
// Library version (single source of truth inside code). Keep in sync with library.json and library.properties.
1919
#ifndef ESP_ASYNC_WEB_CLIENT_VERSION
20-
#define ESP_ASYNC_WEB_CLIENT_VERSION "2.0.0"
20+
#define ESP_ASYNC_WEB_CLIENT_VERSION "2.1.0"
2121
#endif
2222

2323
struct HttpHeader {

0 commit comments

Comments
 (0)