mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-06 07:49:52 +03:00
329 lines
9.7 KiB
C++
329 lines
9.7 KiB
C++
#include "PsychicHttpServer.h"
|
|
#include "PsychicEndpoint.h"
|
|
#include "PsychicHandler.h"
|
|
#include "PsychicWebHandler.h"
|
|
#include "PsychicStaticFileHandler.h"
|
|
#include "PsychicWebSocket.h"
|
|
#include "WiFi.h"
|
|
#include "PsychicJson.h" // added by proddy
|
|
|
|
PsychicHttpServer::PsychicHttpServer()
|
|
: _onOpen(NULL)
|
|
, _onClose(NULL) {
|
|
maxRequestBodySize = MAX_REQUEST_BODY_SIZE;
|
|
maxUploadSize = MAX_UPLOAD_SIZE;
|
|
|
|
defaultEndpoint = new PsychicEndpoint(this, HTTP_GET, "");
|
|
onNotFound(PsychicHttpServer::defaultNotFoundHandler);
|
|
|
|
//for a regular server
|
|
config = HTTPD_DEFAULT_CONFIG();
|
|
config.open_fn = PsychicHttpServer::openCallback;
|
|
config.close_fn = PsychicHttpServer::closeCallback;
|
|
config.uri_match_fn = httpd_uri_match_wildcard;
|
|
config.global_user_ctx = this;
|
|
config.global_user_ctx_free_fn = destroy;
|
|
config.max_uri_handlers = 20;
|
|
|
|
#ifdef ENABLE_ASYNC
|
|
// It is advisable that httpd_config_t->max_open_sockets > MAX_ASYNC_REQUESTS
|
|
// Why? This leaves at least one socket still available to handle
|
|
// quick synchronous requests. Otherwise, all the sockets will
|
|
// get taken by the long async handlers, and your server will no
|
|
// longer be responsive.
|
|
config.max_open_sockets = ASYNC_WORKER_COUNT + 1;
|
|
config.lru_purge_enable = true;
|
|
#endif
|
|
}
|
|
|
|
PsychicHttpServer::~PsychicHttpServer() {
|
|
for (auto * client : _clients)
|
|
delete (client);
|
|
_clients.clear();
|
|
|
|
for (auto * endpoint : _endpoints)
|
|
delete (endpoint);
|
|
_endpoints.clear();
|
|
|
|
for (auto * handler : _handlers)
|
|
delete (handler);
|
|
_handlers.clear();
|
|
|
|
delete defaultEndpoint;
|
|
}
|
|
|
|
void PsychicHttpServer::destroy(void * ctx) {
|
|
PsychicHttpServer * temp = (PsychicHttpServer *)ctx;
|
|
delete temp;
|
|
}
|
|
|
|
esp_err_t PsychicHttpServer::listen(uint16_t port) {
|
|
this->_use_ssl = false;
|
|
this->config.server_port = port;
|
|
|
|
return this->_start();
|
|
}
|
|
|
|
esp_err_t PsychicHttpServer::_start() {
|
|
esp_err_t ret;
|
|
|
|
#ifdef ENABLE_ASYNC
|
|
// start workers
|
|
start_async_req_workers();
|
|
#endif
|
|
|
|
//fire it up.
|
|
ret = _startServer();
|
|
if (ret != ESP_OK) {
|
|
ESP_LOGE(PH_TAG, "Server start failed (%s)", esp_err_to_name(ret));
|
|
return ret;
|
|
}
|
|
|
|
// Register handler
|
|
ret = httpd_register_err_handler(server, HTTPD_404_NOT_FOUND, PsychicHttpServer::notFoundHandler);
|
|
if (ret != ESP_OK)
|
|
ESP_LOGE(PH_TAG, "Add 404 handler failed (%s)", esp_err_to_name(ret));
|
|
|
|
return ret;
|
|
}
|
|
|
|
esp_err_t PsychicHttpServer::_startServer() {
|
|
return httpd_start(&this->server, &this->config);
|
|
}
|
|
|
|
void PsychicHttpServer::stop() {
|
|
httpd_stop(this->server);
|
|
}
|
|
|
|
PsychicHandler & PsychicHttpServer::addHandler(PsychicHandler * handler) {
|
|
_handlers.push_back(handler);
|
|
return *handler;
|
|
}
|
|
|
|
void PsychicHttpServer::removeHandler(PsychicHandler * handler) {
|
|
_handlers.remove(handler);
|
|
}
|
|
|
|
PsychicEndpoint * PsychicHttpServer::on(const char * uri) {
|
|
return on(uri, HTTP_GET);
|
|
}
|
|
|
|
PsychicEndpoint * PsychicHttpServer::on(const char * uri, PsychicHttpRequestCallback fn) {
|
|
return on(uri, HTTP_GET, fn);
|
|
}
|
|
|
|
PsychicEndpoint * PsychicHttpServer::on(const char * uri, http_method method, PsychicHttpRequestCallback fn) {
|
|
//these basic requests need a basic web handler
|
|
PsychicWebHandler * handler = new PsychicWebHandler();
|
|
handler->onRequest(fn);
|
|
|
|
return on(uri, method, handler);
|
|
}
|
|
|
|
// added by Proddy
|
|
PsychicEndpoint * PsychicHttpServer::on(const char * uri, http_method method, PsychicJsonRequestCallback fn) {
|
|
PsychicJsonHandler * handler = new PsychicJsonHandler(fn);
|
|
|
|
return on(uri, method, handler);
|
|
}
|
|
|
|
PsychicEndpoint * PsychicHttpServer::on(const char * uri, http_method method) {
|
|
PsychicWebHandler * handler = new PsychicWebHandler();
|
|
|
|
return on(uri, method, handler);
|
|
}
|
|
|
|
PsychicEndpoint * PsychicHttpServer::on(const char * uri, PsychicHandler * handler) {
|
|
return on(uri, HTTP_GET, handler);
|
|
}
|
|
|
|
PsychicEndpoint * PsychicHttpServer::on(const char * uri, http_method method, PsychicHandler * handler) {
|
|
//make our endpoint
|
|
PsychicEndpoint * endpoint = new PsychicEndpoint(this, method, uri);
|
|
|
|
//set our handler
|
|
endpoint->setHandler(handler);
|
|
|
|
// URI handler structure
|
|
httpd_uri_t my_uri{.uri = uri, .method = method, .handler = PsychicEndpoint::requestCallback, .user_ctx = endpoint, .is_websocket = handler->isWebSocket()};
|
|
|
|
// Register endpoint with ESP-IDF server
|
|
esp_err_t ret = httpd_register_uri_handler(this->server, &my_uri);
|
|
if (ret != ESP_OK) {
|
|
ESP_LOGE(PH_TAG, "Add endpoint %s failed (%s)", uri, esp_err_to_name(ret)); // modified by proddy
|
|
}
|
|
|
|
//save it for later
|
|
_endpoints.push_back(endpoint);
|
|
|
|
return endpoint;
|
|
}
|
|
|
|
void PsychicHttpServer::onNotFound(PsychicHttpRequestCallback fn) {
|
|
PsychicWebHandler * handler = new PsychicWebHandler();
|
|
handler->onRequest(fn);
|
|
|
|
this->defaultEndpoint->setHandler(handler);
|
|
}
|
|
|
|
esp_err_t PsychicHttpServer::notFoundHandler(httpd_req_t * req, httpd_err_code_t err) {
|
|
PsychicHttpServer * server = (PsychicHttpServer *)httpd_get_global_user_ctx(req->handle);
|
|
PsychicRequest request(server, req);
|
|
|
|
//loop through our global handlers and see if anyone wants it
|
|
for (auto * handler : server->_handlers) {
|
|
//are we capable of handling this?
|
|
if (handler->filter(&request) && handler->canHandle(&request)) {
|
|
//check our credentials
|
|
if (handler->needsAuthentication(&request))
|
|
return handler->authenticate(&request);
|
|
else
|
|
return handler->handleRequest(&request);
|
|
}
|
|
}
|
|
|
|
//nothing found, give it to our defaultEndpoint
|
|
PsychicHandler * handler = server->defaultEndpoint->handler();
|
|
if (handler->filter(&request) && handler->canHandle(&request))
|
|
return handler->handleRequest(&request);
|
|
|
|
//not sure how we got this far.
|
|
return ESP_ERR_HTTPD_INVALID_REQ;
|
|
}
|
|
|
|
esp_err_t PsychicHttpServer::defaultNotFoundHandler(PsychicRequest * request) {
|
|
request->reply(404, "text/html", "That URI does not exist.");
|
|
|
|
return ESP_OK;
|
|
}
|
|
|
|
void PsychicHttpServer::onOpen(PsychicClientCallback handler) {
|
|
this->_onOpen = handler;
|
|
}
|
|
|
|
esp_err_t PsychicHttpServer::openCallback(httpd_handle_t hd, int sockfd) {
|
|
ESP_LOGI(PH_TAG, "New client connected %d", sockfd);
|
|
|
|
//get our global server reference
|
|
PsychicHttpServer * server = (PsychicHttpServer *)httpd_get_global_user_ctx(hd);
|
|
|
|
//lookup our client
|
|
PsychicClient * client = server->getClient(sockfd);
|
|
if (client == NULL) {
|
|
client = new PsychicClient(hd, sockfd);
|
|
server->addClient(client);
|
|
}
|
|
|
|
//user callback
|
|
if (server->_onOpen != NULL)
|
|
server->_onOpen(client);
|
|
|
|
return ESP_OK;
|
|
}
|
|
|
|
void PsychicHttpServer::onClose(PsychicClientCallback handler) {
|
|
this->_onClose = handler;
|
|
}
|
|
|
|
void PsychicHttpServer::closeCallback(httpd_handle_t hd, int sockfd) {
|
|
ESP_LOGI(PH_TAG, "Client disconnected %d", sockfd);
|
|
|
|
PsychicHttpServer * server = (PsychicHttpServer *)httpd_get_global_user_ctx(hd);
|
|
|
|
//lookup our client
|
|
PsychicClient * client = server->getClient(sockfd);
|
|
if (client != NULL) {
|
|
//give our handlers a chance to handle a disconnect first
|
|
for (PsychicEndpoint * endpoint : server->_endpoints) {
|
|
PsychicHandler * handler = endpoint->handler();
|
|
handler->checkForClosedClient(client);
|
|
}
|
|
|
|
//do we have a callback attached?
|
|
if (server->_onClose != NULL)
|
|
server->_onClose(client);
|
|
|
|
//remove it from our list
|
|
server->removeClient(client);
|
|
} else
|
|
ESP_LOGE(PH_TAG, "No client record %d", sockfd);
|
|
|
|
//finally close it out.
|
|
close(sockfd);
|
|
}
|
|
|
|
PsychicStaticFileHandler * PsychicHttpServer::serveStatic(const char * uri, fs::FS & fs, const char * path, const char * cache_control) {
|
|
PsychicStaticFileHandler * handler = new PsychicStaticFileHandler(uri, fs, path, cache_control);
|
|
this->addHandler(handler);
|
|
|
|
return handler;
|
|
}
|
|
|
|
void PsychicHttpServer::addClient(PsychicClient * client) {
|
|
_clients.push_back(client);
|
|
}
|
|
|
|
void PsychicHttpServer::removeClient(PsychicClient * client) {
|
|
_clients.remove(client);
|
|
delete client;
|
|
}
|
|
|
|
PsychicClient * PsychicHttpServer::getClient(int socket) {
|
|
for (PsychicClient * client : _clients)
|
|
if (client->socket() == socket)
|
|
return client;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
PsychicClient * PsychicHttpServer::getClient(httpd_req_t * req) {
|
|
return getClient(httpd_req_to_sockfd(req));
|
|
}
|
|
|
|
bool PsychicHttpServer::hasClient(int socket) {
|
|
return getClient(socket) != NULL;
|
|
}
|
|
|
|
const std::list<PsychicClient *> & PsychicHttpServer::getClientList() {
|
|
return _clients;
|
|
}
|
|
|
|
bool ON_STA_FILTER(PsychicRequest * request) {
|
|
return WiFi.localIP() == request->client()->localIP();
|
|
}
|
|
|
|
bool ON_AP_FILTER(PsychicRequest * request) {
|
|
return WiFi.softAPIP() == request->client()->localIP();
|
|
}
|
|
|
|
String urlDecode(const char * encoded) {
|
|
size_t length = strlen(encoded);
|
|
char * decoded = (char *)malloc(length + 1);
|
|
if (!decoded) {
|
|
return "";
|
|
}
|
|
|
|
size_t i, j = 0;
|
|
for (i = 0; i < length; ++i) {
|
|
if (encoded[i] == '%' && isxdigit(encoded[i + 1]) && isxdigit(encoded[i + 2])) {
|
|
// Valid percent-encoded sequence
|
|
int hex;
|
|
sscanf(encoded + i + 1, "%2x", &hex);
|
|
decoded[j++] = (char)hex;
|
|
i += 2; // Skip the two hexadecimal characters
|
|
} else if (encoded[i] == '+') {
|
|
// Convert '+' to space
|
|
decoded[j++] = ' ';
|
|
} else {
|
|
// Copy other characters as they are
|
|
decoded[j++] = encoded[i];
|
|
}
|
|
}
|
|
|
|
decoded[j] = '\0'; // Null-terminate the decoded string
|
|
|
|
String output(decoded);
|
|
free(decoded);
|
|
|
|
return output;
|
|
} |