This commit is contained in:
2025-01-21 12:15:59 +01:00
parent 9dca4f5dee
commit d55bb23fef
16 changed files with 231 additions and 2155 deletions

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

@ -595,7 +595,7 @@ void AsyncWebSocketClient::_onData(void* pbuf, size_t plen) {
}
// restore byte as _handleEvent may have added a null terminator i.e., data[len] = 0;
if (data && datalen > 0)
if (datalen)
data[datalen] = datalast;
data += datalen;

View File

@ -48,11 +48,11 @@
#include "literals.h"
#define ASYNCWEBSERVER_VERSION "3.4.5"
#define ASYNCWEBSERVER_VERSION "3.6.0"
#define ASYNCWEBSERVER_VERSION_MAJOR 3
#define ASYNCWEBSERVER_VERSION_MINOR 4
#define ASYNCWEBSERVER_VERSION_REVISION 5
#define ASYNCWEBSERVER_FORK_mathieucarbou
#define ASYNCWEBSERVER_VERSION_MINOR 6
#define ASYNCWEBSERVER_VERSION_REVISION 0
#define ASYNCWEBSERVER_FORK_ESP32Async
#ifdef ASYNCWEBSERVER_REGEX
#define ASYNCWEBSERVER_REGEX_ATTRIBUTE
@ -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

@ -57,12 +57,19 @@ class AsyncStaticWebHandler : public AsyncWebHandler {
AsyncStaticWebHandler& setIsDir(bool isDir);
AsyncStaticWebHandler& setDefaultFile(const char* filename);
AsyncStaticWebHandler& setCacheControl(const char* cache_control);
/**
* @brief Set the Last-Modified time for the object
*
* @param last_modified
* @return AsyncStaticWebHandler&
*/
AsyncStaticWebHandler& setLastModified(const char* last_modified);
AsyncStaticWebHandler& setLastModified(struct tm* last_modified);
#ifdef ESP8266
AsyncStaticWebHandler& setLastModified(time_t last_modified);
AsyncStaticWebHandler& setLastModified(); // sets to current time. Make sure sntp is runing and time is updated
#endif
// sets to current time. Make sure sntp is runing and time is updated
AsyncStaticWebHandler& setLastModified();
AsyncStaticWebHandler& setTemplateProcessor(AwsTemplateProcessor newCallback);
};

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);
}
@ -70,12 +70,12 @@ AsyncStaticWebHandler& AsyncStaticWebHandler::setIsDir(bool isDir) {
}
AsyncStaticWebHandler& AsyncStaticWebHandler::setDefaultFile(const char* filename) {
_default_file = String(filename);
_default_file = filename;
return *this;
}
AsyncStaticWebHandler& AsyncStaticWebHandler::setCacheControl(const char* cache_control) {
_cache_control = String(cache_control);
_cache_control = cache_control;
return *this;
}
@ -85,16 +85,20 @@ AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified(const char* last_m
}
AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified(struct tm* last_modified) {
auto formatP = PSTR("%a, %d %b %Y %H:%M:%S %Z");
char result[30];
#ifdef ESP8266
auto formatP = PSTR("%a, %d %b %Y %H:%M:%S GMT");
char format[strlen_P(formatP) + 1];
strcpy_P(format, formatP);
#else
static constexpr const char* format = "%a, %d %b %Y %H:%M:%S GMT";
#endif
char result[30];
strftime(result, sizeof(result), format, last_modified);
return setLastModified((const char*)result);
_last_modified = result;
return *this;
}
#ifdef ESP8266
AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified(time_t last_modified) {
return setLastModified((struct tm*)gmtime(&last_modified));
}
@ -105,7 +109,7 @@ AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified() {
return *this;
return setLastModified(last_modified);
}
#endif
bool AsyncStaticWebHandler::canHandle(AsyncWebServerRequest* request) const {
return request->isHTTP() && request->method() == HTTP_GET && request->url().startsWith(_uri) && _getFile(request);
}
@ -194,50 +198,59 @@ uint8_t AsyncStaticWebHandler::_countBits(const uint8_t value) const {
void AsyncStaticWebHandler::handleRequest(AsyncWebServerRequest* request) {
// Get the filename from request->_tempObject and free it
String filename = String((char*)request->_tempObject);
String filename((char*)request->_tempObject);
free(request->_tempObject);
request->_tempObject = NULL;
if (request->_tempFile == true) {
if (request->_tempFile != true){
request->send(404);
return;
}
time_t lw = request->_tempFile.getLastWrite(); // get last file mod time (if supported by FS)
// set etag to lastmod timestamp if available, otherwise to size
String etag;
if (lw) {
setLastModified(gmtime(&lw));
setLastModified(lw);
#if defined(TARGET_RP2040)
// time_t == long long int
const size_t len = 1 + 8 * sizeof(time_t);
constexpr size_t len = 1 + 8 * sizeof(time_t);
char buf[len];
char* ret = lltoa(lw, buf, len, 10);
char* ret = lltoa(lw ^ request->_tempFile.size(), buf, len, 10);
etag = ret ? String(ret) : String(request->_tempFile.size());
#else
etag = String(lw);
etag = lw ^ request->_tempFile.size(); // etag combines file size and lastmod timestamp
#endif
} else {
etag = String(request->_tempFile.size());
etag = request->_tempFile.size();
}
if (_last_modified.length() && _last_modified == request->header(T_IMS)) {
bool not_modified = false;
// if-none-match has precedence over if-modified-since
if (request->hasHeader(T_INM))
not_modified = request->header(T_INM).equals(etag);
else if (_last_modified.length())
not_modified = request->header(T_IMS).equals(_last_modified);
AsyncWebServerResponse* response;
if (not_modified){
request->_tempFile.close();
request->send(304); // Not modified
} else if (_cache_control.length() && request->hasHeader(T_INM) && request->header(T_INM).equals(etag)) {
request->_tempFile.close();
AsyncWebServerResponse* response = new AsyncBasicResponse(304); // Not modified
response->addHeader(T_Cache_Control, _cache_control.c_str());
response->addHeader(T_ETag, etag.c_str());
request->send(response);
response = new AsyncBasicResponse(304); // Not modified
} else {
AsyncWebServerResponse* response = new AsyncFileResponse(request->_tempFile, filename, String(), false, _callback);
if (_last_modified.length())
response->addHeader(T_Last_Modified, _last_modified.c_str());
if (_cache_control.length()) {
response->addHeader(T_Cache_Control, _cache_control.c_str());
response->addHeader(T_ETag, etag.c_str());
}
request->send(response);
response = new AsyncFileResponse(request->_tempFile, filename, emptyString, false, _callback);
}
} else {
request->send(404);
}
response->addHeader(T_ETag, etag.c_str());
if (_last_modified.length())
response->addHeader(T_Last_Modified, _last_modified.c_str());
if (_cache_control.length())
response->addHeader(T_Cache_Control, _cache_control.c_str());
request->send(response);
}
AsyncStaticWebHandler& AsyncStaticWebHandler::setTemplateProcessor(AwsTemplateProcessor newCallback) {