🆚 Updates psychic version to 1.2.1

This commit is contained in:
Rune Harlyk
2024-08-17 12:14:21 +02:00
committed by Rune Harlyk
parent 951bfb4cd2
commit a19d6a2f4f
24 changed files with 474 additions and 371 deletions
@@ -34,7 +34,6 @@ size_t ChunkPrinter::write(uint8_t c)
size_t ChunkPrinter::write(const uint8_t *buffer, size_t size) size_t ChunkPrinter::write(const uint8_t *buffer, size_t size)
{ {
esp_err_t err;
size_t written = 0; size_t written = 0;
while (written < size) while (written < size)
+19 -24
View File
@@ -2,24 +2,21 @@
#include "PsychicHttpServer.h" #include "PsychicHttpServer.h"
#include <lwip/sockets.h> #include <lwip/sockets.h>
PsychicClient::PsychicClient(httpd_handle_t server, int socket) : _server(server), PsychicClient::PsychicClient(httpd_handle_t server, int socket) :
_socket(socket), _server(server),
_friend(NULL), _socket(socket),
isNew(false) _friend(NULL),
{ isNew(false)
{}
PsychicClient::~PsychicClient() {
} }
PsychicClient::~PsychicClient() httpd_handle_t PsychicClient::server() {
{
}
httpd_handle_t PsychicClient::server()
{
return _server; return _server;
} }
int PsychicClient::socket() int PsychicClient::socket() {
{
return _socket; return _socket;
} }
@@ -27,28 +24,27 @@ int PsychicClient::socket()
esp_err_t PsychicClient::close() esp_err_t PsychicClient::close()
{ {
esp_err_t err = httpd_sess_trigger_close(_server, _socket); esp_err_t err = httpd_sess_trigger_close(_server, _socket);
// PsychicHttpServer::closeCallback(_server, _socket); // call this immediately so the client is taken off the list. //PsychicHttpServer::closeCallback(_server, _socket); // call this immediately so the client is taken off the list.
return err; return err;
} }
IPAddress PsychicClient::localIP() IPAddress PsychicClient::localIP()
{ {
IPAddress address(0, 0, 0, 0); IPAddress address(0,0,0,0);
char ipstr[INET6_ADDRSTRLEN]; char ipstr[INET6_ADDRSTRLEN];
struct sockaddr_in6 addr; // esp_http_server uses IPv6 addressing struct sockaddr_in6 addr; // esp_http_server uses IPv6 addressing
socklen_t addr_size = sizeof(addr); socklen_t addr_size = sizeof(addr);
if (getsockname(_socket, (struct sockaddr *)&addr, &addr_size) < 0) if (getsockname(_socket, (struct sockaddr *)&addr, &addr_size) < 0) {
{
ESP_LOGE(PH_TAG, "Error getting client IP"); ESP_LOGE(PH_TAG, "Error getting client IP");
return address; return address;
} }
// Convert to IPv4 string // Convert to IPv4 string
inet_ntop(AF_INET, &addr.sin6_addr.un.u32_addr[3], ipstr, sizeof(ipstr)); inet_ntop(AF_INET, &addr.sin6_addr.un.u32_addr[3], ipstr, sizeof(ipstr));
ESP_LOGI(PH_TAG, "Client Local IP => %s", ipstr); //ESP_LOGD(PH_TAG, "Client Local IP => %s", ipstr);
address.fromString(ipstr); address.fromString(ipstr);
return address; return address;
@@ -56,21 +52,20 @@ IPAddress PsychicClient::localIP()
IPAddress PsychicClient::remoteIP() IPAddress PsychicClient::remoteIP()
{ {
IPAddress address(0, 0, 0, 0); IPAddress address(0,0,0,0);
char ipstr[INET6_ADDRSTRLEN]; char ipstr[INET6_ADDRSTRLEN];
struct sockaddr_in6 addr; // esp_http_server uses IPv6 addressing struct sockaddr_in6 addr; // esp_http_server uses IPv6 addressing
socklen_t addr_size = sizeof(addr); socklen_t addr_size = sizeof(addr);
if (getpeername(_socket, (struct sockaddr *)&addr, &addr_size) < 0) if (getpeername(_socket, (struct sockaddr *)&addr, &addr_size) < 0) {
{
ESP_LOGE(PH_TAG, "Error getting client IP"); ESP_LOGE(PH_TAG, "Error getting client IP");
return address; return address;
} }
// Convert to IPv4 string // Convert to IPv4 string
inet_ntop(AF_INET, &addr.sin6_addr.un.u32_addr[3], ipstr, sizeof(ipstr)); inet_ntop(AF_INET, &addr.sin6_addr.un.u32_addr[3], ipstr, sizeof(ipstr));
ESP_LOGV(PH_TAG, "Client Remote IP => %s", ipstr); //ESP_LOGD(PH_TAG, "Client Remote IP => %s", ipstr);
address.fromString(ipstr); address.fromString(ipstr);
return address; return address;
-1
View File
@@ -30,7 +30,6 @@
#ifdef ARDUINO #ifdef ARDUINO
#include <Arduino.h> #include <Arduino.h>
#include <ArduinoTrace.h>
#endif #endif
#include <esp_http_server.h> #include <esp_http_server.h>
@@ -96,7 +96,6 @@ void PsychicEventSource::openCallback(PsychicClient *client) {
PsychicEventSourceClient *buddy = getClient(client); PsychicEventSourceClient *buddy = getClient(client);
if (buddy == NULL) if (buddy == NULL)
{ {
TRACE();
return; return;
} }
@@ -108,7 +107,6 @@ void PsychicEventSource::closeCallback(PsychicClient *client) {
PsychicEventSourceClient *buddy = getClient(client); PsychicEventSourceClient *buddy = getClient(client);
if (buddy == NULL) if (buddy == NULL)
{ {
TRACE();
return; return;
} }
@@ -104,9 +104,16 @@ esp_err_t PsychicFileResponse::send()
if (size < FILE_CHUNK_SIZE) if (size < FILE_CHUNK_SIZE)
{ {
uint8_t *buffer = (uint8_t *)malloc(size); uint8_t *buffer = (uint8_t *)malloc(size);
int readSize = _content.readBytes((char *)buffer, size); if (buffer == NULL)
{
/* Respond with 500 Internal Server Error */
httpd_resp_send_err(this->_request->request(), HTTPD_500_INTERNAL_SERVER_ERROR, "Unable to allocate memory.");
return ESP_FAIL;
}
this->setContent(buffer, size); size_t readSize = _content.readBytes((char *)buffer, size);
this->setContent(buffer, readSize);
err = PsychicResponse::send(); err = PsychicResponse::send();
free(buffer); free(buffer);
@@ -143,7 +150,7 @@ esp_err_t PsychicFileResponse::send()
if (err == ESP_OK) if (err == ESP_OK)
{ {
ESP_LOGI(PH_TAG, "File sending complete"); ESP_LOGD(PH_TAG, "File sending complete");
this->finishChunking(); this->finishChunking();
} }
} }
+9 -1
View File
@@ -7,7 +7,8 @@ PsychicHandler::PsychicHandler() :
_password(""), _password(""),
_method(DIGEST_AUTH), _method(DIGEST_AUTH),
_realm(""), _realm(""),
_authFailMsg("") _authFailMsg(""),
_subprotocol("")
{} {}
PsychicHandler::~PsychicHandler() { PsychicHandler::~PsychicHandler() {
@@ -26,6 +27,13 @@ bool PsychicHandler::filter(PsychicRequest *request){
return _filter == NULL || _filter(request); return _filter == NULL || _filter(request);
} }
void PsychicHandler::setSubprotocol(const String& subprotocol) {
this->_subprotocol = subprotocol;
}
const char* PsychicHandler::getSubprotocol() const {
return _subprotocol.c_str();
}
PsychicHandler* PsychicHandler::setAuthentication(const char *username, const char *password, HTTPAuthMethod method, const char *realm, const char *authFailMsg) { PsychicHandler* PsychicHandler::setAuthentication(const char *username, const char *password, HTTPAuthMethod method, const char *realm, const char *authFailMsg) {
_username = String(username); _username = String(username);
_password = String(password); _password = String(password);
+6 -1
View File
@@ -24,11 +24,13 @@ class PsychicHandler {
String _realm; String _realm;
String _authFailMsg; String _authFailMsg;
String _subprotocol;
std::list<PsychicClient*> _clients; std::list<PsychicClient*> _clients;
public: public:
PsychicHandler(); PsychicHandler();
~PsychicHandler(); virtual ~PsychicHandler();
PsychicHandler* setFilter(PsychicRequestFilterFunction fn); PsychicHandler* setFilter(PsychicRequestFilterFunction fn);
bool filter(PsychicRequest *request); bool filter(PsychicRequest *request);
@@ -39,6 +41,9 @@ class PsychicHandler {
virtual bool isWebSocket() { return false; }; virtual bool isWebSocket() { return false; };
void setSubprotocol(const String& subprotocol);
const char* getSubprotocol() const;
PsychicClient * checkForNewClient(PsychicClient *client); PsychicClient * checkForNewClient(PsychicClient *client);
void checkForClosedClient(PsychicClient *client); void checkForClosedClient(PsychicClient *client);
+6 -6
View File
@@ -3,19 +3,19 @@
//#define ENABLE_ASYNC // This is something added in ESP-IDF 5.1.x where each request can be handled in its own thread //#define ENABLE_ASYNC // This is something added in ESP-IDF 5.1.x where each request can be handled in its own thread
#include "PsychicEndpoint.h" #include <http_status.h>
#include "PsychicEventSource.h"
#include "PsychicFileResponse.h"
#include "PsychicHandler.h"
#include "PsychicHttpServer.h" #include "PsychicHttpServer.h"
#include "PsychicJson.h"
#include "PsychicRequest.h" #include "PsychicRequest.h"
#include "PsychicResponse.h" #include "PsychicResponse.h"
#include "PsychicEndpoint.h"
#include "PsychicHandler.h"
#include "PsychicStaticFileHandler.h" #include "PsychicStaticFileHandler.h"
#include "PsychicFileResponse.h"
#include "PsychicStreamResponse.h" #include "PsychicStreamResponse.h"
#include "PsychicUploadHandler.h" #include "PsychicUploadHandler.h"
#include "PsychicWebSocket.h" #include "PsychicWebSocket.h"
#include <http_status.h> #include "PsychicEventSource.h"
#include "PsychicJson.h"
#ifdef ENABLE_ASYNC #ifdef ENABLE_ASYNC
#include "async_worker.h" #include "async_worker.h"
@@ -56,8 +56,7 @@ PsychicHttpServer::~PsychicHttpServer()
void PsychicHttpServer::destroy(void *ctx) void PsychicHttpServer::destroy(void *ctx)
{ {
PsychicHttpServer *temp = (PsychicHttpServer *)ctx; // do not release any resource for PsychicHttpServer in order to be able to restart it after stopping
delete temp;
} }
esp_err_t PsychicHttpServer::listen(uint16_t port) esp_err_t PsychicHttpServer::listen(uint16_t port)
@@ -141,7 +140,8 @@ PsychicEndpoint* PsychicHttpServer::on(const char* uri, http_method method, Psyc
.method = method, .method = method,
.handler = PsychicEndpoint::requestCallback, .handler = PsychicEndpoint::requestCallback,
.user_ctx = endpoint, .user_ctx = endpoint,
.is_websocket = handler->isWebSocket() .is_websocket = handler->isWebSocket(),
.supported_subprotocol = handler->getSubprotocol()
}; };
// Register endpoint with ESP-IDF server // Register endpoint with ESP-IDF server
@@ -186,7 +186,7 @@ PsychicEndpoint* PsychicHttpServer::on(const char* uri, http_method method, Psyc
void PsychicHttpServer::onNotFound(PsychicHttpRequestCallback fn) void PsychicHttpServer::onNotFound(PsychicHttpRequestCallback fn)
{ {
PsychicWebHandler *handler = new PsychicWebHandler(); PsychicWebHandler *handler = new PsychicWebHandler();
handler->onRequest(fn); handler->onRequest(fn == nullptr ? PsychicHttpServer::defaultNotFoundHandler : fn);
this->defaultEndpoint->setHandler(handler); this->defaultEndpoint->setHandler(handler);
} }
@@ -232,7 +232,7 @@ void PsychicHttpServer::onOpen(PsychicClientCallback handler) {
esp_err_t PsychicHttpServer::openCallback(httpd_handle_t hd, int sockfd) esp_err_t PsychicHttpServer::openCallback(httpd_handle_t hd, int sockfd)
{ {
ESP_LOGI(PH_TAG, "New client connected %d", sockfd); ESP_LOGD(PH_TAG, "New client connected %d", sockfd);
//get our global server reference //get our global server reference
PsychicHttpServer *server = (PsychicHttpServer*)httpd_get_global_user_ctx(hd); PsychicHttpServer *server = (PsychicHttpServer*)httpd_get_global_user_ctx(hd);
@@ -258,7 +258,7 @@ void PsychicHttpServer::onClose(PsychicClientCallback handler) {
void PsychicHttpServer::closeCallback(httpd_handle_t hd, int sockfd) void PsychicHttpServer::closeCallback(httpd_handle_t hd, int sockfd)
{ {
ESP_LOGI(PH_TAG, "Client disconnected %d", sockfd); ESP_LOGD(PH_TAG, "Client disconnected %d", sockfd);
PsychicHttpServer *server = (PsychicHttpServer*)httpd_get_global_user_ctx(hd); PsychicHttpServer *server = (PsychicHttpServer*)httpd_get_global_user_ctx(hd);
@@ -25,7 +25,7 @@ class PsychicHttpServer
public: public:
PsychicHttpServer(); PsychicHttpServer();
~PsychicHttpServer(); virtual ~PsychicHttpServer();
//esp-idf specific stuff //esp-idf specific stuff
httpd_handle_t server; httpd_handle_t server;
@@ -1,5 +1,7 @@
#include "PsychicHttpsServer.h" #include "PsychicHttpsServer.h"
#ifdef CONFIG_ESP_HTTPS_SERVER_ENABLE
PsychicHttpsServer::PsychicHttpsServer() : PsychicHttpServer() PsychicHttpsServer::PsychicHttpsServer() : PsychicHttpServer()
{ {
//for a SSL server //for a SSL server
@@ -25,8 +27,15 @@ esp_err_t PsychicHttpsServer::listen(uint16_t port, const char *cert, const char
this->_use_ssl = true; this->_use_ssl = true;
this->ssl_config.port_secure = port; this->ssl_config.port_secure = port;
this->ssl_config.cacert_pem = (uint8_t *)cert;
this->ssl_config.cacert_len = strlen(cert)+1; #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 2)
this->ssl_config.servercert = (uint8_t *)cert;
this->ssl_config.servercert_len = strlen(cert)+1;
#else
this->ssl_config.cacert_pem = (uint8_t *)cert;
this->ssl_config.cacert_len = strlen(cert)+1;
#endif
this->ssl_config.prvtkey_pem = (uint8_t *)private_key; this->ssl_config.prvtkey_pem = (uint8_t *)private_key;
this->ssl_config.prvtkey_len = strlen(private_key)+1; this->ssl_config.prvtkey_len = strlen(private_key)+1;
@@ -48,3 +57,5 @@ void PsychicHttpsServer::stop()
else else
httpd_stop(this->server); httpd_stop(this->server);
} }
#endif // CONFIG_ESP_HTTPS_SERVER_ENABLE
@@ -1,10 +1,13 @@
#ifndef PsychicHttpsServer_h #ifndef PsychicHttpsServer_h
#define PsychicHttpsServer_h #define PsychicHttpsServer_h
#include <sdkconfig.h>
#ifdef CONFIG_ESP_HTTPS_SERVER_ENABLE
#include "PsychicCore.h" #include "PsychicCore.h"
#include "PsychicHttpServer.h" #include "PsychicHttpServer.h"
#include <esp_https_server.h> #include <esp_https_server.h>
#if !CONFIG_HTTPD_WS_SUPPORT #if !CONFIG_HTTPD_WS_SUPPORT
#error PsychicHttpsServer cannot be used unless HTTPD_WS_SUPPORT is enabled in esp-http-server component configuration #error PsychicHttpsServer cannot be used unless HTTPD_WS_SUPPORT is enabled in esp-http-server component configuration
#endif #endif
@@ -30,3 +33,7 @@ class PsychicHttpsServer : public PsychicHttpServer
}; };
#endif // PsychicHttpsServer_h #endif // PsychicHttpsServer_h
#else
#error ESP-IDF https server support not enabled.
#endif // CONFIG_ESP_HTTPS_SERVER_ENABLE
+55 -57
View File
@@ -1,24 +1,25 @@
#include "PsychicJson.h" #include "PsychicJson.h"
#ifdef ARDUINOJSON_6_COMPATIBILITY #ifdef ARDUINOJSON_6_COMPATIBILITY
PsychicJsonResponse::PsychicJsonResponse(PsychicRequest *request, bool isArray, size_t maxJsonBufferSize) : PsychicResponse(request), PsychicJsonResponse::PsychicJsonResponse(PsychicRequest *request, bool isArray, size_t maxJsonBufferSize) :
_jsonBuffer(maxJsonBufferSize) PsychicResponse(request),
{ _jsonBuffer(maxJsonBufferSize)
setContentType(JSON_MIMETYPE); {
if (isArray) setContentType(JSON_MIMETYPE);
_root = _jsonBuffer.createNestedArray(); if (isArray)
else _root = _jsonBuffer.createNestedArray();
_root = _jsonBuffer.createNestedObject(); else
} _root = _jsonBuffer.createNestedObject();
}
#else #else
PsychicJsonResponse::PsychicJsonResponse(PsychicRequest *request, bool isArray) : PsychicResponse(request) PsychicJsonResponse::PsychicJsonResponse(PsychicRequest *request, bool isArray) : PsychicResponse(request)
{ {
setContentType(JSON_MIMETYPE); setContentType(JSON_MIMETYPE);
if (isArray) if (isArray)
_root = _jsonBuffer.add<JsonArray>(); _root = _jsonBuffer.add<JsonArray>();
else else
_root = _jsonBuffer.add<JsonObject>(); _root = _jsonBuffer.add<JsonObject>();
} }
#endif #endif
JsonVariant &PsychicJsonResponse::getRoot() { return _root; } JsonVariant &PsychicJsonResponse::getRoot() { return _root; }
@@ -35,28 +36,21 @@ esp_err_t PsychicJsonResponse::send()
size_t buffer_size; size_t buffer_size;
char *buffer; char *buffer;
// DUMP(length); //how big of a buffer do we want?
// how big of a buffer do we want?
if (length < JSON_BUFFER_SIZE) if (length < JSON_BUFFER_SIZE)
buffer_size = length + 1; buffer_size = length+1;
else else
buffer_size = JSON_BUFFER_SIZE; buffer_size = JSON_BUFFER_SIZE;
// DUMP(buffer_size);
buffer = (char *)malloc(buffer_size); buffer = (char *)malloc(buffer_size);
if (buffer == NULL) if (buffer == NULL) {
{
httpd_resp_send_err(this->_request->request(), HTTPD_500_INTERNAL_SERVER_ERROR, "Unable to allocate memory."); httpd_resp_send_err(this->_request->request(), HTTPD_500_INTERNAL_SERVER_ERROR, "Unable to allocate memory.");
return ESP_FAIL; return ESP_FAIL;
} }
// send it in one shot or no? //send it in one shot or no?
if (length < JSON_BUFFER_SIZE) if (length < JSON_BUFFER_SIZE)
{ {
// TRACE();
serializeJson(_root, buffer, buffer_size); serializeJson(_root, buffer, buffer_size);
this->setContent((uint8_t *)buffer, length); this->setContent((uint8_t *)buffer, length);
@@ -66,67 +60,71 @@ esp_err_t PsychicJsonResponse::send()
} }
else else
{ {
// helper class that acts as a stream to print chunked responses //helper class that acts as a stream to print chunked responses
ChunkPrinter dest(this, (uint8_t *)buffer, buffer_size); ChunkPrinter dest(this, (uint8_t *)buffer, buffer_size);
// keep our headers //keep our headers
this->sendHeaders(); this->sendHeaders();
serializeJson(_root, dest); serializeJson(_root, dest);
// send the last bits //send the last bits
dest.flush(); dest.flush();
// done with our chunked response too //done with our chunked response too
err = this->finishChunking(); err = this->finishChunking();
} }
// let the buffer go //let the buffer go
free(buffer); free(buffer);
return err; return err;
} }
#ifdef ARDUINOJSON_6_COMPATIBILITY #ifdef ARDUINOJSON_6_COMPATIBILITY
PsychicJsonHandler::PsychicJsonHandler(size_t maxJsonBufferSize) : _onRequest(NULL), PsychicJsonHandler::PsychicJsonHandler(size_t maxJsonBufferSize) :
_maxJsonBufferSize(maxJsonBufferSize){}; _onRequest(NULL),
_maxJsonBufferSize(maxJsonBufferSize)
{};
PsychicJsonHandler::PsychicJsonHandler(PsychicJsonRequestCallback onRequest, size_t maxJsonBufferSize) : _onRequest(onRequest), PsychicJsonHandler::PsychicJsonHandler(PsychicJsonRequestCallback onRequest, size_t maxJsonBufferSize) :
_maxJsonBufferSize(maxJsonBufferSize) _onRequest(onRequest),
{ _maxJsonBufferSize(maxJsonBufferSize)
} {}
#else #else
PsychicJsonHandler::PsychicJsonHandler() : _onRequest(NULL){}; PsychicJsonHandler::PsychicJsonHandler() :
_onRequest(NULL)
{};
PsychicJsonHandler::PsychicJsonHandler(PsychicJsonRequestCallback onRequest) : _onRequest(onRequest) PsychicJsonHandler::PsychicJsonHandler(PsychicJsonRequestCallback onRequest) :
{ _onRequest(onRequest)
} {}
#endif #endif
void PsychicJsonHandler::onRequest(PsychicJsonRequestCallback fn) { _onRequest = fn; } void PsychicJsonHandler::onRequest(PsychicJsonRequestCallback fn) { _onRequest = fn; }
esp_err_t PsychicJsonHandler::handleRequest(PsychicRequest *request) esp_err_t PsychicJsonHandler::handleRequest(PsychicRequest *request)
{ {
// process basic stuff //process basic stuff
PsychicWebHandler::handleRequest(request); PsychicWebHandler::handleRequest(request);
if (_onRequest) if (_onRequest)
{ {
#ifdef ARDUINOJSON_6_COMPATIBILITY #ifdef ARDUINOJSON_6_COMPATIBILITY
DynamicJsonDocument jsonBuffer(this->_maxJsonBufferSize); DynamicJsonDocument jsonBuffer(this->_maxJsonBufferSize);
DeserializationError error = deserializeJson(jsonBuffer, request->body()); DeserializationError error = deserializeJson(jsonBuffer, request->body());
if (error) if (error)
return request->reply(400); return request->reply(400);
JsonVariant json = jsonBuffer.as<JsonVariant>(); JsonVariant json = jsonBuffer.as<JsonVariant>();
#else #else
JsonDocument jsonBuffer; JsonDocument jsonBuffer;
DeserializationError error = deserializeJson(jsonBuffer, request->body()); DeserializationError error = deserializeJson(jsonBuffer, request->body());
if (error) if (error)
return request->reply(400); return request->reply(400);
JsonVariant json = jsonBuffer.as<JsonVariant>(); JsonVariant json = jsonBuffer.as<JsonVariant>();
#endif #endif
return _onRequest(request, json); return _onRequest(request, json);
} }
+122 -166
View File
@@ -2,17 +2,19 @@
#include "http_status.h" #include "http_status.h"
#include "PsychicHttpServer.h" #include "PsychicHttpServer.h"
PsychicRequest::PsychicRequest(PsychicHttpServer *server, httpd_req_t *req) : _server(server),
_req(req), PsychicRequest::PsychicRequest(PsychicHttpServer *server, httpd_req_t *req) :
_method(HTTP_GET), _server(server),
_query(""), _req(req),
_body(""), _method(HTTP_GET),
_tempObject(NULL) _query(""),
_body(""),
_tempObject(NULL)
{ {
// load up our client. //load up our client.
this->_client = server->getClient(req); this->_client = server->getClient(req);
// handle our session data //handle our session data
if (req->sess_ctx != NULL) if (req->sess_ctx != NULL)
this->_session = (SessionData *)req->sess_ctx; this->_session = (SessionData *)req->sess_ctx;
else else
@@ -21,22 +23,22 @@ PsychicRequest::PsychicRequest(PsychicHttpServer *server, httpd_req_t *req) : _s
req->sess_ctx = this->_session; req->sess_ctx = this->_session;
} }
// callback for freeing the session later //callback for freeing the session later
req->free_ctx = this->freeSession; req->free_ctx = this->freeSession;
// load up some data //load up some data
this->_uri = String(this->_req->uri); this->_uri = String(this->_req->uri);
} }
PsychicRequest::~PsychicRequest() PsychicRequest::~PsychicRequest()
{ {
// temorary user object //temorary user object
if (_tempObject != NULL) if (_tempObject != NULL)
free(_tempObject); free(_tempObject);
// our web parameters //our web parameters
for (auto *param : _params) for (auto *param : _params)
delete (param); delete(param);
_params.clear(); _params.clear();
} }
@@ -44,29 +46,26 @@ void PsychicRequest::freeSession(void *ctx)
{ {
if (ctx != NULL) if (ctx != NULL)
{ {
SessionData *session = (SessionData *)ctx; SessionData *session = (SessionData*)ctx;
delete session; delete session;
} }
} }
PsychicHttpServer *PsychicRequest::server() PsychicHttpServer * PsychicRequest::server() {
{
return _server; return _server;
} }
httpd_req_t *PsychicRequest::request() httpd_req_t * PsychicRequest::request() {
{
return _req; return _req;
} }
PsychicClient *PsychicRequest::client() PsychicClient * PsychicRequest::client() {
{
return _client; return _client;
} }
const String PsychicRequest::getFilename() const String PsychicRequest::getFilename()
{ {
// parse the content-disposition header //parse the content-disposition header
if (this->hasHeader("Content-Disposition")) if (this->hasHeader("Content-Disposition"))
{ {
ContentDisposition cd = this->getContentDisposition(); ContentDisposition cd = this->getContentDisposition();
@@ -74,19 +73,19 @@ const String PsychicRequest::getFilename()
return cd.filename; return cd.filename;
} }
// fall back to passed in query string //fall back to passed in query string
PsychicWebParameter *param = getParam("_filename"); PsychicWebParameter *param = getParam("_filename");
if (param != NULL) if (param != NULL)
return param->name(); return param->name();
// fall back to parsing it from url (useful for wildcard uploads) //fall back to parsing it from url (useful for wildcard uploads)
String uri = this->uri(); String uri = this->uri();
int filenameStart = uri.lastIndexOf('/') + 1; int filenameStart = uri.lastIndexOf('/') + 1;
String filename = uri.substring(filenameStart); String filename = uri.substring(filenameStart);
if (filename != "") if (filename != "")
return filename; return filename;
// finally, unknown. //finally, unknown.
ESP_LOGE(PH_TAG, "Did not get a valid filename from the upload."); ESP_LOGE(PH_TAG, "Did not get a valid filename from the upload.");
return "unknown.txt"; return "unknown.txt";
} }
@@ -110,15 +109,15 @@ const ContentDisposition PsychicRequest::getContentDisposition()
start = header.indexOf("filename="); start = header.indexOf("filename=");
if (start) if (start)
{ {
end = header.indexOf('"', start + 10); end = header.indexOf('"', start+10);
cd.filename = header.substring(start + 10, end - 1); cd.filename = header.substring(start+10, end-1);
} }
start = header.indexOf("name="); start = header.indexOf("name=");
if (start) if (start)
{ {
end = header.indexOf('"', start + 6); end = header.indexOf('"', start+6);
cd.name = header.substring(start + 6, end - 1); cd.name = header.substring(start+6, end-1);
} }
return cd; return cd;
@@ -133,22 +132,18 @@ esp_err_t PsychicRequest::loadBody()
size_t remaining = this->_req->content_len; size_t remaining = this->_req->content_len;
size_t actuallyReceived = 0; size_t actuallyReceived = 0;
char *buf = (char *)malloc(remaining + 1); char *buf = (char *)malloc(remaining + 1);
if (buf == NULL) if (buf == NULL) {
{
ESP_LOGE(PH_TAG, "Failed to allocate memory for body"); ESP_LOGE(PH_TAG, "Failed to allocate memory for body");
return ESP_FAIL; return ESP_FAIL;
} }
while (remaining > 0) while (remaining > 0) {
{
int received = httpd_req_recv(this->_req, buf + actuallyReceived, remaining); int received = httpd_req_recv(this->_req, buf + actuallyReceived, remaining);
if (received == HTTPD_SOCK_ERR_TIMEOUT) if (received == HTTPD_SOCK_ERR_TIMEOUT) {
{
continue; continue;
} }
else if (received == HTTPD_SOCK_ERR_FAIL) else if (received == HTTPD_SOCK_ERR_FAIL) {
{
ESP_LOGE(PH_TAG, "Failed to receive data."); ESP_LOGE(PH_TAG, "Failed to receive data.");
err = ESP_FAIL; err = ESP_FAIL;
break; break;
@@ -164,32 +159,27 @@ esp_err_t PsychicRequest::loadBody()
return err; return err;
} }
http_method PsychicRequest::method() http_method PsychicRequest::method() {
{
return (http_method)this->_req->method; return (http_method)this->_req->method;
} }
const String PsychicRequest::methodStr() const String PsychicRequest::methodStr() {
{
return String(http_method_str((http_method)this->_req->method)); return String(http_method_str((http_method)this->_req->method));
} }
const String PsychicRequest::path() const String PsychicRequest::path() {
{
int index = _uri.indexOf("?"); int index = _uri.indexOf("?");
if (index == -1) if(index == -1)
return _uri; return _uri;
else else
return _uri.substring(0, index); return _uri.substring(0, index);
} }
const String &PsychicRequest::uri() const String& PsychicRequest::uri() {
{
return this->_uri; return this->_uri;
} }
const String &PsychicRequest::query() const String& PsychicRequest::query() {
{
return this->_query; return this->_query;
} }
@@ -202,10 +192,10 @@ const String PsychicRequest::header(const char *name)
{ {
size_t header_len = httpd_req_get_hdr_value_len(this->_req, name); size_t header_len = httpd_req_get_hdr_value_len(this->_req, name);
// if we've got one, allocated it and load it //if we've got one, allocated it and load it
if (header_len) if (header_len)
{ {
char header[header_len + 1]; char header[header_len+1];
httpd_req_get_hdr_value_str(this->_req, name, header, sizeof(header)); httpd_req_get_hdr_value_str(this->_req, name, header, sizeof(header));
return String(header); return String(header);
} }
@@ -218,29 +208,26 @@ bool PsychicRequest::hasHeader(const char *name)
return httpd_req_get_hdr_value_len(this->_req, name) > 0; return httpd_req_get_hdr_value_len(this->_req, name) > 0;
} }
const String PsychicRequest::host() const String PsychicRequest::host() {
{
return this->header("Host"); return this->header("Host");
} }
const String PsychicRequest::contentType() const String PsychicRequest::contentType() {
{
return header("Content-Type"); return header("Content-Type");
} }
size_t PsychicRequest::contentLength() size_t PsychicRequest::contentLength() {
{
return this->_req->content_len; return this->_req->content_len;
} }
const String &PsychicRequest::body() const String& PsychicRequest::body()
{ {
return this->_body; return this->_body;
} }
bool PsychicRequest::isMultipart() bool PsychicRequest::isMultipart()
{ {
const String &type = this->contentType(); const String& type = this->contentType();
return (this->contentType().indexOf("multipart/form-data") >= 0); return (this->contentType().indexOf("multipart/form-data") >= 0);
} }
@@ -260,7 +247,7 @@ bool PsychicRequest::hasCookie(const char *key)
size_t cookieSize = MAX_COOKIE_SIZE; size_t cookieSize = MAX_COOKIE_SIZE;
esp_err_t err = httpd_req_get_cookie_val(this->_req, key, cookie, &cookieSize); esp_err_t err = httpd_req_get_cookie_val(this->_req, key, cookie, &cookieSize);
// did we get anything? //did we get anything?
if (err == ESP_OK) if (err == ESP_OK)
return true; return true;
else if (err == ESP_ERR_HTTPD_RESULT_TRUNC) else if (err == ESP_ERR_HTTPD_RESULT_TRUNC)
@@ -275,7 +262,7 @@ const String PsychicRequest::getCookie(const char *key)
size_t cookieSize = MAX_COOKIE_SIZE; size_t cookieSize = MAX_COOKIE_SIZE;
esp_err_t err = httpd_req_get_cookie_val(this->_req, key, cookie, &cookieSize); esp_err_t err = httpd_req_get_cookie_val(this->_req, key, cookie, &cookieSize);
// did we get anything? //did we get anything?
if (err == ESP_OK) if (err == ESP_OK)
return String(cookie); return String(cookie);
else else
@@ -284,54 +271,49 @@ const String PsychicRequest::getCookie(const char *key)
void PsychicRequest::loadParams() void PsychicRequest::loadParams()
{ {
// did we get a query string? //did we get a query string?
size_t query_len = httpd_req_get_url_query_len(_req); size_t query_len = httpd_req_get_url_query_len(_req);
if (query_len) if (query_len)
{ {
char query[query_len + 1]; char query[query_len+1];
httpd_req_get_url_query_str(_req, query, sizeof(query)); httpd_req_get_url_query_str(_req, query, sizeof(query));
_query = "";
_query.concat(query); _query.concat(query);
// parse them. //parse them.
_addParams(_query); _addParams(_query, false);
} }
// did we get form data as body? //did we get form data as body?
if (this->method() == HTTP_POST && this->contentType() == "application/x-www-form-urlencoded") if (this->method() == HTTP_POST && this->contentType().startsWith("application/x-www-form-urlencoded"))
{ {
_addParams(_body); _addParams(_body, true);
} }
} }
void PsychicRequest::_addParams(const String &params) void PsychicRequest::_addParams(const String& params, bool post){
{
size_t start = 0; size_t start = 0;
while (start < params.length()) while (start < params.length()){
{
int end = params.indexOf('&', start); int end = params.indexOf('&', start);
if (end < 0) if (end < 0) end = params.length();
end = params.length();
int equal = params.indexOf('=', start); int equal = params.indexOf('=', start);
if (equal < 0 || equal > end) if (equal < 0 || equal > end) equal = end;
equal = end;
String name = params.substring(start, equal); String name = params.substring(start, equal);
String value = equal + 1 < end ? params.substring(equal + 1, end) : String(); String value = equal + 1 < end ? params.substring(equal + 1, end) : String();
addParam(name, value); addParam(name, value, true, post);
start = end + 1; start = end + 1;
} }
} }
PsychicWebParameter *PsychicRequest::addParam(const String &name, const String &value, bool decode) PsychicWebParameter * PsychicRequest::addParam(const String &name, const String &value, bool decode, bool post)
{ {
if (decode) if (decode)
return addParam(new PsychicWebParameter(urlDecode(name.c_str()), urlDecode(value.c_str()))); return addParam(new PsychicWebParameter(urlDecode(name.c_str()), urlDecode(value.c_str()), post));
else else
return addParam(new PsychicWebParameter(name, value)); return addParam(new PsychicWebParameter(name, value, post));
} }
PsychicWebParameter *PsychicRequest::addParam(PsychicWebParameter *param) PsychicWebParameter * PsychicRequest::addParam(PsychicWebParameter *param) {
{ // ESP_LOGD(PH_TAG, "Adding param: '%s' = '%s'", param->name().c_str(), param->value().c_str());
_params.push_back(param); _params.push_back(param);
return param; return param;
} }
@@ -341,7 +323,7 @@ bool PsychicRequest::hasParam(const char *key)
return getParam(key) != NULL; return getParam(key) != NULL;
} }
PsychicWebParameter *PsychicRequest::getParam(const char *key) PsychicWebParameter * PsychicRequest::getParam(const char *key)
{ {
for (auto *param : _params) for (auto *param : _params)
if (param->name().equals(key)) if (param->name().equals(key))
@@ -350,12 +332,12 @@ PsychicWebParameter *PsychicRequest::getParam(const char *key)
return NULL; return NULL;
} }
bool PsychicRequest::hasSessionKey(const String &key) bool PsychicRequest::hasSessionKey(const String& key)
{ {
return this->_session->find(key) != this->_session->end(); return this->_session->find(key) != this->_session->end();
} }
const String PsychicRequest::getSessionKey(const String &key) const String PsychicRequest::getSessionKey(const String& key)
{ {
auto it = this->_session->find(key); auto it = this->_session->find(key);
if (it != this->_session->end()) if (it != this->_session->end())
@@ -364,13 +346,12 @@ const String PsychicRequest::getSessionKey(const String &key)
return ""; return "";
} }
void PsychicRequest::setSessionKey(const String &key, const String &value) void PsychicRequest::setSessionKey(const String& key, const String& value)
{ {
this->_session->insert(std::pair<String, String>(key, value)); this->_session->insert(std::pair<String, String>(key, value));
} }
static const String md5str(const String &in) static const String md5str(const String &in){
{
MD5Builder md5 = MD5Builder(); MD5Builder md5 = MD5Builder();
md5.begin(); md5.begin();
md5.add(in); md5.add(in);
@@ -378,32 +359,28 @@ static const String md5str(const String &in)
return md5.toString(); return md5.toString();
} }
bool PsychicRequest::authenticate(const char *username, const char *password) bool PsychicRequest::authenticate(const char * username, const char * password)
{ {
if (hasHeader("Authorization")) if(hasHeader("Authorization"))
{ {
String authReq = header("Authorization"); String authReq = header("Authorization");
if (authReq.startsWith("Basic")) if(authReq.startsWith("Basic")){
{
authReq = authReq.substring(6); authReq = authReq.substring(6);
authReq.trim(); authReq.trim();
char toencodeLen = strlen(username) + strlen(password) + 1; char toencodeLen = strlen(username)+strlen(password)+1;
char *toencode = new char[toencodeLen + 1]; char *toencode = new char[toencodeLen + 1];
if (toencode == NULL) if(toencode == NULL){
{
authReq = ""; authReq = "";
return false; return false;
} }
char *encoded = new char[base64_encode_expected_len(toencodeLen) + 1]; char *encoded = new char[base64_encode_expected_len(toencodeLen)+1];
if (encoded == NULL) if(encoded == NULL){
{
authReq = ""; authReq = "";
delete[] toencode; delete[] toencode;
return false; return false;
} }
sprintf(toencode, "%s:%s", username, password); sprintf(toencode, "%s:%s", username, password);
if (base64_encode_chars(toencode, toencodeLen, encoded) > 0 && authReq.equalsConstantTime(encoded)) if(base64_encode_chars(toencode, toencodeLen, encoded) > 0 && authReq.equalsConstantTime(encoded)) {
{
authReq = ""; authReq = "";
delete[] toencode; delete[] toencode;
delete[] encoded; delete[] encoded;
@@ -412,81 +389,63 @@ bool PsychicRequest::authenticate(const char *username, const char *password)
delete[] toencode; delete[] toencode;
delete[] encoded; delete[] encoded;
} }
else if (authReq.startsWith(F("Digest"))) else if(authReq.startsWith(F("Digest")))
{ {
authReq = authReq.substring(7); authReq = authReq.substring(7);
String _username = _extractParam(authReq, F("username=\""), '\"'); String _username = _extractParam(authReq,F("username=\""),'\"');
if (!_username.length() || _username != String(username)) if(!_username.length() || _username != String(username)) {
{
authReq = ""; authReq = "";
return false; return false;
} }
// extracting required parameters for RFC 2069 simpler Digest // extracting required parameters for RFC 2069 simpler Digest
String _realm = _extractParam(authReq, F("realm=\""), '\"'); String _realm = _extractParam(authReq, F("realm=\""),'\"');
String _nonce = _extractParam(authReq, F("nonce=\""), '\"'); String _nonce = _extractParam(authReq, F("nonce=\""),'\"');
String _uri = _extractParam(authReq, F("uri=\""), '\"'); String _uri = _extractParam(authReq, F("uri=\""),'\"');
String _resp = _extractParam(authReq, F("response=\""), '\"'); String _resp = _extractParam(authReq, F("response=\""),'\"');
String _opaque = _extractParam(authReq, F("opaque=\""), '\"'); String _opaque = _extractParam(authReq, F("opaque=\""),'\"');
if ((!_realm.length()) || (!_nonce.length()) || (!_uri.length()) || (!_resp.length()) || (!_opaque.length())) if((!_realm.length()) || (!_nonce.length()) || (!_uri.length()) || (!_resp.length()) || (!_opaque.length())) {
{
authReq = ""; authReq = "";
return false; return false;
} }
if ((_opaque != this->getSessionKey("opaque")) || (_nonce != this->getSessionKey("nonce")) || (_realm != this->getSessionKey("realm"))) if((_opaque != this->getSessionKey("opaque")) || (_nonce != this->getSessionKey("nonce")) || (_realm != this->getSessionKey("realm")))
{ {
// DUMP(_opaque);
// DUMP(this->getSessionKey("opaque"));
// DUMP(_nonce);
// DUMP(this->getSessionKey("nonce"));
// DUMP(_realm);
// DUMP(this->getSessionKey("realm"));
authReq = ""; authReq = "";
return false; return false;
} }
// parameters for the RFC 2617 newer Digest // parameters for the RFC 2617 newer Digest
String _nc, _cnonce; String _nc,_cnonce;
if (authReq.indexOf("qop=auth") != -1 || authReq.indexOf("qop=\"auth\"") != -1) if(authReq.indexOf("qop=auth") != -1 || authReq.indexOf("qop=\"auth\"") != -1) {
{
_nc = _extractParam(authReq, F("nc="), ','); _nc = _extractParam(authReq, F("nc="), ',');
_cnonce = _extractParam(authReq, F("cnonce=\""), '\"'); _cnonce = _extractParam(authReq, F("cnonce=\""),'\"');
} }
String _H1 = md5str(String(username) + ':' + _realm + ':' + String(password)); String _H1 = md5str(String(username) + ':' + _realm + ':' + String(password));
ESP_LOGD(PH_TAG, "Hash of user:realm:pass=%s", _H1.c_str()); //ESP_LOGD(PH_TAG, "Hash of user:realm:pass=%s", _H1.c_str());
String _H2 = ""; String _H2 = "";
if (_method == HTTP_GET) if(_method == HTTP_GET){
{ _H2 = md5str(String(F("GET:")) + _uri);
_H2 = md5str(String(F("GET:")) + _uri); }else if(_method == HTTP_POST){
_H2 = md5str(String(F("POST:")) + _uri);
}else if(_method == HTTP_PUT){
_H2 = md5str(String(F("PUT:")) + _uri);
}else if(_method == HTTP_DELETE){
_H2 = md5str(String(F("DELETE:")) + _uri);
}else{
_H2 = md5str(String(F("GET:")) + _uri);
} }
else if (_method == HTTP_POST) //ESP_LOGD(PH_TAG, "Hash of GET:uri=%s", _H2.c_str());
{
_H2 = md5str(String(F("POST:")) + _uri);
}
else if (_method == HTTP_PUT)
{
_H2 = md5str(String(F("PUT:")) + _uri);
}
else if (_method == HTTP_DELETE)
{
_H2 = md5str(String(F("DELETE:")) + _uri);
}
else
{
_H2 = md5str(String(F("GET:")) + _uri);
}
ESP_LOGD(PH_TAG, "Hash of GET:uri=%s", _H2.c_str());
String _responsecheck = ""; String _responsecheck = "";
if (authReq.indexOf("qop=auth") != -1 || authReq.indexOf("qop=\"auth\"") != -1) if(authReq.indexOf("qop=auth") != -1 || authReq.indexOf("qop=\"auth\"") != -1) {
{ _responsecheck = md5str(_H1 + ':' + _nonce + ':' + _nc + ':' + _cnonce + F(":auth:") + _H2);
_responsecheck = md5str(_H1 + ':' + _nonce + ':' + _nc + ':' + _cnonce + F(":auth:") + _H2); } else {
_responsecheck = md5str(_H1 + ':' + _nonce + ':' + _H2);
} }
else
{ //ESP_LOGD(PH_TAG, "The Proper response=%s", _responsecheck.c_str());
_responsecheck = md5str(_H1 + ':' + _nonce + ':' + _H2); if(_resp == _responsecheck){
}
ESP_LOGD(PH_TAG, "The Proper response=%s", _responsecheck.c_str());
if (_resp == _responsecheck)
{
authReq = ""; authReq = "";
return true; return true;
} }
@@ -496,29 +455,28 @@ bool PsychicRequest::authenticate(const char *username, const char *password)
return false; return false;
} }
const String PsychicRequest::_extractParam(const String &authReq, const String &param, const char delimit) const String PsychicRequest::_extractParam(const String& authReq, const String& param, const char delimit)
{ {
int _begin = authReq.indexOf(param); int _begin = authReq.indexOf(param);
if (_begin == -1) if (_begin == -1)
return ""; return "";
return authReq.substring(_begin + param.length(), authReq.indexOf(delimit, _begin + param.length())); return authReq.substring(_begin+param.length(),authReq.indexOf(delimit,_begin+param.length()));
} }
const String PsychicRequest::_getRandomHexString() const String PsychicRequest::_getRandomHexString()
{ {
char buffer[33]; // buffer to hold 32 Hex Digit + /0 char buffer[33]; // buffer to hold 32 Hex Digit + /0
int i; int i;
for (i = 0; i < 4; i++) for(i = 0; i < 4; i++) {
{ sprintf (buffer + (i*8), "%08lx", (unsigned long int)esp_random());
sprintf(buffer + (i * 8), "%08lx", esp_random());
} }
return String(buffer); return String(buffer);
} }
esp_err_t PsychicRequest::requestAuthentication(HTTPAuthMethod mode, const char *realm, const char *authFailMsg) esp_err_t PsychicRequest::requestAuthentication(HTTPAuthMethod mode, const char* realm, const char* authFailMsg)
{ {
// what is thy realm, sire? //what is thy realm, sire?
if (!strcmp(realm, "")) if(!strcmp(realm, ""))
this->setSessionKey("realm", "Login Required"); this->setSessionKey("realm", "Login Required");
else else
this->setSessionKey("realm", realm); this->setSessionKey("realm", realm);
@@ -526,15 +484,15 @@ esp_err_t PsychicRequest::requestAuthentication(HTTPAuthMethod mode, const char
PsychicResponse response(this); PsychicResponse response(this);
String authStr; String authStr;
// what kind of auth? //what kind of auth?
if (mode == BASIC_AUTH) if(mode == BASIC_AUTH)
{ {
authStr = "Basic realm=\"" + this->getSessionKey("realm") + "\""; authStr = "Basic realm=\"" + this->getSessionKey("realm") + "\"";
response.addHeader("WWW-Authenticate", authStr.c_str()); response.addHeader("WWW-Authenticate", authStr.c_str());
} }
else else
{ {
// only make new ones if we havent sent them yet //only make new ones if we havent sent them yet
if (this->getSessionKey("nonce").isEmpty()) if (this->getSessionKey("nonce").isEmpty())
this->setSessionKey("nonce", _getRandomHexString()); this->setSessionKey("nonce", _getRandomHexString());
if (this->getSessionKey("opaque").isEmpty()) if (this->getSessionKey("opaque").isEmpty())
@@ -544,8 +502,6 @@ esp_err_t PsychicRequest::requestAuthentication(HTTPAuthMethod mode, const char
response.addHeader("WWW-Authenticate", authStr.c_str()); response.addHeader("WWW-Authenticate", authStr.c_str());
} }
// DUMP(authStr);
response.setCode(401); response.setCode(401);
response.setContentType("text/html"); response.setContentType("text/html");
response.setContent(authStr.c_str()); response.setContent(authStr.c_str());
+2 -2
View File
@@ -33,7 +33,7 @@ class PsychicRequest {
std::list<PsychicWebParameter*> _params; std::list<PsychicWebParameter*> _params;
void _addParams(const String& params); void _addParams(const String& params, bool post);
void _parseGETParams(); void _parseGETParams();
void _parsePOSTParams(); void _parsePOSTParams();
@@ -80,7 +80,7 @@ class PsychicRequest {
void loadParams(); void loadParams();
PsychicWebParameter * addParam(PsychicWebParameter *param); PsychicWebParameter * addParam(PsychicWebParameter *param);
PsychicWebParameter * addParam(const String &name, const String &value, bool decode = true); PsychicWebParameter * addParam(const String &name, const String &value, bool decode = true, bool post = false);
bool hasParam(const char *key); bool hasParam(const char *key);
PsychicWebParameter * getParam(const char *name); PsychicWebParameter * getParam(const char *name);
+12 -5
View File
@@ -13,7 +13,7 @@ PsychicResponse::PsychicResponse(PsychicRequest *request) :
PsychicResponse::~PsychicResponse() PsychicResponse::~PsychicResponse()
{ {
//clean up our header variables. we have to do this since httpd_resp_send doesn't store copies //clean up our header variables. we have to do this on desctruct since httpd_resp_send doesn't store copies
for (HTTPHeader header : _headers) for (HTTPHeader header : _headers)
{ {
free(header.field); free(header.field);
@@ -24,7 +24,7 @@ PsychicResponse::~PsychicResponse()
void PsychicResponse::addHeader(const char *field, const char *value) void PsychicResponse::addHeader(const char *field, const char *value)
{ {
//these get freed during send() //these get freed after send by the destructor
HTTPHeader header; HTTPHeader header;
header.field =(char *)malloc(strlen(field)+1); header.field =(char *)malloc(strlen(field)+1);
header.value = (char *)malloc(strlen(value)+1); header.value = (char *)malloc(strlen(value)+1);
@@ -65,9 +65,6 @@ void PsychicResponse::setCookie(const char *name, const char *value, unsigned lo
addHeader("Set-Cookie", output.c_str()); addHeader("Set-Cookie", output.c_str());
} }
// time_t now = time(nullptr);
// // Set the cookie with the "expires" attribute
void PsychicResponse::setCode(int code) void PsychicResponse::setCode(int code)
{ {
_code = code; _code = code;
@@ -128,6 +125,16 @@ void PsychicResponse::sendHeaders()
//now do our individual headers //now do our individual headers
for (HTTPHeader header : _headers) for (HTTPHeader header : _headers)
httpd_resp_set_hdr(this->_request->request(), header.field, header.value); httpd_resp_set_hdr(this->_request->request(), header.field, header.value);
// DO NOT RELEASE HEADERS HERE... released in the PsychicResponse destructor after they have been sent.
// httpd_resp_set_hdr just passes on the pointer, but its needed after this call.
// clean up our header variables after send
// for (HTTPHeader header : _headers)
// {
// free(header.field);
// free(header.value);
// }
// _headers.clear();
} }
esp_err_t PsychicResponse::sendChunk(uint8_t *chunk, size_t chunksize) esp_err_t PsychicResponse::sendChunk(uint8_t *chunk, size_t chunksize)
@@ -141,24 +141,16 @@ esp_err_t PsychicStaticFileHandler::handleRequest(PsychicRequest *request)
{ {
if (_file == true) if (_file == true)
{ {
DUMP(_filename);
//is it not modified? //is it not modified?
String etag = String(_file.size()); String etag = String(_file.size());
if (_last_modified.length() && _last_modified == request->header("If-Modified-Since")) if (_last_modified.length() && _last_modified == request->header("If-Modified-Since"))
{ {
DUMP("Last Modified Hit");
TRACE();
_file.close(); _file.close();
request->reply(304); // Not modified request->reply(304); // Not modified
} }
//does our Etag match? //does our Etag match?
else if (_cache_control.length() && request->hasHeader("If-None-Match") && request->header("If-None-Match").equals(etag)) else if (_cache_control.length() && request->hasHeader("If-None-Match") && request->header("If-None-Match").equals(etag))
{ {
DUMP("Etag Hit");
DUMP(etag);
DUMP(_cache_control);
_file.close(); _file.close();
PsychicResponse response(request); PsychicResponse response(request);
@@ -170,10 +162,6 @@ esp_err_t PsychicStaticFileHandler::handleRequest(PsychicRequest *request)
//nope, send them the full file. //nope, send them the full file.
else else
{ {
DUMP("No cache hit");
DUMP(_last_modified);
DUMP(_cache_control);
PsychicFileResponse response(request, _fs, _filename); PsychicFileResponse response(request, _fs, _filename);
if (_last_modified.length()) if (_last_modified.length())
@@ -16,7 +16,7 @@ PsychicStreamResponse::PsychicStreamResponse(PsychicRequest *request, const Stri
setContentType(contentType.c_str()); setContentType(contentType.c_str());
char buf[26+name.length()]; char buf[26+name.length()];
snprintf(buf, sizeof (buf), "attachment; filename=\"%s\"", name); snprintf(buf, sizeof (buf), "attachment; filename=\"%s\"", name.c_str());
addHeader("Content-Disposition", buf); addHeader("Content-Disposition", buf);
} }
@@ -29,7 +29,6 @@ esp_err_t PsychicUploadHandler::handleRequest(PsychicRequest *request)
//save it for later (multipart) //save it for later (multipart)
_request = request; _request = request;
_parsedLength = 0; _parsedLength = 0;
/* File cannot be larger than a limit */ /* File cannot be larger than a limit */
if (request->contentLength() > request->server()->maxUploadSize) if (request->contentLength() > request->server()->maxUploadSize)
{ {
@@ -37,7 +36,7 @@ esp_err_t PsychicUploadHandler::handleRequest(PsychicRequest *request)
/* Respond with 400 Bad Request */ /* Respond with 400 Bad Request */
char error[50]; char error[50];
sprintf(error, "File size must be less than %u bytes!", request->server()->maxUploadSize); sprintf(error, "File size must be less than %lu bytes!", request->server()->maxUploadSize);
httpd_resp_send_err(request->request(), HTTPD_400_BAD_REQUEST, error); httpd_resp_send_err(request->request(), HTTPD_400_BAD_REQUEST, error);
/* Return failure to close underlying connection else the incoming file content will keep the socket busy */ /* Return failure to close underlying connection else the incoming file content will keep the socket busy */
@@ -94,7 +93,7 @@ esp_err_t PsychicUploadHandler::_basicUploadHandler(PsychicRequest *request)
httpd_sess_update_lru_counter(request->server()->server, request->client()->socket()); httpd_sess_update_lru_counter(request->server()->server, request->client()->socket());
#endif #endif
ESP_LOGI(PH_TAG, "Remaining size : %d", remaining); //ESP_LOGD(PH_TAG, "Remaining size : %d", remaining);
/* Receive the file part by part into a buffer */ /* Receive the file part by part into a buffer */
if ((received = httpd_req_recv(request->request(), buf, min(remaining, FILE_CHUNK_SIZE))) <= 0) if ((received = httpd_req_recv(request->request(), buf, min(remaining, FILE_CHUNK_SIZE))) <= 0)
@@ -162,7 +161,7 @@ esp_err_t PsychicUploadHandler::_multipartUploadHandler(PsychicRequest *request)
httpd_sess_update_lru_counter(request->server()->server, request->client()->socket()); httpd_sess_update_lru_counter(request->server()->server, request->client()->socket());
#endif #endif
ESP_LOGI(PH_TAG, "Remaining size : %d", remaining); //ESP_LOGD(PH_TAG, "Remaining size : %d", remaining);
/* Receive the file part by part into a buffer */ /* Receive the file part by part into a buffer */
if ((received = httpd_req_recv(request->request(), buf, min(remaining, FILE_CHUNK_SIZE))) <= 0) if ((received = httpd_req_recv(request->request(), buf, min(remaining, FILE_CHUNK_SIZE))) <= 0)
@@ -26,7 +26,7 @@ esp_err_t PsychicWebHandler::handleRequest(PsychicRequest *request)
/* Respond with 400 Bad Request */ /* Respond with 400 Bad Request */
char error[60]; char error[60];
sprintf(error, "Request body must be less than %u bytes!", request->server()->maxRequestBodySize); sprintf(error, "Request body must be less than %lu bytes!", request->server()->maxRequestBodySize);
httpd_resp_send_err(request->request(), HTTPD_400_BAD_REQUEST, error); httpd_resp_send_err(request->request(), HTTPD_400_BAD_REQUEST, error);
/* Return failure to close underlying connection else the incoming file content will keep the socket busy */ /* Return failure to close underlying connection else the incoming file content will keep the socket busy */
+50 -65
View File
@@ -4,8 +4,9 @@
/* PsychicWebSocketRequest */ /* PsychicWebSocketRequest */
/*************************************/ /*************************************/
PsychicWebSocketRequest::PsychicWebSocketRequest(PsychicRequest *req) : PsychicRequest(req->server(), req->request()), PsychicWebSocketRequest::PsychicWebSocketRequest(PsychicRequest *req) :
_client(req->client()) PsychicRequest(req->server(), req->request()),
_client(req->client())
{ {
} }
@@ -13,12 +14,11 @@ PsychicWebSocketRequest::~PsychicWebSocketRequest()
{ {
} }
PsychicWebSocketClient *PsychicWebSocketRequest::client() PsychicWebSocketClient * PsychicWebSocketRequest::client() {
{
return &_client; return &_client;
} }
esp_err_t PsychicWebSocketRequest::reply(httpd_ws_frame_t *ws_pkt) esp_err_t PsychicWebSocketRequest::reply(httpd_ws_frame_t * ws_pkt)
{ {
return httpd_ws_send_frame(this->_req, ws_pkt); return httpd_ws_send_frame(this->_req, ws_pkt);
} }
@@ -28,7 +28,7 @@ esp_err_t PsychicWebSocketRequest::reply(httpd_ws_type_t op, const void *data, s
httpd_ws_frame_t ws_pkt; httpd_ws_frame_t ws_pkt;
memset(&ws_pkt, 0, sizeof(httpd_ws_frame_t)); memset(&ws_pkt, 0, sizeof(httpd_ws_frame_t));
ws_pkt.payload = (uint8_t *)data; ws_pkt.payload = (uint8_t*)data;
ws_pkt.len = len; ws_pkt.len = len;
ws_pkt.type = op; ws_pkt.type = op;
@@ -45,15 +45,14 @@ esp_err_t PsychicWebSocketRequest::reply(const char *buf)
/*************************************/ /*************************************/
PsychicWebSocketClient::PsychicWebSocketClient(PsychicClient *client) PsychicWebSocketClient::PsychicWebSocketClient(PsychicClient *client)
: PsychicClient(client->server(), client->socket()) : PsychicClient(client->server(), client->socket())
{ {
} }
PsychicWebSocketClient::~PsychicWebSocketClient() PsychicWebSocketClient::~PsychicWebSocketClient() {
{
} }
esp_err_t PsychicWebSocketClient::sendMessage(httpd_ws_frame_t *ws_pkt) esp_err_t PsychicWebSocketClient::sendMessage(httpd_ws_frame_t * ws_pkt)
{ {
return httpd_ws_send_frame_async(this->server(), this->socket(), ws_pkt); return httpd_ws_send_frame_async(this->server(), this->socket(), ws_pkt);
} }
@@ -63,7 +62,7 @@ esp_err_t PsychicWebSocketClient::sendMessage(httpd_ws_type_t op, const void *da
httpd_ws_frame_t ws_pkt; httpd_ws_frame_t ws_pkt;
memset(&ws_pkt, 0, sizeof(httpd_ws_frame_t)); memset(&ws_pkt, 0, sizeof(httpd_ws_frame_t));
ws_pkt.payload = (uint8_t *)data; ws_pkt.payload = (uint8_t*)data;
ws_pkt.len = len; ws_pkt.len = len;
ws_pkt.type = op; ws_pkt.type = op;
@@ -75,18 +74,18 @@ esp_err_t PsychicWebSocketClient::sendMessage(const char *buf)
return this->sendMessage(HTTPD_WS_TYPE_TEXT, buf, strlen(buf)); return this->sendMessage(HTTPD_WS_TYPE_TEXT, buf, strlen(buf));
} }
PsychicWebSocketHandler::PsychicWebSocketHandler() : PsychicHandler(), PsychicWebSocketHandler::PsychicWebSocketHandler() :
_onOpen(NULL), PsychicHandler(),
_onFrame(NULL), _onOpen(NULL),
_onClose(NULL) _onFrame(NULL),
{ _onClose(NULL)
{
}
PsychicWebSocketHandler::~PsychicWebSocketHandler() {
} }
PsychicWebSocketHandler::~PsychicWebSocketHandler() PsychicWebSocketClient * PsychicWebSocketHandler::getClient(int socket)
{
}
PsychicWebSocketClient *PsychicWebSocketHandler::getClient(int socket)
{ {
PsychicClient *client = PsychicHandler::getClient(socket); PsychicClient *client = PsychicHandler::getClient(socket);
if (client == NULL) if (client == NULL)
@@ -94,37 +93,31 @@ PsychicWebSocketClient *PsychicWebSocketHandler::getClient(int socket)
if (client->_friend == NULL) if (client->_friend == NULL)
{ {
DUMP(socket);
return NULL; return NULL;
} }
return (PsychicWebSocketClient *)client->_friend; return (PsychicWebSocketClient *)client->_friend;
} }
PsychicWebSocketClient *PsychicWebSocketHandler::getClient(PsychicClient *client) PsychicWebSocketClient * PsychicWebSocketHandler::getClient(PsychicClient *client) {
{
return getClient(client->socket()); return getClient(client->socket());
} }
void PsychicWebSocketHandler::addClient(PsychicClient *client) void PsychicWebSocketHandler::addClient(PsychicClient *client) {
{
client->_friend = new PsychicWebSocketClient(client); client->_friend = new PsychicWebSocketClient(client);
PsychicHandler::addClient(client); PsychicHandler::addClient(client);
} }
void PsychicWebSocketHandler::removeClient(PsychicClient *client) void PsychicWebSocketHandler::removeClient(PsychicClient *client) {
{
PsychicHandler::removeClient(client); PsychicHandler::removeClient(client);
delete (PsychicWebSocketClient *)client->_friend; delete (PsychicWebSocketClient*)client->_friend;
client->_friend = NULL; client->_friend = NULL;
} }
void PsychicWebSocketHandler::openCallback(PsychicClient *client) void PsychicWebSocketHandler::openCallback(PsychicClient *client) {
{
PsychicWebSocketClient *buddy = getClient(client); PsychicWebSocketClient *buddy = getClient(client);
if (buddy == NULL) if (buddy == NULL)
{ {
TRACE();
return; return;
} }
@@ -132,12 +125,10 @@ void PsychicWebSocketHandler::openCallback(PsychicClient *client)
_onOpen(getClient(buddy)); _onOpen(getClient(buddy));
} }
void PsychicWebSocketHandler::closeCallback(PsychicClient *client) void PsychicWebSocketHandler::closeCallback(PsychicClient *client) {
{
PsychicWebSocketClient *buddy = getClient(client); PsychicWebSocketClient *buddy = getClient(client);
if (buddy == NULL) if (buddy == NULL)
{ {
TRACE();
return; return;
} }
@@ -149,7 +140,7 @@ bool PsychicWebSocketHandler::isWebSocket() { return true; }
esp_err_t PsychicWebSocketHandler::handleRequest(PsychicRequest *request) esp_err_t PsychicWebSocketHandler::handleRequest(PsychicRequest *request)
{ {
// lookup our client //lookup our client
PsychicClient *client = checkForNewClient(request->client()); PsychicClient *client = checkForNewClient(request->client());
// beginning of the ws URI handler and our onConnect hook // beginning of the ws URI handler and our onConnect hook
@@ -161,10 +152,10 @@ esp_err_t PsychicWebSocketHandler::handleRequest(PsychicRequest *request)
return ESP_OK; return ESP_OK;
} }
// prep our request //prep our request
PsychicWebSocketRequest wsRequest(request); PsychicWebSocketRequest wsRequest(request);
// init our memory for storing the packet //init our memory for storing the packet
httpd_ws_frame_t ws_pkt; httpd_ws_frame_t ws_pkt;
memset(&ws_pkt, 0, sizeof(httpd_ws_frame_t)); memset(&ws_pkt, 0, sizeof(httpd_ws_frame_t));
ws_pkt.type = HTTPD_WS_TYPE_TEXT; ws_pkt.type = HTTPD_WS_TYPE_TEXT;
@@ -172,33 +163,29 @@ esp_err_t PsychicWebSocketHandler::handleRequest(PsychicRequest *request)
/* Set max_len = 0 to get the frame len */ /* Set max_len = 0 to get the frame len */
esp_err_t ret = httpd_ws_recv_frame(wsRequest.request(), &ws_pkt, 0); esp_err_t ret = httpd_ws_recv_frame(wsRequest.request(), &ws_pkt, 0);
if (ret != ESP_OK) if (ret != ESP_OK) {
{
ESP_LOGE(PH_TAG, "httpd_ws_recv_frame failed to get frame len with %s", esp_err_to_name(ret)); ESP_LOGE(PH_TAG, "httpd_ws_recv_frame failed to get frame len with %s", esp_err_to_name(ret));
return ret; return ret;
} }
// okay, now try to load the packet //okay, now try to load the packet
ESP_LOGV(PH_TAG, "frame len is %d", ws_pkt.len); //ESP_LOGD(PH_TAG, "frame len is %d", ws_pkt.len);
if (ws_pkt.len) if (ws_pkt.len) {
{
/* ws_pkt.len + 1 is for NULL termination as we are expecting a string */ /* ws_pkt.len + 1 is for NULL termination as we are expecting a string */
buf = (uint8_t *)calloc(1, ws_pkt.len + 1); buf = (uint8_t*) calloc(1, ws_pkt.len + 1);
if (buf == NULL) if (buf == NULL) {
{
ESP_LOGE(PH_TAG, "Failed to calloc memory for buf"); ESP_LOGE(PH_TAG, "Failed to calloc memory for buf");
return ESP_ERR_NO_MEM; return ESP_ERR_NO_MEM;
} }
ws_pkt.payload = buf; ws_pkt.payload = buf;
/* Set max_len = ws_pkt.len to get the frame payload */ /* Set max_len = ws_pkt.len to get the frame payload */
ret = httpd_ws_recv_frame(wsRequest.request(), &ws_pkt, ws_pkt.len); ret = httpd_ws_recv_frame(wsRequest.request(), &ws_pkt, ws_pkt.len);
if (ret != ESP_OK) if (ret != ESP_OK) {
{
ESP_LOGE(PH_TAG, "httpd_ws_recv_frame failed with %s", esp_err_to_name(ret)); ESP_LOGE(PH_TAG, "httpd_ws_recv_frame failed with %s", esp_err_to_name(ret));
free(buf); free(buf);
return ret; return ret;
} }
ESP_LOGV(PH_TAG, "Got packet with message: %s", ws_pkt.payload); //ESP_LOGD(PH_TAG, "Got packet with message: %s", ws_pkt.payload);
} }
// Text messages are our payload. // Text messages are our payload.
@@ -208,49 +195,47 @@ esp_err_t PsychicWebSocketHandler::handleRequest(PsychicRequest *request)
ret = this->_onFrame(&wsRequest, &ws_pkt); ret = this->_onFrame(&wsRequest, &ws_pkt);
} }
// logging housekeeping //logging housekeeping
if (ret != ESP_OK) if (ret != ESP_OK)
ESP_LOGE(PH_TAG, "httpd_ws_send_frame failed with %s", esp_err_to_name(ret)); ESP_LOGE(PH_TAG, "httpd_ws_send_frame failed with %s", esp_err_to_name(ret));
// ESP_LOGI(PH_TAG, "ws_handler: httpd_handle_t=%p, sockfd=%d, client_info:%d", request->server(), // ESP_LOGD(PH_TAG, "ws_handler: httpd_handle_t=%p, sockfd=%d, client_info:%d",
// httpd_req_to_sockfd(request->request()), httpd_ws_get_fd_info(request->server(), httpd_req_to_sockfd(request->request()))); // request->server(),
// httpd_req_to_sockfd(request->request()),
// httpd_ws_get_fd_info(request->server()->server, httpd_req_to_sockfd(request->request())));
// dont forget to release our buffer memory //dont forget to release our buffer memory
free(buf); free(buf);
return ret; return ret;
} }
PsychicWebSocketHandler *PsychicWebSocketHandler::onOpen(PsychicWebSocketClientCallback fn) PsychicWebSocketHandler * PsychicWebSocketHandler::onOpen(PsychicWebSocketClientCallback fn) {
{
_onOpen = fn; _onOpen = fn;
return this; return this;
} }
PsychicWebSocketHandler *PsychicWebSocketHandler::onFrame(PsychicWebSocketFrameCallback fn) PsychicWebSocketHandler * PsychicWebSocketHandler::onFrame(PsychicWebSocketFrameCallback fn) {
{
_onFrame = fn; _onFrame = fn;
return this; return this;
} }
PsychicWebSocketHandler *PsychicWebSocketHandler::onClose(PsychicWebSocketClientCallback fn) PsychicWebSocketHandler * PsychicWebSocketHandler::onClose(PsychicWebSocketClientCallback fn) {
{
_onClose = fn; _onClose = fn;
return this; return this;
} }
void PsychicWebSocketHandler::sendAll(httpd_ws_frame_t *ws_pkt) void PsychicWebSocketHandler::sendAll(httpd_ws_frame_t * ws_pkt)
{ {
for (PsychicClient *client : _clients) for (PsychicClient *client : _clients)
{ {
ESP_LOGD(PH_TAG, "Active client (fd=%d) -> sending async message", client->socket()); //ESP_LOGD(PH_TAG, "Active client (fd=%d) -> sending async message", client->socket());
if (client->_friend == NULL) if (client->_friend == NULL)
{ {
TRACE();
return; return;
} }
if (((PsychicWebSocketClient *)client->_friend)->sendMessage(ws_pkt) != ESP_OK) if (((PsychicWebSocketClient*)client->_friend)->sendMessage(ws_pkt) != ESP_OK)
break; break;
} }
} }
@@ -260,7 +245,7 @@ void PsychicWebSocketHandler::sendAll(httpd_ws_type_t op, const void *data, size
httpd_ws_frame_t ws_pkt; httpd_ws_frame_t ws_pkt;
memset(&ws_pkt, 0, sizeof(httpd_ws_frame_t)); memset(&ws_pkt, 0, sizeof(httpd_ws_frame_t));
ws_pkt.payload = (uint8_t *)data; ws_pkt.payload = (uint8_t*)data;
ws_pkt.len = len; ws_pkt.len = len;
ws_pkt.type = op; ws_pkt.type = op;
@@ -0,0 +1,90 @@
/************************************************************
TemplatePrinter Class
A basic templating engine for a stream of text.
This wraps the Arduino Print interface and writes to any
Print interface.
Written by Christopher Andrews (https://github.com/Chris--A)
************************************************************/
#include "TemplatePrinter.h"
void TemplatePrinter::resetParam(bool flush){
if(flush && _inParam){
_stream.write(_delimiter);
if(_paramPos)
_stream.print(_paramBuffer);
}
memset(_paramBuffer, 0, sizeof(_paramBuffer));
_paramPos = 0;
_inParam = false;
}
void TemplatePrinter::flush(){
resetParam(true);
_stream.flush();
}
size_t TemplatePrinter::write(uint8_t data){
if(data == _delimiter){
// End of parameter, send to callback
if(_inParam){
// On false, return the parameter place holder as is: not a parameter
// Bug fix: ignore parameters that are zero length.
if(!_paramPos || !_cb(_stream, _paramBuffer)){
resetParam(true);
_stream.write(data);
}else{
resetParam(false);
}
// Start collecting parameter
}else{
_inParam = true;
}
}else{
// Are we collecting
if(_inParam){
// Is param still valid
if(isalnum(data) || data == '_'){
// Total param len must be 63, 1 for null.
if(_paramPos < sizeof(_paramBuffer) - 1){
_paramBuffer[_paramPos++] = data;
// Not a valid param
}else{
resetParam(true);
}
}else{
resetParam(true);
_stream.write(data);
}
// Just output
}else{
_stream.write(data);
}
}
return 1;
}
size_t TemplatePrinter::copyFrom(Stream &stream){
size_t count = 0;
while(stream.available())
count += this->write(stream.read());
return count;
}
@@ -0,0 +1,51 @@
#ifndef TemplatePrinter_h
#define TemplatePrinter_h
#include "PsychicCore.h"
#include <Print.h>
/************************************************************
TemplatePrinter Class
A basic templating engine for a stream of text.
This wraps the Arduino Print interface and writes to any
Print interface.
Written by Christopher Andrews (https://github.com/Chris--A)
************************************************************/
class TemplatePrinter;
typedef std::function<bool(Print &output, const char *parameter)> TemplateCallback;
typedef std::function<void(TemplatePrinter &printer)> TemplateSourceCallback;
class TemplatePrinter : public Print{
private:
bool _inParam;
char _paramBuffer[64];
uint8_t _paramPos;
Print &_stream;
TemplateCallback _cb;
char _delimiter;
void resetParam(bool flush);
public:
using Print::write;
static void start(Print &stream, TemplateCallback cb, TemplateSourceCallback entry){
TemplatePrinter printer(stream, cb);
entry(printer);
}
TemplatePrinter(Print &stream, TemplateCallback cb, const char delimeter = '%') : _stream(stream), _cb(cb), _delimiter(delimeter) { resetParam(false); }
~TemplatePrinter(){ flush(); }
void flush() override;
size_t write(uint8_t data) override;
size_t copyFrom(Stream &stream);
};
#endif
+1 -1
View File
@@ -166,7 +166,7 @@ esp_err_t httpd_req_async_handler_begin(httpd_req_t *r, httpd_req_t **out)
if (async == NULL) { if (async == NULL) {
return ESP_ERR_NO_MEM; return ESP_ERR_NO_MEM;
} }
memcpy(async, r, sizeof(httpd_req_t)); memcpy((void *)async, (void *)r, sizeof(httpd_req_t));
// alloc async aux // alloc async aux
async->aux = (httpd_req_aux *)malloc(sizeof(struct httpd_req_aux)); async->aux = (httpd_req_aux *)malloc(sizeof(struct httpd_req_aux));