Unverified Commit 1775fedf authored by Ayush Sharma's avatar Ayush Sharma Committed by GitHub

Webserver: Add support for filters and removable routes (#2225)

This PR implements filters and removable routes in RP2040 arduino core, making it API compatible with recent changes to ESP32 & ESP8266 WebServer API.
parent 4ab0ba61
#include <WiFi.h>
#include <WiFiClient.h>
#include <WebServer.h>
#include <LEAmDNS.h>
// 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();
}
...@@ -207,22 +207,65 @@ void HTTPServer::requestAuthentication(HTTPAuthMethod mode, const char* realm, c ...@@ -207,22 +207,65 @@ void HTTPServer::requestAuthentication(HTTPAuthMethod mode, const char* realm, c
send(401, String(FPSTR(mimeTable[html].mimeType)), authFailMsg); send(401, String(FPSTR(mimeTable[html].mimeType)), authFailMsg);
} }
void HTTPServer::on(const Uri &uri, HTTPServer::THandlerFunction handler) { RequestHandler& HTTPServer::on(const Uri &uri, HTTPServer::THandlerFunction handler) {
on(uri, HTTP_ANY, handler); return on(uri, HTTP_ANY, handler);
} }
void HTTPServer::on(const Uri &uri, HTTPMethod method, HTTPServer::THandlerFunction fn) { RequestHandler& HTTPServer::on(const Uri &uri, HTTPMethod method, HTTPServer::THandlerFunction fn) {
on(uri, method, fn, _fileUploadHandler); return on(uri, method, fn, _fileUploadHandler);
} }
void HTTPServer::on(const Uri &uri, HTTPMethod method, HTTPServer::THandlerFunction fn, HTTPServer::THandlerFunction ufn) { RequestHandler& HTTPServer::on(const Uri &uri, HTTPMethod method, HTTPServer::THandlerFunction fn, HTTPServer::THandlerFunction ufn) {
_addRequestHandler(new FunctionRequestHandler(fn, ufn, uri, method)); 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) { void HTTPServer::addHandler(RequestHandler* handler) {
_addRequestHandler(handler); _addRequestHandler(handler);
} }
bool HTTPServer::removeHandler(RequestHandler *handler) {
return _removeRequestHandler(handler);
}
void HTTPServer::_addRequestHandler(RequestHandler* handler) { void HTTPServer::_addRequestHandler(RequestHandler* handler) {
if (!_lastHandler) { if (!_lastHandler) {
_firstHandler = handler; _firstHandler = handler;
...@@ -233,6 +276,32 @@ void HTTPServer::_addRequestHandler(RequestHandler* 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) { void HTTPServer::serveStatic(const char* uri, FS& fs, const char* path, const char* cache_header) {
_addRequestHandler(new StaticRequestHandler(fs, path, uri, cache_header)); _addRequestHandler(new StaticRequestHandler(fs, path, uri, cache_header));
} }
......
...@@ -95,10 +95,16 @@ public: ...@@ -95,10 +95,16 @@ public:
void requestAuthentication(HTTPAuthMethod mode = BASIC_AUTH, const char* realm = nullptr, const String& authFailMsg = String("")); void requestAuthentication(HTTPAuthMethod mode = BASIC_AUTH, const char* realm = nullptr, const String& authFailMsg = String(""));
typedef std::function<void(void)> THandlerFunction; typedef std::function<void(void)> THandlerFunction;
void on(const Uri &uri, THandlerFunction fn); typedef std::function<bool(HTTPServer &server)> FilterFunction;
void on(const Uri &uri, HTTPMethod method, THandlerFunction fn); RequestHandler& on(const Uri &uri, THandlerFunction fn);
void on(const Uri &uri, HTTPMethod method, THandlerFunction fn, THandlerFunction ufn); //ufn handles file uploads 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); 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 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 onNotFound(THandlerFunction fn); //called when handler is not assigned
void onFileUpload(THandlerFunction ufn); //handle file uploads void onFileUpload(THandlerFunction ufn); //handle file uploads
...@@ -109,6 +115,9 @@ public: ...@@ -109,6 +115,9 @@ public:
HTTPMethod method() { HTTPMethod method() {
return _currentMethod; return _currentMethod;
} }
WiFiClient& client() {
return *_currentClient;
}
HTTPUpload& upload() { HTTPUpload& upload() {
return *_currentUpload; return *_currentUpload;
} }
...@@ -230,6 +239,7 @@ protected: ...@@ -230,6 +239,7 @@ protected:
return _currentClient->write(b, l); return _currentClient->write(b, l);
} }
void _addRequestHandler(RequestHandler* handler); void _addRequestHandler(RequestHandler* handler);
bool _removeRequestHandler(RequestHandler *handler);
void _handleRequest(); void _handleRequest();
void _finalizeResponse(); void _finalizeResponse();
ClientFuture _parseRequest(WiFiClient* client); ClientFuture _parseRequest(WiFiClient* client);
......
...@@ -136,7 +136,7 @@ HTTPServer::ClientFuture HTTPServer::_parseRequest(WiFiClient* client) { ...@@ -136,7 +136,7 @@ HTTPServer::ClientFuture HTTPServer::_parseRequest(WiFiClient* client) {
//attach handler //attach handler
RequestHandler* handler; RequestHandler* handler;
for (handler = _firstHandler; handler; handler = handler->next()) { for (handler = _firstHandler; handler; handler = handler->next()) {
if (handler->canHandle(_currentMethod, _currentUri)) { if (handler->canHandle(*this, _currentMethod, _currentUri)) {
break; break;
} }
} }
...@@ -188,7 +188,7 @@ HTTPServer::ClientFuture HTTPServer::_parseRequest(WiFiClient* client) { ...@@ -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"); log_v("Parse raw");
_currentRaw.reset(new HTTPRaw()); _currentRaw.reset(new HTTPRaw());
_currentRaw->status = RAW_START; _currentRaw->status = RAW_START;
...@@ -347,7 +347,7 @@ void HTTPServer::_parseArguments(String data) { ...@@ -347,7 +347,7 @@ void HTTPServer::_parseArguments(String data) {
void HTTPServer::_uploadWriteByte(uint8_t b) { void HTTPServer::_uploadWriteByte(uint8_t b) {
if (_currentUpload->currentSize == HTTP_UPLOAD_BUFLEN) { if (_currentUpload->currentSize == HTTP_UPLOAD_BUFLEN) {
if (_currentHandler && _currentHandler->canUpload(_currentUri)) { if (_currentHandler && _currentHandler->canUpload(*this, _currentUri)) {
_currentHandler->upload(*this, _currentUri, *_currentUpload); _currentHandler->upload(*this, _currentUri, *_currentUpload);
} }
_currentUpload->totalSize += _currentUpload->currentSize; _currentUpload->totalSize += _currentUpload->currentSize;
...@@ -462,7 +462,7 @@ bool HTTPServer::_parseForm(WiFiClient * client, String boundary, uint32_t len) ...@@ -462,7 +462,7 @@ bool HTTPServer::_parseForm(WiFiClient * client, String boundary, uint32_t len)
_currentUpload->totalSize = 0; _currentUpload->totalSize = 0;
_currentUpload->currentSize = 0; _currentUpload->currentSize = 0;
log_v("Start File: %s Type: %s", _currentUpload->filename.c_str(), _currentUpload->type.c_str()); 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); _currentHandler->upload(*this, _currentUri, *_currentUpload);
} }
_currentUpload->status = UPLOAD_FILE_WRITE; _currentUpload->status = UPLOAD_FILE_WRITE;
...@@ -501,12 +501,12 @@ bool HTTPServer::_parseForm(WiFiClient * client, String boundary, uint32_t len) ...@@ -501,12 +501,12 @@ bool HTTPServer::_parseForm(WiFiClient * client, String boundary, uint32_t len)
} }
} }
// Found the boundary string, finish processing this file upload // 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); _currentHandler->upload(*this, _currentUri, *_currentUpload);
} }
_currentUpload->totalSize += _currentUpload->currentSize; _currentUpload->totalSize += _currentUpload->currentSize;
_currentUpload->status = UPLOAD_FILE_END; _currentUpload->status = UPLOAD_FILE_END;
if (_currentHandler && _currentHandler->canUpload(_currentUri)) { if (_currentHandler && _currentHandler->canUpload(*this, _currentUri)) {
_currentHandler->upload(*this, _currentUri, *_currentUpload); _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); 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) { ...@@ -580,7 +580,7 @@ String HTTPServer::urlDecode(const String & text) {
bool HTTPServer::_parseFormUploadAborted() { bool HTTPServer::_parseFormUploadAborted() {
_currentUpload->status = UPLOAD_FILE_ABORTED; _currentUpload->status = UPLOAD_FILE_ABORTED;
if (_currentHandler && _currentHandler->canUpload(_currentUri)) { if (_currentHandler && _currentHandler->canUpload(*this, _currentUri)) {
_currentHandler->upload(*this, _currentUri, *_currentUpload); _currentHandler->upload(*this, _currentUri, *_currentUpload);
} }
return false; return false;
......
...@@ -6,17 +6,43 @@ ...@@ -6,17 +6,43 @@
class RequestHandler { class RequestHandler {
public: public:
virtual ~RequestHandler() { } virtual ~RequestHandler() { }
/*
note: old handler API for backward compatibility
*/
virtual bool canHandle(HTTPMethod method, String uri) { virtual bool canHandle(HTTPMethod method, String uri) {
(void) method; (void)method;
(void) uri; (void)uri;
return false; return false;
} }
virtual bool canUpload(String uri) { virtual bool canUpload(String uri) {
(void) uri; (void)uri;
return false; return false;
} }
virtual bool canRaw(String uri) { 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; return false;
} }
virtual bool handle(HTTPServer& server, HTTPMethod requestMethod, String requestUri) { virtual bool handle(HTTPServer& server, HTTPMethod requestMethod, String requestUri) {
...@@ -43,6 +69,11 @@ public: ...@@ -43,6 +69,11 @@ public:
_next = r; _next = r;
} }
virtual RequestHandler& setFilter(std::function<bool(HTTPServer&)> filter) {
(void)filter;
return *this;
}
private: private:
RequestHandler* _next = nullptr; RequestHandler* _next = nullptr;
......
...@@ -51,9 +51,31 @@ public: ...@@ -51,9 +51,31 @@ public:
return true; 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 { bool handle(HTTPServer& server, HTTPMethod requestMethod, String requestUri) override {
(void) server; if (!canHandle(server, requestMethod, requestUri)) {
if (!canHandle(requestMethod, requestUri)) {
return false; return false;
} }
...@@ -62,9 +84,8 @@ public: ...@@ -62,9 +84,8 @@ public:
} }
void upload(HTTPServer& server, String requestUri, HTTPUpload& upload) override { void upload(HTTPServer& server, String requestUri, HTTPUpload& upload) override {
(void) server;
(void) upload; (void) upload;
if (canUpload(requestUri)) { if (canUpload(server, requestUri)) {
_ufn(); _ufn();
} }
} }
...@@ -72,14 +93,22 @@ public: ...@@ -72,14 +93,22 @@ public:
void raw(HTTPServer& server, String requestUri, HTTPRaw& raw) override { void raw(HTTPServer& server, String requestUri, HTTPRaw& raw) override {
(void)server; (void)server;
(void)raw; (void)raw;
if (canRaw(requestUri)) { if (canRaw(server, requestUri)) {
_ufn(); _ufn();
} }
} }
FunctionRequestHandler& setFilter(HTTPServer::FilterFunction filter) {
_filter = filter;
return *this;
}
protected: protected:
HTTPServer::THandlerFunction _fn; HTTPServer::THandlerFunction _fn;
HTTPServer::THandlerFunction _ufn; 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; Uri *_uri;
HTTPMethod _method; HTTPMethod _method;
}; };
...@@ -109,8 +138,24 @@ public: ...@@ -109,8 +138,24 @@ public:
return true; 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 { bool handle(HTTPServer& server, HTTPMethod requestMethod, String requestUri) override {
if (!canHandle(requestMethod, requestUri)) { if (!canHandle(server, requestMethod, requestUri)) {
return false; return false;
} }
...@@ -169,7 +214,15 @@ public: ...@@ -169,7 +214,15 @@ public:
return String(buff); return String(buff);
} }
StaticRequestHandler& setFilter(HTTPServer::FilterFunction filter) {
_filter = filter;
return *this;
}
protected: protected:
// _filter should return 'true' when the request should be handled
// and 'false' when the request should be ignored
HTTPServer::FilterFunction _filter;
FS _fs; FS _fs;
String _uri; String _uri;
String _path; String _path;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment