Breaking: Renamed Middleware classes to not conflict with Arduino Core 3.1.1 which now has Middleware support

This commit is contained in:
Mathieu Carbou
2025-01-09 15:31:27 +01:00
parent fd7dbdd9d2
commit 21323aa46f
7 changed files with 65 additions and 65 deletions

View File

@ -22,7 +22,7 @@ This fork is based on [yubox-node-org/ESPAsyncWebServer](https://github.com/yubo
- [`AsyncWebSocketMessageBuffer` and `makeBuffer()`](#asyncwebsocketmessagebuffer-and-makebuffer)
- [How to replace a response](#how-to-replace-a-response)
- [How to use Middleware](#how-to-use-middleware)
- [How to use authentication with AuthenticationMiddleware](#how-to-use-authentication-with-authenticationmiddleware)
- [How to use authentication with AsyncAuthenticationMiddleware](#how-to-use-authentication-with-authenticationmiddleware)
- [Migration to Middleware to improve performance and memory usage](#migration-to-middleware-to-improve-performance-and-memory-usage)
- [Original Documentation](#original-documentation)
@ -270,24 +270,24 @@ AsyncMiddlewareFunction complexAuth([](AsyncWebServerRequest* request, ArMiddlew
**Here are the list of available middlewares:**
- `AsyncMiddlewareFunction`: can convert a lambda function (`ArMiddlewareCallback`) to a middleware
- `AuthenticationMiddleware`: to handle basic/digest authentication globally or per handler
- `AuthorizationMiddleware`: to handle authorization globally or per handler
- `CorsMiddleware`: to handle CORS preflight request globally or per handler
- `HeaderFilterMiddleware`: to filter out headers from the request
- `HeaderFreeMiddleware`: to only keep some headers from the request, and remove the others
- `AsyncAuthenticationMiddleware`: to handle basic/digest authentication globally or per handler
- `AsyncAuthorizationMiddleware`: to handle authorization globally or per handler
- `AsyncCorsMiddleware`: to handle CORS preflight request globally or per handler
- `AsyncHeaderFilterMiddleware`: to filter out headers from the request
- `AsyncHeaderFreeMiddleware`: to only keep some headers from the request, and remove the others
- `LoggerMiddleware`: to log requests globally or per handler with the same pattern as curl. Will also record request processing time
- `RateLimitMiddleware`: to limit the number of requests on a windows of time globally or per handler
- `AsyncRateLimitMiddleware`: to limit the number of requests on a windows of time globally or per handler
## How to use authentication with AuthenticationMiddleware
## How to use authentication with AsyncAuthenticationMiddleware
Do not use the `setUsername()` and `setPassword()` methods on the hanlders anymore.
They are deprecated.
These methods were causing a copy of the username and password for each handler, which is not efficient.
Now, you can use the `AuthenticationMiddleware` to handle authentication globally or per handler.
Now, you can use the `AsyncAuthenticationMiddleware` to handle authentication globally or per handler.
```c++
AuthenticationMiddleware authMiddleware;
AsyncAuthenticationMiddleware authMiddleware;
// [...]
@ -309,10 +309,10 @@ myHandler.addMiddleware(&authMiddleware); // add authentication to a specific ha
## Migration to Middleware to improve performance and memory usage
- `AsyncEventSource.authorizeConnect(...)` => do not use this method anymore: add a common `AuthorizationMiddleware` to the handler or server, and make sure to add it AFTER the `AuthenticationMiddleware` if you use authentication.
- `AsyncWebHandler.setAuthentication(...)` => do not use this method anymore: add a common `AuthenticationMiddleware` to the handler or server
- `AsyncEventSource.authorizeConnect(...)` => do not use this method anymore: add a common `AsyncAuthorizationMiddleware` to the handler or server, and make sure to add it AFTER the `AsyncAuthenticationMiddleware` if you use authentication.
- `AsyncWebHandler.setAuthentication(...)` => do not use this method anymore: add a common `AsyncAuthenticationMiddleware` to the handler or server
- `ArUploadHandlerFunction` and `ArBodyHandlerFunction` => these callbacks receiving body data and upload and not calling anymore the authentication code for performance reasons.
These callbacks can be called multiple times during request parsing, so this is up to the user to now call the `AuthenticationMiddleware.allowed(request)` if needed and ideally when the method is called for the first time.
These callbacks can be called multiple times during request parsing, so this is up to the user to now call the `AsyncAuthenticationMiddleware.allowed(request)` if needed and ideally when the method is called for the first time.
These callbacks are also not triggering the whole middleware chain since they are not part of the request processing workflow (they are not the final handler).

View File

@ -22,7 +22,7 @@ This fork is based on [yubox-node-org/ESPAsyncWebServer](https://github.com/yubo
- [`AsyncWebSocketMessageBuffer` and `makeBuffer()`](#asyncwebsocketmessagebuffer-and-makebuffer)
- [How to replace a response](#how-to-replace-a-response)
- [How to use Middleware](#how-to-use-middleware)
- [How to use authentication with AuthenticationMiddleware](#how-to-use-authentication-with-authenticationmiddleware)
- [How to use authentication with AsyncAuthenticationMiddleware](#how-to-use-authentication-with-authenticationmiddleware)
- [Migration to Middleware to improve performance and memory usage](#migration-to-middleware-to-improve-performance-and-memory-usage)
- [Original Documentation](#original-documentation)
@ -270,24 +270,24 @@ AsyncMiddlewareFunction complexAuth([](AsyncWebServerRequest* request, ArMiddlew
**Here are the list of available middlewares:**
- `AsyncMiddlewareFunction`: can convert a lambda function (`ArMiddlewareCallback`) to a middleware
- `AuthenticationMiddleware`: to handle basic/digest authentication globally or per handler
- `AuthorizationMiddleware`: to handle authorization globally or per handler
- `CorsMiddleware`: to handle CORS preflight request globally or per handler
- `HeaderFilterMiddleware`: to filter out headers from the request
- `HeaderFreeMiddleware`: to only keep some headers from the request, and remove the others
- `AsyncAuthenticationMiddleware`: to handle basic/digest authentication globally or per handler
- `AsyncAuthorizationMiddleware`: to handle authorization globally or per handler
- `AsyncCorsMiddleware`: to handle CORS preflight request globally or per handler
- `AsyncHeaderFilterMiddleware`: to filter out headers from the request
- `AsyncHeaderFreeMiddleware`: to only keep some headers from the request, and remove the others
- `LoggerMiddleware`: to log requests globally or per handler with the same pattern as curl. Will also record request processing time
- `RateLimitMiddleware`: to limit the number of requests on a windows of time globally or per handler
- `AsyncRateLimitMiddleware`: to limit the number of requests on a windows of time globally or per handler
## How to use authentication with AuthenticationMiddleware
## How to use authentication with AsyncAuthenticationMiddleware
Do not use the `setUsername()` and `setPassword()` methods on the hanlders anymore.
They are deprecated.
These methods were causing a copy of the username and password for each handler, which is not efficient.
Now, you can use the `AuthenticationMiddleware` to handle authentication globally or per handler.
Now, you can use the `AsyncAuthenticationMiddleware` to handle authentication globally or per handler.
```c++
AuthenticationMiddleware authMiddleware;
AsyncAuthenticationMiddleware authMiddleware;
// [...]
@ -309,10 +309,10 @@ myHandler.addMiddleware(&authMiddleware); // add authentication to a specific ha
## Migration to Middleware to improve performance and memory usage
- `AsyncEventSource.authorizeConnect(...)` => do not use this method anymore: add a common `AuthorizationMiddleware` to the handler or server, and make sure to add it AFTER the `AuthenticationMiddleware` if you use authentication.
- `AsyncWebHandler.setAuthentication(...)` => do not use this method anymore: add a common `AuthenticationMiddleware` to the handler or server
- `AsyncEventSource.authorizeConnect(...)` => do not use this method anymore: add a common `AsyncAuthorizationMiddleware` to the handler or server, and make sure to add it AFTER the `AsyncAuthenticationMiddleware` if you use authentication.
- `AsyncWebHandler.setAuthentication(...)` => do not use this method anymore: add a common `AsyncAuthenticationMiddleware` to the handler or server
- `ArUploadHandlerFunction` and `ArBodyHandlerFunction` => these callbacks receiving body data and upload and not calling anymore the authentication code for performance reasons.
These callbacks can be called multiple times during request parsing, so this is up to the user to now call the `AuthenticationMiddleware.allowed(request)` if needed and ideally when the method is called for the first time.
These callbacks can be called multiple times during request parsing, so this is up to the user to now call the `AsyncAuthenticationMiddleware.allowed(request)` if needed and ideally when the method is called for the first time.
These callbacks are also not triggering the whole middleware chain since they are not part of the request processing workflow (they are not the final handler).

View File

@ -110,27 +110,27 @@ AsyncWebSocket ws("/ws");
/////////////////////////////////////////////////////////////////////////////////////////////////////
// log incoming requests
LoggingMiddleware requestLogger;
AsyncLoggingMiddleware requestLogger;
// CORS
CorsMiddleware cors;
AsyncCorsMiddleware cors;
// maximum 5 requests per 10 seconds
RateLimitMiddleware rateLimit;
AsyncRateLimitMiddleware rateLimit;
// filter out specific headers from the incoming request
HeaderFilterMiddleware headerFilter;
AsyncHeaderFilterMiddleware headerFilter;
// remove all headers from the incoming request except the ones provided in the constructor
HeaderFreeMiddleware headerFree;
AsyncHeaderFreeMiddleware headerFree;
// basicAuth
AuthenticationMiddleware basicAuth;
AuthenticationMiddleware basicAuthHash;
AsyncAuthenticationMiddleware basicAuth;
AsyncAuthenticationMiddleware basicAuthHash;
// simple digest authentication
AuthenticationMiddleware digestAuth;
AuthenticationMiddleware digestAuthHash;
AsyncAuthenticationMiddleware digestAuth;
AsyncAuthenticationMiddleware digestAuthHash;
// complex authentication which adds request attributes for the next middlewares and handler
AsyncMiddlewareFunction complexAuth([](AsyncWebServerRequest* request, ArMiddlewareNext next) {
@ -145,7 +145,7 @@ AsyncMiddlewareFunction complexAuth([](AsyncWebServerRequest* request, ArMiddlew
request->getResponse()->addHeader("X-Rate-Limit", "200");
});
AuthorizationMiddleware authz([](AsyncWebServerRequest* request) { return request->getAttribute("role") == "staff"; });
AsyncAuthorizationMiddleware authz([](AsyncWebServerRequest* request) { return request->getAttribute("role") == "staff"; });
int wsClients = 0;

View File

@ -332,7 +332,7 @@ void AsyncEventSourceClient::set_max_inflight_bytes(size_t value) {
/* AsyncEventSource */
void AsyncEventSource::authorizeConnect(ArAuthorizeConnectHandler cb) {
AuthorizationMiddleware* m = new AuthorizationMiddleware(401, cb);
AsyncAuthorizationMiddleware* m = new AsyncAuthorizationMiddleware(401, cb);
m->_freeOnRemoval = true;
addMiddleware(m);
}

View File

@ -285,12 +285,12 @@ class AsyncWebServerRequest {
void setHandler(AsyncWebHandler* handler) { _handler = handler; }
#ifndef ESP8266
[[deprecated("All headers are now collected. Use removeHeader(name) or HeaderFreeMiddleware if you really need to free some headers.")]]
[[deprecated("All headers are now collected. Use removeHeader(name) or AsyncHeaderFreeMiddleware if you really need to free some headers.")]]
#endif
void addInterestingHeader(__unused const char* name) {
}
#ifndef ESP8266
[[deprecated("All headers are now collected. Use removeHeader(name) or HeaderFreeMiddleware if you really need to free some headers.")]]
[[deprecated("All headers are now collected. Use removeHeader(name) or AsyncHeaderFreeMiddleware if you really need to free some headers.")]]
#endif
void addInterestingHeader(__unused const String& name) {
}
@ -557,8 +557,8 @@ class AsyncMiddlewareChain {
std::list<AsyncMiddleware*> _middlewares;
};
// AuthenticationMiddleware is a middleware that checks if the request is authenticated
class AuthenticationMiddleware : public AsyncMiddleware {
// AsyncAuthenticationMiddleware is a middleware that checks if the request is authenticated
class AsyncAuthenticationMiddleware : public AsyncMiddleware {
public:
void setUsername(const char* username);
void setPassword(const char* password);
@ -601,11 +601,11 @@ class AuthenticationMiddleware : public AsyncMiddleware {
};
using ArAuthorizeFunction = std::function<bool(AsyncWebServerRequest* request)>;
// AuthorizationMiddleware is a middleware that checks if the request is authorized
class AuthorizationMiddleware : public AsyncMiddleware {
// AsyncAuthorizationMiddleware is a middleware that checks if the request is authorized
class AsyncAuthorizationMiddleware : public AsyncMiddleware {
public:
AuthorizationMiddleware(ArAuthorizeFunction authorizeConnectHandler) : _code(403), _authz(authorizeConnectHandler) {}
AuthorizationMiddleware(int code, ArAuthorizeFunction authorizeConnectHandler) : _code(code), _authz(authorizeConnectHandler) {}
AsyncAuthorizationMiddleware(ArAuthorizeFunction authorizeConnectHandler) : _code(403), _authz(authorizeConnectHandler) {}
AsyncAuthorizationMiddleware(int code, ArAuthorizeFunction authorizeConnectHandler) : _code(code), _authz(authorizeConnectHandler) {}
void run(AsyncWebServerRequest* request, ArMiddlewareNext next) { return _authz && !_authz(request) ? request->send(_code) : next(); }
@ -615,7 +615,7 @@ class AuthorizationMiddleware : public AsyncMiddleware {
};
// remove all headers from the incoming request except the ones provided in the constructor
class HeaderFreeMiddleware : public AsyncMiddleware {
class AsyncHeaderFreeMiddleware : public AsyncMiddleware {
public:
void keep(const char* name) { _toKeep.push_back(name); }
void unKeep(const char* name) { _toKeep.erase(std::remove(_toKeep.begin(), _toKeep.end(), name), _toKeep.end()); }
@ -627,7 +627,7 @@ class HeaderFreeMiddleware : public AsyncMiddleware {
};
// filter out specific headers from the incoming request
class HeaderFilterMiddleware : public AsyncMiddleware {
class AsyncHeaderFilterMiddleware : public AsyncMiddleware {
public:
void filter(const char* name) { _toRemove.push_back(name); }
void unFilter(const char* name) { _toRemove.erase(std::remove(_toRemove.begin(), _toRemove.end(), name), _toRemove.end()); }
@ -639,7 +639,7 @@ class HeaderFilterMiddleware : public AsyncMiddleware {
};
// curl-like logging of incoming requests
class LoggingMiddleware : public AsyncMiddleware {
class AsyncLoggingMiddleware : public AsyncMiddleware {
public:
void setOutput(Print& output) { _out = &output; }
void setEnabled(bool enabled) { _enabled = enabled; }
@ -653,7 +653,7 @@ class LoggingMiddleware : public AsyncMiddleware {
};
// CORS Middleware
class CorsMiddleware : public AsyncMiddleware {
class AsyncCorsMiddleware : public AsyncMiddleware {
public:
void setOrigin(const char* origin) { _origin = origin; }
void setMethods(const char* methods) { _methods = methods; }
@ -674,7 +674,7 @@ class CorsMiddleware : public AsyncMiddleware {
};
// Rate limit Middleware
class RateLimitMiddleware : public AsyncMiddleware {
class AsyncRateLimitMiddleware : public AsyncMiddleware {
public:
void setMaxRequests(size_t maxRequests) { _maxRequests = maxRequests; }
void setWindowSize(uint32_t seconds) { _windowSizeMillis = seconds * 1000; }
@ -727,7 +727,7 @@ class AsyncWebRewrite {
class AsyncWebHandler : public AsyncMiddlewareChain {
protected:
ArRequestFilterFunction _filter = nullptr;
AuthenticationMiddleware* _authMiddleware = nullptr;
AsyncAuthenticationMiddleware* _authMiddleware = nullptr;
public:
AsyncWebHandler() {}

View File

@ -53,24 +53,24 @@ void AsyncMiddlewareChain::_runChain(AsyncWebServerRequest* request, ArMiddlewar
return next();
}
void AuthenticationMiddleware::setUsername(const char* username) {
void AsyncAuthenticationMiddleware::setUsername(const char* username) {
_username = username;
_hasCreds = _username.length() && _credentials.length();
}
void AuthenticationMiddleware::setPassword(const char* password) {
void AsyncAuthenticationMiddleware::setPassword(const char* password) {
_credentials = password;
_hash = false;
_hasCreds = _username.length() && _credentials.length();
}
void AuthenticationMiddleware::setPasswordHash(const char* hash) {
void AsyncAuthenticationMiddleware::setPasswordHash(const char* hash) {
_credentials = hash;
_hash = _credentials.length();
_hasCreds = _username.length() && _credentials.length();
}
bool AuthenticationMiddleware::generateHash() {
bool AsyncAuthenticationMiddleware::generateHash() {
// ensure we have all the necessary data
if (!_hasCreds)
return false;
@ -95,7 +95,7 @@ bool AuthenticationMiddleware::generateHash() {
}
}
bool AuthenticationMiddleware::allowed(AsyncWebServerRequest* request) const {
bool AsyncAuthenticationMiddleware::allowed(AsyncWebServerRequest* request) const {
if (_authMethod == AsyncAuthType::AUTH_NONE)
return true;
@ -108,11 +108,11 @@ bool AuthenticationMiddleware::allowed(AsyncWebServerRequest* request) const {
return request->authenticate(_username.c_str(), _credentials.c_str(), _realm.c_str(), _hash);
}
void AuthenticationMiddleware::run(AsyncWebServerRequest* request, ArMiddlewareNext next) {
void AsyncAuthenticationMiddleware::run(AsyncWebServerRequest* request, ArMiddlewareNext next) {
return allowed(request) ? next() : request->requestAuthentication(_authMethod, _realm.c_str(), _authFailMsg.c_str());
}
void HeaderFreeMiddleware::run(AsyncWebServerRequest* request, ArMiddlewareNext next) {
void AsyncHeaderFreeMiddleware::run(AsyncWebServerRequest* request, ArMiddlewareNext next) {
std::vector<const char*> reqHeaders;
request->getHeaderNames(reqHeaders);
for (const char* h : reqHeaders) {
@ -130,13 +130,13 @@ void HeaderFreeMiddleware::run(AsyncWebServerRequest* request, ArMiddlewareNext
next();
}
void HeaderFilterMiddleware::run(AsyncWebServerRequest* request, ArMiddlewareNext next) {
void AsyncHeaderFilterMiddleware::run(AsyncWebServerRequest* request, ArMiddlewareNext next) {
for (auto it = _toRemove.begin(); it != _toRemove.end(); ++it)
request->removeHeader(*it);
next();
}
void LoggingMiddleware::run(AsyncWebServerRequest* request, ArMiddlewareNext next) {
void AsyncLoggingMiddleware::run(AsyncWebServerRequest* request, ArMiddlewareNext next) {
if (!isEnabled()) {
next();
return;
@ -194,7 +194,7 @@ void LoggingMiddleware::run(AsyncWebServerRequest* request, ArMiddlewareNext nex
}
}
void CorsMiddleware::addCORSHeaders(AsyncWebServerResponse* response) {
void AsyncCorsMiddleware::addCORSHeaders(AsyncWebServerResponse* response) {
response->addHeader(asyncsrv::T_CORS_ACAO, _origin.c_str());
response->addHeader(asyncsrv::T_CORS_ACAM, _methods.c_str());
response->addHeader(asyncsrv::T_CORS_ACAH, _headers.c_str());
@ -202,7 +202,7 @@ void CorsMiddleware::addCORSHeaders(AsyncWebServerResponse* response) {
response->addHeader(asyncsrv::T_CORS_ACMA, String(_maxAge).c_str());
}
void CorsMiddleware::run(AsyncWebServerRequest* request, ArMiddlewareNext next) {
void AsyncCorsMiddleware::run(AsyncWebServerRequest* request, ArMiddlewareNext next) {
// Origin header ? => CORS handling
if (request->hasHeader(asyncsrv::T_CORS_O)) {
// check if this is a preflight request => handle it and return
@ -226,7 +226,7 @@ void CorsMiddleware::run(AsyncWebServerRequest* request, ArMiddlewareNext next)
}
}
bool RateLimitMiddleware::isRequestAllowed(uint32_t& retryAfterSeconds) {
bool AsyncRateLimitMiddleware::isRequestAllowed(uint32_t& retryAfterSeconds) {
uint32_t now = millis();
while (!_requestTimes.empty() && _requestTimes.front() <= now - _windowSizeMillis)
@ -244,7 +244,7 @@ bool RateLimitMiddleware::isRequestAllowed(uint32_t& retryAfterSeconds) {
return true;
}
void RateLimitMiddleware::run(AsyncWebServerRequest* request, ArMiddlewareNext next) {
void AsyncRateLimitMiddleware::run(AsyncWebServerRequest* request, ArMiddlewareNext next) {
uint32_t retryAfterSeconds;
if (isRequestAllowed(retryAfterSeconds)) {
next();

View File

@ -29,7 +29,7 @@ AsyncWebHandler& AsyncWebHandler::setFilter(ArRequestFilterFunction fn) {
}
AsyncWebHandler& AsyncWebHandler::setAuthentication(const char* username, const char* password, AsyncAuthType authMethod) {
if (!_authMiddleware) {
_authMiddleware = new AuthenticationMiddleware();
_authMiddleware = new AsyncAuthenticationMiddleware();
_authMiddleware->_freeOnRemoval = true;
addMiddleware(_authMiddleware);
}