Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemented support for filters and removable routes in ESP8266WebServer #9152

Merged
merged 11 commits into from
Jun 15, 2024
21 changes: 20 additions & 1 deletion libraries/ESP8266WebServer/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,10 @@ Client request handlers

.. code:: cpp

void on();
RequestHandler<ServerType>& on();
bool removeRoute();
void addHandler();
bool removeHandler();
void onNotFound();
void onFileUpload();

Expand All @@ -64,9 +66,26 @@ Client request handlers
.. code:: cpp

server.on("/", handlerFunction);
server.removeRoute("/"); // Removes any route which points to "/" and has HTTP_ANY attribute
server.removeRoute("/", HTTP_GET); // Removes any route which points to "/" and has HTTP_GET attribute
server.onNotFound(handlerFunction); // called when handler is not assigned
server.onFileUpload(handlerFunction); // handle file uploads

Client request filters
^^^^^^^^^^^^^^^^^^^^^^

.. code:: cpp

RequestHandler<ServerType>& setFilter();

*Example:*

More details about this in `Filters.ino` example.

.. code:: cpp

server.on("/", handlerFunction).setFilter(ON_AP_FILTER)

Sending responses to the client
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Expand Down
101 changes: 101 additions & 0 deletions libraries/ESP8266WebServer/examples/Filters/Filters.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.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 = "ESP8266_Demo";
const char *ap_password = "";

ESP8266WebServer server(80);

const int led = 13;

// ON_STA_FILTER - Only accept requests coming from STA interface
bool ON_STA_FILTER(ESP8266WebServer &server) {
return WiFi.localIP() == server.client().localIP();
}

// ON_AP_FILTER - Only accept requests coming from AP interface
bool ON_AP_FILTER(ESP8266WebServer &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("esp8266")) {
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);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://github.com/esp8266/Arduino/actions/runs/9516245776/job/26232190207#step:4:400
./tests/restyle.sh runs clang-format-15 on everything, chaining makes extra indentation

lose [&] and maybe do something explicit with 'on' return value?

auto& onlyAp = ...;
onlyAp.setFilter(ON_AP_FILTER);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried running ./tests/restyle.sh on my PC, odd enough that did nothing. Do we have to install anything for it to run?

Regarding Example:
I think it's better if we keep it same as ESP32 Filters.ino example, that way people are not confused.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clang-format-15 in $PATH / env CLANG_FORMAT=path-to-clang-format ./tests/restyle.sh
(e.g. https://github.com/muttleyxd/clang-tools-static-binaries)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's better if we keep it same as ESP32 Filters.ino example, that way people are not confused.

Right, but it definitely got me confused

  • [&] meant to capture by reference, but server callback should not do that. if you do it in setup() and capture value from there, later callback in loop() references invalid memory location
  • nothing is actually captured, don't wanna do that anyway

Specifically for us here and clang-format, without chaining - there are less spaces.


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();
}
87 changes: 81 additions & 6 deletions libraries/ESP8266WebServer/src/ESP8266WebServer-impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -230,25 +230,73 @@ void ESP8266WebServerTemplate<ServerType>::requestAuthentication(HTTPAuthMethod
}

