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:
parent
1d895e58e7
commit
3428eb6a9e
7 changed files with 226 additions and 23 deletions
110
libraries/WebServer/examples/Filters/Filters.ino
Normal file
110
libraries/WebServer/examples/Filters/Filters.ino
Normal 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
|
||||||
|
}
|
||||||
5
libraries/WebServer/examples/Filters/ci.json
Normal file
5
libraries/WebServer/examples/Filters/ci.json
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"targets": {
|
||||||
|
"esp32h2": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue