You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
- **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.
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.
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.
425
434
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.
427
436
428
437
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`.
429
438
@@ -461,9 +470,9 @@ Common HTTPS errors:
461
470
462
471
## Thread Safety
463
472
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.
467
476
468
477
## Dependencies
469
478
@@ -490,14 +499,13 @@ Common HTTPS errors:
490
499
## Object lifecycle / Ownership
491
500
492
501
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.
0 commit comments