diff --git a/libraries/WebServer/examples/Filters/Filters.ino b/libraries/WebServer/examples/Filters/Filters.ino new file mode 100644 index 000000000..b9cd80d93 --- /dev/null +++ b/libraries/WebServer/examples/Filters/Filters.ino @@ -0,0 +1,100 @@ +#include +#include +#include +#include + +// Your STA WiFi Credentials +// ( This is the AP your RP2040 will connect to ) +const char *ssid = "........"; +const char *password = "........"; + +// Your AP WiFi Credentials +// ( This is the AP your RP2040 will broadcast ) +const char *ap_ssid = "RP2040_Demo"; +const char *ap_password = ""; + +WebServer server(80); + +const int led = LED_BUILTIN; + +// ON_STA_FILTER - Only accept requests coming from STA interface +bool ON_STA_FILTER(HTTPServer &server) { + return WiFi.localIP() == server.client().localIP(); +} + +// ON_AP_FILTER - Only accept requests coming from AP interface +bool ON_AP_FILTER(HTTPServer &server) { + return WiFi.softAPIP() == 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("picow")) { + 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(); + MDNS.update(); +} diff --git a/libraries/WebServer/src/HTTPServer.cpp b/libraries/WebServer/src/HTTPServer.cpp index e95098bf4..697dd4f5b 100644 --- a/libraries/WebServer/src/HTTPServer.cpp +++ b/libraries/WebServer/src/HTTPServer.cpp @@ -207,22 +207,65 @@ void HTTPServer::requestAuthentication(HTTPAuthMethod mode, const char* realm, c send(401, String(FPSTR(mimeTable[html].mimeType)), authFailMsg); } -void HTTPServer::on(const Uri &uri, HTTPServer::THandlerFunction handler) { - on(uri, HTTP_ANY, handler); +RequestHandler& HTTPServer::on(const Uri &uri, HTTPServer::THandlerFunction handler) { + return on(uri, HTTP_ANY, handler); } -void HTTPServer::on(const Uri &uri, HTTPMethod method, HTTPServer::THandlerFunction fn) { - on(uri, method, fn, _fileUploadHandler); +RequestHandler& HTTPServer::on(const Uri &uri, HTTPMethod method, HTTPServer::THandlerFunction fn) { + return on(uri, method, fn, _fileUploadHandler); } -void HTTPServer::on(const Uri &uri, HTTPMethod method, HTTPServer::THandlerFunction fn, HTTPServer::THandlerFunction ufn) { - _addRequestHandler(new FunctionRequestHandler(fn, ufn, uri, method)); +RequestHandler& HTTPServer::on(const Uri &uri, HTTPMethod method, HTTPServer::THandlerFunction fn, HTTPServer::THandlerFunction ufn) { + FunctionRequestHandler *handler = new FunctionRequestHandler(fn, ufn, uri, method); + _addRequestHandler(handler); + return *handler; +} + +bool HTTPServer::removeRoute(const char *uri) { + return removeRoute(String(uri), HTTP_ANY); +} + +bool HTTPServer::removeRoute(const char *uri, HTTPMethod method) { + return removeRoute(String(uri), method); +} + +bool HTTPServer::removeRoute(const String &uri) { + return removeRoute(uri, HTTP_ANY); +} + +bool HTTPServer::removeRoute(const String &uri, HTTPMethod method) { + bool anyHandlerRemoved = false; + RequestHandler *handler = _firstHandler; + RequestHandler *previousHandler = nullptr; + + while (handler) { + if (handler->canHandle(method, uri)) { + if (_removeRequestHandler(handler)) { + anyHandlerRemoved = true; + // Move to the next handler + if (previousHandler) { + handler = previousHandler->next(); + } else { + handler = _firstHandler; + } + continue; + } + } + previousHandler = handler; + handler = handler->next(); + } + + return anyHandlerRemoved; } void HTTPServer::addHandler(RequestHandler* handler) { _addRequestHandler(handler); } +bool HTTPServer::removeHandler(RequestHandler *handler) { + return _removeRequestHandler(handler); +} + void HTTPServer::_addRequestHandler(RequestHandler* handler) { if (!_lastHandler) { _firstHandler = handler; @@ -233,6 +276,32 @@ void HTTPServer::_addRequestHandler(RequestHandler* handler) { } } +bool HTTPServer::_removeRequestHandler(RequestHandler *handler) { + RequestHandler *current = _firstHandler; + RequestHandler *previous = nullptr; + + while (current != nullptr) { + if (current == handler) { + if (previous == nullptr) { + _firstHandler = current->next(); + } else { + previous->next(current->next()); + } + + if (current == _lastHandler) { + _lastHandler = previous; + } + + // Delete 'matching' handler + delete current; + return true; + } + previous = current; + current = current->next(); + } + return false; +} + void HTTPServer::serveStatic(const char* uri, FS& fs, const char* path, const char* cache_header) { _addRequestHandler(new StaticRequestHandler(fs, path, uri, cache_header)); } diff --git a/libraries/WebServer/src/HTTPServer.h b/libraries/WebServer/src/HTTPServer.h index 50eb03018..82f9b7709 100644 --- a/libraries/WebServer/src/HTTPServer.h +++ b/libraries/WebServer/src/HTTPServer.h @@ -95,10 +95,16 @@ class HTTPServer { void requestAuthentication(HTTPAuthMethod mode = BASIC_AUTH, const char* realm = nullptr, 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); + bool removeRoute(const String &uri, HTTPMethod method); void addHandler(RequestHandler* handler); + bool removeHandler(RequestHandler *handler); void serveStatic(const char* uri, fs::FS& fs, const char* path, const char* cache_header = nullptr); void onNotFound(THandlerFunction fn); //called when handler is not assigned void onFileUpload(THandlerFunction ufn); //handle file uploads @@ -109,6 +115,9 @@ class HTTPServer { HTTPMethod method() { return _currentMethod; } + WiFiClient& client() { + return *_currentClient; + } HTTPUpload& upload() { return *_currentUpload; } @@ -230,6 +239,7 @@ class HTTPServer { return _currentClient->write(b, l); } void _addRequestHandler(RequestHandler* handler); + bool _removeRequestHandler(RequestHandler *handler); void _handleRequest(); void _finalizeResponse(); ClientFuture _parseRequest(WiFiClient* client); diff --git a/libraries/WebServer/src/Parsing.cpp b/libraries/WebServer/src/Parsing.cpp index a0a88ef25..f404f221c 100644 --- a/libraries/WebServer/src/Parsing.cpp +++ b/libraries/WebServer/src/Parsing.cpp @@ -136,7 +136,7 @@ HTTPServer::ClientFuture HTTPServer::_parseRequest(WiFiClient* client) { //attach handler RequestHandler* handler; for (handler = _firstHandler; handler; handler = handler->next()) { - if (handler->canHandle(_currentMethod, _currentUri)) { + if (handler->canHandle(*this, _currentMethod, _currentUri)) { break; } } @@ -188,7 +188,7 @@ HTTPServer::ClientFuture HTTPServer::_parseRequest(WiFiClient* 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; @@ -347,7 +347,7 @@ void HTTPServer::_parseArguments(String data) { void HTTPServer::_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; @@ -462,7 +462,7 @@ bool HTTPServer::_parseForm(WiFiClient * 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; @@ -501,12 +501,12 @@ bool HTTPServer::_parseForm(WiFiClient * 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); @@ -580,7 +580,7 @@ String HTTPServer::urlDecode(const String & text) { bool HTTPServer::_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/detail/RequestHandler.h b/libraries/WebServer/src/detail/RequestHandler.h index f40a6ffc2..b5eb038ac 100644 --- a/libraries/WebServer/src/detail/RequestHandler.h +++ b/libraries/WebServer/src/detail/RequestHandler.h @@ -6,17 +6,43 @@ class RequestHandler { public: virtual ~RequestHandler() { } + + /* + note: old handler API for backward compatibility + */ + virtual bool canHandle(HTTPMethod method, String uri) { - (void) method; - (void) uri; + (void)method; + (void)uri; return false; } virtual bool canUpload(String uri) { - (void) uri; + (void)uri; return false; } virtual bool canRaw(String uri) { - (void) uri; + (void)uri; + return false; + } + + /* + note: new handler API with support for filters etc. + */ + + virtual bool canHandle(HTTPServer &server, HTTPMethod method, String uri) { + (void)server; + (void)method; + (void)uri; + return false; + } + virtual bool canUpload(HTTPServer &server, String uri) { + (void)server; + (void)uri; + return false; + } + virtual bool canRaw(HTTPServer &server, String uri) { + (void)server; + (void)uri; return false; } virtual bool handle(HTTPServer& server, HTTPMethod requestMethod, String requestUri) { @@ -43,6 +69,11 @@ class RequestHandler { _next = r; } + virtual RequestHandler& setFilter(std::function filter) { + (void)filter; + return *this; + } + private: RequestHandler* _next = nullptr; diff --git a/libraries/WebServer/src/detail/RequestHandlersImpl.h b/libraries/WebServer/src/detail/RequestHandlersImpl.h index 39642b755..b355cfee0 100644 --- a/libraries/WebServer/src/detail/RequestHandlersImpl.h +++ b/libraries/WebServer/src/detail/RequestHandlersImpl.h @@ -51,9 +51,31 @@ class FunctionRequestHandler : public RequestHandler { return true; } + bool canHandle(HTTPServer &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(HTTPServer &server, String requestUri) override { + if (!_ufn || !canHandle(server, HTTP_POST, requestUri)) { + return false; + } + + return true; + } + bool canRaw(HTTPServer &server, String requestUri) override { + (void) requestUri; + if (!_ufn || _method == HTTP_GET || (_filter != NULL ? _filter(server) == false : false)) { + return false; + } + + return true; + } + bool handle(HTTPServer& server, HTTPMethod requestMethod, String requestUri) override { - (void) server; - if (!canHandle(requestMethod, requestUri)) { + if (!canHandle(server, requestMethod, requestUri)) { return false; } @@ -62,9 +84,8 @@ class FunctionRequestHandler : public RequestHandler { } void upload(HTTPServer& server, String requestUri, HTTPUpload& upload) override { - (void) server; (void) upload; - if (canUpload(requestUri)) { + if (canUpload(server, requestUri)) { _ufn(); } } @@ -72,14 +93,22 @@ class FunctionRequestHandler : public RequestHandler { void raw(HTTPServer& server, String requestUri, HTTPRaw& raw) override { (void)server; (void)raw; - if (canRaw(requestUri)) { + if (canRaw(server, requestUri)) { _ufn(); } } + FunctionRequestHandler& setFilter(HTTPServer::FilterFunction filter) { + _filter = filter; + return *this; + } + protected: HTTPServer::THandlerFunction _fn; HTTPServer::THandlerFunction _ufn; + // _filter should return 'true' when the request should be handled + // and 'false' when the request should be ignored + HTTPServer::FilterFunction _filter; Uri *_uri; HTTPMethod _method; }; @@ -109,8 +138,24 @@ class StaticRequestHandler : public RequestHandler { return true; } + bool canHandle(HTTPServer &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(HTTPServer& server, HTTPMethod requestMethod, String requestUri) override { - if (!canHandle(requestMethod, requestUri)) { + if (!canHandle(server, requestMethod, requestUri)) { return false; } @@ -169,7 +214,15 @@ class StaticRequestHandler : public RequestHandler { return String(buff); } + StaticRequestHandler& setFilter(HTTPServer::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 + HTTPServer::FilterFunction _filter; FS _fs; String _uri; String _path;