PsychichHTTP v2-dev

This commit is contained in:
iranl
2024-12-30 14:37:09 +01:00
parent 2cf5201285
commit 78459c2d08
118 changed files with 5453 additions and 4972 deletions

View File

@@ -23,7 +23,7 @@ PsychicHttp is a webserver library for ESP32 + Arduino framework which uses the
## Differences from ESPAsyncWebserver
* No templating system (anyone actually use this?)
* No url rewriting (but you can use request->redirect)
* No url rewriting (but you can use response->redirect)
# Usage
@@ -120,7 +120,7 @@ If you have existing code using ESPAsyncWebserver, you will feel right at home w
## setup() Stuff
* no more server.begin(), call server.listen(80), before you add your handlers
* add your handlers and call server.begin()
* server has a configurable limit on .on() endpoints. change it with ```server.config.max_uri_handlers = 20;``` as needed.
* check your callback function definitions:
* AsyncWebServerRequest -> PsychicRequest
@@ -136,7 +136,7 @@ If you have existing code using ESPAsyncWebserver, you will feel right at home w
## Requests / Responses
* request->send is now request->reply()
* request->send is now response->send()
* if you create a response, call response->send() directly, not request->send(reply)
* request->headers() is not supported by ESP-IDF, you have to just check for the header you need.
* No AsyncCallbackJsonWebHandler (for now... can add if needed)
@@ -164,9 +164,6 @@ void setup()
//connect to wifi
//start the server listening on port 80 (standard HTTP port)
server.listen(80);
//call server methods to attach endpoints and handlers
server.on(...);
server.serveStatic(...);
@@ -198,7 +195,7 @@ The ```server.on(...)``` returns a pointer to the endpoint, which can be used to
```cpp
//respond to /url only from requests to the AP
server.on("/url", HTTP_GET, request_callback)->setFilter(ON_AP_FILTER);
server.on("/url", HTTP_GET, request_callback)->addFilter(ON_AP_FILTER);
//require authentication on /url
server.on("/url", HTTP_GET, request_callback)->setAuthentication("user", "pass");
@@ -212,7 +209,7 @@ server.on("/ws")->attachHandler(&websocketHandler);
The ```PsychicWebHandler``` class is for handling standard web requests. It provides a single callback: ```onRequest()```. This callback is called when the handler receives a valid HTTP request.
One major difference from ESPAsyncWebserver is that this callback needs to return an esp_err_t variable to let the server know the result of processing the request. The ```response->reply()``` and ```request->send()``` functions will return this. It is a good habit to return the result of these functions as sending the response will close the connection.
One major difference from ESPAsyncWebserver is that this callback needs to return an esp_err_t variable to let the server know the result of processing the request. The ```response->send()``` and ```request->send()``` functions will return this. It is a good habit to return the result of these functions as sending the response will close the connection.
The function definition for the onRequest callback is:
@@ -226,7 +223,7 @@ Here is a simple example that sends back the client's IP on the URL /ip
server.on("/ip", [](PsychicRequest *request)
{
String output = "Your IP is: " + request->client()->remoteIP().toString();
return request->reply(output.c_str());
return response->send(output.c_str());
});
```
@@ -294,7 +291,7 @@ It's worth noting that there is no standard way of passing in a filename for thi
String url = "/" + request->getFilename();
String output = "<a href=\"" + url + "\">" + url + "</a>";
return request->reply(output.c_str());
return response->send(output.c_str());
});
//wildcard basic file upload - POST to /upload/filename.ext
@@ -352,7 +349,7 @@ Very similar to the basic upload, with 2 key differences:
output += "Param 1: " + request->getParam("param1")->value() + "<br/>\n";
output += "Param 2: " + request->getParam("param2")->value() + "<br/>\n";
return request->reply(output.c_str());
return response->send(output.c_str());
});
//upload to /multipart url
@@ -374,11 +371,11 @@ The ```server.serveStatic()``` function handles creating the handler and assigni
```cpp
//serve static files from LittleFS/www on / only to clients on same wifi network
//this is where our /index.html file lives
server.serveStatic("/", LittleFS, "/www/")->setFilter(ON_STA_FILTER);
server.serveStatic("/", LittleFS, "/www/")->addFilter(ON_STA_FILTER);
//serve static files from LittleFS/www-ap on / only to clients on SoftAP
//this is where our /index.html file lives
server.serveStatic("/", LittleFS, "/www-ap/")->setFilter(ON_AP_FILTER);
server.serveStatic("/", LittleFS, "/www-ap/")->addFilter(ON_AP_FILTER);
//serve static files from LittleFS/img on /img
//it's more efficient to serve everything from a single www directory, but this is also possible.
@@ -426,17 +423,17 @@ Here is a basic example of using WebSockets:
PsychicWebSocketHandler websocketHandler();
websocketHandler.onOpen([](PsychicWebSocketClient *client) {
Serial.printf("[socket] connection #%u connected from %s\n", client->socket(), client->remoteIP().toString());
Serial.printf("[socket] connection #%u connected from %s\n", client->socket(), client->remoteIP().toString().c_str());
client->sendMessage("Hello!");
});
websocketHandler.onFrame([](PsychicWebSocketRequest *request, httpd_ws_frame *frame) {
Serial.printf("[socket] #%d sent: %s\n", request->client()->socket(), (char *)frame->payload);
return request->reply(frame);
return response->send(frame);
});
websocketHandler.onClose([](PsychicWebSocketClient *client) {
Serial.printf("[socket] connection #%u closed from %s\n", client->socket(), client->remoteIP().toString());
Serial.printf("[socket] connection #%u closed from %s\n", client->socket(), client->remoteIP().toString().c_str());
});
//attach the handler to /ws. You can then connect to ws://ip.address/ws
@@ -452,7 +449,7 @@ The onFrame() callback has 2 parameters:
For sending data on the websocket connection, there are 3 methods:
* ```request->reply()``` - only available in the onFrame() callback context.
* ```response->send()``` - only available in the onFrame() callback context.
* ```webSocketHandler.sendAll()``` - can be used anywhere to send websocket messages to all connected clients.
* ```client->send()``` - can be used anywhere* to send a websocket message to a specific client
@@ -488,12 +485,12 @@ Here is a basic example of using PsychicEventSource:
PsychicEventSource eventSource;
eventSource.onOpen([](PsychicEventSourceClient *client) {
Serial.printf("[eventsource] connection #%u connected from %s\n", client->socket(), client->remoteIP().toString());
Serial.printf("[eventsource] connection #%u connected from %s\n", client->socket(), client->remoteIP().toString().c_str());
client->send("Hello user!", NULL, millis(), 1000);
});
eventSource.onClose([](PsychicEventSourceClient *client) {
Serial.printf("[eventsource] connection #%u closed from %s\n", client->socket(), client->remoteIP().toString());
Serial.printf("[eventsource] connection #%u closed from %s\n", client->socket(), client->remoteIP().toString().c_str());
});
//attach the handler to /events
@@ -524,7 +521,7 @@ PsychicHttp supports HTTPS / SSL out of the box, however there are some limitati
#include <PsychicHttp.h>
#include <PsychicHttpsServer.h>
PsychicHttpsServer server;
server.listen(443, server_cert, server_key);
server.setCertificate(server_cert, server_key);
```
```server_cert``` and ```server_key``` are both ```const char *``` parameters which contain the server certificate and private key, respectively.
@@ -552,10 +549,9 @@ Last, but not least, you can create a separate HTTP server on port 80 that redir
//this creates a 2nd server listening on port 80 and redirects all requests HTTPS
PsychicHttpServer *redirectServer = new PsychicHttpServer();
redirectServer->config.ctrl_port = 20420; // just a random port different from the default one
redirectServer->listen(80);
redirectServer->onNotFound([](PsychicRequest *request) {
String url = "https://" + request->host() + request->url();
return request->redirect(url.c_str());
return response->redirect(url.c_str());
});
```
@@ -775,51 +771,22 @@ With all due respect to @me-no-dev who has done some amazing work in the open so
ArduinoMongoose is a good alternative, although the latency issues when it gets fully loaded can be very annoying. I believe it is also cross platform to other microcontrollers as well, but I haven't tested that. The other issue here is that it is based on an old version of a modified Mongoose library that will be difficult to update as it is a major revision behind and several security updates behind as well. Big thanks to @jeremypoulter though as PsychicHttp is a fork of ArduinoMongoose so it's built on strong bones.
# Community / Support
The best way to get support is probably with Github issues. There is also a [Discord chat](https://discord.gg/CM5abjGG) that is pretty active.
# Roadmap
## v1.2: ESPAsyncWebserver Parity
Change:
Modify the request handling to bring initail url matching and filtering into PsychicHttpServer itself.
Benefits:
* Fix a bug with filter() where endpoint is matched, but filter fails and it doesn't continue matching further endpoints (checks are in different codebases)
* HTTP_ANY support
* unlimited endpoints
* we would use a List to store endpoints
* dont have to pre-declare config.max_uri_handlers;
* much more flexibility for future
Issues
* it would log a warning on every request as if its a 404. (httpd_uri.c:298)
* req->user_ctx is not passed in. (httpd_uri.c:309)
* but... user_ctx is something we could store in the psychicendpoint data
* Websocket support assumes an endpoint with matching url / method (httpd_uri.c:312)
* we could copy and bring this code into our own internal request processor
* would need to manually maintain more code (~100 lines?) and be more prone to esp-idf http_server updates causing problems.
How to implement
* set config.max_uri_handlers = 1;
* possibly do not register any uri_handlers (looks like it would be fastest way to exit httpd_find_uri_handler (httpd_uri.c:94))
* looks like 404 is set by default, so should work.
* modify PsychicEndpoint to store the stuff we would pass to http_server
* create a new function handleRequest() before PsychicHttpServer::defaultNotFoundHandler to process incoming requests.
* bring in code from PsychicHttpServer::notFoundHandler
* add new code to loop over endpoints to call match and filter
* bring code from esp-idf library
* templating system
* regex url matching
* rewrite urls?
* What else are we missing?
## v2.0: ESPAsyncWebserver Parity
* As much ESPAsyncWebServer compatibility as possible
* Update benchmarks and get new data
* we should also track program size and memory usage
## Longterm Wants
* investigate websocket performance gap
* support for esp-idf framework
* support for arduino 3.0 framework
* Enable worker based multithreading with esp-idf v5.x
* 100-continue support?