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

@@ -9,66 +9,69 @@
*/
/**********************************************************************************************
* Note: this demo relies on the following libraries (Install via Library Manager)
* ArduinoJson UrlEncode
**********************************************************************************************/
* Note: this demo relies on the following libraries (Install via Library Manager)
* ArduinoJson UrlEncode
**********************************************************************************************/
/**********************************************************************************************
* Note: this demo relies on various files to be uploaded on the LittleFS partition
* Follow instructions here: https://randomnerdtutorials.com/esp32-littlefs-arduino-ide/
**********************************************************************************************/
* Note: this demo relies on various files to be uploaded on the LittleFS partition
* Follow instructions here: https://randomnerdtutorials.com/esp32-littlefs-arduino-ide/
**********************************************************************************************/
#include "secret.h"
#include <Arduino.h>
#include <WiFi.h>
#include <LittleFS.h>
#include <ArduinoJson.h>
#include <ESPmDNS.h>
#include "secret.h"
#include <LittleFS.h>
#include <PsychicHttp.h>
#ifdef CONFIG_ESP_HTTPS_SERVER_ENABLE //set this to y in menuconfig to enable SSL
#include <PsychicHttpsServer.h>
#include <WiFi.h>
#ifdef CONFIG_ESP_HTTPS_SERVER_ENABLE // set this to y in menuconfig to enable SSL
#include <PsychicHttpsServer.h>
#endif
#ifndef WIFI_SSID
#error "You need to enter your wifi credentials. Rename secret.h to _secret.h and enter your credentials there."
#endif
//Enter your WIFI credentials in secret.h
const char *ssid = WIFI_SSID;
const char *password = WIFI_PASS;
// Enter your WIFI credentials in secret.h
const char* ssid = WIFI_SSID;
const char* password = WIFI_PASS;
// Set your SoftAP credentials
const char *softap_ssid = "PsychicHttp";
const char *softap_password = "";
IPAddress softap_ip(10, 0, 0, 1);
const char* softap_ssid = "PsychicHttp";
const char* softap_password = "";
IPAddress softap_ip(10, 0, 0, 1);
//credentials for the /auth-basic and /auth-digest examples
const char *app_user = "admin";
const char *app_pass = "admin";
const char *app_name = "Your App";
// credentials for the /auth-basic and /auth-digest examples
const char* app_user = "admin";
const char* app_pass = "admin";
const char* app_name = "Your App";
//hostname for mdns (psychic.local)
const char *local_hostname = "psychic";
AuthenticationMiddleware basicAuth;
AuthenticationMiddleware digestAuth;
//#define CONFIG_ESP_HTTPS_SERVER_ENABLE to enable ssl
// hostname for mdns (psychic.local)
const char* local_hostname = "psychic";
// #define CONFIG_ESP_HTTPS_SERVER_ENABLE to enable ssl
#ifdef CONFIG_ESP_HTTPS_SERVER_ENABLE
bool app_enable_ssl = true;
String server_cert;
String server_key;
bool app_enable_ssl = true;
String server_cert;
String server_key;
#endif
//our main server object
// our main server object
#ifdef CONFIG_ESP_HTTPS_SERVER_ENABLE
PsychicHttpsServer server;
PsychicHttpsServer server;
#else
PsychicHttpServer server;
PsychicHttpServer server;
#endif
PsychicWebSocketHandler websocketHandler;
PsychicEventSource eventSource;
bool connectToWifi()
{
//dual client and AP mode
// dual client and AP mode
WiFi.mode(WIFI_AP_STA);
// Configure SoftAP
@@ -92,10 +95,8 @@ bool connectToWifi()
int numberOfTries = 20;
// Wait for the WiFi event
while (true)
{
switch (WiFi.status())
{
while (true) {
switch (WiFi.status()) {
case WL_NO_SSID_AVAIL:
Serial.println("[WiFi] SSID not found");
break;
@@ -125,15 +126,12 @@ bool connectToWifi()
}
delay(tryDelay);
if (numberOfTries <= 0)
{
if (numberOfTries <= 0) {
Serial.print("[WiFi] Failed to connect to WiFi!");
// Use disconnect function to force stop trying to connect
WiFi.disconnect();
return false;
}
else
{
} else {
numberOfTries--;
}
}
@@ -148,111 +146,102 @@ void setup()
// We start by connecting to a WiFi network
// To debug, please enable Core Debug Level to Verbose
if (connectToWifi())
{
//set up our esp32 to listen on the local_hostname.local domain
if (connectToWifi()) {
// set up our esp32 to listen on the local_hostname.local domain
if (!MDNS.begin(local_hostname)) {
Serial.println("Error starting mDNS");
return;
}
MDNS.addService("http", "tcp", 80);
if(!LittleFS.begin())
{
if (!LittleFS.begin()) {
Serial.println("LittleFS Mount Failed. Do Platform -> Build Filesystem Image and Platform -> Upload Filesystem Image from VSCode");
return;
}
//look up our keys?
#ifdef CONFIG_ESP_HTTPS_SERVER_ENABLE
if (app_enable_ssl)
{
File fp = LittleFS.open("/server.crt");
if (fp)
{
server_cert = fp.readString();
// look up our keys?
#ifdef CONFIG_ESP_HTTPS_SERVER_ENABLE
if (app_enable_ssl) {
File fp = LittleFS.open("/server.crt");
if (fp) {
server_cert = fp.readString();
// Serial.println("Server Cert:");
// Serial.println(server_cert);
}
else
{
Serial.println("server.pem not found, SSL not available");
app_enable_ssl = false;
}
fp.close();
File fp2 = LittleFS.open("/server.key");
if (fp2)
{
server_key = fp2.readString();
// Serial.println("Server Key:");
// Serial.println(server_key);
}
else
{
Serial.println("server.key not found, SSL not available");
app_enable_ssl = false;
}
fp2.close();
// Serial.println("Server Cert:");
// Serial.println(server_cert);
} else {
Serial.println("server.pem not found, SSL not available");
app_enable_ssl = false;
}
#endif
fp.close();
//setup server config stuff here
server.config.max_uri_handlers = 20; //maximum number of uri handlers (.on() calls)
File fp2 = LittleFS.open("/server.key");
if (fp2) {
server_key = fp2.readString();
#ifdef CONFIG_ESP_HTTPS_SERVER_ENABLE
server.ssl_config.httpd.max_uri_handlers = 20; //maximum number of uri handlers (.on() calls)
// Serial.println("Server Key:");
// Serial.println(server_key);
} else {
Serial.println("server.key not found, SSL not available");
app_enable_ssl = false;
}
fp2.close();
}
#endif
//do we want secure or not?
if (app_enable_ssl)
{
server.listen(443, server_cert.c_str(), server_key.c_str());
//this creates a 2nd server listening on port 80 and redirects all requests HTTPS
PsychicHttpServer *redirectServer = new PsychicHttpServer();
redirectServer->config.ctrl_port = 20424; // just a random port different from the default one
redirectServer->listen(80);
redirectServer->onNotFound([](PsychicRequest *request) {
// setup server config stuff here
server.config.max_uri_handlers = 20; // maximum number of uri handlers (.on() calls)
#ifdef CONFIG_ESP_HTTPS_SERVER_ENABLE
server.ssl_config.httpd.max_uri_handlers = 20; // maximum number of uri handlers (.on() calls)
// do we want secure or not?
if (app_enable_ssl) {
server.setCertificate(server_cert.c_str(), server_key.c_str());
// this creates a 2nd server listening on port 80 and redirects all requests HTTPS
PsychicHttpServer* redirectServer = new PsychicHttpServer();
redirectServer->config.ctrl_port = 20424; // just a random port different from the default one
redirectServer->onNotFound([](PsychicRequest* request, PsychicResponse* response) {
String url = "https://" + request->host() + request->url();
return request->redirect(url.c_str());
});
}
else
server.listen(80);
#else
server.listen(80);
#endif
return response->redirect(url.c_str()); });
}
#endif
//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);
basicAuth.setUsername(app_user);
basicAuth.setPassword(app_pass);
basicAuth.setRealm(app_name);
basicAuth.setAuthMethod(HTTPAuthMethod::BASIC_AUTH);
basicAuth.setAuthFailureMessage("You must log in.");
//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);
digestAuth.setUsername(app_user);
digestAuth.setPassword(app_pass);
digestAuth.setRealm(app_name);
digestAuth.setAuthMethod(HTTPAuthMethod::DIGEST_AUTH);
digestAuth.setAuthFailureMessage("You must log in.");
//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.
// 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/")->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/")->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.
server.serveStatic("/img", LittleFS, "/img/");
//you can also serve single files
// you can also serve single files
server.serveStatic("/myfile.txt", LittleFS, "/custom.txt");
//example callback everytime a connection is opened
server.onOpen([](PsychicClient *client) {
Serial.printf("[http] connection #%u connected from %s\n", client->socket(), client->localIP().toString().c_str());
});
// example callback everytime a connection is opened
server.onOpen([](PsychicClient* client) { Serial.printf("[http] connection #%u connected from %s\n", client->socket(), client->localIP().toString().c_str()); });
//example callback everytime a connection is closed
server.onClose([](PsychicClient *client) {
Serial.printf("[http] connection #%u closed from %s\n", client->socket(), client->localIP().toString().c_str());
});
// example callback everytime a connection is closed
server.onClose([](PsychicClient* client) { Serial.printf("[http] connection #%u closed from %s\n", client->socket(), client->localIP().toString().c_str()); });
//api - json message passed in as post body
server.on("/api", HTTP_POST, [](PsychicRequest *request)
{
// api - json message passed in as post body
server.on("/api", HTTP_POST, [](PsychicRequest* request, PsychicResponse* response) {
//load our JSON request
JsonDocument json;
String body = request->body();
@@ -284,19 +273,15 @@ void setup()
//serialize and return
String jsonBuffer;
serializeJson(output, jsonBuffer);
return request->reply(200, "application/json", jsonBuffer.c_str());
});
return response->send(200, "application/json", jsonBuffer.c_str()); });
//api - parameters passed in via query eg. /api/endpoint?foo=bar
server.on("/ip", HTTP_GET, [](PsychicRequest *request)
{
// api - parameters passed in via query eg. /api/endpoint?foo=bar
server.on("/ip", HTTP_GET, [](PsychicRequest* request, PsychicResponse* response) {
String output = "Your IP is: " + request->client()->remoteIP().toString();
return request->reply(output.c_str());
});
return response->send(output.c_str()); });
//api - parameters passed in via query eg. /api/endpoint?foo=bar
server.on("/api", HTTP_GET, [](PsychicRequest *request)
{
// api - parameters passed in via query eg. /api/endpoint?foo=bar
server.on("/api", HTTP_GET, [](PsychicRequest* request, PsychicResponse* response) {
//create a response object
JsonDocument output;
output["msg"] = "status";
@@ -313,70 +298,48 @@ void setup()
//serialize and return
String jsonBuffer;
serializeJson(output, jsonBuffer);
return request->reply(200, "application/json", jsonBuffer.c_str());
});
return response->send(200, "application/json", jsonBuffer.c_str()); });
//how to redirect a request
server.on("/redirect", HTTP_GET, [](PsychicRequest *request)
{
return request->redirect("/alien.png");
});
// how to redirect a request
server.on("/redirect", HTTP_GET, [](PsychicRequest* request, PsychicResponse* response) { return response->redirect("/alien.png"); });
//how to do basic auth
server.on("/auth-basic", HTTP_GET, [](PsychicRequest *request)
{
if (!request->authenticate(app_user, app_pass))
return request->requestAuthentication(BASIC_AUTH, app_name, "You must log in.");
return request->reply("Auth Basic Success!");
});
// how to do basic auth
server.on("/auth-basic", HTTP_GET, [](PsychicRequest* request, PsychicResponse* response) { return response->send("Auth Basic Success!"); })->addMiddleware(&basicAuth);
//how to do digest auth
server.on("/auth-digest", HTTP_GET, [](PsychicRequest *request)
{
if (!request->authenticate(app_user, app_pass))
return request->requestAuthentication(DIGEST_AUTH, app_name, "You must log in.");
return request->reply("Auth Digest Success!");
});
//example of getting / setting cookies
server.on("/cookies", HTTP_GET, [](PsychicRequest *request)
{
PsychicResponse response(request);
// how to do digest auth
server.on("/auth-digest", HTTP_GET, [](PsychicRequest* request, PsychicResponse* response) { return response->send("Auth Digest Success!"); })->addMiddleware(&digestAuth);
// example of getting / setting cookies
server.on("/cookies", HTTP_GET, [](PsychicRequest* request, PsychicResponse* response) {
int counter = 0;
if (request->hasCookie("counter"))
char cookie[14];
size_t size = 14;
if (request->getCookie("counter", cookie, &size) == ESP_OK)
{
counter = std::stoi(request->getCookie("counter").c_str());
// value is null-terminated.
counter = std::stoi(cookie);
counter++;
}
sprintf(cookie, "%d", counter);
char cookie[12];
sprintf(cookie, "%i", counter);
response->setCookie("counter", cookie);
response->setContent(cookie);
return response->send(); });
response.setCookie("counter", cookie);
response.setContent(cookie);
return response.send();
});
//example of getting POST variables
server.on("/post", HTTP_POST, [](PsychicRequest *request)
{
// example of getting POST variables
server.on("/post", HTTP_POST, [](PsychicRequest* request, PsychicResponse* response) {
String output;
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()); });
//you can set up a custom 404 handler.
server.onNotFound([](PsychicRequest *request)
{
return request->reply(404, "text/html", "Custom 404 Handler");
});
// you can set up a custom 404 handler.
server.onNotFound([](PsychicRequest* request, PsychicResponse* response) { return response->send(404, "text/html", "Custom 404 Handler"); });
//handle a very basic upload as post body
PsychicUploadHandler *uploadHandler = new PsychicUploadHandler();
uploadHandler->onUpload([](PsychicRequest *request, const String& filename, uint64_t index, uint8_t *data, size_t len, bool last) {
// handle a very basic upload as post body
PsychicUploadHandler* uploadHandler = new PsychicUploadHandler();
uploadHandler->onUpload([](PsychicRequest* request, const String& filename, uint64_t index, uint8_t* data, size_t len, bool last) {
File file;
String path = "/www/" + filename;
@@ -401,24 +364,21 @@ void setup()
return ESP_FAIL;
}
return ESP_OK;
});
return ESP_OK; });
//gets called after upload has been handled
uploadHandler->onRequest([](PsychicRequest *request)
{
// gets called after upload has been handled
uploadHandler->onRequest([](PsychicRequest* request, PsychicResponse* response) {
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
// wildcard basic file upload - POST to /upload/filename.ext
server.on("/upload/*", HTTP_POST, uploadHandler);
//a little bit more complicated multipart form
PsychicUploadHandler *multipartHandler = new PsychicUploadHandler();
multipartHandler->onUpload([](PsychicRequest *request, const String& filename, uint64_t index, uint8_t *data, size_t len, bool last) {
// a little bit more complicated multipart form
PsychicUploadHandler* multipartHandler = new PsychicUploadHandler();
multipartHandler->onUpload([](PsychicRequest* request, const String& filename, uint64_t index, uint8_t* data, size_t len, bool last) {
File file;
String path = "/www/" + filename;
@@ -443,12 +403,10 @@ void setup()
return ESP_FAIL;
}
return ESP_OK;
});
return ESP_OK; });
//gets called after upload has been handled
multipartHandler->onRequest([](PsychicRequest *request)
{
// gets called after upload has been handled
multipartHandler->onRequest([](PsychicRequest* request, PsychicResponse* response) {
PsychicWebParameter *file = request->getParam("file_upload");
String url = "/" + file->value();
@@ -459,34 +417,26 @@ void setup()
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()); });
//wildcard basic file upload - POST to /upload/filename.ext
// wildcard basic file upload - POST to /upload/filename.ext
server.on("/multipart", HTTP_POST, multipartHandler);
//a websocket echo server
websocketHandler.onOpen([](PsychicWebSocketClient *client) {
// a websocket echo server
websocketHandler.onOpen([](PsychicWebSocketClient* client) {
Serial.printf("[socket] connection #%u connected from %s\n", client->socket(), client->localIP().toString().c_str());
client->sendMessage("Hello!");
});
websocketHandler.onFrame([](PsychicWebSocketRequest *request, httpd_ws_frame *frame) {
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);
});
websocketHandler.onClose([](PsychicWebSocketClient *client) {
Serial.printf("[socket] connection #%u closed from %s\n", client->socket(), client->localIP().toString().c_str());
});
return request->reply(frame); });
websocketHandler.onClose([](PsychicWebSocketClient* client) { Serial.printf("[socket] connection #%u closed from %s\n", client->socket(), client->localIP().toString().c_str()); });
server.on("/ws", &websocketHandler);
//EventSource server
eventSource.onOpen([](PsychicEventSourceClient *client) {
// EventSource server
eventSource.onOpen([](PsychicEventSourceClient* client) {
Serial.printf("[eventsource] connection #%u connected from %s\n", client->socket(), client->localIP().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->localIP().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->localIP().toString().c_str()); });
server.on("/events", &eventSource);
}
}
@@ -496,8 +446,7 @@ char output[60];
void loop()
{
if (millis() - lastUpdate > 2000)
{
if (millis() - lastUpdate > 2000) {
sprintf(output, "Millis: %lu\n", millis());
websocketHandler.sendAll(output);