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
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));
}
......
......@@ -95,10 +95,16 @@ public:
void requestAuthentication(HTTPAuthMethod mode = BASIC_AUTH, const char* realm = nullptr, const String& authFailMsg = String(""));
typedef std::function<void(void)> 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<bool(HTTPServer &server)> 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 @@ public:
HTTPMethod method() {
return _currentMethod;
}
WiFiClient& client() {
return *_currentClient;
}
HTTPUpload& upload() {
return *_currentUpload;
}
......@@ -230,6 +239,7 @@ protected:
return _currentClient->write(b, l);
}
void _addRequestHandler(RequestHandler* handler);
bool _removeRequestHandler(RequestHandler *handler);
void _handleRequest();
void _finalizeResponse();
ClientFuture _parseRequest(WiFiClient* client);
......
......@@ -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;
......
......@@ -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 @@ public:
_next = r;
}
virtual RequestHandler& setFilter(std::function<bool(HTTPServer&)> filter) {
(void)filter;
return *this;
}
private:
RequestHandler* _next = nullptr;
......
......@@ -51,9 +51,31 @@ public:
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 @@ public:
}
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 @@ public:
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 @@ public:
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 @@ public:
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;
......
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