Performance¶
Benchmark Setup¶
All data was collected by running 100 consecutive HTTP GET requests with a JSON payload on a real ESP32 device against the public JSONPlaceholder /users endpoint. Both libraries were configured with default settings. The WiFi connection was already established before the benchmark started.
Results¶
| Metric / Feature | Standard (HTTPClient + ArduinoJson) | ESP32-HTTP-Client | Comparison |
|---|---|---|---|
| Heap allocation per request | ~58.2 KB | ~15 bytes | ⬇ ~99.9% less RAM |
| Avg. RAM footprint | 34.2% | 24.3% | ⬇ ~29% less overall |
| Minimum free heap | 114.3 KB | 128.6 KB | ⬆ Safer headroom |
| Avg. execution time | ~750 ms | ~59 ms | 🚀 ~12× faster |
| Code verbosity | ~15 lines of boilerplate | 1 fluent chain | ⬇ Clean & maintainable |
| JSON parsing | deserializeJson() |
Automatic, direct binding | ⬆ No document allocation |
Why is it so much faster?¶
HTTP Keep-Alive¶
ESP32HTTPClient enables HTTP/1.1 Keep-Alive by default via _http.setReuse(true). This allows the underlying TCP/TLS connection to be reused across multiple requests to the same server.
The standard approach typically closes and reopens the connection on every call, paying the full TLS handshake cost (~700–800 ms on a typical WiFi network) each time.
Zero-Copy Stream Parsing¶
Instead of calling http.getString() (which allocates the entire response in heap memory) and then passing it to deserializeJson() (which allocates a DynamicJsonDocument), this library:
- Obtains a raw
Stream*pointer directly from theHTTPClient. - Parses the JSON stream byte by byte through
BufferedStreamReader. - Writes matched values directly into your pre-declared C variables.
The full response body is never stored in memory. The total heap allocation for a typical response is ~15 bytes of internal state.
Chunked Transfer Encoding¶
Modern APIs commonly respond with Transfer-Encoding: chunked. The library's BufferedStreamReader automatically decodes chunked responses without requiring the full payload to be buffered first.
Memory Trade-offs & TLS Heap Lifecycle¶
| Scenario | Heap allocated during request | RAM freed by end() |
|---|---|---|
Plain HTTP connection (http://) |
~15 bytes | ~5–10 KB socket buffers |
TLS without config (https:// default) |
Core default | Core default |
Configured TLS (setCACert, bundle, etc.) |
~45 KB (mbedTLS buffers) | ~45 KB (TLS context + record buffers) |
On-Demand TLS Allocation¶
ESP32HTTPClient uses lazy on-demand allocation for secure connections. The internal secure client is not initialized during object construction. Sketches communicating solely over http:// incur zero TLS heap overhead.
When an https:// connection is established:
1. Initial Handshake: mbedTLS allocates internal SSL contexts and TX/RX record buffers (~45 KB).
2. Session Reuse (Keep-Alive): Subsequent requests to the same server reuse the already established TLS session, incurring no additional heap allocations and executing up to 12× faster.
3. Releasing TLS Memory: Calling client.end() terminates the TLS session, disconnects the socket, and frees the ~45 KB of mbedTLS buffers back to the heap. Subsequent calls will automatically initiate a fresh handshake when needed.
Call client.end() after a burst of requests if your sketch enters a long idle period:
// Burst of requests
for (int i = 0; i < 10; i++) {
client.get("/data").getBody("v", &val);
// ... process val
}
client.end(); // free ~45KB of TLS buffers
delay(60 * 1000); // sleep for 1 minute
Benchmark Source¶
The full benchmark sketches and comparison scripts are in the bench/ directory of the repository.