feat: added support for filters in WebServer library (#9842)

* feat: added support for filters in webserver

* feat: add setFilter function in StaticRequestHandler

* fix: ON_STA_FILTER & ON_AP_FILTER

* fix: make request handlers backward compatible

* fix: ON_STA_FILTER & ON_AP_FILTER

* fix: more filters to their own example

* chore: grammar

* fix: remove filters from header file

* fix: use same root route for both interfaces

* ci(pre-commit): Apply automatic fixes

---------

Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
This commit is contained in:
Ayush Sharma 2024-06-14 02:55:20 +05:30 committed by GitHub
parent 1d895e58e7
commit 3428eb6a9e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 226 additions and 23 deletions

View file

@ -0,0 +1,110 @@
#include <WiFi.h>
#include <NetworkClient.h>
#include <WebServer.h>
#include <ESPmDNS.h>
// 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
}

View file

@ -0,0 +1,5 @@
{
"targets": {
"esp32h2": false
}
}

View file

@ -124,7 +124,7 @@ bool WebServer::_parseRequest(NetworkClient &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;
} }
} }
@ -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"); log_v("Parse raw");
_currentRaw.reset(new HTTPRaw()); _currentRaw.reset(new HTTPRaw());
_currentRaw->status = RAW_START; _currentRaw->status = RAW_START;
@ -334,7 +334,7 @@ void WebServer::_parseArguments(String data) {
void WebServer::_uploadWriteByte(uint8_t b) { void WebServer::_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;
@ -449,7 +449,7 @@ bool WebServer::_parseForm(NetworkClient &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;
@ -488,12 +488,12 @@ bool WebServer::_parseForm(NetworkClient &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);
@ -567,7 +567,7 @@ String WebServer::urlDecode(const String &text) {
bool WebServer::_parseFormUploadAborted() { bool WebServer::_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;

View file

@ -306,16 +306,18 @@ void WebServer::requestAuthentication(HTTPAuthMethod mode, const char *realm, co
send(401, String(FPSTR(mimeTable[html].mimeType)), authFailMsg); send(401, String(FPSTR(mimeTable[html].mimeType)), authFailMsg);
} }
void WebServer::on(const Uri &uri, WebServer::THandlerFunction handler) { RequestHandler &WebServer::on(const Uri &uri, WebServer::THandlerFunction handler) {
on(uri, HTTP_ANY, handler); return on(uri, HTTP_ANY, handler);
} }
void WebServer::on(const Uri &uri, HTTPMethod method, WebServer::THandlerFunction fn) { RequestHandler &WebServer::on(const Uri &uri, HTTPMethod method, WebServer::THandlerFunction fn) {
on(uri, method, fn, _fileUploadHandler); return on(uri, method, fn, _fileUploadHandler);
} }
void WebServer::on(const Uri &uri, HTTPMethod method, WebServer::THandlerFunction fn, WebServer::THandlerFunction ufn) { RequestHandler &WebServer::on(const Uri &uri, HTTPMethod method, WebServer::THandlerFunction fn, WebServer::THandlerFunction ufn) {
_addRequestHandler(new FunctionRequestHandler(fn, ufn, uri, method)); FunctionRequestHandler *handler = new FunctionRequestHandler(fn, ufn, uri, method);
_addRequestHandler(handler);
return *handler;
} }
bool WebServer::removeRoute(const char *uri) { bool WebServer::removeRoute(const char *uri) {

View file

@ -144,9 +144,10 @@ public:
void requestAuthentication(HTTPAuthMethod mode = BASIC_AUTH, const char *realm = NULL, const String &authFailMsg = String("")); void requestAuthentication(HTTPAuthMethod mode = BASIC_AUTH, const char *realm = NULL, 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(WebServer &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);
bool removeRoute(const char *uri, HTTPMethod method); bool removeRoute(const char *uri, HTTPMethod method);
bool removeRoute(const String &uri); bool removeRoute(const String &uri);

View file

@ -7,6 +7,11 @@
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;
@ -20,6 +25,27 @@ public:
(void)uri; (void)uri;
return false; 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) { virtual bool handle(WebServer &server, HTTPMethod requestMethod, String requestUri) {
(void)server; (void)server;
(void)requestMethod; (void)requestMethod;
@ -37,6 +63,11 @@ public:
(void)raw; (void)raw;
} }
virtual RequestHandler &setFilter(std::function<bool(WebServer &)> filter) {
(void)filter;
return *this;
}
RequestHandler *next() { RequestHandler *next() {
return _next; return _next;
} }

View file

@ -36,6 +36,7 @@ public:
return true; return true;
} }
bool canRaw(String requestUri) override { bool canRaw(String requestUri) override {
if (!_ufn || _method == HTTP_GET) { if (!_ufn || _method == HTTP_GET) {
return false; return false;
@ -44,9 +45,32 @@ public:
return true; 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 { bool handle(WebServer &server, HTTPMethod requestMethod, String requestUri) override {
(void)server; if (!canHandle(server, requestMethod, requestUri)) {
if (!canHandle(requestMethod, requestUri)) {
return false; return false;
} }
@ -55,24 +79,30 @@ public:
} }
void upload(WebServer &server, String requestUri, HTTPUpload &upload) override { void upload(WebServer &server, String requestUri, HTTPUpload &upload) override {
(void)server;
(void)upload; (void)upload;
if (canUpload(requestUri)) { if (canUpload(server, requestUri)) {
_ufn(); _ufn();
} }
} }
void raw(WebServer &server, String requestUri, HTTPRaw &raw) override { void raw(WebServer &server, String requestUri, HTTPRaw &raw) override {
(void)server;
(void)raw; (void)raw;
if (canRaw(requestUri)) { if (canRaw(server, requestUri)) {
_ufn(); _ufn();
} }
} }
FunctionRequestHandler &setFilter(WebServer::FilterFunction filter) {
_filter = filter;
return *this;
}
protected: protected:
WebServer::THandlerFunction _fn; WebServer::THandlerFunction _fn;
WebServer::THandlerFunction _ufn; 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; Uri *_uri;
HTTPMethod _method; HTTPMethod _method;
}; };
@ -100,8 +130,24 @@ public:
return true; 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 { bool handle(WebServer &server, HTTPMethod requestMethod, String requestUri) override {
if (!canHandle(requestMethod, requestUri)) { if (!canHandle(server, requestMethod, requestUri)) {
return false; return false;
} }
@ -197,7 +243,15 @@ public:
return (result); return (result);
} // calcETag } // calcETag
StaticRequestHandler &setFilter(WebServer::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
WebServer::FilterFunction _filter;
FS _fs; FS _fs;
String _uri; String _uri;
String _path; String _path;