template <typename ServerType>
void ESP8266WebServerTemplate<ServerType>::on(const Uri &uri, ESP8266WebServerTemplate<ServerType>::THandlerFunction handler) {
on(uri, HTTP_ANY, handler);
RequestHandler<ServerType>& ESP8266WebServerTemplate<ServerType>::on(const Uri &uri, ESP8266WebServerTemplate<ServerType>::THandlerFunction handler) {
return on(uri, HTTP_ANY, handler);
}

template <typename ServerType>
void ESP8266WebServerTemplate<ServerType>::on(const Uri &uri, HTTPMethod method, ESP8266WebServerTemplate<ServerType>::THandlerFunction fn) {
on(uri, method, fn, _fileUploadHandler);
RequestHandler<ServerType>& ESP8266WebServerTemplate<ServerType>::on(const Uri &uri, HTTPMethod method, ESP8266WebServerTemplate<ServerType>::THandlerFunction fn) {
return on(uri, method, fn, _fileUploadHandler);
}

template <typename ServerType>
void ESP8266WebServerTemplate<ServerType>::on(const Uri &uri, HTTPMethod method, ESP8266WebServerTemplate<ServerType>::THandlerFunction fn, ESP8266WebServerTemplate<ServerType>::THandlerFunction ufn) {
_addRequestHandler(new FunctionRequestHandler<ServerType>(fn, ufn, uri, method));
RequestHandler<ServerType>& ESP8266WebServerTemplate<ServerType>::on(const Uri &uri, HTTPMethod method, ESP8266WebServerTemplate<ServerType>::THandlerFunction fn, ESP8266WebServerTemplate<ServerType>::THandlerFunction ufn) {
RequestHandler<ServerType> *handler = new FunctionRequestHandler<ServerType>(fn, ufn, uri, method);
_addRequestHandler(handler);
return *handler;
}

template <typename ServerType>
bool ESP8266WebServerTemplate<ServerType>::removeRoute(const char *uri) {
return removeRoute(String(uri), HTTP_ANY);
}

template <typename ServerType>
bool ESP8266WebServerTemplate<ServerType>::removeRoute(const char *uri, HTTPMethod method) {
return removeRoute(String(uri), method);
}

template <typename ServerType>
bool ESP8266WebServerTemplate<ServerType>::removeRoute(const String &uri) {
return removeRoute(uri, HTTP_ANY);
}

template <typename ServerType>
bool ESP8266WebServerTemplate<ServerType>::removeRoute(const String &uri, HTTPMethod method) {
bool anyHandlerRemoved = false;
RequestHandlerType *handler = _firstHandler;
RequestHandlerType *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;
}

template <typename ServerType>
void ESP8266WebServerTemplate<ServerType>::addHandler(RequestHandlerType* handler) {
_addRequestHandler(handler);
}

template <typename ServerType>
bool ESP8266WebServerTemplate<ServerType>::removeHandler(RequestHandlerType *handler) {
return _removeRequestHandler(handler);
}

template <typename ServerType>
void ESP8266WebServerTemplate<ServerType>::_addRequestHandler(RequestHandlerType* handler) {
if (!_lastHandler) {
Expand All @@ -261,6 +309,33 @@ void ESP8266WebServerTemplate<ServerType>::_addRequestHandler(RequestHandlerType
}
}

template <typename ServerType>
bool ESP8266WebServerTemplate<ServerType>::_removeRequestHandler(RequestHandlerType *handler) {
RequestHandlerType *current = _firstHandler;
RequestHandlerType *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;
}

template <typename ServerType>
void ESP8266WebServerTemplate<ServerType>::serveStatic(const char* uri, FS& fs, const char* path, const char* cache_header) {
bool is_file = false;
Expand Down
15 changes: 11 additions & 4 deletions libraries/ESP8266WebServer/src/ESP8266WebServer.h
Original file line number Diff line number Diff line change
Expand Up @@ -116,11 +116,17 @@ class ESP8266WebServerTemplate

typedef std::function<void(void)> THandlerFunction;
typedef std::function<String(FS &fs, const String &fName)> ETagFunction;

void on(const Uri &uri, THandlerFunction handler);
void on(const Uri &uri, HTTPMethod method, THandlerFunction fn);
void on(const Uri &uri, HTTPMethod method, THandlerFunction fn, THandlerFunction ufn);
typedef std::function<bool(ESP8266WebServerTemplate<ServerType> &server)> FilterFunction;

RequestHandler<ServerType>& on(const Uri &uri, THandlerFunction handler);
RequestHandler<ServerType>& on(const Uri &uri, HTTPMethod method, THandlerFunction fn);
RequestHandler<ServerType>& on(const Uri &uri, HTTPMethod method, THandlerFunction fn, THandlerFunction ufn);
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(RequestHandlerType* handler);
bool removeHandler(RequestHandlerType* handler);
void serveStatic(const char* uri, fs::FS& fs, const char* path, const char* cache_header = NULL );
void onNotFound(THandlerFunction fn); //called when handler is not assigned
void onFileUpload(THandlerFunction fn); //handle file uploads
Expand Down Expand Up @@ -306,6 +312,7 @@ class ESP8266WebServerTemplate

protected:
void _addRequestHandler(RequestHandlerType* handler);
bool _removeRequestHandler(RequestHandlerType *handler);
void _handleRequest();
void _finalizeResponse();
ClientFuture _parseRequest(ClientType& client);
Expand Down
12 changes: 6 additions & 6 deletions libraries/ESP8266WebServer/src/Parsing-impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ typename ESP8266WebServerTemplate<ServerType>::ClientFuture ESP8266WebServerTemp
//attach handler
RequestHandlerType* handler;
for (handler = _firstHandler; handler; handler = handler->next()) {
if (handler->canHandle(_currentMethod, _currentUri))
if (handler->canHandle(*this, _currentMethod, _currentUri))
break;
}
_currentHandler = handler;
Expand Down Expand Up @@ -324,7 +324,7 @@ int ESP8266WebServerTemplate<ServerType>::_parseArgumentsPrivate(const String& d
template <typename ServerType>
void ESP8266WebServerTemplate<ServerType>::_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;
_currentUpload->currentSize = 0;
Expand Down Expand Up @@ -425,7 +425,7 @@ bool ESP8266WebServerTemplate<ServerType>::_parseForm(ClientType& client, const
_currentUpload->currentSize = 0;
_currentUpload->contentLength = len;
DBGWS("Start File: %s Type: %s\n", _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;

Expand Down Expand Up @@ -463,11 +463,11 @@ bool ESP8266WebServerTemplate<ServerType>::_parseForm(ClientType& client, const
}
}
// 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);
DBGWS("End File: %s Type: %s Size: %d\n",
_currentUpload->filename.c_str(),
Expand Down Expand Up @@ -542,7 +542,7 @@ String ESP8266WebServerTemplate<ServerType>::urlDecode(const String& text)
template <typename ServerType>
bool ESP8266WebServerTemplate<ServerType>::_parseFormUploadAborted(){
_currentUpload->status = UPLOAD_FILE_ABORTED;
if(_currentHandler && _currentHandler->canUpload(_currentUri))
if(_currentHandler && _currentHandler->canUpload(*this, _currentUri))
_currentHandler->upload(*this, _currentUri, *_currentUpload);
return false;
}
Expand Down
Loading
Loading