Skip to content

Commit aa23647

Browse files
authored
Merge pull request #20 from playmiel/dev
Update to version 2.1.0 and improvement of asynchronous HTTP request …
2 parents 9544e1a + 7a44202 commit aa23647

7 files changed

Lines changed: 198 additions & 95 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

library.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "ESPAsyncWebClient",
3-
"version": "2.0.0",
3+
"version": "2.1.0",
44
"description": "Asynchronous HTTP client library for ESP32 ",
55
"keywords": [
66
"http",

library.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name=ESPAsyncWebClient
2-
version=2.0.0
2+
version=2.1.0
33
author=playmiel
44
maintainer=playmiel
55
sentence=Asynchronous HTTP client library for ESP32 microcontrollers

platformio.ini

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,9 @@ build_flags =
1212
-DCORE_DEBUG_LEVEL=3
1313

1414
[env:esp32dev]
15-
platform = espressif32
15+
platform = https://github.com/pioarduino/platform-espressif32/releases/download/stable/platform-espressif32.zip
1616
board = esp32dev
1717
framework = arduino
18-
platform_packages =
19-
framework-arduinoespressif32@^3
2018
build_src_filter = -<*> +<../src/> +<../test/compile_test_internal/compile_test.cpp>
2119
; Run only Arduino-suitable tests on the device build
2220
test_filter = test_parse_url, test_chunk_parse, test_keep_alive, test_cookies, test_redirects
@@ -27,11 +25,9 @@ lib_deps =
2725

2826
# Environment for testing compilation without networking
2927
[env:compile_test]
30-
platform = espressif32
28+
platform = https://github.com/pioarduino/platform-espressif32/releases/download/stable/platform-espressif32.zip
3129
board = esp32dev
3230
framework = arduino
33-
platform_packages =
34-
framework-arduinoespressif32@^3
3531
build_flags =
3632
${env.build_flags}
3733
-DCOMPILE_TEST_ONLY
@@ -40,11 +36,9 @@ lib_deps =
4036
ESP32Async/AsyncTCP @ ^3.4.8
4137

4238
[env:compile_test_gzip]
43-
platform = espressif32
39+
platform = https://github.com/pioarduino/platform-espressif32/releases/download/stable/platform-espressif32.zip
4440
board = esp32dev
4541
framework = arduino
46-
platform_packages =
47-
framework-arduinoespressif32@^3
4842
build_flags =
4943
${env.build_flags}
5044
-DCOMPILE_TEST_ONLY
@@ -56,11 +50,9 @@ lib_deps =
5650

5751
# Environment for testing with different AsyncTCP versions
5852
[env:esp32dev_asynctcp_dev]
59-
platform = espressif32
53+
platform = https://github.com/pioarduino/platform-espressif32/releases/download/stable/platform-espressif32.zip
6054
board = esp32dev
6155
framework = arduino
62-
platform_packages =
63-
framework-arduinoespressif32@^3
6456
build_flags =
6557
${env.build_flags}
6658
-DTEST_ASYNCTCP_DEV
@@ -71,11 +63,9 @@ lib_deps =
7163

7264
# Environment for testing with AsyncTCP stable version
7365
[env:test_asynctcp_stable]
74-
platform = espressif32
66+
platform = https://github.com/pioarduino/platform-espressif32/releases/download/stable/platform-espressif32.zip
7567
board = esp32dev
7668
framework = arduino
77-
platform_packages =
78-
framework-arduinoespressif32@^3
7969
build_flags =
8070
${env.build_flags}
8171
-DTEST_ASYNCTCP_STABLE

0 commit comments

Comments
 (0)