Skip to content

Commit 7eb04c6

Browse files
committed
feat(http): Skipping Middleware
- Allow to skip global server middleware on any handler - Allow to access the catch-all handler to add middlewares on it
1 parent 3634268 commit 7eb04c6

File tree

5 files changed

+103
-7
lines changed

5 files changed

+103
-7
lines changed
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
// SPDX-License-Identifier: LGPL-3.0-or-later
2+
// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov
3+
4+
//
5+
// Authentication and authorization middlewares
6+
//
7+
8+
#include <Arduino.h>
9+
#ifdef ESP32
10+
#include <AsyncTCP.h>
11+
#include <WiFi.h>
12+
#elif defined(ESP8266)
13+
#include <ESP8266WiFi.h>
14+
#include <ESPAsyncTCP.h>
15+
#elif defined(TARGET_RP2040)
16+
#include <WebServer.h>
17+
#include <WiFi.h>
18+
#endif
19+
20+
#include <ESPAsyncWebServer.h>
21+
22+
static AsyncWebServer server(80);
23+
24+
static AsyncAuthenticationMiddleware basicAuth;
25+
static AsyncLoggingMiddleware logging;
26+
27+
void setup() {
28+
Serial.begin(115200);
29+
30+
#ifndef CONFIG_IDF_TARGET_ESP32H2
31+
WiFi.mode(WIFI_AP);
32+
WiFi.softAP("esp-captive");
33+
#endif
34+
35+
// basic authentication
36+
basicAuth.setUsername("admin");
37+
basicAuth.setPassword("admin");
38+
basicAuth.setRealm("MyApp");
39+
basicAuth.setAuthFailureMessage("Authentication failed");
40+
basicAuth.setAuthType(AsyncAuthType::AUTH_BASIC);
41+
basicAuth.generateHash(); // precompute hash (optional but recommended)
42+
43+
// logging middleware
44+
logging.setEnabled(true);
45+
logging.setOutput(Serial);
46+
47+
// we apply auth middleware to the server globally
48+
server.addMiddleware(&basicAuth);
49+
50+
// protected endpoint: requires basic authentication
51+
// curl -v -u admin:admin http://192.168.4.1/
52+
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
53+
request->send(200, "text/plain", "Hello, world!");
54+
});
55+
56+
// we skip all global middleware from the catchall handler
57+
server.catchAllHandler().skipServerMiddlewares();
58+
// we apply a specific middleware to the catchall handler only to log requests without a handler defined
59+
server.catchAllHandler().addMiddleware(&logging);
60+
61+
// standard 404 handler: will display the request in the console i na curl-like style
62+
// curl -v -H "Foo: Bar" http://192.168.4.1/foo
63+
server.onNotFound([](AsyncWebServerRequest *request) {
64+
request->send(404, "text/plain", "Not found");
65+
});
66+
67+
server.begin();
68+
}
69+
70+
// not needed
71+
void loop() {}

platformio.ini

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ src_dir = examples/PerfTests
2222
; src_dir = examples/ResumableDownload
2323
; src_dir = examples/Rewrite
2424
; src_dir = examples/ServerSentEvents
25+
; src_dir = examples/SkipServerMiddleware
2526
; src_dir = examples/SlowChunkResponse
2627
; src_dir = examples/StaticFile
2728
; src_dir = examples/Templates

src/ESPAsyncWebServer.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -906,6 +906,7 @@ class AsyncWebHandler : public AsyncMiddlewareChain {
906906
protected:
907907
ArRequestFilterFunction _filter = nullptr;
908908
AsyncAuthenticationMiddleware *_authMiddleware = nullptr;
909+
bool _skipServerMiddlewares = false;
909910

910911
public:
911912
AsyncWebHandler() {}
@@ -915,6 +916,17 @@ class AsyncWebHandler : public AsyncMiddlewareChain {
915916
AsyncWebHandler &setAuthentication(const String &username, const String &password, AsyncAuthType authMethod = AsyncAuthType::AUTH_DIGEST) {
916917
return setAuthentication(username.c_str(), password.c_str(), authMethod);
917918
};
919+
AsyncWebHandler &setSkipServerMiddlewares(bool state) {
920+
_skipServerMiddlewares = state;
921+
return *this;
922+
}
923+
// skip all glboally defined server middlewares for this handler and only execute those defined for this handler specifically
924+
AsyncWebHandler &skipServerMiddlewares() {
925+
return setSkipServerMiddlewares(true);
926+
}
927+
bool mustSkipServerMiddlewares() const {
928+
return _skipServerMiddlewares;
929+
}
918930
bool filter(AsyncWebServerRequest *request) {
919931
return _filter == NULL || _filter(request);
920932
}
@@ -1096,6 +1108,8 @@ class AsyncWebServer : public AsyncMiddlewareChain {
10961108
void onNotFound(ArRequestHandlerFunction fn); // called when handler is not assigned
10971109
void onFileUpload(ArUploadHandlerFunction fn); // handle file uploads
10981110
void onRequestBody(ArBodyHandlerFunction fn); // handle posts with plain body content (JSON often transmitted this way as a request)
1111+
// give access to the handler used to catch all requests, so that middleware can be added to it
1112+
AsyncWebHandler &catchAllHandler() const;
10991113

11001114
void reset(); // remove all writers and handlers, with onNotFound/onFileUpload/onRequestBody
11011115

src/WebRequest.cpp

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -660,13 +660,19 @@ void AsyncWebServerRequest::_parseLine() {
660660
}
661661

662662
void AsyncWebServerRequest::_runMiddlewareChain() {
663-
_server->_runChain(this, [this]() {
664-
if (_handler) {
665-
_handler->_runChain(this, [this]() {
666-
_handler->handleRequest(this);
667-
});
668-
}
669-
});
663+
if (_handler && _handler->mustSkipServerMiddlewares()) {
664+
_handler->_runChain(this, [this]() {
665+
_handler->handleRequest(this);
666+
});
667+
} else {
668+
_server->_runChain(this, [this]() {
669+
if (_handler) {
670+
_handler->_runChain(this, [this]() {
671+
_handler->handleRequest(this);
672+
});
673+
}
674+
});
675+
}
670676
}
671677

672678
void AsyncWebServerRequest::_send() {

src/WebServer.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,10 @@ void AsyncWebServer::onRequestBody(ArBodyHandlerFunction fn) {
175175
_catchAllHandler->onBody(fn);
176176
}
177177

178+
AsyncWebHandler &AsyncWebServer::catchAllHandler() const {
179+
return *_catchAllHandler;
180+
}
181+
178182
void AsyncWebServer::reset() {
179183
_rewrites.clear();
180184
_handlers.clear();

0 commit comments

Comments
 (0)