diff --git a/libraries/WebServer/examples/Filters/Filters.ino b/libraries/WebServer/examples/Filters/Filters.ino new file mode 100644 index 00000000000..8974e55d322 --- /dev/null +++ b/libraries/WebServer/examples/Filters/Filters.ino @@ -0,0 +1,110 @@ +#include +#include +#include +#include + +// Your STA WiFi Credentials +// ( This is the AP your ESP will connect to ) +const char *ssid = "........"; +const char *password = "........"; + +// Your AP WiFi Credentials +// ( This is the AP your ESP will broadcast ) +const char *ap_ssid = "ESP32_Demo"; +const char *ap_password = ""; + +WebServer server(80); + +const int led = 13; + +// ON_STA_FILTER - Only accept requests coming from STA interface +bool ON_STA_FILTER(WebServer &server) { + return WiFi.STA.hasIP() && WiFi.STA.localIP() == server.client().localIP(); +} + +// ON_AP_FILTER - Only accept requests coming from AP interface +bool ON_AP_FILTER(WebServer &server) { + return WiFi.AP.hasIP() && WiFi.AP.localIP() == server.client().localIP(); +} + +void handleNotFound() { + digitalWrite(led, 1); + String message = "File Not Found\n\n"; + message += "URI: "; + message += server.uri(); + message += "\nMethod: "; + message += (server.method() == HTTP_GET) ? "GET" : "POST"; + message += "\nArguments: "; + message += server.args(); + message += "\n"; + for (uint8_t i = 0; i < server.args(); i++) { + message += " " + server.argName(i) + ": " + server.arg(i) + "\n"; + } + server.send(404, "text/plain", message); + digitalWrite(led, 0); +} + +void setup(void) { + pinMode(led, OUTPUT); + digitalWrite(led, 0); + Serial.begin(115200); + WiFi.mode(WIFI_AP_STA); + // Connect to STA + WiFi.begin(ssid, password); + // Start AP + WiFi.softAP(ap_ssid, ap_password); + Serial.println(""); + + // Wait for connection + while (WiFi.status() != WL_CONNECTED) { + delay(500); + Serial.print("."); + } + Serial.println(""); + Serial.print("Connected to "); + Serial.println(ssid); + Serial.print("IP address: "); + Serial.println(WiFi.localIP()); + + if (MDNS.begin("esp32")) { + Serial.println("MDNS responder started"); + } + + // This route will be accessible by STA clients only + server + .on( + "/", + [&]() { + digitalWrite(led, 1); + server.send(200, "text/plain", "Hi!, This route is accessible for STA clients only"); + digitalWrite(led, 0); + } + ) + .setFilter(ON_STA_FILTER); + + // This route will be accessible by AP clients only + server + .on( + "/", + [&]() { + digitalWrite(led, 1); + server.send(200, "text/plain", "Hi!, This route is accessible for AP clients only"); + digitalWrite(led, 0); + } + ) + .setFilter(ON_AP_FILTER); + + server.on("/inline", []() { + server.send(200, "text/plain", "this works as well"); + }); + + server.onNotFound(handleNotFound); + + server.begin(); + Serial.println("HTTP server started"); +} + +void loop(void) { + server.handleClient(); + delay(2); //allow the cpu to switch to other tasks +} diff --git a/libraries/WebServer/examples/Filters/ci.json b/libraries/WebServer/examples/Filters/ci.json new file mode 100644 index 00000000000..d8b3664bc65 --- /dev/null +++ b/libraries/WebServer/examples/Filters/ci.json @@ -0,0 +1,5 @@ +{ + "targets": { + "esp32h2": false + } +} diff --git a/libraries/WebServer/src/Parsing.cpp b/libraries/WebServer/src/Parsing.cpp index 3d3e7d1c55a..200244e6848 100644 --- a/libraries/WebServer/src/Parsing.cpp +++ b/libraries/WebServer/src/Parsing.cpp @@ -124,7 +124,7 @@ bool WebServer::_parseRequest(NetworkClient &client) { //attach handler RequestHandler *handler; for (handler = _firstHandler; handler; handler = handler->next()) { - if (handler->canHandle(_currentMethod, _currentUri)) { + if (handler->canHandle(*this, _currentMethod, _currentUri)) { break; } } @@ -176,7 +176,7 @@ bool WebServer::_parseRequest(NetworkClient &client) { } } - if (!isForm && _currentHandler && _currentHandler->canRaw(_currentUri)) { + if (!isForm && _currentHandler && _currentHandler->canRaw(*this, _currentUri)) { log_v("Parse raw"); _currentRaw.reset(new HTTPRaw()); _currentRaw->status = RAW_START; @@ -334,7 +334,7 @@ void WebServer::_parseArguments(String data) { void WebServer::_uploadWriteByte(uint8_t b) { if (_currentUpload->currentSize == HTTP_UPLOAD_BUFLEN) { - if (_currentHandler && _currentHandler->canUpload(_currentUri)) { + if (_currentHandler && _currentHandler->canUpload(*this, _currentUri)) { _currentHandler->upload(*this, _currentUri, *_currentUpload); } _currentUpload->totalSize += _currentUpload->currentSize; @@ -449,7 +449,7 @@ bool WebServer::_parseForm(NetworkClient &client, String boundary, uint32_t len) _currentUpload->totalSize = 0; _currentUpload->currentSize = 0; log_v("Start File: %s Type: %s", _currentUpload->filename.c_str(), _currentUpload->type.c_str()); - if (_currentHandler && _currentHandler->canUpload(_currentUri)) { + if (_currentHandler && _currentHandler->canUpload(*this, _currentUri)) { _currentHandler->upload(*this, _currentUri, *_currentUpload); } _currentUpload->status = UPLOAD_FILE_WRITE; @@ -488,12 +488,12 @@ bool WebServer::_parseForm(NetworkClient &client, String boundary, uint32_t len) } } // Found the boundary string, finish processing this file upload - if (_currentHandler && _currentHandler->canUpload(_currentUri)) { + if (_currentHandler && _currentHandler->canUpload(*this, _currentUri)) { _currentHandler->upload(*this, _currentUri, *_currentUpload); } _currentUpload->totalSize += _currentUpload->currentSize; _currentUpload->status = UPLOAD_FILE_END; - if (_currentHandler && _currentHandler->canUpload(_currentUri)) { + if (_currentHandler && _currentHandler->canUpload(*this, _currentUri)) { _currentHandler->upload(*this, _currentUri, *_currentUpload); } log_v("End File: %s Type: %s Size: %d", _currentUpload->filename.c_str(), _currentUpload->type.c_str(), (int)_currentUpload->totalSize); @@ -567,7 +567,7 @@ String WebServer::urlDecode(const String &text) { bool WebServer::_parseFormUploadAborted() { _currentUpload->status = UPLOAD_FILE_ABORTED; - if (_currentHandler && _currentHandler->canUpload(_currentUri)) { + if (_currentHandler && _currentHandler->canUpload(*this, _currentUri)) { _currentHandler->upload(*this, _currentUri, *_currentUpload); } return false; diff --git a/libraries/WebServer/src/WebServer.cpp b/libraries/WebServer/src/WebServer.cpp index 83c22b7e493..048bd529d7b 100644 --- a/libraries/WebServer/src/WebServer.cpp +++ b/libraries/WebServer/src/WebServer.cpp @@ -306,16 +306,18 @@ void WebServer::requestAuthentication(HTTPAuthMethod mode, const char *realm, co send(401, String(FPSTR(mimeTable[html].mimeType)), authFailMsg); } -void WebServer::on(const Uri &uri, WebServer::THandlerFunction handler) { - on(uri, HTTP_ANY, handler); +RequestHandler &WebServer::on(const Uri &uri, WebServer::THandlerFunction handler) { + return on(uri, HTTP_ANY, handler); } -void WebServer::on(const Uri &uri, HTTPMethod method, WebServer::THandlerFunction fn) { - on(uri, method, fn, _fileUploadHandler); +RequestHandler &WebServer::on(const Uri &uri, HTTPMethod method, WebServer::THandlerFunction fn) { + return on(uri, method, fn, _fileUploadHandler); } -void WebServer::on(const Uri &uri, HTTPMethod method, WebServer::THandlerFunction fn, WebServer::THandlerFunction ufn) { - _addRequestHandler(new FunctionRequestHandler(fn, ufn, uri, method)); +RequestHandler &WebServer::on(const Uri &uri, HTTPMethod method, WebServer::THandlerFunction fn, WebServer::THandlerFunction ufn) { + FunctionRequestHandler *handler = new FunctionRequestHandler(fn, ufn, uri, method); + _addRequestHandler(handler); + return *handler; } bool WebServer::removeRoute(const char *uri) { diff --git a/libraries/WebServer/src/WebServer.h b/libraries/WebServer/src/WebServer.h index f69c08f22b6..c43dd4542ea 100644 --- a/libraries/WebServer/src/WebServer.h +++ b/libraries/WebServer/src/WebServer.h @@ -144,9 +144,10 @@ class WebServer { void requestAuthentication(HTTPAuthMethod mode = BASIC_AUTH, const char *realm = NULL, const String &authFailMsg = String("")); typedef std::function THandlerFunction; - void on(const Uri &uri, THandlerFunction fn); - void on(const Uri &uri, HTTPMethod method, THandlerFunction fn); - void on(const Uri &uri, HTTPMethod method, THandlerFunction fn, THandlerFunction ufn); //ufn handles file uploads + typedef std::function FilterFunction; + RequestHandler &on(const Uri &uri, THandlerFunction fn); + RequestHandler &on(const Uri &uri, HTTPMethod method, THandlerFunction fn); + RequestHandler &on(const Uri &uri, HTTPMethod method, THandlerFunction fn, THandlerFunction ufn); //ufn handles file uploads bool removeRoute(const char *uri); bool removeRoute(const char *uri, HTTPMethod method); bool removeRoute(const String &uri); diff --git a/libraries/WebServer/src/detail/RequestHandler.h b/libraries/WebServer/src/detail/RequestHandler.h index 4ef4b1d0075..f19e7ab4613 100644 --- a/libraries/WebServer/src/detail/RequestHandler.h +++ b/libraries/WebServer/src/detail/RequestHandler.h @@ -7,6 +7,11 @@ class RequestHandler { public: virtual ~RequestHandler() {} + + /* + note: old handler API for backward compatibility + */ + virtual bool canHandle(HTTPMethod method, String uri) { (void)method; (void)uri; @@ -20,6 +25,27 @@ class RequestHandler { (void)uri; return false; } + + /* + note: new handler API with support for filters etc. + */ + + virtual bool canHandle(WebServer &server, HTTPMethod method, String uri) { + (void)server; + (void)method; + (void)uri; + return false; + } + virtual bool canUpload(WebServer &server, String uri) { + (void)server; + (void)uri; + return false; + } + virtual bool canRaw(WebServer &server, String uri) { + (void)server; + (void)uri; + return false; + } virtual bool handle(WebServer &server, HTTPMethod requestMethod, String requestUri) { (void)server; (void)requestMethod; @@ -37,6 +63,11 @@ class RequestHandler { (void)raw; } + virtual RequestHandler &setFilter(std::function filter) { + (void)filter; + return *this; + } + RequestHandler *next() { return _next; } diff --git a/libraries/WebServer/src/detail/RequestHandlersImpl.h b/libraries/WebServer/src/detail/RequestHandlersImpl.h index d24d36fd9d4..b6eae6adea0 100644 --- a/libraries/WebServer/src/detail/RequestHandlersImpl.h +++ b/libraries/WebServer/src/detail/RequestHandlersImpl.h @@ -36,6 +36,7 @@ class FunctionRequestHandler : public RequestHandler { return true; } + bool canRaw(String requestUri) override { if (!_ufn || _method == HTTP_GET) { return false; @@ -44,9 +45,32 @@ class FunctionRequestHandler : public RequestHandler { return true; } + bool canHandle(WebServer &server, HTTPMethod requestMethod, String requestUri) override { + if (_method != HTTP_ANY && _method != requestMethod) { + return false; + } + + return _uri->canHandle(requestUri, pathArgs) && (_filter != NULL ? _filter(server) : true); + } + + bool canUpload(WebServer &server, String requestUri) override { + if (!_ufn || !canHandle(server, HTTP_POST, requestUri)) { + return false; + } + + return true; + } + + bool canRaw(WebServer &server, String requestUri) override { + if (!_ufn || _method == HTTP_GET || (_filter != NULL ? _filter(server) == false : false)) { + return false; + } + + return true; + } + bool handle(WebServer &server, HTTPMethod requestMethod, String requestUri) override { - (void)server; - if (!canHandle(requestMethod, requestUri)) { + if (!canHandle(server, requestMethod, requestUri)) { return false; } @@ -55,24 +79,30 @@ class FunctionRequestHandler : public RequestHandler { } void upload(WebServer &server, String requestUri, HTTPUpload &upload) override { - (void)server; (void)upload; - if (canUpload(requestUri)) { + if (canUpload(server, requestUri)) { _ufn(); } } void raw(WebServer &server, String requestUri, HTTPRaw &raw) override { - (void)server; (void)raw; - if (canRaw(requestUri)) { + if (canRaw(server, requestUri)) { _ufn(); } } + FunctionRequestHandler &setFilter(WebServer::FilterFunction filter) { + _filter = filter; + return *this; + } + protected: WebServer::THandlerFunction _fn; WebServer::THandlerFunction _ufn; + // _filter should return 'true' when the request should be handled + // and 'false' when the request should be ignored + WebServer::FilterFunction _filter; Uri *_uri; HTTPMethod _method; }; @@ -100,8 +130,24 @@ class StaticRequestHandler : public RequestHandler { return true; } + bool canHandle(WebServer &server, HTTPMethod requestMethod, String requestUri) override { + if (requestMethod != HTTP_GET) { + return false; + } + + if ((_isFile && requestUri != _uri) || !requestUri.startsWith(_uri)) { + return false; + } + + if (_filter != NULL ? _filter(server) == false : false) { + return false; + } + + return true; + } + bool handle(WebServer &server, HTTPMethod requestMethod, String requestUri) override { - if (!canHandle(requestMethod, requestUri)) { + if (!canHandle(server, requestMethod, requestUri)) { return false; } @@ -197,7 +243,15 @@ class StaticRequestHandler : public RequestHandler { return (result); } // calcETag + StaticRequestHandler &setFilter(WebServer::FilterFunction filter) { + _filter = filter; + return *this; + } + protected: + // _filter should return 'true' when the request should be handled + // and 'false' when the request should be ignored + WebServer::FilterFunction _filter; FS _fs; String _uri; String _path;