summaryrefslogtreecommitdiff
path: root/libraries/ESP_Async_WebServer/examples
diff options
context:
space:
mode:
Diffstat (limited to 'libraries/ESP_Async_WebServer/examples')
-rw-r--r--libraries/ESP_Async_WebServer/examples/AsyncResponseStream/AsyncResponseStream.ino47
-rw-r--r--libraries/ESP_Async_WebServer/examples/Auth/Auth.ino157
-rw-r--r--libraries/ESP_Async_WebServer/examples/CORS/CORS.ino60
-rw-r--r--libraries/ESP_Async_WebServer/examples/CaptivePortal/CaptivePortal.ino60
-rw-r--r--libraries/ESP_Async_WebServer/examples/CatchAllHandler/CatchAllHandler.ino133
-rw-r--r--libraries/ESP_Async_WebServer/examples/ChunkResponse/ChunkResponse.ino140
-rw-r--r--libraries/ESP_Async_WebServer/examples/ChunkRetryResponse/ChunkRetryResponse.ino216
-rw-r--r--libraries/ESP_Async_WebServer/examples/EndBegin/EndBegin.ino49
-rw-r--r--libraries/ESP_Async_WebServer/examples/Filters/Filters.ino136
-rw-r--r--libraries/ESP_Async_WebServer/examples/FlashResponse/FlashResponse.ino107
-rw-r--r--libraries/ESP_Async_WebServer/examples/HeaderManipulation/HeaderManipulation.ino88
-rw-r--r--libraries/ESP_Async_WebServer/examples/Headers/Headers.ino69
-rw-r--r--libraries/ESP_Async_WebServer/examples/Json/Json.ino90
-rw-r--r--libraries/ESP_Async_WebServer/examples/Logging/Logging.ino49
-rw-r--r--libraries/ESP_Async_WebServer/examples/MessagePack/MessagePack.ino88
-rw-r--r--libraries/ESP_Async_WebServer/examples/Middleware/Middleware.ino82
-rw-r--r--libraries/ESP_Async_WebServer/examples/Params/Params.ino122
-rw-r--r--libraries/ESP_Async_WebServer/examples/PartitionDownloader/PartitionDownloader.ino130
-rw-r--r--libraries/ESP_Async_WebServer/examples/PerfTests/PerfTests.ino243
-rw-r--r--libraries/ESP_Async_WebServer/examples/RateLimit/RateLimit.ino64
-rw-r--r--libraries/ESP_Async_WebServer/examples/Redirect/Redirect.ino48
-rw-r--r--libraries/ESP_Async_WebServer/examples/RequestContinuation/RequestContinuation.ino91
-rw-r--r--libraries/ESP_Async_WebServer/examples/RequestContinuationComplete/RequestContinuationComplete.ino165
-rw-r--r--libraries/ESP_Async_WebServer/examples/ResumableDownload/ResumableDownload.ino61
-rw-r--r--libraries/ESP_Async_WebServer/examples/Rewrite/Rewrite.ino52
-rw-r--r--libraries/ESP_Async_WebServer/examples/ServerSentEvents/ServerSentEvents.ino105
-rw-r--r--libraries/ESP_Async_WebServer/examples/ServerSentEvents_PR156/ServerSentEvents_PR156.ino141
-rw-r--r--libraries/ESP_Async_WebServer/examples/ServerState/ServerState.ino66
-rw-r--r--libraries/ESP_Async_WebServer/examples/SkipServerMiddleware/SkipServerMiddleware.ino73
-rw-r--r--libraries/ESP_Async_WebServer/examples/SlowChunkResponse/SlowChunkResponse.ino152
-rw-r--r--libraries/ESP_Async_WebServer/examples/StaticFile/StaticFile.ino144
-rw-r--r--libraries/ESP_Async_WebServer/examples/Templates/Templates.ino99
-rw-r--r--libraries/ESP_Async_WebServer/examples/Upload/Upload.ino171
-rw-r--r--libraries/ESP_Async_WebServer/examples/WebSocket/WebSocket.ino115
-rw-r--r--libraries/ESP_Async_WebServer/examples/WebSocketEasy/WebSocketEasy.ino124
35 files changed, 3737 insertions, 0 deletions
diff --git a/libraries/ESP_Async_WebServer/examples/AsyncResponseStream/AsyncResponseStream.ino b/libraries/ESP_Async_WebServer/examples/AsyncResponseStream/AsyncResponseStream.ino
new file mode 100644
index 0000000..62fa799
--- /dev/null
+++ b/libraries/ESP_Async_WebServer/examples/AsyncResponseStream/AsyncResponseStream.ino
@@ -0,0 +1,47 @@
+// SPDX-License-Identifier: LGPL-3.0-or-later
+// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
+
+#include <DNSServer.h>
+#ifdef ESP32
+#include <AsyncTCP.h>
+#include <WiFi.h>
+#elif defined(ESP8266)
+#include <ESP8266WiFi.h>
+#include <ESPAsyncTCP.h>
+#elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
+#include <RPAsyncTCP.h>
+#include <WiFi.h>
+#endif
+
+#include <ESPAsyncWebServer.h>
+
+static AsyncWebServer server(80);
+
+void setup() {
+ Serial.begin(115200);
+
+#ifndef CONFIG_IDF_TARGET_ESP32H2
+ WiFi.mode(WIFI_AP);
+ WiFi.softAP("esp-captive");
+#endif
+
+ // Shows how to use AsyncResponseStream.
+ // The internal buffer will be allocated and data appended to it,
+ // until the response is sent, then this buffer is read and committed on the network.
+ //
+ // curl -v http://192.168.4.1/
+ //
+ server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
+ AsyncResponseStream *response = request->beginResponseStream("plain/text", 40 * 1024);
+ for (int i = 0; i < 32 * 1024; i++) {
+ response->write('a');
+ }
+ request->send(response);
+ });
+
+ server.begin();
+}
+
+void loop() {
+ delay(100);
+}
diff --git a/libraries/ESP_Async_WebServer/examples/Auth/Auth.ino b/libraries/ESP_Async_WebServer/examples/Auth/Auth.ino
new file mode 100644
index 0000000..c3751e0
--- /dev/null
+++ b/libraries/ESP_Async_WebServer/examples/Auth/Auth.ino
@@ -0,0 +1,157 @@
+// SPDX-License-Identifier: LGPL-3.0-or-later
+// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
+
+//
+// Authentication and authorization middlewares
+//
+
+#include <Arduino.h>
+#ifdef ESP32
+#include <AsyncTCP.h>
+#include <WiFi.h>
+#elif defined(ESP8266)
+#include <ESP8266WiFi.h>
+#include <ESPAsyncTCP.h>
+#elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
+#include <RPAsyncTCP.h>
+#include <WiFi.h>
+#endif
+
+#include <ESPAsyncWebServer.h>
+
+static AsyncWebServer server(80);
+
+// basicAuth
+static AsyncAuthenticationMiddleware basicAuth;
+static AsyncAuthenticationMiddleware basicAuthHash;
+
+// simple digest authentication
+static AsyncAuthenticationMiddleware digestAuth;
+static AsyncAuthenticationMiddleware digestAuthHash;
+
+// complex authentication which adds request attributes for the next middlewares and handler
+static AsyncMiddlewareFunction complexAuth([](AsyncWebServerRequest *request, ArMiddlewareNext next) {
+ if (!request->authenticate("user", "password")) {
+ return request->requestAuthentication();
+ }
+
+ // add attributes to the request for the next middlewares and handler
+ request->setAttribute("user", "Mathieu");
+ request->setAttribute("role", "staff");
+ if (request->hasParam("token")) {
+ request->setAttribute("token", request->getParam("token")->value().c_str());
+ }
+
+ next();
+});
+
+static AsyncAuthorizationMiddleware authz([](AsyncWebServerRequest *request) {
+ return request->getAttribute("token") == "123";
+});
+
+void setup() {
+ Serial.begin(115200);
+
+#ifndef CONFIG_IDF_TARGET_ESP32H2
+ WiFi.mode(WIFI_AP);
+ WiFi.softAP("esp-captive");
+#endif
+
+ // basic authentication
+ basicAuth.setUsername("admin");
+ basicAuth.setPassword("admin");
+ basicAuth.setRealm("MyApp");
+ basicAuth.setAuthFailureMessage("Authentication failed");
+ basicAuth.setAuthType(AsyncAuthType::AUTH_BASIC);
+ basicAuth.generateHash(); // precompute hash (optional but recommended)
+
+ // basic authentication with hash
+ basicAuthHash.setUsername("admin");
+ basicAuthHash.setPasswordHash("YWRtaW46YWRtaW4="); // BASE64(admin:admin)
+ basicAuthHash.setRealm("MyApp");
+ basicAuthHash.setAuthFailureMessage("Authentication failed");
+ basicAuthHash.setAuthType(AsyncAuthType::AUTH_BASIC);
+
+ // digest authentication
+ digestAuth.setUsername("admin");
+ digestAuth.setPassword("admin");
+ digestAuth.setRealm("MyApp");
+ digestAuth.setAuthFailureMessage("Authentication failed");
+ digestAuth.setAuthType(AsyncAuthType::AUTH_DIGEST);
+ digestAuth.generateHash(); // precompute hash (optional but recommended)
+
+ // digest authentication with hash
+ digestAuthHash.setUsername("admin");
+ digestAuthHash.setPasswordHash("f499b71f9a36d838b79268e145e132f7"); // MD5(user:realm:pass)
+ digestAuthHash.setRealm("MyApp");
+ digestAuthHash.setAuthFailureMessage("Authentication failed");
+ digestAuthHash.setAuthType(AsyncAuthType::AUTH_DIGEST);
+
+ // basic authentication method
+ // curl -v -u admin:admin http://192.168.4.1/auth-basic
+ server
+ .on(
+ "/auth-basic", HTTP_GET,
+ [](AsyncWebServerRequest *request) {
+ request->send(200, "text/plain", "Hello, world!");
+ }
+ )
+ .addMiddleware(&basicAuth);
+
+ // basic authentication method with hash
+ // curl -v -u admin:admin http://192.168.4.1/auth-basic-hash
+ server
+ .on(
+ "/auth-basic-hash", HTTP_GET,
+ [](AsyncWebServerRequest *request) {
+ request->send(200, "text/plain", "Hello, world!");
+ }
+ )
+ .addMiddleware(&basicAuthHash);
+
+ // digest authentication
+ // curl -v -u admin:admin --digest http://192.168.4.1/auth-digest
+ server
+ .on(
+ "/auth-digest", HTTP_GET,
+ [](AsyncWebServerRequest *request) {
+ request->send(200, "text/plain", "Hello, world!");
+ }
+ )
+ .addMiddleware(&digestAuth);
+
+ // digest authentication with hash
+ // curl -v -u admin:admin --digest http://192.168.4.1/auth-digest-hash
+ server
+ .on(
+ "/auth-digest-hash", HTTP_GET,
+ [](AsyncWebServerRequest *request) {
+ request->send(200, "text/plain", "Hello, world!");
+ }
+ )
+ .addMiddleware(&digestAuthHash);
+
+ // test digest auth custom authorization middleware
+ // curl -v --digest -u user:password http://192.168.4.1/auth-custom?token=123 => OK
+ // curl -v --digest -u user:password http://192.168.4.1/auth-custom?token=456 => 403
+ // curl -v --digest -u user:FAILED http://192.168.4.1/auth-custom?token=456 => 401
+ server
+ .on(
+ "/auth-custom", HTTP_GET,
+ [](AsyncWebServerRequest *request) {
+ String buffer = "Hello ";
+ buffer.concat(request->getAttribute("user"));
+ buffer.concat(" with role: ");
+ buffer.concat(request->getAttribute("role"));
+ request->send(200, "text/plain", buffer);
+ }
+ )
+ .addMiddlewares({&complexAuth, &authz});
+
+ server.begin();
+}
+
+// not needed
+void loop() {
+ delay(100);
+}
diff --git a/libraries/ESP_Async_WebServer/examples/CORS/CORS.ino b/libraries/ESP_Async_WebServer/examples/CORS/CORS.ino
new file mode 100644
index 0000000..3be46fd
--- /dev/null
+++ b/libraries/ESP_Async_WebServer/examples/CORS/CORS.ino
@@ -0,0 +1,60 @@
+// SPDX-License-Identifier: LGPL-3.0-or-later
+// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
+
+//
+// How to use CORS middleware
+//
+
+#include <Arduino.h>
+#ifdef ESP32
+#include <AsyncTCP.h>
+#include <WiFi.h>
+#elif defined(ESP8266)
+#include <ESP8266WiFi.h>
+#include <ESPAsyncTCP.h>
+#elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
+#include <RPAsyncTCP.h>
+#include <WiFi.h>
+#endif
+
+#include <ESPAsyncWebServer.h>
+
+static AsyncWebServer server(80);
+static AsyncCorsMiddleware cors;
+
+void setup() {
+ Serial.begin(115200);
+
+#ifndef CONFIG_IDF_TARGET_ESP32H2
+ WiFi.mode(WIFI_AP);
+ WiFi.softAP("esp-captive");
+#endif
+
+ cors.setOrigin("http://192.168.4.1");
+ cors.setMethods("POST, GET, OPTIONS, DELETE");
+ cors.setHeaders("X-Custom-Header");
+ cors.setAllowCredentials(false);
+ cors.setMaxAge(600);
+
+ server.addMiddleware(&cors);
+
+ // Test CORS preflight request
+ // curl -v -X OPTIONS -H "origin: http://192.168.4.1" http://192.168.4.1/cors
+ //
+ // Test CORS request
+ // curl -v -H "origin: http://192.168.4.1" http://192.168.4.1/cors
+ //
+ // Test non-CORS request
+ // curl -v http://192.168.4.1/cors
+ //
+ server.on("/cors", HTTP_GET, [](AsyncWebServerRequest *request) {
+ request->send(200, "text/plain", "Hello, world!");
+ });
+
+ server.begin();
+}
+
+// not needed
+void loop() {
+ delay(100);
+}
diff --git a/libraries/ESP_Async_WebServer/examples/CaptivePortal/CaptivePortal.ino b/libraries/ESP_Async_WebServer/examples/CaptivePortal/CaptivePortal.ino
new file mode 100644
index 0000000..a872a9b
--- /dev/null
+++ b/libraries/ESP_Async_WebServer/examples/CaptivePortal/CaptivePortal.ino
@@ -0,0 +1,60 @@
+// SPDX-License-Identifier: LGPL-3.0-or-later
+// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
+
+#include <DNSServer.h>
+#ifdef ESP32
+#include <AsyncTCP.h>
+#include <WiFi.h>
+#elif defined(ESP8266)
+#include <ESP8266WiFi.h>
+#include <ESPAsyncTCP.h>
+#elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
+#include <RPAsyncTCP.h>
+#include <WiFi.h>
+#endif
+#include "ESPAsyncWebServer.h"
+
+static DNSServer dnsServer;
+static AsyncWebServer server(80);
+
+class CaptiveRequestHandler : public AsyncWebHandler {
+public:
+ bool canHandle(__unused AsyncWebServerRequest *request) const override {
+ return true;
+ }
+
+ void handleRequest(AsyncWebServerRequest *request) {
+ AsyncResponseStream *response = request->beginResponseStream("text/html");
+ response->print("<!DOCTYPE html><html><head><title>Captive Portal</title></head><body>");
+ response->print("<p>This is our captive portal front page.</p>");
+ response->printf("<p>You were trying to reach: http://%s%s</p>", request->host().c_str(), request->url().c_str());
+#ifndef CONFIG_IDF_TARGET_ESP32H2
+ response->printf("<p>Try opening <a href='http://%s'>this link</a> instead</p>", WiFi.softAPIP().toString().c_str());
+#endif
+ response->print("</body></html>");
+ request->send(response);
+ }
+};
+
+void setup() {
+ Serial.begin(115200);
+ Serial.println();
+ Serial.println("Configuring access point...");
+
+#ifndef CONFIG_IDF_TARGET_ESP32H2
+ if (!WiFi.softAP("esp-captive")) {
+ Serial.println("Soft AP creation failed.");
+ while (1);
+ }
+
+ dnsServer.start(53, "*", WiFi.softAPIP());
+#endif
+
+ server.addHandler(new CaptiveRequestHandler()).setFilter(ON_AP_FILTER); // only when requested from AP
+ // more handlers...
+ server.begin();
+}
+
+void loop() {
+ dnsServer.processNextRequest();
+}
diff --git a/libraries/ESP_Async_WebServer/examples/CatchAllHandler/CatchAllHandler.ino b/libraries/ESP_Async_WebServer/examples/CatchAllHandler/CatchAllHandler.ino
new file mode 100644
index 0000000..42a3698
--- /dev/null
+++ b/libraries/ESP_Async_WebServer/examples/CatchAllHandler/CatchAllHandler.ino
@@ -0,0 +1,133 @@
+// SPDX-License-Identifier: LGPL-3.0-or-later
+// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
+
+//
+// Shows how to catch all requests and send a 404 Not Found response
+//
+
+#include <Arduino.h>
+#ifdef ESP32
+#include <AsyncTCP.h>
+#include <WiFi.h>
+#elif defined(ESP8266)
+#include <ESP8266WiFi.h>
+#include <ESPAsyncTCP.h>
+#elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
+#include <RPAsyncTCP.h>
+#include <WiFi.h>
+#endif
+
+#include <ESPAsyncWebServer.h>
+
+static AsyncWebServer server(80);
+
+static const char *htmlContent PROGMEM = R"(
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Sample HTML</title>
+</head>
+<body>
+ <h1>Hello, World!</h1>
+ <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
+ rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
+ arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
+ accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
+ Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
+ dapibus elit, id varius sem dui id lacus.</p>
+ <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
+ rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
+ arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
+ accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
+ Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
+ dapibus elit, id varius sem dui id lacus.</p>
+ <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
+ rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
+ arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
+ accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
+ Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
+ dapibus elit, id varius sem dui id lacus.</p>
+ <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
+ rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
+ arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
+ accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
+ Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
+ dapibus elit, id varius sem dui id lacus.</p>
+ <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
+ rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
+ arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
+ accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
+ Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
+ dapibus elit, id varius sem dui id lacus.</p>
+ <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
+ rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
+ arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
+ accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
+ Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
+ dapibus elit, id varius sem dui id lacus.</p>
+ <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
+ rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
+ arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
+ accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
+ Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
+ dapibus elit, id varius sem dui id lacus.</p>
+ <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
+ rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
+ arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
+ accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
+ Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
+ dapibus elit, id varius sem dui id lacus.</p>
+</body>
+</html>
+)";
+
+static const size_t htmlContentLength = strlen_P(htmlContent);
+
+void setup() {
+ Serial.begin(115200);
+
+#ifndef CONFIG_IDF_TARGET_ESP32H2
+ WiFi.mode(WIFI_AP);
+ WiFi.softAP("esp-captive");
+#endif
+
+ // curl -v http://192.168.4.1/
+ server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
+ // need to cast to uint8_t*
+ // if you do not, the const char* will be copied in a temporary String buffer
+ request->send(200, "text/html", (uint8_t *)htmlContent, htmlContentLength);
+ });
+
+ // catch any request, and send a 404 Not Found response
+ // except for /game_log which is handled by onRequestBody
+ //
+ // curl -v http://192.168.4.1/foo
+ //
+ server.onNotFound([](AsyncWebServerRequest *request) {
+ if (request->url() == "/game_log") {
+ return; // response object already created by onRequestBody
+ }
+
+ request->send(404, "text/plain", "Not found");
+ });
+
+ // See: https://github.com/ESP32Async/ESPAsyncWebServer/issues/6
+ // catch any POST request and send a 200 OK response
+ //
+ // curl -v -X POST http://192.168.4.1/game_log -H "Content-Type: application/json" -d '{"game": "test"}'
+ //
+ server.onRequestBody([](AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) {
+ if (request->url() == "/game_log") {
+ request->send(200, "application/json", "{\"status\":\"OK\"}");
+ }
+ // note that there is no else here: the goal is only to prepare a response based on some body content
+ // onNotFound will always be called after this, and will not override the response object if `/game_log` is requested
+ });
+
+ server.begin();
+}
+
+// not needed
+void loop() {
+ delay(100);
+}
diff --git a/libraries/ESP_Async_WebServer/examples/ChunkResponse/ChunkResponse.ino b/libraries/ESP_Async_WebServer/examples/ChunkResponse/ChunkResponse.ino
new file mode 100644
index 0000000..e7d4838
--- /dev/null
+++ b/libraries/ESP_Async_WebServer/examples/ChunkResponse/ChunkResponse.ino
@@ -0,0 +1,140 @@
+// SPDX-License-Identifier: LGPL-3.0-or-later
+// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
+
+//
+// Chunk response with caching example
+//
+
+#include <Arduino.h>
+#ifdef ESP32
+#include <AsyncTCP.h>
+#include <WiFi.h>
+#elif defined(ESP8266)
+#include <ESP8266WiFi.h>
+#include <ESPAsyncTCP.h>
+#elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
+#include <RPAsyncTCP.h>
+#include <WiFi.h>
+#endif
+
+#include <ESPAsyncWebServer.h>
+
+static AsyncWebServer server(80);
+
+static const char *htmlContent PROGMEM = R"(
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Sample HTML</title>
+</head>
+<body>
+ <h1>Hello, World!</h1>
+ <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
+ rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
+ arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
+ accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
+ Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
+ dapibus elit, id varius sem dui id lacus.</p>
+ <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
+ rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
+ arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
+ accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
+ Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
+ dapibus elit, id varius sem dui id lacus.</p>
+ <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
+ rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
+ arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
+ accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
+ Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
+ dapibus elit, id varius sem dui id lacus.</p>
+ <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
+ rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
+ arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
+ accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
+ Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
+ dapibus elit, id varius sem dui id lacus.</p>
+ <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
+ rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
+ arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
+ accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
+ Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
+ dapibus elit, id varius sem dui id lacus.</p>
+ <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
+ rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
+ arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
+ accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
+ Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
+ dapibus elit, id varius sem dui id lacus.</p>
+ <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
+ rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
+ arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
+ accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
+ Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
+ dapibus elit, id varius sem dui id lacus.</p>
+ <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
+ rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
+ arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
+ accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
+ Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
+ dapibus elit, id varius sem dui id lacus.</p>
+</body>
+</html>
+)";
+
+static const size_t htmlContentLength = strlen_P(htmlContent);
+
+void setup() {
+ Serial.begin(115200);
+
+#ifndef CONFIG_IDF_TARGET_ESP32H2
+ WiFi.mode(WIFI_AP);
+ WiFi.softAP("esp-captive");
+#endif
+
+ // first time: serves the file and cache headers
+ // curl -N -v http://192.168.4.1/ --output -
+ //
+ // secodn time: serves 304
+ // curl -N -v -H "if-none-match: 4272" http://192.168.4.1/ --output -
+ //
+ server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
+ String etag = String(htmlContentLength);
+
+ if (request->header(asyncsrv::T_INM) == etag) {
+ request->send(304);
+ return;
+ }
+
+ AsyncWebServerResponse *response = request->beginChunkedResponse("text/html", [](uint8_t *buffer, size_t maxLen, size_t index) -> size_t {
+ Serial.printf("%u / %u\n", index, htmlContentLength);
+
+ // finished ?
+ if (htmlContentLength <= index) {
+ Serial.println("finished");
+ return 0;
+ }
+
+ // serve a maximum of 256 or maxLen bytes of the remaining content
+ // this small number is specifically chosen to demonstrate the chunking
+ // DO NOT USE SUCH SMALL NUMBER IN PRODUCTION
+ // Reducing the chunk size will increase the response time, thus reducing the server's capacity in processing concurrent requests
+ const int chunkSize = min((size_t)256, min(maxLen, htmlContentLength - index));
+ Serial.printf("sending: %u\n", chunkSize);
+
+ memcpy(buffer, htmlContent + index, chunkSize);
+
+ return chunkSize;
+ });
+
+ response->addHeader(asyncsrv::T_Cache_Control, "public,max-age=60");
+ response->addHeader(asyncsrv::T_ETag, etag);
+
+ request->send(response);
+ });
+
+ server.begin();
+}
+
+void loop() {
+ delay(100);
+}
diff --git a/libraries/ESP_Async_WebServer/examples/ChunkRetryResponse/ChunkRetryResponse.ino b/libraries/ESP_Async_WebServer/examples/ChunkRetryResponse/ChunkRetryResponse.ino
new file mode 100644
index 0000000..48772cc
--- /dev/null
+++ b/libraries/ESP_Async_WebServer/examples/ChunkRetryResponse/ChunkRetryResponse.ino
@@ -0,0 +1,216 @@
+// SPDX-License-Identifier: LGPL-3.0-or-later
+// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
+
+//
+// Shows how to wait in a chunk response for incoming data
+//
+
+#include <Arduino.h>
+#ifdef ESP32
+#include <AsyncTCP.h>
+#include <WiFi.h>
+#elif defined(ESP8266)
+#include <ESP8266WiFi.h>
+#include <ESPAsyncTCP.h>
+#elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
+#include <RPAsyncTCP.h>
+#include <WiFi.h>
+#endif
+
+#include <ESPAsyncWebServer.h>
+
+#if __has_include("ArduinoJson.h")
+#include <ArduinoJson.h>
+#include <AsyncJson.h>
+#include <AsyncMessagePack.h>
+#endif
+
+static const char *htmlContent PROGMEM = R"(
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Sample HTML</title>
+</head>
+<body>
+ <h1>Hello, World!</h1>
+ <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
+ rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
+ arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
+ accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
+ Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
+ dapibus elit, id varius sem dui id lacus.</p>
+ <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
+ rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
+ arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
+ accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
+ Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
+ dapibus elit, id varius sem dui id lacus.</p>
+ <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
+ rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
+ arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
+ accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
+ Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
+ dapibus elit, id varius sem dui id lacus.</p>
+ <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
+ rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
+ arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
+ accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
+ Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
+ dapibus elit, id varius sem dui id lacus.</p>
+ <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
+ rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
+ arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
+ accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
+ Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
+ dapibus elit, id varius sem dui id lacus.</p>
+ <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
+ rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
+ arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
+ accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
+ Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
+ dapibus elit, id varius sem dui id lacus.</p>
+ <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
+ rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
+ arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
+ accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
+ Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
+ dapibus elit, id varius sem dui id lacus.</p>
+ <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
+ rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
+ arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
+ accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
+ Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
+ dapibus elit, id varius sem dui id lacus.</p>
+</body>
+</html>
+)";
+
+static const size_t htmlContentLength = strlen_P(htmlContent);
+
+static AsyncWebServer server(80);
+static AsyncLoggingMiddleware requestLogger;
+
+static String triggerUART;
+static int key = -1;
+
+void setup() {
+ Serial.begin(115200);
+
+#ifndef CONFIG_IDF_TARGET_ESP32H2
+ WiFi.mode(WIFI_AP);
+ WiFi.softAP("esp-captive");
+#endif
+
+ // adds some internal request logging for debugging
+ requestLogger.setEnabled(true);
+ requestLogger.setOutput(Serial);
+
+ server.addMiddleware(&requestLogger);
+
+#if __has_include("ArduinoJson.h")
+
+ //
+ // HOW TO RUN THIS EXAMPLE:
+ //
+ // 1. Trigger a request that will be blocked for a long time:
+ // > time curl -v -X POST http://192.168.4.1/api -H "Content-Type: application/json" -d '{"input": "Please type a key to continue in Serial console..."}' --output -
+ //
+ // 2. While waiting, in another terminal, run some concurrent requests:
+ // > time curl -v http://192.168.4.1/
+ //
+ // 3. Type a key in the Serial console to continue the processing within 30 seconds.
+ // This should unblock the first request.
+ //
+ server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
+ // need to cast to uint8_t*
+ // if you do not, the const char* will be copied in a temporary String buffer
+ request->send(200, "text/html", (uint8_t *)htmlContent, htmlContentLength);
+ });
+
+ server.on(
+ "/api", HTTP_POST,
+ [](AsyncWebServerRequest *request) {
+ // request parsing has finished
+
+ // no data ?
+ if (!((String *)request->_tempObject)->length()) {
+ request->send(400);
+ return;
+ }
+
+ JsonDocument doc;
+
+ // deserialize and check for errors
+ if (deserializeJson(doc, *(String *)request->_tempObject)) {
+ request->send(400);
+ return;
+ }
+
+ // start UART com: UART will send the data to the Serial console and wait for the key press
+ triggerUART = doc["input"].as<const char *>();
+ key = -1;
+
+ AsyncWebServerResponse *response = request->beginChunkedResponse("text/plain", [](uint8_t *buffer, size_t maxLen, size_t index) -> size_t {
+ // still waiting for UARY ?
+ if (triggerUART.length() && key == -1) {
+ return RESPONSE_TRY_AGAIN;
+ }
+
+ // finished ?
+ if (!triggerUART.length() && key == -1) {
+ return 0; // 0 means we are done
+ }
+
+ // log_d("UART answered!");
+
+ String answer = "You typed: ";
+ answer.concat((char)key);
+
+ // note: I did not check for maxLen, but you should (see ChunkResponse.ino)
+ memcpy(buffer, answer.c_str(), answer.length());
+
+ // finish!
+ triggerUART = emptyString;
+ key = -1;
+
+ return answer.length();
+ });
+
+ request->send(response);
+ },
+ NULL, // upload handler is not used so it should be NULL
+ [](AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) {
+ // log_d("Body: index: %u, len: %u, total: %u", index, len, total);
+
+ if (!index) {
+ // log_d("Start body parsing");
+ request->_tempObject = new String();
+ // cast request->_tempObject pointer to String and reserve total size
+ ((String *)request->_tempObject)->reserve(total);
+ // set timeout 30s
+ request->client()->setRxTimeout(30);
+ }
+
+ // log_d("Append body data");
+ ((String *)request->_tempObject)->concat((const char *)data, len);
+ }
+ );
+
+#endif
+
+ server.begin();
+}
+
+void loop() {
+ if (triggerUART.length() && key == -1) {
+ Serial.println(triggerUART);
+ // log_d("Waiting for UART input...");
+ while (!Serial.available()) {
+ delay(100);
+ }
+ key = Serial.read();
+ Serial.flush();
+ // log_d("UART input: %c", key);
+ triggerUART = emptyString;
+ }
+}
diff --git a/libraries/ESP_Async_WebServer/examples/EndBegin/EndBegin.ino b/libraries/ESP_Async_WebServer/examples/EndBegin/EndBegin.ino
new file mode 100644
index 0000000..acfc6ff
--- /dev/null
+++ b/libraries/ESP_Async_WebServer/examples/EndBegin/EndBegin.ino
@@ -0,0 +1,49 @@
+// SPDX-License-Identifier: LGPL-3.0-or-later
+// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
+
+//
+// https://github.com/ESP32Async/ESPAsyncWebServer/discussions/23
+//
+
+#include <Arduino.h>
+#ifdef ESP32
+#include <AsyncTCP.h>
+#include <WiFi.h>
+#elif defined(ESP8266)
+#include <ESP8266WiFi.h>
+#include <ESPAsyncTCP.h>
+#elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
+#include <RPAsyncTCP.h>
+#include <WiFi.h>
+#endif
+
+#include <ESPAsyncWebServer.h>
+
+static AsyncWebServer server(80);
+
+void setup() {
+ Serial.begin(115200);
+
+#ifndef CONFIG_IDF_TARGET_ESP32H2
+ WiFi.mode(WIFI_AP);
+ WiFi.softAP("esp-captive");
+#endif
+
+ server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
+ request->send(200, "text/plain", "Hello, world");
+ });
+
+ server.begin();
+ Serial.println("begin() - run: curl -v http://192.168.4.1/ => should succeed");
+ delay(10000);
+
+ Serial.println("end()");
+ server.end();
+ server.begin();
+ Serial.println("begin() - run: curl -v http://192.168.4.1/ => should succeed");
+}
+
+// not needed
+void loop() {
+ delay(100);
+}
diff --git a/libraries/ESP_Async_WebServer/examples/Filters/Filters.ino b/libraries/ESP_Async_WebServer/examples/Filters/Filters.ino
new file mode 100644
index 0000000..519478c
--- /dev/null
+++ b/libraries/ESP_Async_WebServer/examples/Filters/Filters.ino
@@ -0,0 +1,136 @@
+// SPDX-License-Identifier: LGPL-3.0-or-later
+// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
+
+//
+// Shows how to use setFilter to route requests to different handlers based on WiFi mode
+//
+
+#include <DNSServer.h>
+#ifdef ESP32
+#include <AsyncTCP.h>
+#include <WiFi.h>
+#elif defined(ESP8266)
+#include <ESP8266WiFi.h>
+#include <ESPAsyncTCP.h>
+#elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
+#include <RPAsyncTCP.h>
+#include <WiFi.h>
+#endif
+#include "ESPAsyncWebServer.h"
+
+static DNSServer dnsServer;
+static AsyncWebServer server(80);
+
+class CaptiveRequestHandler : public AsyncWebHandler {
+public:
+ bool canHandle(__unused AsyncWebServerRequest *request) const override {
+ return true;
+ }
+
+ void handleRequest(AsyncWebServerRequest *request) override {
+ AsyncResponseStream *response = request->beginResponseStream("text/html");
+ response->print("<!DOCTYPE html><html><head><title>Captive Portal</title></head><body>");
+ response->print("<p>This is out captive portal front page.</p>");
+ response->printf("<p>You were trying to reach: http://%s%s</p>", request->host().c_str(), request->url().c_str());
+#ifndef CONFIG_IDF_TARGET_ESP32H2
+ response->printf("<p>Try opening <a href='http://%s'>this link</a> instead</p>", WiFi.softAPIP().toString().c_str());
+#endif
+ response->print("</body></html>");
+ request->send(response);
+ }
+};
+
+bool hit1 = false;
+bool hit2 = false;
+
+void setup() {
+ Serial.begin(115200);
+
+ server
+ .on(
+ "/", HTTP_GET,
+ [](AsyncWebServerRequest *request) {
+ Serial.println("Captive portal request...");
+#ifndef CONFIG_IDF_TARGET_ESP32H2
+ Serial.println("WiFi.localIP(): " + WiFi.localIP().toString());
+#endif
+ Serial.println("request->client()->localIP(): " + request->client()->localIP().toString());
+#if ESP_IDF_VERSION_MAJOR >= 5
+#ifndef CONFIG_IDF_TARGET_ESP32H2
+ Serial.println("WiFi.type(): " + String((int)WiFi.localIP().type()));
+#endif
+ Serial.println("request->client()->type(): " + String((int)request->client()->localIP().type()));
+#endif
+#ifndef CONFIG_IDF_TARGET_ESP32H2
+ Serial.println(WiFi.localIP() == request->client()->localIP() ? "should be: ON_STA_FILTER" : "should be: ON_AP_FILTER");
+ Serial.println(WiFi.localIP() == request->client()->localIP());
+ Serial.println(WiFi.localIP().toString() == request->client()->localIP().toString());
+#endif
+ request->send(200, "text/plain", "This is the captive portal");
+ hit1 = true;
+ }
+ )
+ .setFilter(ON_AP_FILTER);
+
+ server
+ .on(
+ "/", HTTP_GET,
+ [](AsyncWebServerRequest *request) {
+ Serial.println("Website request...");
+#ifndef CONFIG_IDF_TARGET_ESP32H2
+ Serial.println("WiFi.localIP(): " + WiFi.localIP().toString());
+#endif
+ Serial.println("request->client()->localIP(): " + request->client()->localIP().toString());
+#if ESP_IDF_VERSION_MAJOR >= 5
+#ifndef CONFIG_IDF_TARGET_ESP32H2
+ Serial.println("WiFi.type(): " + String((int)WiFi.localIP().type()));
+#endif
+ Serial.println("request->client()->type(): " + String((int)request->client()->localIP().type()));
+#endif
+#ifndef CONFIG_IDF_TARGET_ESP32H2
+ Serial.println(WiFi.localIP() == request->client()->localIP() ? "should be: ON_STA_FILTER" : "should be: ON_AP_FILTER");
+ Serial.println(WiFi.localIP() == request->client()->localIP());
+ Serial.println(WiFi.localIP().toString() == request->client()->localIP().toString());
+#endif
+ request->send(200, "text/plain", "This is the website");
+ hit2 = true;
+ }
+ )
+ .setFilter(ON_STA_FILTER);
+
+ // assert(WiFi.softAP("esp-captive-portal"));
+ // dnsServer.start(53, "*", WiFi.softAPIP());
+ // server.begin();
+ // Serial.println("Captive portal started!");
+
+ // while (!hit1) {
+ // dnsServer.processNextRequest();
+ // yield();
+ // }
+ // delay(1000); // Wait for the client to process the response
+
+ // Serial.println("Captive portal opened, stopping it and connecting to WiFi...");
+ // dnsServer.stop();
+ // WiFi.softAPdisconnect();
+
+#ifndef CONFIG_IDF_TARGET_ESP32H2
+ WiFi.persistent(false);
+ WiFi.begin("IoT");
+ while (WiFi.status() != WL_CONNECTED) {
+ delay(500);
+ }
+ Serial.println("Connected to WiFi with IP address: " + WiFi.localIP().toString());
+#endif
+
+ server.begin();
+
+ // while (!hit2) {
+ // delay(10);
+ // }
+ // delay(1000); // Wait for the client to process the response
+ // ESP.restart();
+}
+
+void loop() {
+ delay(100);
+}
diff --git a/libraries/ESP_Async_WebServer/examples/FlashResponse/FlashResponse.ino b/libraries/ESP_Async_WebServer/examples/FlashResponse/FlashResponse.ino
new file mode 100644
index 0000000..6948cd2
--- /dev/null
+++ b/libraries/ESP_Async_WebServer/examples/FlashResponse/FlashResponse.ino
@@ -0,0 +1,107 @@
+// SPDX-License-Identifier: LGPL-3.0-or-later
+// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
+
+//
+// Shows how to serve a large HTML page from flash memory without copying it to heap in a temporary buffer
+//
+
+#include <Arduino.h>
+#ifdef ESP32
+#include <AsyncTCP.h>
+#include <WiFi.h>
+#elif defined(ESP8266)
+#include <ESP8266WiFi.h>
+#include <ESPAsyncTCP.h>
+#elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
+#include <RPAsyncTCP.h>
+#include <WiFi.h>
+#endif
+
+#include <ESPAsyncWebServer.h>
+
+static AsyncWebServer server(80);
+
+static const char *htmlContent PROGMEM = R"(
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Sample HTML</title>
+</head>
+<body>
+ <h1>Hello, World!</h1>
+ <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
+ rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
+ arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
+ accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
+ Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
+ dapibus elit, id varius sem dui id lacus.</p>
+ <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
+ rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
+ arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
+ accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
+ Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
+ dapibus elit, id varius sem dui id lacus.</p>
+ <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
+ rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
+ arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
+ accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
+ Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
+ dapibus elit, id varius sem dui id lacus.</p>
+ <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
+ rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
+ arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
+ accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
+ Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
+ dapibus elit, id varius sem dui id lacus.</p>
+ <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
+ rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
+ arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
+ accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
+ Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
+ dapibus elit, id varius sem dui id lacus.</p>
+ <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
+ rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
+ arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
+ accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
+ Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
+ dapibus elit, id varius sem dui id lacus.</p>
+ <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
+ rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
+ arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
+ accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
+ Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
+ dapibus elit, id varius sem dui id lacus.</p>
+ <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
+ rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
+ arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
+ accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
+ Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
+ dapibus elit, id varius sem dui id lacus.</p>
+</body>
+</html>
+)";
+
+static const size_t htmlContentLength = strlen_P(htmlContent);
+
+void setup() {
+ Serial.begin(115200);
+
+#ifndef CONFIG_IDF_TARGET_ESP32H2
+ WiFi.mode(WIFI_AP);
+ WiFi.softAP("esp-captive");
+#endif
+
+ // curl -v http://192.168.4.1/
+ server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
+ // need to cast to uint8_t*
+ // if you do not, the const char* will be copied in a temporary String buffer
+ request->send(200, "text/html", (uint8_t *)htmlContent, htmlContentLength);
+ });
+
+ server.begin();
+}
+
+// not needed
+void loop() {
+ delay(100);
+}
diff --git a/libraries/ESP_Async_WebServer/examples/HeaderManipulation/HeaderManipulation.ino b/libraries/ESP_Async_WebServer/examples/HeaderManipulation/HeaderManipulation.ino
new file mode 100644
index 0000000..4fe34dc
--- /dev/null
+++ b/libraries/ESP_Async_WebServer/examples/HeaderManipulation/HeaderManipulation.ino
@@ -0,0 +1,88 @@
+// SPDX-License-Identifier: LGPL-3.0-or-later
+// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
+
+//
+// Show how to manipulate headers in the request / response
+//
+
+#include <Arduino.h>
+#ifdef ESP32
+#include <AsyncTCP.h>
+#include <WiFi.h>
+#elif defined(ESP8266)
+#include <ESP8266WiFi.h>
+#include <ESPAsyncTCP.h>
+#elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
+#include <RPAsyncTCP.h>
+#include <WiFi.h>
+#endif
+
+#include <ESPAsyncWebServer.h>
+
+static AsyncWebServer server(80);
+
+// request logger
+static AsyncLoggingMiddleware requestLogger;
+
+// filter out specific headers from the incoming request
+static AsyncHeaderFilterMiddleware headerFilter;
+
+// remove all headers from the incoming request except the ones provided in the constructor
+AsyncHeaderFreeMiddleware headerFree;
+
+void setup() {
+ Serial.begin(115200);
+
+#ifndef CONFIG_IDF_TARGET_ESP32H2
+ WiFi.mode(WIFI_AP);
+ WiFi.softAP("esp-captive");
+#endif
+
+ requestLogger.setEnabled(true);
+ requestLogger.setOutput(Serial);
+
+ headerFilter.filter("X-Remove-Me");
+
+ headerFree.keep("X-Keep-Me");
+ headerFree.keep("host");
+
+ server.addMiddlewares({&requestLogger, &headerFilter});
+
+ // x-remove-me header will be removed
+ //
+ // curl -v -H "X-Header: Foo" -H "x-remove-me: value" http://192.168.4.1/remove
+ //
+ server.on("/remove", HTTP_GET, [](AsyncWebServerRequest *request) {
+ // print all headers
+ for (size_t i = 0; i < request->headers(); i++) {
+ const AsyncWebHeader *h = request->getHeader(i);
+ Serial.printf("Header[%s]: %s\n", h->name().c_str(), h->value().c_str());
+ }
+ request->send(200, "text/plain", "Hello, world!");
+ });
+
+ // Only headers x-keep-me and host will be kept
+ //
+ // curl -v -H "x-keep-me: value" -H "x-remove-me: value" http://192.168.4.1/keep
+ //
+ server
+ .on(
+ "/keep", HTTP_GET,
+ [](AsyncWebServerRequest *request) {
+ // print all headers
+ for (size_t i = 0; i < request->headers(); i++) {
+ const AsyncWebHeader *h = request->getHeader(i);
+ Serial.printf("Header[%s]: %s\n", h->name().c_str(), h->value().c_str());
+ }
+ request->send(200, "text/plain", "Hello, world!");
+ }
+ )
+ .addMiddleware(&headerFree);
+
+ server.begin();
+}
+
+// not needed
+void loop() {
+ delay(100);
+}
diff --git a/libraries/ESP_Async_WebServer/examples/Headers/Headers.ino b/libraries/ESP_Async_WebServer/examples/Headers/Headers.ino
new file mode 100644
index 0000000..e07c515
--- /dev/null
+++ b/libraries/ESP_Async_WebServer/examples/Headers/Headers.ino
@@ -0,0 +1,69 @@
+// SPDX-License-Identifier: LGPL-3.0-or-later
+// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
+
+//
+// Query and send headers
+//
+
+#include <Arduino.h>
+#ifdef ESP32
+#include <AsyncTCP.h>
+#include <WiFi.h>
+#elif defined(ESP8266)
+#include <ESP8266WiFi.h>
+#include <ESPAsyncTCP.h>
+#elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
+#include <RPAsyncTCP.h>
+#include <WiFi.h>
+#endif
+
+#include <ESPAsyncWebServer.h>
+
+static AsyncWebServer server(80);
+
+void setup() {
+ Serial.begin(115200);
+
+#ifndef CONFIG_IDF_TARGET_ESP32H2
+ WiFi.mode(WIFI_AP);
+ WiFi.softAP("esp-captive");
+#endif
+
+ //
+ // curl -v http://192.168.4.1
+ //
+ server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
+ //List all collected headers
+ int headers = request->headers();
+ int i;
+ for (i = 0; i < headers; i++) {
+ const AsyncWebHeader *h = request->getHeader(i);
+ Serial.printf("HEADER[%s]: %s\n", h->name().c_str(), h->value().c_str());
+ }
+
+ AsyncWebServerResponse *response = request->beginResponse(200, "text/plain", "Hello World!");
+
+ //Add header to the response
+ response->addHeader("Server", "ESP Async Web Server");
+
+ //Add multiple headers with the same name
+ response->addHeader("Set-Cookie", "sessionId=38afes7a8", false);
+ response->addHeader("Set-Cookie", "id=a3fWa; Max-Age=2592000", false);
+ response->addHeader("Set-Cookie", "qwerty=219ffwef9w0f; Domain=example.com", false);
+
+ //Remove specific header
+ response->removeHeader("Set-Cookie", "sessionId=38afes7a8");
+
+ //Remove all headers with the same name
+ response->removeHeader("Set-Cookie");
+
+ request->send(response);
+ });
+
+ server.begin();
+}
+
+void loop() {
+ //Sleep in the loop task to not keep the CPU busy
+ delay(1000);
+}
diff --git a/libraries/ESP_Async_WebServer/examples/Json/Json.ino b/libraries/ESP_Async_WebServer/examples/Json/Json.ino
new file mode 100644
index 0000000..0ea8892
--- /dev/null
+++ b/libraries/ESP_Async_WebServer/examples/Json/Json.ino
@@ -0,0 +1,90 @@
+// SPDX-License-Identifier: LGPL-3.0-or-later
+// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
+
+//
+// Shows how to send and receive Json data
+//
+
+#include <Arduino.h>
+#ifdef ESP32
+#include <AsyncTCP.h>
+#include <WiFi.h>
+#elif defined(ESP8266)
+#include <ESP8266WiFi.h>
+#include <ESPAsyncTCP.h>
+#elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
+#include <RPAsyncTCP.h>
+#include <WiFi.h>
+#endif
+
+#include <ESPAsyncWebServer.h>
+
+#if __has_include("ArduinoJson.h")
+#include <ArduinoJson.h>
+#include <AsyncJson.h>
+#include <AsyncMessagePack.h>
+#endif
+
+static AsyncWebServer server(80);
+
+#if __has_include("ArduinoJson.h")
+static AsyncCallbackJsonWebHandler *handler = new AsyncCallbackJsonWebHandler("/json2");
+#endif
+
+void setup() {
+ Serial.begin(115200);
+
+#ifndef CONFIG_IDF_TARGET_ESP32H2
+ WiFi.mode(WIFI_AP);
+ WiFi.softAP("esp-captive");
+#endif
+
+#if __has_include("ArduinoJson.h")
+ //
+ // sends JSON using AsyncJsonResponse
+ //
+ // curl -v http://192.168.4.1/json1
+ //
+ server.on("/json1", HTTP_GET, [](AsyncWebServerRequest *request) {
+ AsyncJsonResponse *response = new AsyncJsonResponse();
+ JsonObject root = response->getRoot().to<JsonObject>();
+ root["hello"] = "world";
+ response->setLength();
+ request->send(response);
+ });
+
+ // Send JSON using AsyncResponseStream
+ //
+ // curl -v http://192.168.4.1/json2
+ //
+ server.on("/json2", HTTP_GET, [](AsyncWebServerRequest *request) {
+ AsyncResponseStream *response = request->beginResponseStream("application/json");
+ JsonDocument doc;
+ JsonObject root = doc.to<JsonObject>();
+ root["foo"] = "bar";
+ serializeJson(root, *response);
+ request->send(response);
+ });
+
+ // curl -v -X POST -H 'Content-Type: application/json' -d '{"name":"You"}' http://192.168.4.1/json2
+ // curl -v -X PUT -H 'Content-Type: application/json' -d '{"name":"You"}' http://192.168.4.1/json2
+ handler->setMethod(HTTP_POST | HTTP_PUT);
+ handler->onRequest([](AsyncWebServerRequest *request, JsonVariant &json) {
+ serializeJson(json, Serial);
+ AsyncJsonResponse *response = new AsyncJsonResponse();
+ JsonObject root = response->getRoot().to<JsonObject>();
+ root["hello"] = json.as<JsonObject>()["name"];
+ response->setLength();
+ request->send(response);
+ });
+
+ server.addHandler(handler);
+#endif
+
+ server.begin();
+}
+
+// not needed
+void loop() {
+ delay(100);
+}
diff --git a/libraries/ESP_Async_WebServer/examples/Logging/Logging.ino b/libraries/ESP_Async_WebServer/examples/Logging/Logging.ino
new file mode 100644
index 0000000..6485185
--- /dev/null
+++ b/libraries/ESP_Async_WebServer/examples/Logging/Logging.ino
@@ -0,0 +1,49 @@
+// SPDX-License-Identifier: LGPL-3.0-or-later
+// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
+
+//
+// Show how to log the incoming request and response as a curl-like syntax
+//
+
+#include <Arduino.h>
+#ifdef ESP32
+#include <AsyncTCP.h>
+#include <WiFi.h>
+#elif defined(ESP8266)
+#include <ESP8266WiFi.h>
+#include <ESPAsyncTCP.h>
+#elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
+#include <RPAsyncTCP.h>
+#include <WiFi.h>
+#endif
+
+#include <ESPAsyncWebServer.h>
+
+static AsyncWebServer server(80);
+static AsyncLoggingMiddleware requestLogger;
+
+void setup() {
+ Serial.begin(115200);
+
+#ifndef CONFIG_IDF_TARGET_ESP32H2
+ WiFi.mode(WIFI_AP);
+ WiFi.softAP("esp-captive");
+#endif
+
+ requestLogger.setEnabled(true);
+ requestLogger.setOutput(Serial);
+
+ server.addMiddleware(&requestLogger);
+
+ // curl -v -H "X-Header:Foo" http://192.168.4.1/
+ server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
+ request->send(200, "text/plain", "Hello, world!");
+ });
+
+ server.begin();
+}
+
+// not needed
+void loop() {
+ delay(100);
+}
diff --git a/libraries/ESP_Async_WebServer/examples/MessagePack/MessagePack.ino b/libraries/ESP_Async_WebServer/examples/MessagePack/MessagePack.ino
new file mode 100644
index 0000000..4fea247
--- /dev/null
+++ b/libraries/ESP_Async_WebServer/examples/MessagePack/MessagePack.ino
@@ -0,0 +1,88 @@
+// SPDX-License-Identifier: LGPL-3.0-or-later
+// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
+
+//
+// Shows how to send and receive Message Pack data
+//
+
+#include <Arduino.h>
+#ifdef ESP32
+#include <AsyncTCP.h>
+#include <WiFi.h>
+#elif defined(ESP8266)
+#include <ESP8266WiFi.h>
+#include <ESPAsyncTCP.h>
+#elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
+#include <RPAsyncTCP.h>
+#include <WiFi.h>
+#endif
+
+#include <ESPAsyncWebServer.h>
+
+#if __has_include("ArduinoJson.h")
+#include <ArduinoJson.h>
+#include <AsyncJson.h>
+#include <AsyncMessagePack.h>
+#endif
+
+static AsyncWebServer server(80);
+
+#if __has_include("ArduinoJson.h")
+static AsyncCallbackMessagePackWebHandler *handler = new AsyncCallbackMessagePackWebHandler("/msgpack2");
+#endif
+
+void setup() {
+ Serial.begin(115200);
+
+#ifndef CONFIG_IDF_TARGET_ESP32H2
+ WiFi.mode(WIFI_AP);
+ WiFi.softAP("esp-captive");
+#endif
+
+#if __has_include("ArduinoJson.h")
+ //
+ // sends MessagePack using AsyncMessagePackResponse
+ //
+ // curl -v http://192.168.4.1/msgpack1
+ //
+ server.on("/msgpack1", HTTP_GET, [](AsyncWebServerRequest *request) {
+ AsyncMessagePackResponse *response = new AsyncMessagePackResponse();
+ JsonObject root = response->getRoot().to<JsonObject>();
+ root["hello"] = "world";
+ response->setLength();
+ request->send(response);
+ });
+
+ // Send MessagePack using AsyncResponseStream
+ //
+ // curl -v http://192.168.4.1/msgpack2
+ //
+ server.on("/msgpack2", HTTP_GET, [](AsyncWebServerRequest *request) {
+ AsyncResponseStream *response = request->beginResponseStream("application/msgpack");
+ JsonDocument doc;
+ JsonObject root = doc.to<JsonObject>();
+ root["foo"] = "bar";
+ serializeMsgPack(root, *response);
+ request->send(response);
+ });
+
+ handler->setMethod(HTTP_POST | HTTP_PUT);
+ handler->onRequest([](AsyncWebServerRequest *request, JsonVariant &json) {
+ serializeJson(json, Serial);
+ AsyncMessagePackResponse *response = new AsyncMessagePackResponse();
+ JsonObject root = response->getRoot().to<JsonObject>();
+ root["hello"] = json.as<JsonObject>()["name"];
+ response->setLength();
+ request->send(response);
+ });
+
+ server.addHandler(handler);
+#endif
+
+ server.begin();
+}
+
+// not needed
+void loop() {
+ delay(100);
+}
diff --git a/libraries/ESP_Async_WebServer/examples/Middleware/Middleware.ino b/libraries/ESP_Async_WebServer/examples/Middleware/Middleware.ino
new file mode 100644
index 0000000..c52f949
--- /dev/null
+++ b/libraries/ESP_Async_WebServer/examples/Middleware/Middleware.ino
@@ -0,0 +1,82 @@
+// SPDX-License-Identifier: LGPL-3.0-or-later
+// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
+
+//
+// Show how to sue Middleware
+//
+
+#include <Arduino.h>
+#ifdef ESP32
+#include <AsyncTCP.h>
+#include <WiFi.h>
+#elif defined(ESP8266)
+#include <ESP8266WiFi.h>
+#include <ESPAsyncTCP.h>
+#elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
+#include <RPAsyncTCP.h>
+#include <WiFi.h>
+#endif
+
+#include <ESPAsyncWebServer.h>
+
+static AsyncWebServer server(80);
+
+// New middleware classes can be created!
+class MyMiddleware : public AsyncMiddleware {
+public:
+ void run(AsyncWebServerRequest *request, ArMiddlewareNext next) override {
+ Serial.printf("Before handler: %s %s\n", request->methodToString(), request->url().c_str());
+ next(); // continue middleware chain
+ Serial.printf("After handler: response code=%d\n", request->getResponse()->code());
+ }
+};
+
+void setup() {
+ Serial.begin(115200);
+
+#ifndef CONFIG_IDF_TARGET_ESP32H2
+ WiFi.mode(WIFI_AP);
+ WiFi.softAP("esp-captive");
+#endif
+
+ // add a global middleware to the server
+ server.addMiddleware(new MyMiddleware());
+
+ // Test with:
+ //
+ // - curl -v http://192.168.4.1/ => 200 OK
+ // - curl -v http://192.168.4.1/?user=anon => 403 Forbidden
+ // - curl -v http://192.168.4.1/?user=foo => 200 OK
+ // - curl -v http://192.168.4.1/?user=error => 400 ERROR
+ //
+ AsyncCallbackWebHandler &handler = server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
+ Serial.printf("In Handler: %s %s\n", request->methodToString(), request->url().c_str());
+ request->send(200, "text/plain", "Hello, world!");
+ });
+
+ // add a middleware to this handler only to send 403 if the user is anon
+ handler.addMiddleware([](AsyncWebServerRequest *request, ArMiddlewareNext next) {
+ Serial.println("Checking user=anon");
+ if (request->hasParam("user") && request->getParam("user")->value() == "anon") {
+ request->send(403, "text/plain", "Forbidden");
+ } else {
+ next();
+ }
+ });
+
+ // add a middleware to this handler that will replace the previously created response by another one
+ handler.addMiddleware([](AsyncWebServerRequest *request, ArMiddlewareNext next) {
+ next();
+ Serial.println("Checking user=error");
+ if (request->hasParam("user") && request->getParam("user")->value() == "error") {
+ request->send(400, "text/plain", "ERROR");
+ }
+ });
+
+ server.begin();
+}
+
+// not needed
+void loop() {
+ delay(100);
+}
diff --git a/libraries/ESP_Async_WebServer/examples/Params/Params.ino b/libraries/ESP_Async_WebServer/examples/Params/Params.ino
new file mode 100644
index 0000000..2c438a5
--- /dev/null
+++ b/libraries/ESP_Async_WebServer/examples/Params/Params.ino
@@ -0,0 +1,122 @@
+// SPDX-License-Identifier: LGPL-3.0-or-later
+// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
+
+//
+// Query parameters and body parameters
+//
+
+#include <Arduino.h>
+#ifdef ESP32
+#include <AsyncTCP.h>
+#include <WiFi.h>
+#elif defined(ESP8266)
+#include <ESP8266WiFi.h>
+#include <ESPAsyncTCP.h>
+#elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
+#include <RPAsyncTCP.h>
+#include <WiFi.h>
+#endif
+
+#include <ESPAsyncWebServer.h>
+
+static AsyncWebServer server(80);
+
+static const char *htmlContent PROGMEM = R"(
+<!DOCTYPE html>
+<html>
+<head>
+ <title>POST Request with Multiple Parameters</title>
+</head>
+<body>
+ <form action="http://192.168.4.1" method="POST">
+ <label for="who">Who?</label>
+ <input type="text" id="who" name="who" value="Carl"><br>
+ <label for="g0">g0:</label>
+ <input type="text" id="g0" name="g0" value="1"><br>
+ <label for="a0">a0:</label>
+ <input type="text" id="a0" name="a0" value="2"><br>
+ <label for="n0">n0:</label>
+ <input type="text" id="n0" name="n0" value="3"><br>
+ <label for="t10">t10:</label>
+ <input type="text" id="t10" name="t10" value="3"><br>
+ <label for="t20">t20:</label>
+ <input type="text" id="t20" name="t20" value="4"><br>
+ <label for="t30">t30:</label>
+ <input type="text" id="t30" name="t30" value="5"><br>
+ <label for="t40">t40:</label>
+ <input type="text" id="t40" name="t40" value="6"><br>
+ <label for="t50">t50:</label>
+ <input type="text" id="t50" name="t50" value="7"><br>
+ <label for="g1">g1:</label>
+ <input type="text" id="g1" name="g1" value="2"><br>
+ <label for="a1">a1:</label>
+ <input type="text" id="a1" name="a1" value="2"><br>
+ <label for="n1">n1:</label>
+ <input type="text" id="n1" name="n1" value="3"><br>
+ <label for="t11">t11:</label>
+ <input type="text" id="t11" name="t11" value="13"><br>
+ <label for="t21">t21:</label>
+ <input type="text" id="t21" name="t21" value="14"><br>
+ <label for="t31">t31:</label>
+ <input type="text" id="t31" name="t31" value="15"><br>
+ <label for="t41">t41:</label>
+ <input type="text" id="t41" name="t41" value="16"><br>
+ <label for="t51">t51:</label>
+ <input type="text" id="t51" name="t51" value="17"><br>
+ <input type="submit" value="Submit">
+ </form>
+</body>
+</html>
+)";
+
+static const size_t htmlContentLength = strlen_P(htmlContent);
+
+void setup() {
+ Serial.begin(115200);
+
+#ifndef CONFIG_IDF_TARGET_ESP32H2
+ WiFi.mode(WIFI_AP);
+ WiFi.softAP("esp-captive");
+#endif
+
+ // Get query parameters
+ //
+ // curl -v http://192.168.4.1/?who=Bob
+ //
+ server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
+ if (request->hasParam("who")) {
+ Serial.printf("Who? %s\n", request->getParam("who")->value().c_str());
+ }
+
+ request->send(200, "text/html", (uint8_t *)htmlContent, htmlContentLength);
+ });
+
+ // Get form body parameters
+ //
+ // curl -v -H "Content-Type: application/x-www-form-urlencoded" -d "who=Carl" -d "param=value" http://192.168.4.1/
+ //
+ server.on("/", HTTP_POST, [](AsyncWebServerRequest *request) {
+ // display params
+ size_t count = request->params();
+ for (size_t i = 0; i < count; i++) {
+ const AsyncWebParameter *p = request->getParam(i);
+ Serial.printf("PARAM[%u]: %s = %s\n", i, p->name().c_str(), p->value().c_str());
+ }
+
+ // get who param
+ String who;
+ if (request->hasParam("who", true)) {
+ who = request->getParam("who", true)->value();
+ } else {
+ who = "No message sent";
+ }
+ request->send(200, "text/plain", "Hello " + who + "!");
+ });
+
+ server.begin();
+}
+
+// not needed
+void loop() {
+ delay(100);
+}
diff --git a/libraries/ESP_Async_WebServer/examples/PartitionDownloader/PartitionDownloader.ino b/libraries/ESP_Async_WebServer/examples/PartitionDownloader/PartitionDownloader.ino
new file mode 100644
index 0000000..3c76366
--- /dev/null
+++ b/libraries/ESP_Async_WebServer/examples/PartitionDownloader/PartitionDownloader.ino
@@ -0,0 +1,130 @@
+// SPDX-License-Identifier: LGPL-3.0-or-later
+// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
+
+//
+// - Download ESP32 partition by name and/or type and/or subtype
+// - Support encrypted and non-encrypted partitions
+//
+
+#include <Arduino.h>
+#ifdef ESP32
+#include <AsyncTCP.h>
+#include <WiFi.h>
+#elif defined(ESP8266)
+#include <ESP8266WiFi.h>
+#include <ESPAsyncTCP.h>
+#elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
+#include <RPAsyncTCP.h>
+#include <WiFi.h>
+#endif
+
+#include <ESPAsyncWebServer.h>
+#include <LittleFS.h>
+
+#ifndef ESP32
+// this example is only for the ESP32
+void setup() {}
+void loop() {}
+#else
+
+#include <esp_partition.h>
+
+static AsyncWebServer server(80);
+
+void setup() {
+ Serial.begin(115200);
+
+#ifndef CONFIG_IDF_TARGET_ESP32H2
+ WiFi.mode(WIFI_AP);
+ WiFi.softAP("esp-captive");
+#endif
+
+ LittleFS.begin(true);
+
+ // To upload the FS partition, run:
+ // > pio run -e arduino-3 -t buildfs
+ // > pio run -e arduino-3 -t uploadfs
+ //
+ // Examples:
+ //
+ // - Download the partition named "spiffs": http://192.168.4.1/partition?label=spiffs
+ // - Download the partition named "spiffs" with type "data": http://192.168.4.1/partition?label=spiffs&type=1
+ // - Download the partition named "spiffs" with type "data" and subtype "spiffs": http://192.168.4.1/partition?label=spiffs&type=1&subtype=130
+ // - Download the partition with subtype "nvs": http://192.168.4.1/partition?type=1&subtype=2
+ //
+ // "type" and "subtype" IDs can be found in esp_partition.h header file.
+ //
+ // Add "&raw=false" parameter to download the partition unencrypted (for encrypted partitions).
+ // By default, the raw partition is downloaded, so if a partition is encrypted, the encrypted data will be downloaded.
+ //
+ // To browse a downloaded LittleFS partition, you can use https://tniessen.github.io/littlefs-disk-img-viewer/ (block size is 4096)
+ //
+ server.on("/partition", HTTP_GET, [](AsyncWebServerRequest *request) {
+ const AsyncWebParameter *pLabel = request->getParam("label");
+ const AsyncWebParameter *pType = request->getParam("type");
+ const AsyncWebParameter *pSubtype = request->getParam("subtype");
+ const AsyncWebParameter *pRaw = request->getParam("raw");
+
+ if (!pLabel && !pType && !pSubtype) {
+ request->send(400, "text/plain", "Bad request: missing parameter");
+ return;
+ }
+
+ esp_partition_type_t type = ESP_PARTITION_TYPE_ANY;
+ esp_partition_subtype_t subtype = ESP_PARTITION_SUBTYPE_ANY;
+ const char *label = nullptr;
+ bool raw = true;
+
+ if (pLabel) {
+ label = pLabel->value().c_str();
+ }
+
+ if (pType) {
+ type = (esp_partition_type_t)pType->value().toInt();
+ }
+
+ if (pSubtype) {
+ subtype = (esp_partition_subtype_t)pSubtype->value().toInt();
+ }
+
+ if (pRaw && pRaw->value() == "false") {
+ raw = false;
+ }
+
+ const esp_partition_t *partition = esp_partition_find_first(type, subtype, label);
+
+ if (!partition) {
+ request->send(404, "text/plain", "Partition not found");
+ return;
+ }
+
+ AsyncWebServerResponse *response =
+ request->beginChunkedResponse("application/octet-stream", [partition, raw](uint8_t *buffer, size_t maxLen, size_t index) -> size_t {
+ const size_t remaining = partition->size - index;
+ if (!remaining) {
+ return 0;
+ }
+ const size_t len = std::min(maxLen, remaining);
+ if (raw && esp_partition_read_raw(partition, index, buffer, len) == ESP_OK) {
+ return len;
+ }
+ if (!raw && esp_partition_read(partition, index, buffer, len) == ESP_OK) {
+ return len;
+ }
+ return 0;
+ });
+
+ response->addHeader("Content-Disposition", "attachment; filename=" + String(partition->label) + ".bin");
+ response->setContentLength(partition->size);
+
+ request->send(response);
+ });
+
+ server.begin();
+}
+
+void loop() {
+ delay(100);
+}
+
+#endif
diff --git a/libraries/ESP_Async_WebServer/examples/PerfTests/PerfTests.ino b/libraries/ESP_Async_WebServer/examples/PerfTests/PerfTests.ino
new file mode 100644
index 0000000..6467d2c
--- /dev/null
+++ b/libraries/ESP_Async_WebServer/examples/PerfTests/PerfTests.ino
@@ -0,0 +1,243 @@
+// SPDX-License-Identifier: LGPL-3.0-or-later
+// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
+
+//
+// Perf tests
+//
+
+#include <Arduino.h>
+#ifdef ESP32
+#include <AsyncTCP.h>
+#include <WiFi.h>
+#elif defined(ESP8266)
+#include <ESP8266WiFi.h>
+#include <ESPAsyncTCP.h>
+#elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
+#include <RPAsyncTCP.h>
+#include <WiFi.h>
+#endif
+
+#include <ESPAsyncWebServer.h>
+
+static const char *htmlContent PROGMEM = R"(
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Sample HTML</title>
+</head>
+<body>
+ <h1>Hello, World!</h1>
+ <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
+ rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
+ arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
+ accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
+ Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
+ dapibus elit, id varius sem dui id lacus.</p>
+ <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
+ rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
+ arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
+ accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
+ Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
+ dapibus elit, id varius sem dui id lacus.</p>
+ <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
+ rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
+ arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
+ accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
+ Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
+ dapibus elit, id varius sem dui id lacus.</p>
+ <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
+ rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
+ arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
+ accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
+ Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
+ dapibus elit, id varius sem dui id lacus.</p>
+ <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
+ rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
+ arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
+ accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
+ Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
+ dapibus elit, id varius sem dui id lacus.</p>
+ <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
+ rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
+ arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
+ accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
+ Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
+ dapibus elit, id varius sem dui id lacus.</p>
+ <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
+ rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
+ arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
+ accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
+ Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
+ dapibus elit, id varius sem dui id lacus.</p>
+ <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
+ rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
+ arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
+ accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
+ Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
+ dapibus elit, id varius sem dui id lacus.</p>
+</body>
+</html>
+)";
+
+static const size_t htmlContentLength = strlen_P(htmlContent);
+static constexpr char characters[] = "0123456789ABCDEF";
+static size_t charactersIndex = 0;
+
+static AsyncWebServer server(80);
+static AsyncEventSource events("/events");
+
+static volatile size_t requests = 0;
+
+void setup() {
+ Serial.begin(115200);
+
+#ifndef CONFIG_IDF_TARGET_ESP32H2
+ WiFi.mode(WIFI_AP);
+ WiFi.softAP("esp-captive");
+#endif
+
+ // Pauses in the request parsing phase
+ //
+ // autocannon -c 32 -w 32 -a 96 -t 30 --renderStatusCodes -m POST -H "Content-Type: application/json" -b '{"foo": "bar"}' http://192.168.4.1/delay
+ //
+ // curl -v -X POST -H "Content-Type: application/json" -d '{"game": "test"}' http://192.168.4.1/delay
+ //
+ server.onNotFound([](AsyncWebServerRequest *request) {
+ requests = requests + 1;
+ if (request->url() == "/delay") {
+ request->send(200, "application/json", "{\"status\":\"OK\"}");
+ } else {
+ request->send(404, "text/plain", "Not found");
+ }
+ });
+ server.onRequestBody([](AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) {
+ if (request->url() == "/delay") {
+ delay(3000);
+ }
+ });
+
+ // HTTP endpoint
+ //
+ // > brew install autocannon
+ // > autocannon -c 10 -w 10 -d 20 http://192.168.4.1
+ // > autocannon -c 16 -w 16 -d 20 http://192.168.4.1
+ //
+ server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
+ // need to cast to uint8_t*
+ // if you do not, the const char* will be copied in a temporary String buffer
+ requests = requests + 1;
+ request->send(200, "text/html", (uint8_t *)htmlContent, htmlContentLength);
+ });
+
+ // IMPORTANT - DO NOT WRITE SUCH CODE IN PRODUCTON !
+ //
+ // This example simulates the slowdown that can happen when:
+ // - downloading a huge file from sdcard
+ // - doing some file listing on SDCard because it is horribly slow to get a file listing with file stats on SDCard.
+ // So in both cases, ESP would deadlock or TWDT would trigger.
+ //
+ // This example simulats that by slowing down the chunk callback:
+ // - d=2000 is the delay in ms in the callback
+ // - l=10000 is the length of the response
+ //
+ // time curl -N -v -G -d 'd=2000' -d 'l=10000' http://192.168.4.1/slow.html --output -
+ //
+ server.on("/slow.html", HTTP_GET, [](AsyncWebServerRequest *request) {
+ requests = requests + 1;
+ uint32_t d = request->getParam("d")->value().toInt();
+ uint32_t l = request->getParam("l")->value().toInt();
+ Serial.printf("d = %" PRIu32 ", l = %" PRIu32 "\n", d, l);
+ AsyncWebServerResponse *response = request->beginChunkedResponse("text/html", [d, l](uint8_t *buffer, size_t maxLen, size_t index) -> size_t {
+ Serial.printf("%u\n", index);
+ // finished ?
+ if (index >= l) {
+ return 0;
+ }
+
+ // slow down the task to simulate some heavy processing, like SD card reading
+ delay(d);
+
+ memset(buffer, characters[charactersIndex], 256);
+ charactersIndex = (charactersIndex + 1) % sizeof(characters);
+ return 256;
+ });
+
+ request->send(response);
+ });
+
+ // SSS endpoint
+ //
+ // launch 16 concurrent workers for 30 seconds
+ // > for i in {1..10}; do ( count=$(gtimeout 30 curl -s -N -H "Accept: text/event-stream" http://192.168.4.1/events 2>&1 | grep -c "^data:"); echo "Total: $count events, $(echo "$count / 4" | bc -l) events / second" ) & done;
+ // > for i in {1..16}; do ( count=$(gtimeout 30 curl -s -N -H "Accept: text/event-stream" http://192.168.4.1/events 2>&1 | grep -c "^data:"); echo "Total: $count events, $(echo "$count / 4" | bc -l) events / second" ) & done;
+ //
+ // With AsyncTCP, with 16 workers: a lot of "Event message queue overflow: discard message", no crash
+ //
+ // Total: 1711 events, 427.75 events / second
+ // Total: 1711 events, 427.75 events / second
+ // Total: 1626 events, 406.50 events / second
+ // Total: 1562 events, 390.50 events / second
+ // Total: 1706 events, 426.50 events / second
+ // Total: 1659 events, 414.75 events / second
+ // Total: 1624 events, 406.00 events / second
+ // Total: 1706 events, 426.50 events / second
+ // Total: 1487 events, 371.75 events / second
+ // Total: 1573 events, 393.25 events / second
+ // Total: 1569 events, 392.25 events / second
+ // Total: 1559 events, 389.75 events / second
+ // Total: 1560 events, 390.00 events / second
+ // Total: 1562 events, 390.50 events / second
+ // Total: 1626 events, 406.50 events / second
+ //
+ // With AsyncTCP, with 10 workers:
+ //
+ // Total: 2038 events, 509.50 events / second
+ // Total: 2120 events, 530.00 events / second
+ // Total: 2119 events, 529.75 events / second
+ // Total: 2038 events, 509.50 events / second
+ // Total: 2037 events, 509.25 events / second
+ // Total: 2119 events, 529.75 events / second
+ // Total: 2119 events, 529.75 events / second
+ // Total: 2120 events, 530.00 events / second
+ // Total: 2038 events, 509.50 events / second
+ // Total: 2038 events, 509.50 events / second
+ //
+ // With AsyncTCPSock, with 16 workers: ESP32 CRASH !!!
+ //
+ // With AsyncTCPSock, with 10 workers:
+ //
+ // Total: 1242 events, 310.50 events / second
+ // Total: 1242 events, 310.50 events / second
+ // Total: 1242 events, 310.50 events / second
+ // Total: 1242 events, 310.50 events / second
+ // Total: 1181 events, 295.25 events / second
+ // Total: 1182 events, 295.50 events / second
+ // Total: 1240 events, 310.00 events / second
+ // Total: 1181 events, 295.25 events / second
+ // Total: 1181 events, 295.25 events / second
+ // Total: 1183 events, 295.75 events / second
+ //
+ server.addHandler(&events);
+
+ server.begin();
+}
+
+static uint32_t lastSSE = 0;
+static uint32_t deltaSSE = 10;
+
+static uint32_t lastHeap = 0;
+
+void loop() {
+ uint32_t now = millis();
+ if (now - lastSSE >= deltaSSE) {
+ events.send(String("ping-") + now, "heartbeat", now);
+ lastSSE = millis();
+ }
+
+#ifdef ESP32
+ if (now - lastHeap >= 2000) {
+ Serial.printf("Uptime: %3lu s, requests: %3u, Free heap: %" PRIu32 "\n", millis() / 1000, requests, ESP.getFreeHeap());
+ lastHeap = now;
+ }
+#endif
+}
diff --git a/libraries/ESP_Async_WebServer/examples/RateLimit/RateLimit.ino b/libraries/ESP_Async_WebServer/examples/RateLimit/RateLimit.ino
new file mode 100644
index 0000000..89d6090
--- /dev/null
+++ b/libraries/ESP_Async_WebServer/examples/RateLimit/RateLimit.ino
@@ -0,0 +1,64 @@
+// SPDX-License-Identifier: LGPL-3.0-or-later
+// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
+
+//
+// Show how to rate limit the server or some endpoints
+//
+
+#include <Arduino.h>
+#ifdef ESP32
+#include <AsyncTCP.h>
+#include <WiFi.h>
+#elif defined(ESP8266)
+#include <ESP8266WiFi.h>
+#include <ESPAsyncTCP.h>
+#elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
+#include <RPAsyncTCP.h>
+#include <WiFi.h>
+#endif
+
+#include <ESPAsyncWebServer.h>
+
+static AsyncWebServer server(80);
+static AsyncRateLimitMiddleware rateLimit;
+
+void setup() {
+ Serial.begin(115200);
+
+#ifndef CONFIG_IDF_TARGET_ESP32H2
+ WiFi.mode(WIFI_AP);
+ WiFi.softAP("esp-captive");
+#endif
+
+ // maximum 5 requests per 10 seconds
+ rateLimit.setMaxRequests(5);
+ rateLimit.setWindowSize(10);
+
+ // run quickly several times:
+ //
+ // curl -v http://192.168.4.1/
+ //
+ server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
+ request->send(200, "text/plain", "Hello, world!");
+ });
+
+ // run quickly several times:
+ //
+ // curl -v http://192.168.4.1/rate-limited
+ //
+ server
+ .on(
+ "/rate-limited", HTTP_GET,
+ [](AsyncWebServerRequest *request) {
+ request->send(200, "text/plain", "Hello, world!");
+ }
+ )
+ .addMiddleware(&rateLimit); // only rate limit this endpoint, but could be applied globally to the server
+
+ server.begin();
+}
+
+// not needed
+void loop() {
+ delay(100);
+}
diff --git a/libraries/ESP_Async_WebServer/examples/Redirect/Redirect.ino b/libraries/ESP_Async_WebServer/examples/Redirect/Redirect.ino
new file mode 100644
index 0000000..ce1b9fb
--- /dev/null
+++ b/libraries/ESP_Async_WebServer/examples/Redirect/Redirect.ino
@@ -0,0 +1,48 @@
+// SPDX-License-Identifier: LGPL-3.0-or-later
+// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
+
+//
+// Shows how to redirect
+//
+
+#include <Arduino.h>
+#ifdef ESP32
+#include <AsyncTCP.h>
+#include <WiFi.h>
+#elif defined(ESP8266)
+#include <ESP8266WiFi.h>
+#include <ESPAsyncTCP.h>
+#elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
+#include <RPAsyncTCP.h>
+#include <WiFi.h>
+#endif
+
+#include <ESPAsyncWebServer.h>
+
+static AsyncWebServer server(80);
+
+void setup() {
+ Serial.begin(115200);
+
+#ifndef CONFIG_IDF_TARGET_ESP32H2
+ WiFi.mode(WIFI_AP);
+ WiFi.softAP("esp-captive");
+#endif
+
+ // curl -v http://192.168.4.1/
+ server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
+ request->redirect("/index.txt");
+ });
+
+ // curl -v http://192.168.4.1/index.txt
+ server.on("/index.txt", HTTP_GET, [](AsyncWebServerRequest *request) {
+ request->send(200, "text/plain", "Hello, world!");
+ });
+
+ server.begin();
+}
+
+// not needed
+void loop() {
+ delay(100);
+}
diff --git a/libraries/ESP_Async_WebServer/examples/RequestContinuation/RequestContinuation.ino b/libraries/ESP_Async_WebServer/examples/RequestContinuation/RequestContinuation.ino
new file mode 100644
index 0000000..0584cf1
--- /dev/null
+++ b/libraries/ESP_Async_WebServer/examples/RequestContinuation/RequestContinuation.ino
@@ -0,0 +1,91 @@
+// SPDX-License-Identifier: LGPL-3.0-or-later
+// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
+
+//
+// Shows how to use request continuation to pause a request for a long processing task, and be able to resume it later.
+//
+
+#include <Arduino.h>
+#ifdef ESP32
+#include <AsyncTCP.h>
+#include <WiFi.h>
+#elif defined(ESP8266)
+#include <ESP8266WiFi.h>
+#include <ESPAsyncTCP.h>
+#elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
+#include <RPAsyncTCP.h>
+#include <WiFi.h>
+#endif
+
+#include <ESPAsyncWebServer.h>
+#include <list>
+#include <mutex>
+
+static AsyncWebServer server(80);
+
+// request handler that is saved from the paused request to communicate with Serial
+static String message;
+static AsyncWebServerRequestPtr serialRequest;
+
+// request handler that is saved from the paused request to communicate with GPIO
+static uint8_t pin = 35;
+static AsyncWebServerRequestPtr gpioRequest;
+
+void setup() {
+ Serial.begin(115200);
+
+#ifndef CONFIG_IDF_TARGET_ESP32H2
+ WiFi.mode(WIFI_AP);
+ WiFi.softAP("esp-captive");
+#endif
+
+ // Post a message that will be sent to the Serial console, and pause the request until the user types a key
+ //
+ // curl -v -X POST -H "Content-Type: application/x-www-form-urlencoded" -d "question=Name%3F%20" http://192.168.4.1/serial
+ //
+ // curl output should show "Answer: [y/n]" as the response
+ server.on("/serial", HTTP_POST, [](AsyncWebServerRequest *request) {
+ message = request->getParam("question", true)->value();
+ serialRequest = request->pause();
+ });
+
+ // Wait for a GPIO to be high
+ //
+ // curl -v http://192.168.4.1/gpio
+ //
+ // curl output should show "GPIO is high!" as the response
+ server.on("/gpio", HTTP_GET, [](AsyncWebServerRequest *request) {
+ gpioRequest = request->pause();
+ });
+
+ pinMode(pin, INPUT);
+
+ server.begin();
+}
+
+void loop() {
+ delay(500);
+
+ // Check for a high voltage on the RX1 pin
+ if (digitalRead(pin) == HIGH) {
+ if (auto request = gpioRequest.lock()) {
+ request->send(200, "text/plain", "GPIO is high!");
+ }
+ }
+
+ // check for an incoming message from the Serial console
+ if (message.length()) {
+ Serial.printf("%s", message.c_str());
+ // drops buffer
+ while (Serial.available()) {
+ Serial.read();
+ }
+ Serial.setTimeout(10000);
+ String response = Serial.readStringUntil('\n'); // waits for a key to be pressed
+ Serial.println();
+ message = emptyString;
+ if (auto request = serialRequest.lock()) {
+ request->send(200, "text/plain", "Answer: " + response);
+ }
+ }
+}
diff --git a/libraries/ESP_Async_WebServer/examples/RequestContinuationComplete/RequestContinuationComplete.ino b/libraries/ESP_Async_WebServer/examples/RequestContinuationComplete/RequestContinuationComplete.ino
new file mode 100644
index 0000000..ccd16fd
--- /dev/null
+++ b/libraries/ESP_Async_WebServer/examples/RequestContinuationComplete/RequestContinuationComplete.ino
@@ -0,0 +1,165 @@
+// SPDX-License-Identifier: LGPL-3.0-or-later
+// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
+
+//
+// Shows how to use request continuation to pause a request for a long processing task, and be able to resume it later.
+//
+
+#include <Arduino.h>
+#ifdef ESP32
+#include <AsyncTCP.h>
+#include <WiFi.h>
+#elif defined(ESP8266)
+#include <ESP8266WiFi.h>
+#include <ESPAsyncTCP.h>
+#elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
+#include <RPAsyncTCP.h>
+#include <WiFi.h>
+#endif
+
+#include <ESPAsyncWebServer.h>
+
+#include <list>
+#include <memory>
+#include <mutex>
+
+static AsyncWebServer server(80);
+
+// ===============================================================
+// The code below is used to simulate some long running operations
+// ===============================================================
+
+typedef struct {
+ size_t id;
+ AsyncWebServerRequestPtr requestPtr;
+ uint8_t data;
+} LongRunningOperation;
+
+static std::list<std::unique_ptr<LongRunningOperation>> longRunningOperations;
+static size_t longRunningOperationsCount = 0;
+#ifdef ESP32
+static std::mutex longRunningOperationsMutex;
+#endif
+
+static void startLongRunningOperation(AsyncWebServerRequestPtr &&requestPtr) {
+#ifdef ESP32
+ std::lock_guard<std::mutex> lock(longRunningOperationsMutex);
+#endif
+
+ // LongRunningOperation *op = new LongRunningOperation();
+ std::unique_ptr<LongRunningOperation> op(new LongRunningOperation());
+ op->id = ++longRunningOperationsCount;
+ op->data = 10;
+
+ // you need to hold the AsyncWebServerRequestPtr returned by pause();
+ // This object is authorized to leave the scope of the request handler.
+ op->requestPtr = std::move(requestPtr);
+
+ Serial.printf("[%u] Start long running operation for %" PRIu8 " seconds...\n", op->id, op->data);
+ longRunningOperations.push_back(std::move(op));
+}
+
+static bool processLongRunningOperation(LongRunningOperation *op) {
+ // request was deleted ?
+ if (op->requestPtr.expired()) {
+ Serial.printf("[%u] Request was deleted - stopping long running operation\n", op->id);
+ return true; // operation finished
+ }
+
+ // processing the operation
+ Serial.printf("[%u] Long running operation processing... %" PRIu8 " seconds left\n", op->id, op->data);
+
+ // check if we have finished ?
+ op->data--;
+ if (op->data) {
+ // not finished yet
+ return false;
+ }
+
+ // Try to get access to the request pointer if it is still exist.
+ // If there has been a disconnection during that time, the pointer won't be valid anymore
+ if (auto request = op->requestPtr.lock()) {
+ Serial.printf("[%u] Long running operation finished! Sending back response...\n", op->id);
+ request->send(200, "text/plain", String(op->id) + " ");
+
+ } else {
+ Serial.printf("[%u] Long running operation finished, but request was deleted!\n", op->id);
+ }
+
+ return true; // operation finished
+}
+
+/// ==========================================================
+
+void setup() {
+ Serial.begin(115200);
+
+#ifndef CONFIG_IDF_TARGET_ESP32H2
+ WiFi.mode(WIFI_AP);
+ WiFi.softAP("esp-captive");
+#endif
+
+ // Add a middleware to see how pausing a request affects the middleware chain
+ server.addMiddleware([](AsyncWebServerRequest *request, ArMiddlewareNext next) {
+ Serial.printf("Middleware chain start\n");
+
+ // continue to the next middleware, and at the end the request handler
+ next();
+
+ // we can check the request pause state after the handler was executed
+ if (request->isPaused()) {
+ Serial.printf("Request was paused!\n");
+ }
+
+ Serial.printf("Middleware chain ends\n");
+ });
+
+ // HOW TO RUN THIS EXAMPLE:
+ //
+ // 1. Open several terminals to trigger some requests concurrently that will be paused with:
+ // > time curl -v http://192.168.4.1/
+ //
+ // 2. Look at the output of the Serial console to see how the middleware chain is executed
+ // and to see the long running operations being processed and resume the requests.
+ //
+ // 3. You can try close your curl command to cancel the request and check that the request is deleted.
+ // Note: in case the network is disconnected, the request will be deleted.
+ //
+ server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
+ // Print a message in case the request is disconnected (network disconnection, client close, etc.)
+ request->onDisconnect([]() {
+ Serial.printf("Request was disconnected!\n");
+ });
+
+ // Instruct ESPAsyncWebServer to pause the request and get a AsyncWebServerRequestPtr to be able to access the request later.
+ // The AsyncWebServerRequestPtr is the ONLY object authorized to leave the scope of the request handler.
+ // The Middleware chain will continue to run until the end after this handler exit, but the request will be paused and will not
+ // be sent to the client until send() is called later.
+ Serial.printf("Pausing request...\n");
+ AsyncWebServerRequestPtr requestPtr = request->pause();
+
+ // start our long operation...
+ startLongRunningOperation(std::move(requestPtr));
+ });
+
+ server.begin();
+}
+
+static uint32_t lastTime = 0;
+
+void loop() {
+ if (millis() - lastTime >= 1000) {
+
+#ifdef ESP32
+ Serial.printf("Free heap: %" PRIu32 "\n", ESP.getFreeHeap());
+ std::lock_guard<std::mutex> lock(longRunningOperationsMutex);
+#endif
+
+ // process all long running operations
+ longRunningOperations.remove_if([](const std::unique_ptr<LongRunningOperation> &op) {
+ return processLongRunningOperation(op.get());
+ });
+
+ lastTime = millis();
+ }
+}
diff --git a/libraries/ESP_Async_WebServer/examples/ResumableDownload/ResumableDownload.ino b/libraries/ESP_Async_WebServer/examples/ResumableDownload/ResumableDownload.ino
new file mode 100644
index 0000000..373ca24
--- /dev/null
+++ b/libraries/ESP_Async_WebServer/examples/ResumableDownload/ResumableDownload.ino
@@ -0,0 +1,61 @@
+// SPDX-License-Identifier: LGPL-3.0-or-later
+// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
+
+//
+// Make sure resumable downloads can be implemented (HEAD request / response and Range header)
+//
+
+#include <Arduino.h>
+#ifdef ESP32
+#include <AsyncTCP.h>
+#include <WiFi.h>
+#elif defined(ESP8266)
+#include <ESP8266WiFi.h>
+#include <ESPAsyncTCP.h>
+#elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
+#include <RPAsyncTCP.h>
+#include <WiFi.h>
+#endif
+
+#include <ESPAsyncWebServer.h>
+
+static AsyncWebServer server(80);
+
+void setup() {
+ Serial.begin(115200);
+
+#ifndef CONFIG_IDF_TARGET_ESP32H2
+ WiFi.mode(WIFI_AP);
+ WiFi.softAP("esp-captive");
+#endif
+
+ /*
+ ❯ curl -I -X HEAD http://192.168.4.1/download
+ HTTP/1.1 200 OK
+ Content-Length: 1024
+ Content-Type: application/octet-stream
+ Connection: close
+ Accept-Ranges: bytes
+ */
+ // Ref: https://github.com/mathieucarbou/ESPAsyncWebServer/pull/80
+ server.on("/download", HTTP_HEAD | HTTP_GET, [](AsyncWebServerRequest *request) {
+ if (request->method() == HTTP_HEAD) {
+ AsyncWebServerResponse *response = request->beginResponse(200, "application/octet-stream");
+ response->addHeader(asyncsrv::T_Accept_Ranges, "bytes");
+ response->addHeader(asyncsrv::T_Content_Length, 10);
+ response->setContentLength(1024); // make sure we can overrides previously set content length
+ response->addHeader(asyncsrv::T_Content_Type, "foo");
+ response->setContentType("application/octet-stream"); // make sure we can overrides previously set content type
+ // ...
+ request->send(response);
+ } else {
+ // ...
+ }
+ });
+
+ server.begin();
+}
+
+void loop() {
+ delay(100);
+}
diff --git a/libraries/ESP_Async_WebServer/examples/Rewrite/Rewrite.ino b/libraries/ESP_Async_WebServer/examples/Rewrite/Rewrite.ino
new file mode 100644
index 0000000..6981b11
--- /dev/null
+++ b/libraries/ESP_Async_WebServer/examples/Rewrite/Rewrite.ino
@@ -0,0 +1,52 @@
+// SPDX-License-Identifier: LGPL-3.0-or-later
+// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
+
+//
+// Shows how to rewrite URLs
+//
+
+#include <Arduino.h>
+#ifdef ESP32
+#include <AsyncTCP.h>
+#include <WiFi.h>
+#elif defined(ESP8266)
+#include <ESP8266WiFi.h>
+#include <ESPAsyncTCP.h>
+#elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
+#include <RPAsyncTCP.h>
+#include <WiFi.h>
+#endif
+
+#include <ESPAsyncWebServer.h>
+
+static AsyncWebServer server(80);
+
+void setup() {
+ Serial.begin(115200);
+
+#ifndef CONFIG_IDF_TARGET_ESP32H2
+ WiFi.mode(WIFI_AP);
+ WiFi.softAP("esp-captive");
+#endif
+
+ // curl -v http://192.168.4.1/index.txt
+ server.on("/index.txt", HTTP_GET, [](AsyncWebServerRequest *request) {
+ request->send(200, "text/plain", "Hello, world!");
+ });
+
+ // curl -v http://192.168.4.1/index.txt
+ server.on("/index.html", HTTP_GET, [](AsyncWebServerRequest *request) {
+ request->send(200, "text/html", "<h1>Hello, world!</h1>");
+ });
+
+ // curl -v http://192.168.4.1/
+ server.rewrite("/", "/index.html");
+ server.rewrite("/index.txt", "/index.html"); // will hide the .txt file
+
+ server.begin();
+}
+
+// not needed
+void loop() {
+ delay(100);
+}
diff --git a/libraries/ESP_Async_WebServer/examples/ServerSentEvents/ServerSentEvents.ino b/libraries/ESP_Async_WebServer/examples/ServerSentEvents/ServerSentEvents.ino
new file mode 100644
index 0000000..91e2c1d
--- /dev/null
+++ b/libraries/ESP_Async_WebServer/examples/ServerSentEvents/ServerSentEvents.ino
@@ -0,0 +1,105 @@
+// SPDX-License-Identifier: LGPL-3.0-or-later
+// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
+
+//
+// SSE example
+//
+
+#include <Arduino.h>
+#ifdef ESP32
+#include <AsyncTCP.h>
+#include <WiFi.h>
+#elif defined(ESP8266)
+#include <ESP8266WiFi.h>
+#include <ESPAsyncTCP.h>
+#elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
+#include <RPAsyncTCP.h>
+#include <WiFi.h>
+#endif
+
+#include <ESPAsyncWebServer.h>
+
+static const char *htmlContent PROGMEM = R"(
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Server-Sent Events</title>
+ <script>
+ if (!!window.EventSource) {
+ var source = new EventSource('/events');
+ source.addEventListener('open', function(e) {
+ console.log("Events Connected");
+ }, false);
+ source.addEventListener('error', function(e) {
+ if (e.target.readyState != EventSource.OPEN) {
+ console.log("Events Disconnected");
+ }
+ }, false);
+ source.addEventListener('message', function(e) {
+ console.log("message", e.data);
+ }, false);
+ source.addEventListener('heartbeat', function(e) {
+ console.log("heartbeat", e.data);
+ }, false);
+ }
+ </script>
+</head>
+<body>
+ <h1>Open your browser console!</h1>
+</body>
+</html>
+)";
+
+static const size_t htmlContentLength = strlen_P(htmlContent);
+
+static AsyncWebServer server(80);
+static AsyncEventSource events("/events");
+
+void setup() {
+ Serial.begin(115200);
+
+#ifndef CONFIG_IDF_TARGET_ESP32H2
+ WiFi.mode(WIFI_AP);
+ WiFi.softAP("esp-captive");
+#endif
+
+ // curl -v http://192.168.4.1/
+ server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
+ // need to cast to uint8_t*
+ // if you do not, the const char* will be copied in a temporary String buffer
+ request->send(200, "text/html", (uint8_t *)htmlContent, htmlContentLength);
+ });
+
+ events.onConnect([](AsyncEventSourceClient *client) {
+ Serial.printf("SSE Client connected! ID: %" PRIu32 "\n", client->lastId());
+ client->send("hello!", NULL, millis(), 1000);
+ });
+
+ events.onDisconnect([](AsyncEventSourceClient *client) {
+ Serial.printf("SSE Client disconnected! ID: %" PRIu32 "\n", client->lastId());
+ });
+
+ server.addHandler(&events);
+
+ server.begin();
+}
+
+static uint32_t lastSSE = 0;
+static uint32_t deltaSSE = 3000;
+
+static uint32_t lastHeap = 0;
+
+void loop() {
+ uint32_t now = millis();
+ if (now - lastSSE >= deltaSSE) {
+ events.send(String("ping-") + now, "heartbeat", now);
+ lastSSE = millis();
+ }
+
+#ifdef ESP32
+ if (now - lastHeap >= 2000) {
+ Serial.printf("Free heap: %" PRIu32 "\n", ESP.getFreeHeap());
+ lastHeap = now;
+ }
+#endif
+}
diff --git a/libraries/ESP_Async_WebServer/examples/ServerSentEvents_PR156/ServerSentEvents_PR156.ino b/libraries/ESP_Async_WebServer/examples/ServerSentEvents_PR156/ServerSentEvents_PR156.ino
new file mode 100644
index 0000000..928c9ad
--- /dev/null
+++ b/libraries/ESP_Async_WebServer/examples/ServerSentEvents_PR156/ServerSentEvents_PR156.ino
@@ -0,0 +1,141 @@
+// SPDX-License-Identifier: LGPL-3.0-or-later
+// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
+
+//
+// SSE example
+//
+
+#include <Arduino.h>
+#ifdef ESP32
+#include <AsyncTCP.h>
+#include <WiFi.h>
+#elif defined(ESP8266)
+#include <ESP8266WiFi.h>
+#include <ESPAsyncTCP.h>
+#elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
+#include <RPAsyncTCP.h>
+#include <WiFi.h>
+#endif
+
+#include <ESPAsyncWebServer.h>
+
+static const char *htmlContent PROGMEM = R"(
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Server-Sent Events</title>
+ <script>
+ if (!!window.EventSource) {
+ var source = new EventSource('/events');
+ source.onopen = function(e) {
+ console.log("Events Connected");
+ };
+ source.onerror = function(e) {
+ if (e.target.readyState != EventSource.OPEN) {
+ console.log("Events Disconnected");
+ }
+ // Uncomment below to prevent the client from proactively establishing a new connection.
+ // source.close();
+ };
+ source.onmessage = function(e) {
+ console.log("Message: " + e.data);
+ };
+ source.addEventListener('heartbeat', function(e) {
+ console.log("Heartbeat", e.data);
+ }, false);
+ }
+ </script>
+</head>
+<body>
+ <h1>Open your browser console!</h1>
+</body>
+</html>
+)";
+
+static const size_t htmlContentLength = strlen_P(htmlContent);
+
+static AsyncWebServer server(80);
+static AsyncEventSource events("/events");
+
+static volatile size_t connectionCount = 0;
+static volatile uint32_t timestampConnected = 0;
+static constexpr uint32_t timeoutClose = 15000;
+
+void setup() {
+ Serial.begin(115200);
+
+#ifndef CONFIG_IDF_TARGET_ESP32H2
+ WiFi.mode(WIFI_AP);
+ WiFi.softAP("esp-captive");
+#endif
+
+ // curl -v http://192.168.4.1/
+ server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
+ // need to cast to uint8_t*
+ // if you do not, the const char* will be copied in a temporary String buffer
+ request->send(200, "text/html", (uint8_t *)htmlContent, htmlContentLength);
+ });
+
+ events.onConnect([](AsyncEventSourceClient *client) {
+ /**
+ * @brief: Purpose for a test case: count() function
+ * Task watchdog shall be triggered due to a self-deadlock by mutex handling of the AsyncEventSource.
+ *
+ * E (61642) task_wdt: Task watchdog got triggered. The following tasks did not reset the watchdog in time:
+ * E (61642) task_wdt: - async_tcp (CPU 0/1)
+ *
+ * Resolve: using recursive_mutex insteads of mutex.
+ */
+ connectionCount = events.count();
+
+ timestampConnected = millis();
+ Serial.printf("SSE Client connected! ID: %" PRIu32 "\n", client->lastId());
+ client->send("hello!", NULL, millis(), 1000);
+ Serial.printf("Number of connected clients: %u\n", connectionCount);
+ });
+
+ events.onDisconnect([](AsyncEventSourceClient *client) {
+ connectionCount = events.count();
+ Serial.printf("SSE Client disconnected! ID: %" PRIu32 "\n", client->lastId());
+ Serial.printf("Number of connected clients: %u\n", connectionCount);
+ });
+
+ server.addHandler(&events);
+
+ server.begin();
+}
+
+static constexpr uint32_t deltaSSE = 3000;
+static uint32_t lastSSE = 0;
+static uint32_t lastHeap = 0;
+
+void loop() {
+ uint32_t now = millis();
+ if (connectionCount > 0) {
+ if (now - lastSSE >= deltaSSE) {
+ events.send(String("ping-") + now, "heartbeat", now);
+ lastSSE = millis();
+ }
+
+ /**
+ * @brief: Purpose for a test case: close() function
+ * Task watchdog shall be triggered due to a self-deadlock by mutex handling of the AsyncEventSource.
+ *
+ * E (61642) task_wdt: Task watchdog got triggered. The following tasks did not reset the watchdog in time:
+ * E (61642) task_wdt: - async_tcp (CPU 0/1)
+ *
+ * Resolve: using recursive_mutex insteads of mutex.
+ */
+ if (now - timestampConnected >= timeoutClose) {
+ Serial.printf("SSE Clients close\n");
+ events.close();
+ }
+ }
+
+#ifdef ESP32
+ if (now - lastHeap >= 2000) {
+ Serial.printf("Free heap: %" PRIu32 "\n", ESP.getFreeHeap());
+ lastHeap = now;
+ }
+#endif
+}
diff --git a/libraries/ESP_Async_WebServer/examples/ServerState/ServerState.ino b/libraries/ESP_Async_WebServer/examples/ServerState/ServerState.ino
new file mode 100644
index 0000000..8501758
--- /dev/null
+++ b/libraries/ESP_Async_WebServer/examples/ServerState/ServerState.ino
@@ -0,0 +1,66 @@
+// SPDX-License-Identifier: LGPL-3.0-or-later
+// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
+
+//
+// Server state example
+//
+
+#include <Arduino.h>
+#ifdef ESP32
+#include <AsyncTCP.h>
+#include <WiFi.h>
+#elif defined(ESP8266)
+#include <ESP8266WiFi.h>
+#include <ESPAsyncTCP.h>
+#elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
+#include <RPAsyncTCP.h>
+#include <WiFi.h>
+#endif
+
+#include <ESPAsyncWebServer.h>
+
+static AsyncWebServer server1(80);
+static AsyncWebServer server2(80);
+
+void setup() {
+ Serial.begin(115200);
+
+#ifndef CONFIG_IDF_TARGET_ESP32H2
+ WiFi.mode(WIFI_AP);
+ WiFi.softAP("esp-captive");
+#endif
+
+ // server state returns one of the tcp_state enum values:
+ // enum tcp_state {
+ // CLOSED = 0,
+ // LISTEN = 1,
+ // SYN_SENT = 2,
+ // SYN_RCVD = 3,
+ // ESTABLISHED = 4,
+ // FIN_WAIT_1 = 5,
+ // FIN_WAIT_2 = 6,
+ // CLOSE_WAIT = 7,
+ // CLOSING = 8,
+ // LAST_ACK = 9,
+ // TIME_WAIT = 10
+ // };
+
+ assert(server1.state() == tcp_state::CLOSED);
+ assert(server2.state() == tcp_state::CLOSED);
+
+ server1.begin();
+
+ assert(server1.state() == tcp_state::LISTEN);
+ assert(server2.state() == tcp_state::CLOSED);
+
+ server2.begin();
+
+ assert(server1.state() == tcp_state::LISTEN);
+ assert(server2.state() == tcp_state::CLOSED);
+
+ Serial.println("Done!");
+}
+
+void loop() {
+ delay(100);
+}
diff --git a/libraries/ESP_Async_WebServer/examples/SkipServerMiddleware/SkipServerMiddleware.ino b/libraries/ESP_Async_WebServer/examples/SkipServerMiddleware/SkipServerMiddleware.ino
new file mode 100644
index 0000000..d232c71
--- /dev/null
+++ b/libraries/ESP_Async_WebServer/examples/SkipServerMiddleware/SkipServerMiddleware.ino
@@ -0,0 +1,73 @@
+// SPDX-License-Identifier: LGPL-3.0-or-later
+// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
+
+//
+// Authentication and authorization middlewares
+//
+
+#include <Arduino.h>
+#ifdef ESP32
+#include <AsyncTCP.h>
+#include <WiFi.h>
+#elif defined(ESP8266)
+#include <ESP8266WiFi.h>
+#include <ESPAsyncTCP.h>
+#elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
+#include <RPAsyncTCP.h>
+#include <WiFi.h>
+#endif
+
+#include <ESPAsyncWebServer.h>
+
+static AsyncWebServer server(80);
+
+static AsyncAuthenticationMiddleware basicAuth;
+static AsyncLoggingMiddleware logging;
+
+void setup() {
+ Serial.begin(115200);
+
+#ifndef CONFIG_IDF_TARGET_ESP32H2
+ WiFi.mode(WIFI_AP);
+ WiFi.softAP("esp-captive");
+#endif
+
+ // basic authentication
+ basicAuth.setUsername("admin");
+ basicAuth.setPassword("admin");
+ basicAuth.setRealm("MyApp");
+ basicAuth.setAuthFailureMessage("Authentication failed");
+ basicAuth.setAuthType(AsyncAuthType::AUTH_BASIC);
+ basicAuth.generateHash(); // precompute hash (optional but recommended)
+
+ // logging middleware
+ logging.setEnabled(true);
+ logging.setOutput(Serial);
+
+ // we apply auth middleware to the server globally
+ server.addMiddleware(&basicAuth);
+
+ // protected endpoint: requires basic authentication
+ // curl -v -u admin:admin http://192.168.4.1/
+ server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
+ request->send(200, "text/plain", "Hello, world!");
+ });
+
+ // we skip all global middleware from the catchall handler
+ server.catchAllHandler().skipServerMiddlewares();
+ // we apply a specific middleware to the catchall handler only to log requests without a handler defined
+ server.catchAllHandler().addMiddleware(&logging);
+
+ // standard 404 handler: will display the request in the console i na curl-like style
+ // curl -v -H "Foo: Bar" http://192.168.4.1/foo
+ server.onNotFound([](AsyncWebServerRequest *request) {
+ request->send(404, "text/plain", "Not found");
+ });
+
+ server.begin();
+}
+
+// not needed
+void loop() {
+ delay(100);
+}
diff --git a/libraries/ESP_Async_WebServer/examples/SlowChunkResponse/SlowChunkResponse.ino b/libraries/ESP_Async_WebServer/examples/SlowChunkResponse/SlowChunkResponse.ino
new file mode 100644
index 0000000..bbf70b6
--- /dev/null
+++ b/libraries/ESP_Async_WebServer/examples/SlowChunkResponse/SlowChunkResponse.ino
@@ -0,0 +1,152 @@
+// SPDX-License-Identifier: LGPL-3.0-or-later
+// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
+
+//
+// Simulate a slow response in a chunk response (like file download from SD Card)
+// poll events will be throttled.
+//
+
+#include <Arduino.h>
+#ifdef ESP32
+#include <AsyncTCP.h>
+#include <WiFi.h>
+#elif defined(ESP8266)
+#include <ESP8266WiFi.h>
+#include <ESPAsyncTCP.h>
+#elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
+#include <RPAsyncTCP.h>
+#include <WiFi.h>
+#endif
+
+#include <ESPAsyncWebServer.h>
+
+static AsyncWebServer server(80);
+
+static const char *htmlContent PROGMEM = R"(
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Sample HTML</title>
+</head>
+<body>
+ <h1>Hello, World!</h1>
+ <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
+ rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
+ arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
+ accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
+ Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
+ dapibus elit, id varius sem dui id lacus.</p>
+ <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
+ rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
+ arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
+ accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
+ Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
+ dapibus elit, id varius sem dui id lacus.</p>
+ <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
+ rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
+ arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
+ accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
+ Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
+ dapibus elit, id varius sem dui id lacus.</p>
+ <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
+ rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
+ arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
+ accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
+ Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
+ dapibus elit, id varius sem dui id lacus.</p>
+ <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
+ rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
+ arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
+ accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
+ Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
+ dapibus elit, id varius sem dui id lacus.</p>
+ <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
+ rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
+ arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
+ accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
+ Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
+ dapibus elit, id varius sem dui id lacus.</p>
+ <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
+ rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
+ arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
+ accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
+ Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
+ dapibus elit, id varius sem dui id lacus.</p>
+ <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
+ rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
+ arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
+ accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
+ Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
+ dapibus elit, id varius sem dui id lacus.</p>
+</body>
+</html>
+)";
+
+static const size_t htmlContentLength = strlen_P(htmlContent);
+static constexpr char characters[] = "0123456789ABCDEF";
+static size_t charactersIndex = 0;
+
+void setup() {
+ Serial.begin(115200);
+
+#ifndef CONFIG_IDF_TARGET_ESP32H2
+ WiFi.mode(WIFI_AP);
+ WiFi.softAP("esp-captive");
+#endif
+
+ // curl -v http://192.168.4.1/
+ server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
+ // need to cast to uint8_t*
+ // if you do not, the const char* will be copied in a temporary String buffer
+ request->send(200, "text/html", (uint8_t *)htmlContent, htmlContentLength);
+ });
+
+ // IMPORTANT - DO NOT WRITE SUCH CODE IN PRODUCTON !
+ //
+ // This example simulates the slowdown that can happen when:
+ // - downloading a huge file from sdcard
+ // - doing some file listing on SDCard because it is horribly slow to get a file listing with file stats on SDCard.
+ // So in both cases, ESP would deadlock or TWDT would trigger.
+ //
+ // This example simulats that by slowing down the chunk callback:
+ // - d=2000 is the delay in ms in the callback
+ // - l=10000 is the length of the response
+ //
+ // time curl -N -v -G -d 'd=2000' -d 'l=10000' http://192.168.4.1/slow.html --output -
+ //
+ server.on("/slow.html", HTTP_GET, [](AsyncWebServerRequest *request) {
+ uint32_t d = request->getParam("d")->value().toInt();
+ uint32_t l = request->getParam("l")->value().toInt();
+ Serial.printf("d = %" PRIu32 ", l = %" PRIu32 "\n", d, l);
+ AsyncWebServerResponse *response = request->beginChunkedResponse("text/html", [d, l](uint8_t *buffer, size_t maxLen, size_t index) -> size_t {
+ Serial.printf("%u\n", index);
+ // finished ?
+ if (index >= l) {
+ return 0;
+ }
+
+ // slow down the task to simulate some heavy processing, like SD card reading
+ delay(d);
+
+ memset(buffer, characters[charactersIndex], 256);
+ charactersIndex = (charactersIndex + 1) % sizeof(characters);
+ return 256;
+ });
+
+ request->send(response);
+ });
+
+ server.begin();
+}
+
+static uint32_t lastHeap = 0;
+
+void loop() {
+#ifdef ESP32
+ uint32_t now = millis();
+ if (now - lastHeap >= 2000) {
+ Serial.printf("Free heap: %" PRIu32 "\n", ESP.getFreeHeap());
+ lastHeap = now;
+ }
+#endif
+}
diff --git a/libraries/ESP_Async_WebServer/examples/StaticFile/StaticFile.ino b/libraries/ESP_Async_WebServer/examples/StaticFile/StaticFile.ino
new file mode 100644
index 0000000..331f287
--- /dev/null
+++ b/libraries/ESP_Async_WebServer/examples/StaticFile/StaticFile.ino
@@ -0,0 +1,144 @@
+// SPDX-License-Identifier: LGPL-3.0-or-later
+// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
+
+//
+// Shows how to serve a static file
+//
+
+#include <Arduino.h>
+#ifdef ESP32
+#include <AsyncTCP.h>
+#include <WiFi.h>
+#elif defined(ESP8266)
+#include <ESP8266WiFi.h>
+#include <ESPAsyncTCP.h>
+#elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
+#include <RPAsyncTCP.h>
+#include <WiFi.h>
+#endif
+
+#include <ESPAsyncWebServer.h>
+#include <LittleFS.h>
+
+static AsyncWebServer server(80);
+
+static const char *htmlContent PROGMEM = R"(
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Sample HTML</title>
+</head>
+<body>
+ <h1>Hello, World!</h1>
+ <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
+ rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
+ arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
+ accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
+ Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
+ dapibus elit, id varius sem dui id lacus.</p>
+ <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
+ rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
+ arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
+ accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
+ Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
+ dapibus elit, id varius sem dui id lacus.</p>
+ <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
+ rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
+ arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
+ accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
+ Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
+ dapibus elit, id varius sem dui id lacus.</p>
+ <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
+ rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
+ arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
+ accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
+ Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
+ dapibus elit, id varius sem dui id lacus.</p>
+ <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
+ rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
+ arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
+ accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
+ Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
+ dapibus elit, id varius sem dui id lacus.</p>
+ <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
+ rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
+ arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
+ accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
+ Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
+ dapibus elit, id varius sem dui id lacus.</p>
+ <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
+ rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
+ arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
+ accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
+ Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
+ dapibus elit, id varius sem dui id lacus.</p>
+ <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod
+ rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper
+ arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit
+ accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi.
+ Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo
+ dapibus elit, id varius sem dui id lacus.</p>
+</body>
+</html>
+)";
+
+static const size_t htmlContentLength = strlen_P(htmlContent);
+
+void setup() {
+ Serial.begin(115200);
+
+#ifndef CONFIG_IDF_TARGET_ESP32H2
+ WiFi.mode(WIFI_AP);
+ WiFi.softAP("esp-captive");
+#endif
+
+#ifdef ESP32
+ LittleFS.begin(true);
+#else
+ LittleFS.begin();
+#endif
+
+ {
+ File f = LittleFS.open("/index.html", "w");
+ assert(f);
+ f.print(htmlContent);
+ f.close();
+ }
+
+ LittleFS.mkdir("/files");
+
+ {
+ File f = LittleFS.open("/files/a.txt", "w");
+ assert(f);
+ f.print("Hello from a.txt");
+ f.close();
+ }
+
+ {
+ File f = LittleFS.open("/files/b.txt", "w");
+ assert(f);
+ f.print("Hello from b.txt");
+ f.close();
+ }
+
+ // curl -v http://192.168.4.1/
+ server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
+ request->redirect("/index.html");
+ });
+
+ // curl -v http://192.168.4.1/index.html
+ server.serveStatic("/index.html", LittleFS, "/index.html");
+
+ // Example to serve a directory content
+ // curl -v http://192.168.4.1/base/ => serves a.txt
+ // curl -v http://192.168.4.1/base/a.txt => serves a.txt
+ // curl -v http://192.168.4.1/base/b.txt => serves b.txt
+ server.serveStatic("/base", LittleFS, "/files").setDefaultFile("a.txt");
+
+ server.begin();
+}
+
+// not needed
+void loop() {
+ delay(100);
+}
diff --git a/libraries/ESP_Async_WebServer/examples/Templates/Templates.ino b/libraries/ESP_Async_WebServer/examples/Templates/Templates.ino
new file mode 100644
index 0000000..edc02c2
--- /dev/null
+++ b/libraries/ESP_Async_WebServer/examples/Templates/Templates.ino
@@ -0,0 +1,99 @@
+// SPDX-License-Identifier: LGPL-3.0-or-later
+// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
+
+//
+// Shows how to serve a static and dynamic template
+//
+
+#include <Arduino.h>
+#ifdef ESP32
+#include <AsyncTCP.h>
+#include <WiFi.h>
+#elif defined(ESP8266)
+#include <ESP8266WiFi.h>
+#include <ESPAsyncTCP.h>
+#elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
+#include <RPAsyncTCP.h>
+#include <WiFi.h>
+#endif
+
+#include <ESPAsyncWebServer.h>
+#include <LittleFS.h>
+
+static AsyncWebServer server(80);
+
+static const char *htmlContent PROGMEM = R"(
+<!DOCTYPE html>
+<html>
+<body>
+ <h1>Hello, %USER%</h1>
+</body>
+</html>
+)";
+
+static const size_t htmlContentLength = strlen_P(htmlContent);
+
+void setup() {
+ Serial.begin(115200);
+
+#ifndef CONFIG_IDF_TARGET_ESP32H2
+ WiFi.mode(WIFI_AP);
+ WiFi.softAP("esp-captive");
+#endif
+
+#ifdef ESP32
+ LittleFS.begin(true);
+#else
+ LittleFS.begin();
+#endif
+
+ {
+ File f = LittleFS.open("/template.html", "w");
+ assert(f);
+ f.print(htmlContent);
+ f.close();
+ }
+
+ // Serve the static template file
+ //
+ // curl -v http://192.168.4.1/template.html
+ server.serveStatic("/template.html", LittleFS, "/template.html");
+
+ // Serve the static template with a template processor
+ //
+ // ServeStatic static is used to serve static output which never changes over time.
+ // This special endpoints automatically adds caching headers.
+ // If a template processor is used, it must ensure that the outputted content will always be the same over time and never changes.
+ // Otherwise, do not use serveStatic.
+ // Example below: IP never changes.
+ //
+ // curl -v http://192.168.4.1/index.html
+ server.serveStatic("/index.html", LittleFS, "/template.html").setTemplateProcessor([](const String &var) -> String {
+ if (var == "USER") {
+ return "Bob";
+ }
+ return emptyString;
+ });
+
+ // Serve a template with dynamic content
+ //
+ // to serve a template with dynamic content (output changes over time), use normal
+ // Example below: content changes over tinme do not use serveStatic.
+ //
+ // curl -v http://192.168.4.1/dynamic.html
+ server.on("/dynamic.html", HTTP_GET, [](AsyncWebServerRequest *request) {
+ request->send(LittleFS, "/template.html", "text/html", false, [](const String &var) -> String {
+ if (var == "USER") {
+ return String("Bob ") + millis();
+ }
+ return emptyString;
+ });
+ });
+
+ server.begin();
+}
+
+// not needed
+void loop() {
+ delay(100);
+}
diff --git a/libraries/ESP_Async_WebServer/examples/Upload/Upload.ino b/libraries/ESP_Async_WebServer/examples/Upload/Upload.ino
new file mode 100644
index 0000000..ceac47d
--- /dev/null
+++ b/libraries/ESP_Async_WebServer/examples/Upload/Upload.ino
@@ -0,0 +1,171 @@
+// SPDX-License-Identifier: LGPL-3.0-or-later
+// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
+
+//
+// Demo text, binary and file upload
+//
+
+#include <Arduino.h>
+#ifdef ESP32
+#include <AsyncTCP.h>
+#include <WiFi.h>
+#elif defined(ESP8266)
+#include <ESP8266WiFi.h>
+#include <ESPAsyncTCP.h>
+#elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
+#include <RPAsyncTCP.h>
+#include <WiFi.h>
+#endif
+
+#include <ESPAsyncWebServer.h>
+#include <StreamString.h>
+#include <LittleFS.h>
+
+static AsyncWebServer server(80);
+
+void setup() {
+ Serial.begin(115200);
+
+ if (!LittleFS.begin()) {
+ LittleFS.format();
+ LittleFS.begin();
+ }
+
+#ifndef CONFIG_IDF_TARGET_ESP32H2
+ WiFi.mode(WIFI_AP);
+ WiFi.softAP("esp-captive");
+#endif
+
+ // 1. Generate a Lorem_ipsum.txt file of about 20KB of text
+ //
+ // 3. Run: curl -v -F "data=@Lorem_ipsum.txt" http://192.168.4.1/upload/text
+ //
+ server.on(
+ "/upload/text", HTTP_POST,
+ [](AsyncWebServerRequest *request) {
+ if (!request->_tempObject) {
+ return request->send(400, "text/plain", "Nothing uploaded");
+ }
+ StreamString *buffer = reinterpret_cast<StreamString *>(request->_tempObject);
+ Serial.printf("Text uploaded:\n%s\n", buffer->c_str());
+ delete buffer;
+ request->_tempObject = nullptr;
+ request->send(200, "text/plain", "OK");
+ },
+ [](AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final) {
+ Serial.printf("Upload[%s]: start=%u, len=%u, final=%d\n", filename.c_str(), index, len, final);
+
+ if (!index) {
+ // first pass
+ StreamString *buffer = new StreamString();
+ size_t size = std::max(4094l, request->header("Content-Length").toInt());
+ Serial.printf("Allocating string buffer of %u bytes\n", size);
+ if (!buffer->reserve(size)) {
+ delete buffer;
+ request->abort();
+ }
+ request->_tempObject = buffer;
+ }
+
+ if (len) {
+ reinterpret_cast<StreamString *>(request->_tempObject)->write(data, len);
+ }
+ }
+ );
+
+ // 1. Generate a Lorem_ipsum.txt file of about 20KB of text
+ //
+ // 3. Run: curl -v -F "data=@Lorem_ipsum.txt" http://192.168.4.1/upload/file
+ //
+ server.on(
+ "/upload/file", HTTP_POST,
+ [](AsyncWebServerRequest *request) {
+ if (request->getResponse()) {
+ // 400 File not available for writing
+ return;
+ }
+
+ if (!LittleFS.exists("/my_file.txt")) {
+ return request->send(400, "text/plain", "Nothing uploaded");
+ }
+
+ // sends back the uploaded file
+ request->send(LittleFS, "/my_file.txt", "text/plain");
+ },
+ [](AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final) {
+ Serial.printf("Upload[%s]: start=%u, len=%u, final=%d\n", filename.c_str(), index, len, final);
+
+ if (!index) {
+ request->_tempFile = LittleFS.open("/my_file.txt", "w");
+
+ if (!request->_tempFile) {
+ request->send(400, "text/plain", "File not available for writing");
+ }
+ }
+ if (len) {
+ request->_tempFile.write(data, len);
+ }
+ if (final) {
+ request->_tempFile.close();
+ }
+ }
+ );
+
+ //
+ // Upload a binary file: curl -v -F "data=@file.mp3" http://192.168.4.1/upload/binary
+ //
+ server.on(
+ "/upload/binary", HTTP_POST,
+ [](AsyncWebServerRequest *request) {
+ // response already set ?
+ if (request->getResponse()) {
+ // 400 No Content-Length
+ return;
+ }
+
+ // nothing uploaded ?
+ if (!request->_tempObject) {
+ return request->send(400, "text/plain", "Nothing uploaded");
+ }
+
+ uint8_t *buffer = reinterpret_cast<uint8_t *>(request->_tempObject);
+ // process the buffer
+
+ delete buffer;
+ request->_tempObject = nullptr;
+
+ request->send(200, "text/plain", "OK");
+ },
+ [](AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final) {
+ Serial.printf("Upload[%s]: start=%u, len=%u, final=%d\n", filename.c_str(), index, len, final);
+
+ // first pass ?
+ if (!index) {
+ size_t size = request->header("Content-Length").toInt();
+ if (!size) {
+ request->send(400, "text/plain", "No Content-Length");
+ } else {
+ Serial.printf("Allocating buffer of %u bytes\n", size);
+ uint8_t *buffer = new (std::nothrow) uint8_t[size];
+ if (!buffer) {
+ // not enough memory
+ request->abort();
+ } else {
+ request->_tempObject = buffer;
+ }
+ }
+ }
+
+ if (len) {
+ memcpy(reinterpret_cast<uint8_t *>(request->_tempObject) + index, data, len);
+ }
+ }
+ );
+
+ server.begin();
+}
+
+// not needed
+void loop() {
+ delay(100);
+}
diff --git a/libraries/ESP_Async_WebServer/examples/WebSocket/WebSocket.ino b/libraries/ESP_Async_WebServer/examples/WebSocket/WebSocket.ino
new file mode 100644
index 0000000..8c5e5a5
--- /dev/null
+++ b/libraries/ESP_Async_WebServer/examples/WebSocket/WebSocket.ino
@@ -0,0 +1,115 @@
+// SPDX-License-Identifier: LGPL-3.0-or-later
+// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
+
+//
+// WebSocket example
+//
+
+#include <Arduino.h>
+#ifdef ESP32
+#include <AsyncTCP.h>
+#include <WiFi.h>
+#elif defined(ESP8266)
+#include <ESP8266WiFi.h>
+#include <ESPAsyncTCP.h>
+#elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
+#include <RPAsyncTCP.h>
+#include <WiFi.h>
+#endif
+
+#include <ESPAsyncWebServer.h>
+
+static AsyncWebServer server(80);
+static AsyncWebSocket ws("/ws");
+
+void setup() {
+ Serial.begin(115200);
+
+#ifndef CONFIG_IDF_TARGET_ESP32H2
+ WiFi.mode(WIFI_AP);
+ WiFi.softAP("esp-captive");
+#endif
+
+ //
+ // Run in terminal 1: websocat ws://192.168.4.1/ws => should stream data
+ // Run in terminal 2: websocat ws://192.168.4.1/ws => should stream data
+ // Run in terminal 3: websocat ws://192.168.4.1/ws => should fail:
+ //
+ // To send a message to the WebSocket server:
+ //
+ // echo "Hello!" | websocat ws://192.168.4.1/ws
+ //
+ ws.onEvent([](AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len) {
+ (void)len;
+
+ if (type == WS_EVT_CONNECT) {
+ ws.textAll("new client connected");
+ Serial.println("ws connect");
+ client->setCloseClientOnQueueFull(false);
+ client->ping();
+
+ } else if (type == WS_EVT_DISCONNECT) {
+ ws.textAll("client disconnected");
+ Serial.println("ws disconnect");
+
+ } else if (type == WS_EVT_ERROR) {
+ Serial.println("ws error");
+
+ } else if (type == WS_EVT_PONG) {
+ Serial.println("ws pong");
+
+ } else if (type == WS_EVT_DATA) {
+ AwsFrameInfo *info = (AwsFrameInfo *)arg;
+ Serial.printf("index: %" PRIu64 ", len: %" PRIu64 ", final: %" PRIu8 ", opcode: %" PRIu8 "\n", info->index, info->len, info->final, info->opcode);
+ String msg = "";
+ if (info->final && info->index == 0 && info->len == len) {
+ if (info->opcode == WS_TEXT) {
+ data[len] = 0;
+ Serial.printf("ws text: %s\n", (char *)data);
+ }
+ }
+ }
+ });
+
+ // shows how to prevent a third WS client to connect
+ server.addHandler(&ws).addMiddleware([](AsyncWebServerRequest *request, ArMiddlewareNext next) {
+ // ws.count() is the current count of WS clients: this one is trying to upgrade its HTTP connection
+ if (ws.count() > 1) {
+ // if we have 2 clients or more, prevent the next one to connect
+ request->send(503, "text/plain", "Server is busy");
+ } else {
+ // process next middleware and at the end the handler
+ next();
+ }
+ });
+
+ server.addHandler(&ws);
+
+ server.begin();
+}
+
+static uint32_t lastWS = 0;
+static uint32_t deltaWS = 100;
+
+static uint32_t lastHeap = 0;
+
+void loop() {
+ uint32_t now = millis();
+
+ if (now - lastWS >= deltaWS) {
+ ws.printfAll("kp%.4f", (10.0 / 3.0));
+ lastWS = millis();
+ }
+
+ if (now - lastHeap >= 2000) {
+ Serial.printf("Connected clients: %u / %u total\n", ws.count(), ws.getClients().size());
+
+ // this can be called to also set a soft limit on the number of connected clients
+ ws.cleanupClients(2); // no more than 2 clients
+
+#ifdef ESP32
+ Serial.printf("Free heap: %" PRIu32 "\n", ESP.getFreeHeap());
+#endif
+ lastHeap = now;
+ }
+}
diff --git a/libraries/ESP_Async_WebServer/examples/WebSocketEasy/WebSocketEasy.ino b/libraries/ESP_Async_WebServer/examples/WebSocketEasy/WebSocketEasy.ino
new file mode 100644
index 0000000..12b03ce
--- /dev/null
+++ b/libraries/ESP_Async_WebServer/examples/WebSocketEasy/WebSocketEasy.ino
@@ -0,0 +1,124 @@
+// SPDX-License-Identifier: LGPL-3.0-or-later
+// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
+
+//
+// WebSocket example using the easy to use AsyncWebSocketMessageHandler handler that only supports unfragmented messages
+//
+
+#include <Arduino.h>
+#ifdef ESP32
+#include <AsyncTCP.h>
+#include <WiFi.h>
+#elif defined(ESP8266)
+#include <ESP8266WiFi.h>
+#include <ESPAsyncTCP.h>
+#elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
+#include <RPAsyncTCP.h>
+#include <WiFi.h>
+#endif
+
+#include <ESPAsyncWebServer.h>
+
+static AsyncWebServer server(80);
+
+// create an easy-to-use handler
+static AsyncWebSocketMessageHandler wsHandler;
+
+// add it to the websocket server
+static AsyncWebSocket ws("/ws", wsHandler.eventHandler());
+
+// alternatively you can do as usual:
+//
+// static AsyncWebSocket ws("/ws");
+// ws.onEvent(wsHandler.eventHandler());
+
+static const char *htmlContent PROGMEM = R"(
+<!DOCTYPE html>
+<html>
+<head>
+ <title>WebSocket</title>
+</head>
+<body>
+ <h1>WebSocket Example</h1>
+ <>Open your browser console!</p>
+ <input type="text" id="message" placeholder="Type a message">
+ <button onclick='sendMessage()'>Send</button>
+ <script>
+ var ws = new WebSocket('ws://192.168.4.1/ws');
+ ws.onopen = function() {
+ console.log("WebSocket connected");
+ };
+ ws.onmessage = function(event) {
+ console.log("WebSocket message: " + event.data);
+ };
+ ws.onclose = function() {
+ console.log("WebSocket closed");
+ };
+ ws.onerror = function(error) {
+ console.log("WebSocket error: " + error);
+ };
+ function sendMessage() {
+ var message = document.getElementById("message").value;
+ ws.send(message);
+ console.log("WebSocket sent: " + message);
+ }
+ </script>
+</body>
+</html>
+ )";
+static const size_t htmlContentLength = strlen_P(htmlContent);
+
+void setup() {
+ Serial.begin(115200);
+
+#ifndef CONFIG_IDF_TARGET_ESP32H2
+ WiFi.mode(WIFI_AP);
+ WiFi.softAP("esp-captive");
+#endif
+
+ // serves root html page
+ server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
+ request->send(200, "text/html", (const uint8_t *)htmlContent, htmlContentLength);
+ });
+
+ wsHandler.onConnect([](AsyncWebSocket *server, AsyncWebSocketClient *client) {
+ Serial.printf("Client %" PRIu32 " connected\n", client->id());
+ server->textAll("New client: " + String(client->id()));
+ });
+
+ wsHandler.onDisconnect([](AsyncWebSocket *server, uint32_t clientId) {
+ Serial.printf("Client %" PRIu32 " disconnected\n", clientId);
+ server->textAll("Client " + String(clientId) + " disconnected");
+ });
+
+ wsHandler.onError([](AsyncWebSocket *server, AsyncWebSocketClient *client, uint16_t errorCode, const char *reason, size_t len) {
+ Serial.printf("Client %" PRIu32 " error: %" PRIu16 ": %s\n", client->id(), errorCode, reason);
+ });
+
+ wsHandler.onMessage([](AsyncWebSocket *server, AsyncWebSocketClient *client, const uint8_t *data, size_t len) {
+ Serial.printf("Client %" PRIu32 " data: %s\n", client->id(), (const char *)data);
+ });
+
+ wsHandler.onFragment([](AsyncWebSocket *server, AsyncWebSocketClient *client, const AwsFrameInfo *frameInfo, const uint8_t *data, size_t len) {
+ Serial.printf("Client %" PRIu32 " fragment %" PRIu32 ": %s\n", client->id(), frameInfo->num, (const char *)data);
+ });
+
+ server.addHandler(&ws);
+ server.begin();
+}
+
+static uint32_t lastWS = 0;
+static uint32_t deltaWS = 2000;
+
+void loop() {
+ uint32_t now = millis();
+
+ if (now - lastWS >= deltaWS) {
+ ws.cleanupClients();
+ ws.printfAll("now: %" PRIu32 "\n", now);
+ lastWS = millis();
+#ifdef ESP32
+ Serial.printf("Free heap: %" PRIu32 "\n", ESP.getFreeHeap());
+#endif
+ }
+}