mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-06 07:49:52 +03:00
json is always chunked
This commit is contained in:
@@ -1,51 +1,55 @@
|
||||
#ifndef ChunkPrinter_h
|
||||
#define ChunkPrinter_h
|
||||
|
||||
#include "PsychicCore.h"
|
||||
// #include "PsychicCore.h"
|
||||
#include "PsychicResponse.h"
|
||||
#include <Print.h>
|
||||
|
||||
class ChunkPrinter : public Print {
|
||||
class ChunkPrinter : public Print
|
||||
{
|
||||
private:
|
||||
PsychicResponse * _response;
|
||||
uint8_t * _buffer;
|
||||
size_t _length;
|
||||
size_t _pos;
|
||||
PsychicResponse *_response;
|
||||
uint8_t *_buffer;
|
||||
size_t _length;
|
||||
size_t _pos;
|
||||
|
||||
public:
|
||||
ChunkPrinter(PsychicResponse * response, uint8_t * buffer, size_t len)
|
||||
: _response(response)
|
||||
, _buffer(buffer)
|
||||
, _length(len)
|
||||
, _pos(0) {
|
||||
ChunkPrinter(PsychicResponse *response, uint8_t *buffer, size_t len) :
|
||||
_response(response),
|
||||
_buffer(buffer),
|
||||
_length(len),
|
||||
_pos(0)
|
||||
{}
|
||||
|
||||
virtual ~ChunkPrinter() {}
|
||||
|
||||
size_t write(uint8_t c)
|
||||
{
|
||||
esp_err_t err;
|
||||
|
||||
_buffer[_pos] = c;
|
||||
_pos++;
|
||||
|
||||
//if we're full, send a chunk
|
||||
if (_pos == _length)
|
||||
{
|
||||
_pos = 0;
|
||||
|
||||
err = _response->sendChunk(_buffer, _length);
|
||||
if (err != ESP_OK)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
virtual ~ChunkPrinter() {
|
||||
}
|
||||
|
||||
size_t write(uint8_t c) {
|
||||
esp_err_t err;
|
||||
|
||||
_buffer[_pos] = c;
|
||||
_pos++;
|
||||
|
||||
//if we're full, send a chunk
|
||||
if (_pos == _length) {
|
||||
_pos = 0;
|
||||
|
||||
err = _response->sendChunk(_buffer, _length);
|
||||
if (err != ESP_OK)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
virtual void flush() override {
|
||||
if (_pos) {
|
||||
_response->sendChunk(_buffer, _pos);
|
||||
_pos = 0;
|
||||
}
|
||||
virtual void flush() override
|
||||
{
|
||||
if (_pos)
|
||||
{
|
||||
_response->sendChunk(_buffer, _pos);
|
||||
_pos = 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,67 +1,72 @@
|
||||
#include "PsychicClient.h"
|
||||
#include "PsychicHttpServer.h"
|
||||
#include <lwip/sockets.h>
|
||||
|
||||
PsychicClient::PsychicClient(httpd_handle_t server, int socket)
|
||||
: _server(server)
|
||||
, _socket(socket)
|
||||
, _friend(NULL)
|
||||
, isNew(false) {
|
||||
}
|
||||
PsychicClient::PsychicClient(httpd_handle_t server, int socket) :
|
||||
_server(server),
|
||||
_socket(socket),
|
||||
_friend(NULL),
|
||||
isNew(false)
|
||||
{}
|
||||
|
||||
PsychicClient::~PsychicClient() {
|
||||
}
|
||||
|
||||
httpd_handle_t PsychicClient::server() {
|
||||
return _server;
|
||||
return _server;
|
||||
}
|
||||
|
||||
int PsychicClient::socket() {
|
||||
return _socket;
|
||||
return _socket;
|
||||
}
|
||||
|
||||
// I'm not sure this is entirely safe to call. I was having issues with race conditions when highly loaded using this.
|
||||
esp_err_t PsychicClient::close() {
|
||||
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.
|
||||
esp_err_t PsychicClient::close()
|
||||
{
|
||||
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.
|
||||
|
||||
return err;
|
||||
return err;
|
||||
}
|
||||
|
||||
IPAddress PsychicClient::localIP() {
|
||||
IPAddress address(0, 0, 0, 0);
|
||||
IPAddress PsychicClient::localIP()
|
||||
{
|
||||
IPAddress address(0,0,0,0);
|
||||
|
||||
char ipstr[INET6_ADDRSTRLEN];
|
||||
struct sockaddr_in6 addr; // esp_http_server uses IPv6 addressing
|
||||
socklen_t addr_size = sizeof(addr);
|
||||
|
||||
if (getsockname(_socket, (struct sockaddr *)&addr, &addr_size) < 0) {
|
||||
ESP_LOGE(PH_TAG, "Error getting client IP");
|
||||
return address;
|
||||
}
|
||||
|
||||
// Convert to IPv4 string
|
||||
inet_ntop(AF_INET, &addr.sin6_addr.un.u32_addr[3], ipstr, sizeof(ipstr));
|
||||
ESP_LOGI(PH_TAG, "Client Local IP => %s", ipstr);
|
||||
address.fromString(ipstr);
|
||||
char ipstr[INET6_ADDRSTRLEN];
|
||||
struct sockaddr_in6 addr; // esp_http_server uses IPv6 addressing
|
||||
socklen_t addr_size = sizeof(addr);
|
||||
|
||||
if (getsockname(_socket, (struct sockaddr *)&addr, &addr_size) < 0) {
|
||||
ESP_LOGE(PH_TAG, "Error getting client IP");
|
||||
return address;
|
||||
}
|
||||
|
||||
// Convert to IPv4 string
|
||||
inet_ntop(AF_INET, &addr.sin6_addr.un.u32_addr[3], ipstr, sizeof(ipstr));
|
||||
ESP_LOGI(PH_TAG, "Client Local IP => %s", ipstr);
|
||||
address.fromString(ipstr);
|
||||
|
||||
return address;
|
||||
}
|
||||
|
||||
IPAddress PsychicClient::remoteIP() {
|
||||
IPAddress address(0, 0, 0, 0);
|
||||
IPAddress PsychicClient::remoteIP()
|
||||
{
|
||||
IPAddress address(0,0,0,0);
|
||||
|
||||
char ipstr[INET6_ADDRSTRLEN];
|
||||
struct sockaddr_in6 addr; // esp_http_server uses IPv6 addressing
|
||||
socklen_t addr_size = sizeof(addr);
|
||||
|
||||
if (getpeername(_socket, (struct sockaddr *)&addr, &addr_size) < 0) {
|
||||
ESP_LOGE(PH_TAG, "Error getting client IP");
|
||||
return address;
|
||||
}
|
||||
|
||||
// Convert to IPv4 string
|
||||
inet_ntop(AF_INET, &addr.sin6_addr.un.u32_addr[3], ipstr, sizeof(ipstr));
|
||||
ESP_LOGI(PH_TAG, "Client Remote IP => %s", ipstr);
|
||||
address.fromString(ipstr);
|
||||
char ipstr[INET6_ADDRSTRLEN];
|
||||
struct sockaddr_in6 addr; // esp_http_server uses IPv6 addressing
|
||||
socklen_t addr_size = sizeof(addr);
|
||||
|
||||
if (getpeername(_socket, (struct sockaddr *)&addr, &addr_size) < 0) {
|
||||
ESP_LOGE(PH_TAG, "Error getting client IP");
|
||||
return address;
|
||||
}
|
||||
|
||||
// Convert to IPv4 string
|
||||
inet_ntop(AF_INET, &addr.sin6_addr.un.u32_addr[3], ipstr, sizeof(ipstr));
|
||||
ESP_LOGI(PH_TAG, "Client Remote IP => %s", ipstr);
|
||||
address.fromString(ipstr);
|
||||
|
||||
return address;
|
||||
}
|
||||
@@ -2,8 +2,6 @@
|
||||
#define PsychicClient_h
|
||||
|
||||
#include "PsychicCore.h"
|
||||
#include "PsychicHttpServer.h"
|
||||
#include <lwip/sockets.h>
|
||||
|
||||
/*
|
||||
* PsychicClient :: Generic wrapper around the ESP-IDF socket
|
||||
@@ -12,7 +10,7 @@
|
||||
class PsychicClient {
|
||||
protected:
|
||||
httpd_handle_t _server;
|
||||
int _socket;
|
||||
int _socket;
|
||||
|
||||
public:
|
||||
PsychicClient(httpd_handle_t server, int socket);
|
||||
@@ -20,17 +18,15 @@ class PsychicClient {
|
||||
|
||||
//no idea if this is the right way to do it or not, but lets see.
|
||||
//pointer to our derived class (eg. PsychicWebSocketConnection)
|
||||
void * _friend;
|
||||
void *_friend;
|
||||
|
||||
bool isNew = false;
|
||||
|
||||
bool operator==(PsychicClient & rhs) const {
|
||||
return _socket == rhs.socket();
|
||||
}
|
||||
bool operator==(PsychicClient& rhs) const { return _socket == rhs.socket(); }
|
||||
|
||||
httpd_handle_t server();
|
||||
int socket();
|
||||
esp_err_t close();
|
||||
int socket();
|
||||
esp_err_t close();
|
||||
|
||||
IPAddress localIP();
|
||||
IPAddress remoteIP();
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
#define PH_TAG "psychic"
|
||||
|
||||
//version numbers
|
||||
// version numbers
|
||||
#define PSYCHIC_HTTP_VERSION_MAJOR 1
|
||||
#define PSYCHIC_HTTP_VERSION_MINOR 1
|
||||
#define PSYCHIC_HTTP_VERSION_PATCH 0
|
||||
@@ -16,12 +16,16 @@
|
||||
#define FILE_CHUNK_SIZE 8 * 1024
|
||||
#endif
|
||||
|
||||
#ifndef STREAM_CHUNK_SIZE
|
||||
#define STREAM_CHUNK_SIZE 1024
|
||||
#endif
|
||||
|
||||
#ifndef MAX_UPLOAD_SIZE
|
||||
#define MAX_UPLOAD_SIZE (2048 * 1024) // 2MB
|
||||
#endif
|
||||
|
||||
#ifndef MAX_REQUEST_BODY_SIZE
|
||||
#define MAX_REQUEST_BODY_SIZE (16 * 1024) //16K
|
||||
#define MAX_REQUEST_BODY_SIZE (16 * 1024) // 16K
|
||||
#endif
|
||||
|
||||
#ifdef ARDUINO
|
||||
@@ -37,65 +41,75 @@
|
||||
#include "MD5Builder.h"
|
||||
#include <UrlEncode.h>
|
||||
#include "FS.h"
|
||||
#include <ArduinoJson.h>
|
||||
|
||||
enum HTTPAuthMethod { BASIC_AUTH, DIGEST_AUTH };
|
||||
enum HTTPAuthMethod
|
||||
{
|
||||
BASIC_AUTH,
|
||||
DIGEST_AUTH
|
||||
};
|
||||
|
||||
String urlDecode(const char * encoded);
|
||||
String urlDecode(const char *encoded);
|
||||
|
||||
class PsychicHttpServer;
|
||||
class PsychicRequest;
|
||||
class PsychicWebSocketRequest;
|
||||
class PsychicClient;
|
||||
|
||||
//filter function definition
|
||||
typedef std::function<bool(PsychicRequest * request)> PsychicRequestFilterFunction;
|
||||
// filter function definition
|
||||
typedef std::function<bool(PsychicRequest *request)> PsychicRequestFilterFunction;
|
||||
|
||||
//client connect callback
|
||||
typedef std::function<void(PsychicClient * client)> PsychicClientCallback;
|
||||
// client connect callback
|
||||
typedef std::function<void(PsychicClient *client)> PsychicClientCallback;
|
||||
|
||||
// callback definitions
|
||||
typedef std::function<esp_err_t(PsychicRequest *request)> PsychicHttpRequestCallback;
|
||||
typedef std::function<esp_err_t(PsychicRequest *request, JsonVariant &json)> PsychicJsonRequestCallback;
|
||||
|
||||
struct HTTPHeader {
|
||||
char * field;
|
||||
char * value;
|
||||
struct HTTPHeader
|
||||
{
|
||||
char *field;
|
||||
char *value;
|
||||
};
|
||||
|
||||
class DefaultHeaders {
|
||||
std::list<HTTPHeader> _headers;
|
||||
class DefaultHeaders
|
||||
{
|
||||
std::list<HTTPHeader> _headers;
|
||||
|
||||
public:
|
||||
DefaultHeaders() {
|
||||
}
|
||||
public:
|
||||
DefaultHeaders() {}
|
||||
|
||||
void addHeader(const String & field, const String & value) {
|
||||
addHeader(field.c_str(), value.c_str());
|
||||
}
|
||||
void addHeader(const String &field, const String &value)
|
||||
{
|
||||
addHeader(field.c_str(), value.c_str());
|
||||
}
|
||||
|
||||
void addHeader(const char * field, const char * value) {
|
||||
HTTPHeader header;
|
||||
void addHeader(const char *field, const char *value)
|
||||
{
|
||||
HTTPHeader header;
|
||||
|
||||
//these are just going to stick around forever.
|
||||
header.field = (char *)malloc(strlen(field) + 1);
|
||||
header.value = (char *)malloc(strlen(value) + 1);
|
||||
// these are just going to stick around forever.
|
||||
header.field = (char *)malloc(strlen(field) + 1);
|
||||
header.value = (char *)malloc(strlen(value) + 1);
|
||||
|
||||
strlcpy(header.field, field, strlen(field) + 1);
|
||||
strlcpy(header.value, value, strlen(value) + 1);
|
||||
strlcpy(header.field, field, strlen(field) + 1);
|
||||
strlcpy(header.value, value, strlen(value) + 1);
|
||||
|
||||
_headers.push_back(header);
|
||||
}
|
||||
_headers.push_back(header);
|
||||
}
|
||||
|
||||
const std::list<HTTPHeader> & getHeaders() {
|
||||
return _headers;
|
||||
}
|
||||
const std::list<HTTPHeader> &getHeaders() { return _headers; }
|
||||
|
||||
//delete the copy constructor, singleton class
|
||||
DefaultHeaders(DefaultHeaders const &) = delete;
|
||||
DefaultHeaders & operator=(DefaultHeaders const &) = delete;
|
||||
// delete the copy constructor, singleton class
|
||||
DefaultHeaders(DefaultHeaders const &) = delete;
|
||||
DefaultHeaders &operator=(DefaultHeaders const &) = delete;
|
||||
|
||||
//single static class interface
|
||||
static DefaultHeaders & Instance() {
|
||||
static DefaultHeaders instance;
|
||||
return instance;
|
||||
}
|
||||
// single static class interface
|
||||
static DefaultHeaders &Instance()
|
||||
{
|
||||
static DefaultHeaders instance;
|
||||
return instance;
|
||||
}
|
||||
};
|
||||
|
||||
#endif //PsychicCore_h
|
||||
#endif // PsychicCore_h
|
||||
@@ -1,82 +1,90 @@
|
||||
#include "PsychicEndpoint.h"
|
||||
#include "PsychicHttpServer.h"
|
||||
|
||||
PsychicEndpoint::PsychicEndpoint()
|
||||
: _server(NULL)
|
||||
, _uri("")
|
||||
, _method(HTTP_GET)
|
||||
, _handler(NULL) {
|
||||
PsychicEndpoint::PsychicEndpoint() :
|
||||
_server(NULL),
|
||||
_uri(""),
|
||||
_method(HTTP_GET),
|
||||
_handler(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
PsychicEndpoint::PsychicEndpoint(PsychicHttpServer * server, http_method method, const char * uri)
|
||||
: _server(server)
|
||||
, _uri(uri)
|
||||
, _method(method)
|
||||
, _handler(NULL) {
|
||||
PsychicEndpoint::PsychicEndpoint(PsychicHttpServer *server, http_method method, const char * uri) :
|
||||
_server(server),
|
||||
_uri(uri),
|
||||
_method(method),
|
||||
_handler(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
PsychicEndpoint * PsychicEndpoint::setHandler(PsychicHandler * handler) {
|
||||
//clean up old / default handler
|
||||
if (_handler != NULL)
|
||||
delete _handler;
|
||||
PsychicEndpoint * PsychicEndpoint::setHandler(PsychicHandler *handler)
|
||||
{
|
||||
//clean up old / default handler
|
||||
if (_handler != NULL)
|
||||
delete _handler;
|
||||
|
||||
//get our new pointer
|
||||
_handler = handler;
|
||||
//get our new pointer
|
||||
_handler = handler;
|
||||
|
||||
//keep a pointer to the server
|
||||
_handler->_server = _server;
|
||||
//keep a pointer to the server
|
||||
_handler->_server = _server;
|
||||
|
||||
return this;
|
||||
return this;
|
||||
}
|
||||
|
||||
PsychicHandler * PsychicEndpoint::handler() {
|
||||
return _handler;
|
||||
PsychicHandler * PsychicEndpoint::handler()
|
||||
{
|
||||
return _handler;
|
||||
}
|
||||
|
||||
String PsychicEndpoint::uri() {
|
||||
return _uri;
|
||||
return _uri;
|
||||
}
|
||||
|
||||
esp_err_t PsychicEndpoint::requestCallback(httpd_req_t * req) {
|
||||
#ifdef ENABLE_ASYNC
|
||||
esp_err_t PsychicEndpoint::requestCallback(httpd_req_t *req)
|
||||
{
|
||||
#ifdef ENABLE_ASYNC
|
||||
if (is_on_async_worker_thread() == false) {
|
||||
if (submit_async_req(req, PsychicEndpoint::requestCallback) == ESP_OK) {
|
||||
return ESP_OK;
|
||||
} else {
|
||||
httpd_resp_set_status(req, "503 Busy");
|
||||
httpd_resp_sendstr(req, "No workers available. Server busy.</div>");
|
||||
return ESP_OK;
|
||||
}
|
||||
if (submit_async_req(req, PsychicEndpoint::requestCallback) == ESP_OK) {
|
||||
return ESP_OK;
|
||||
} else {
|
||||
httpd_resp_set_status(req, "503 Busy");
|
||||
httpd_resp_sendstr(req, "No workers available. Server busy.</div>");
|
||||
return ESP_OK;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
PsychicEndpoint * self = (PsychicEndpoint *)req->user_ctx;
|
||||
PsychicHandler * handler = self->handler();
|
||||
PsychicRequest request(self->_server, req);
|
||||
PsychicEndpoint *self = (PsychicEndpoint *)req->user_ctx;
|
||||
PsychicHandler *handler = self->handler();
|
||||
PsychicRequest request(self->_server, req);
|
||||
|
||||
//make sure we have a handler
|
||||
if (handler != NULL) {
|
||||
if (handler->filter(&request) && handler->canHandle(&request)) {
|
||||
//check our credentials
|
||||
if (handler->needsAuthentication(&request))
|
||||
return handler->authenticate(&request);
|
||||
//make sure we have a handler
|
||||
if (handler != NULL)
|
||||
{
|
||||
if (handler->filter(&request) && handler->canHandle(&request))
|
||||
{
|
||||
//check our credentials
|
||||
if (handler->needsAuthentication(&request))
|
||||
return handler->authenticate(&request);
|
||||
|
||||
//pass it to our handler
|
||||
return handler->handleRequest(&request);
|
||||
}
|
||||
//pass it to our generic handlers
|
||||
else
|
||||
return PsychicHttpServer::notFoundHandler(req, HTTPD_500_INTERNAL_SERVER_ERROR);
|
||||
} else
|
||||
return request.reply(500, "text/html", "No handler registered.");
|
||||
//pass it to our handler
|
||||
return handler->handleRequest(&request);
|
||||
}
|
||||
//pass it to our generic handlers
|
||||
else
|
||||
return PsychicHttpServer::notFoundHandler(req, HTTPD_500_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
else
|
||||
return request.reply(500, "text/html", "No handler registered.");
|
||||
}
|
||||
|
||||
PsychicEndpoint * PsychicEndpoint::setFilter(PsychicRequestFilterFunction fn) {
|
||||
_handler->setFilter(fn);
|
||||
return this;
|
||||
PsychicEndpoint* PsychicEndpoint::setFilter(PsychicRequestFilterFunction fn) {
|
||||
_handler->setFilter(fn);
|
||||
return this;
|
||||
}
|
||||
|
||||
PsychicEndpoint *
|
||||
PsychicEndpoint::setAuthentication(const char * username, const char * password, HTTPAuthMethod method, const char * realm, const char * authFailMsg) {
|
||||
_handler->setAuthentication(username, password, method, realm, authFailMsg);
|
||||
return this;
|
||||
PsychicEndpoint* PsychicEndpoint::setAuthentication(const char *username, const char *password, HTTPAuthMethod method, const char *realm, const char *authFailMsg) {
|
||||
_handler->setAuthentication(username, password, method, realm, authFailMsg);
|
||||
return this;
|
||||
};
|
||||
@@ -2,35 +2,36 @@
|
||||
#define PsychicEndpoint_h
|
||||
|
||||
#include "PsychicCore.h"
|
||||
#include "PsychicHttpServer.h"
|
||||
|
||||
class PsychicHandler;
|
||||
|
||||
#ifdef ENABLE_ASYNC
|
||||
#include "async_worker.h"
|
||||
#include "async_worker.h"
|
||||
#endif
|
||||
|
||||
class PsychicEndpoint {
|
||||
friend PsychicHttpServer;
|
||||
class PsychicEndpoint
|
||||
{
|
||||
friend PsychicHttpServer;
|
||||
|
||||
private:
|
||||
PsychicHttpServer * _server;
|
||||
String _uri;
|
||||
http_method _method;
|
||||
PsychicHandler * _handler;
|
||||
PsychicHttpServer *_server;
|
||||
String _uri;
|
||||
http_method _method;
|
||||
PsychicHandler *_handler;
|
||||
|
||||
public:
|
||||
PsychicEndpoint();
|
||||
PsychicEndpoint(PsychicHttpServer * server, http_method method, const char * uri);
|
||||
PsychicEndpoint(PsychicHttpServer *server, http_method method, const char * uri);
|
||||
|
||||
PsychicEndpoint * setHandler(PsychicHandler * handler);
|
||||
PsychicHandler * handler();
|
||||
PsychicEndpoint *setHandler(PsychicHandler *handler);
|
||||
PsychicHandler *handler();
|
||||
|
||||
PsychicEndpoint * setFilter(PsychicRequestFilterFunction fn);
|
||||
PsychicEndpoint *
|
||||
setAuthentication(const char * username, const char * password, HTTPAuthMethod method = BASIC_AUTH, const char * realm = "", const char * authFailMsg = "");
|
||||
PsychicEndpoint* setFilter(PsychicRequestFilterFunction fn);
|
||||
PsychicEndpoint* setAuthentication(const char *username, const char *password, HTTPAuthMethod method = BASIC_AUTH, const char *realm = "", const char *authFailMsg = "");
|
||||
|
||||
String uri();
|
||||
|
||||
static esp_err_t requestCallback(httpd_req_t * req);
|
||||
static esp_err_t requestCallback(httpd_req_t *req);
|
||||
};
|
||||
|
||||
#endif // PsychicEndpoint_h
|
||||
@@ -24,194 +24,204 @@
|
||||
// PsychicEventSource - Handler
|
||||
/*****************************************/
|
||||
|
||||
PsychicEventSource::PsychicEventSource()
|
||||
: PsychicHandler()
|
||||
, _onOpen(NULL)
|
||||
, _onClose(NULL) {
|
||||
}
|
||||
PsychicEventSource::PsychicEventSource() :
|
||||
PsychicHandler(),
|
||||
_onOpen(NULL),
|
||||
_onClose(NULL)
|
||||
{}
|
||||
|
||||
PsychicEventSource::~PsychicEventSource() {
|
||||
}
|
||||
|
||||
PsychicEventSourceClient * PsychicEventSource::getClient(int socket) {
|
||||
PsychicClient * client = PsychicHandler::getClient(socket);
|
||||
PsychicEventSourceClient * PsychicEventSource::getClient(int socket)
|
||||
{
|
||||
PsychicClient *client = PsychicHandler::getClient(socket);
|
||||
|
||||
if (client == NULL)
|
||||
return NULL;
|
||||
if (client == NULL)
|
||||
return NULL;
|
||||
|
||||
return (PsychicEventSourceClient *)client->_friend;
|
||||
return (PsychicEventSourceClient *)client->_friend;
|
||||
}
|
||||
|
||||
PsychicEventSourceClient * PsychicEventSource::getClient(PsychicClient * client) {
|
||||
return getClient(client->socket());
|
||||
PsychicEventSourceClient * PsychicEventSource::getClient(PsychicClient *client) {
|
||||
return getClient(client->socket());
|
||||
}
|
||||
|
||||
esp_err_t PsychicEventSource::handleRequest(PsychicRequest * request) {
|
||||
//start our open ended HTTP response
|
||||
PsychicEventSourceResponse response(request);
|
||||
esp_err_t err = response.send();
|
||||
esp_err_t PsychicEventSource::handleRequest(PsychicRequest *request)
|
||||
{
|
||||
//start our open ended HTTP response
|
||||
PsychicEventSourceResponse response(request);
|
||||
esp_err_t err = response.send();
|
||||
|
||||
//lookup our client
|
||||
PsychicClient * client = checkForNewClient(request->client());
|
||||
if (client->isNew) {
|
||||
//did we get our last id?
|
||||
if (request->hasHeader("Last-Event-ID")) {
|
||||
PsychicEventSourceClient * buddy = getClient(client);
|
||||
buddy->_lastId = atoi(request->header("Last-Event-ID").c_str());
|
||||
}
|
||||
|
||||
//let our handler know.
|
||||
openCallback(client);
|
||||
//lookup our client
|
||||
PsychicClient *client = checkForNewClient(request->client());
|
||||
if (client->isNew)
|
||||
{
|
||||
//did we get our last id?
|
||||
if(request->hasHeader("Last-Event-ID"))
|
||||
{
|
||||
PsychicEventSourceClient *buddy = getClient(client);
|
||||
buddy->_lastId = atoi(request->header("Last-Event-ID").c_str());
|
||||
}
|
||||
|
||||
return err;
|
||||
//let our handler know.
|
||||
openCallback(client);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
PsychicEventSource * PsychicEventSource::onOpen(PsychicEventSourceClientCallback fn) {
|
||||
_onOpen = fn;
|
||||
return this;
|
||||
_onOpen = fn;
|
||||
return this;
|
||||
}
|
||||
|
||||
PsychicEventSource * PsychicEventSource::onClose(PsychicEventSourceClientCallback fn) {
|
||||
_onClose = fn;
|
||||
return this;
|
||||
_onClose = fn;
|
||||
return this;
|
||||
}
|
||||
|
||||
void PsychicEventSource::addClient(PsychicClient * client) {
|
||||
client->_friend = new PsychicEventSourceClient(client);
|
||||
PsychicHandler::addClient(client);
|
||||
void PsychicEventSource::addClient(PsychicClient *client) {
|
||||
client->_friend = new PsychicEventSourceClient(client);
|
||||
PsychicHandler::addClient(client);
|
||||
}
|
||||
|
||||
void PsychicEventSource::removeClient(PsychicClient * client) {
|
||||
PsychicHandler::removeClient(client);
|
||||
delete (PsychicEventSourceClient *)client->_friend;
|
||||
client->_friend = NULL;
|
||||
void PsychicEventSource::removeClient(PsychicClient *client) {
|
||||
PsychicHandler::removeClient(client);
|
||||
delete (PsychicEventSourceClient*)client->_friend;
|
||||
client->_friend = NULL;
|
||||
}
|
||||
|
||||
void PsychicEventSource::openCallback(PsychicClient * client) {
|
||||
PsychicEventSourceClient * buddy = getClient(client);
|
||||
if (buddy == NULL) {
|
||||
TRACE();
|
||||
return;
|
||||
}
|
||||
void PsychicEventSource::openCallback(PsychicClient *client) {
|
||||
PsychicEventSourceClient *buddy = getClient(client);
|
||||
if (buddy == NULL)
|
||||
{
|
||||
TRACE();
|
||||
return;
|
||||
}
|
||||
|
||||
if (_onOpen != NULL)
|
||||
_onOpen(buddy);
|
||||
if (_onOpen != NULL)
|
||||
_onOpen(buddy);
|
||||
}
|
||||
|
||||
void PsychicEventSource::closeCallback(PsychicClient * client) {
|
||||
PsychicEventSourceClient * buddy = getClient(client);
|
||||
if (buddy == NULL) {
|
||||
TRACE();
|
||||
return;
|
||||
}
|
||||
void PsychicEventSource::closeCallback(PsychicClient *client) {
|
||||
PsychicEventSourceClient *buddy = getClient(client);
|
||||
if (buddy == NULL)
|
||||
{
|
||||
TRACE();
|
||||
return;
|
||||
}
|
||||
|
||||
if (_onClose != NULL)
|
||||
_onClose(getClient(buddy));
|
||||
if (_onClose != NULL)
|
||||
_onClose(getClient(buddy));
|
||||
}
|
||||
|
||||
void PsychicEventSource::send(const char * message, const char * event, uint32_t id, uint32_t reconnect) {
|
||||
String ev = generateEventMessage(message, event, id, reconnect);
|
||||
for (PsychicClient * c : _clients) {
|
||||
((PsychicEventSourceClient *)c->_friend)->sendEvent(ev.c_str());
|
||||
}
|
||||
void PsychicEventSource::send(const char *message, const char *event, uint32_t id, uint32_t reconnect)
|
||||
{
|
||||
String ev = generateEventMessage(message, event, id, reconnect);
|
||||
for(PsychicClient *c : _clients) {
|
||||
((PsychicEventSourceClient*)c->_friend)->sendEvent(ev.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
/*****************************************/
|
||||
// PsychicEventSourceClient
|
||||
/*****************************************/
|
||||
|
||||
PsychicEventSourceClient::PsychicEventSourceClient(PsychicClient * client)
|
||||
: PsychicClient(client->server(), client->socket())
|
||||
, _lastId(0) {
|
||||
PsychicEventSourceClient::PsychicEventSourceClient(PsychicClient *client) :
|
||||
PsychicClient(client->server(), client->socket()),
|
||||
_lastId(0)
|
||||
{
|
||||
}
|
||||
|
||||
PsychicEventSourceClient::~PsychicEventSourceClient() {
|
||||
PsychicEventSourceClient::~PsychicEventSourceClient(){
|
||||
}
|
||||
|
||||
void PsychicEventSourceClient::send(const char * message, const char * event, uint32_t id, uint32_t reconnect) {
|
||||
String ev = generateEventMessage(message, event, id, reconnect);
|
||||
sendEvent(ev.c_str());
|
||||
void PsychicEventSourceClient::send(const char *message, const char *event, uint32_t id, uint32_t reconnect){
|
||||
String ev = generateEventMessage(message, event, id, reconnect);
|
||||
sendEvent(ev.c_str());
|
||||
}
|
||||
|
||||
void PsychicEventSourceClient::sendEvent(const char * event) {
|
||||
int result;
|
||||
do {
|
||||
result = httpd_socket_send(this->server(), this->socket(), event, strlen(event), 0);
|
||||
} while (result == HTTPD_SOCK_ERR_TIMEOUT);
|
||||
void PsychicEventSourceClient::sendEvent(const char *event) {
|
||||
int result;
|
||||
do {
|
||||
result = httpd_socket_send(this->server(), this->socket(), event, strlen(event), 0);
|
||||
} while (result == HTTPD_SOCK_ERR_TIMEOUT);
|
||||
|
||||
//if (result < 0)
|
||||
//error log here
|
||||
//if (result < 0)
|
||||
//error log here
|
||||
}
|
||||
|
||||
/*****************************************/
|
||||
// PsychicEventSourceResponse
|
||||
/*****************************************/
|
||||
|
||||
PsychicEventSourceResponse::PsychicEventSourceResponse(PsychicRequest * request)
|
||||
: PsychicResponse(request) {
|
||||
PsychicEventSourceResponse::PsychicEventSourceResponse(PsychicRequest *request)
|
||||
: PsychicResponse(request)
|
||||
{
|
||||
}
|
||||
|
||||
esp_err_t PsychicEventSourceResponse::send() {
|
||||
//build our main header
|
||||
String out = String();
|
||||
out.concat("HTTP/1.1 200 OK\r\n");
|
||||
out.concat("Content-Type: text/event-stream\r\n");
|
||||
out.concat("Cache-Control: no-cache\r\n");
|
||||
out.concat("Connection: keep-alive\r\n");
|
||||
|
||||
//get our global headers out of the way first
|
||||
for (HTTPHeader header : DefaultHeaders::Instance().getHeaders())
|
||||
out.concat(String(header.field) + ": " + String(header.value) + "\r\n");
|
||||
//build our main header
|
||||
String out = String();
|
||||
out.concat("HTTP/1.1 200 OK\r\n");
|
||||
out.concat("Content-Type: text/event-stream\r\n");
|
||||
out.concat("Cache-Control: no-cache\r\n");
|
||||
out.concat("Connection: keep-alive\r\n");
|
||||
|
||||
//separator
|
||||
out.concat("\r\n");
|
||||
//get our global headers out of the way first
|
||||
for (HTTPHeader header : DefaultHeaders::Instance().getHeaders())
|
||||
out.concat(String(header.field) + ": " + String(header.value) + "\r\n");
|
||||
|
||||
int result;
|
||||
do {
|
||||
result = httpd_send(_request->request(), out.c_str(), out.length());
|
||||
} while (result == HTTPD_SOCK_ERR_TIMEOUT);
|
||||
//separator
|
||||
out.concat("\r\n");
|
||||
|
||||
if (result < 0)
|
||||
ESP_LOGE(PH_TAG, "EventSource send failed with %s", esp_err_to_name(result));
|
||||
int result;
|
||||
do {
|
||||
result = httpd_send(_request->request(), out.c_str(), out.length());
|
||||
} while (result == HTTPD_SOCK_ERR_TIMEOUT);
|
||||
|
||||
if (result > 0)
|
||||
return ESP_OK;
|
||||
else
|
||||
return ESP_ERR_HTTPD_RESP_SEND;
|
||||
if (result < 0)
|
||||
ESP_LOGE(PH_TAG, "EventSource send failed with %s", esp_err_to_name(result));
|
||||
|
||||
if (result > 0)
|
||||
return ESP_OK;
|
||||
else
|
||||
return ESP_ERR_HTTPD_RESP_SEND;
|
||||
}
|
||||
|
||||
/*****************************************/
|
||||
// Event Message Generator
|
||||
/*****************************************/
|
||||
|
||||
String generateEventMessage(const char * message, const char * event, uint32_t id, uint32_t reconnect) {
|
||||
String ev = "";
|
||||
String generateEventMessage(const char *message, const char *event, uint32_t id, uint32_t reconnect) {
|
||||
String ev = "";
|
||||
|
||||
if (reconnect) {
|
||||
ev += "retry: ";
|
||||
ev += String(reconnect);
|
||||
ev += "\r\n";
|
||||
}
|
||||
|
||||
if (id) {
|
||||
ev += "id: ";
|
||||
ev += String(id);
|
||||
ev += "\r\n";
|
||||
}
|
||||
|
||||
if (event != NULL) {
|
||||
ev += "event: ";
|
||||
ev += String(event);
|
||||
ev += "\r\n";
|
||||
}
|
||||
|
||||
if (message != NULL) {
|
||||
ev += "data: ";
|
||||
ev += String(message);
|
||||
ev += "\r\n";
|
||||
}
|
||||
if(reconnect){
|
||||
ev += "retry: ";
|
||||
ev += String(reconnect);
|
||||
ev += "\r\n";
|
||||
}
|
||||
|
||||
return ev;
|
||||
if(id){
|
||||
ev += "id: ";
|
||||
ev += String(id);
|
||||
ev += "\r\n";
|
||||
}
|
||||
|
||||
if(event != NULL){
|
||||
ev += "event: ";
|
||||
ev += String(event);
|
||||
ev += "\r\n";
|
||||
}
|
||||
|
||||
if(message != NULL){
|
||||
ev += "data: ";
|
||||
ev += String(message);
|
||||
ev += "\r\n";
|
||||
}
|
||||
ev += "\r\n";
|
||||
|
||||
return ev;
|
||||
}
|
||||
@@ -21,8 +21,8 @@
|
||||
#define PsychicEventSource_H_
|
||||
|
||||
#include "PsychicCore.h"
|
||||
#include "PsychicClient.h"
|
||||
#include "PsychicHandler.h"
|
||||
#include "PsychicClient.h"
|
||||
#include "PsychicResponse.h"
|
||||
|
||||
class PsychicEventSource;
|
||||
@@ -30,23 +30,21 @@ class PsychicEventSourceResponse;
|
||||
class PsychicEventSourceClient;
|
||||
class PsychicResponse;
|
||||
|
||||
typedef std::function<void(PsychicEventSourceClient * client)> PsychicEventSourceClientCallback;
|
||||
typedef std::function<void(PsychicEventSourceClient *client)> PsychicEventSourceClientCallback;
|
||||
|
||||
class PsychicEventSourceClient : public PsychicClient {
|
||||
friend PsychicEventSource;
|
||||
friend PsychicEventSource;
|
||||
|
||||
protected:
|
||||
uint32_t _lastId;
|
||||
|
||||
public:
|
||||
PsychicEventSourceClient(PsychicClient * client);
|
||||
PsychicEventSourceClient(PsychicClient *client);
|
||||
~PsychicEventSourceClient();
|
||||
|
||||
uint32_t lastId() const {
|
||||
return _lastId;
|
||||
}
|
||||
void send(const char * message, const char * event = NULL, uint32_t id = 0, uint32_t reconnect = 0);
|
||||
void sendEvent(const char * event);
|
||||
uint32_t lastId() const { return _lastId; }
|
||||
void send(const char *message, const char *event=NULL, uint32_t id=0, uint32_t reconnect=0);
|
||||
void sendEvent(const char *event);
|
||||
};
|
||||
|
||||
class PsychicEventSource : public PsychicHandler {
|
||||
@@ -59,26 +57,26 @@ class PsychicEventSource : public PsychicHandler {
|
||||
~PsychicEventSource();
|
||||
|
||||
PsychicEventSourceClient * getClient(int socket) override;
|
||||
PsychicEventSourceClient * getClient(PsychicClient * client) override;
|
||||
void addClient(PsychicClient * client) override;
|
||||
void removeClient(PsychicClient * client) override;
|
||||
void openCallback(PsychicClient * client) override;
|
||||
void closeCallback(PsychicClient * client) override;
|
||||
PsychicEventSourceClient * getClient(PsychicClient *client) override;
|
||||
void addClient(PsychicClient *client) override;
|
||||
void removeClient(PsychicClient *client) override;
|
||||
void openCallback(PsychicClient *client) override;
|
||||
void closeCallback(PsychicClient *client) override;
|
||||
|
||||
PsychicEventSource * onOpen(PsychicEventSourceClientCallback fn);
|
||||
PsychicEventSource * onClose(PsychicEventSourceClientCallback fn);
|
||||
PsychicEventSource *onOpen(PsychicEventSourceClientCallback fn);
|
||||
PsychicEventSource *onClose(PsychicEventSourceClientCallback fn);
|
||||
|
||||
esp_err_t handleRequest(PsychicRequest * request) override final;
|
||||
esp_err_t handleRequest(PsychicRequest *request) override final;
|
||||
|
||||
void send(const char * message, const char * event = NULL, uint32_t id = 0, uint32_t reconnect = 0);
|
||||
void send(const char *message, const char *event=NULL, uint32_t id=0, uint32_t reconnect=0);
|
||||
};
|
||||
|
||||
class PsychicEventSourceResponse : public PsychicResponse {
|
||||
class PsychicEventSourceResponse: public PsychicResponse {
|
||||
public:
|
||||
PsychicEventSourceResponse(PsychicRequest * request);
|
||||
PsychicEventSourceResponse(PsychicRequest *request);
|
||||
virtual esp_err_t send() override;
|
||||
};
|
||||
|
||||
String generateEventMessage(const char * message, const char * event, uint32_t id, uint32_t reconnect);
|
||||
String generateEventMessage(const char *message, const char *event, uint32_t id, uint32_t reconnect);
|
||||
|
||||
#endif /* PsychicEventSource_H_ */
|
||||
@@ -3,167 +3,156 @@
|
||||
#include "PsychicRequest.h"
|
||||
|
||||
|
||||
PsychicFileResponse::PsychicFileResponse(PsychicRequest * request, FS & fs, const String & path, const String & contentType, bool download)
|
||||
: PsychicResponse(request) {
|
||||
//_code = 200;
|
||||
_path = path;
|
||||
PsychicFileResponse::PsychicFileResponse(PsychicRequest *request, FS &fs, const String& path, const String& contentType, bool download)
|
||||
: PsychicResponse(request) {
|
||||
//_code = 200;
|
||||
_path = path;
|
||||
|
||||
if (!download && !fs.exists(_path) && fs.exists(_path + ".gz")) {
|
||||
_path = _path + ".gz";
|
||||
addHeader("Content-Encoding", "gzip");
|
||||
_sendContentLength = true;
|
||||
_chunked = false;
|
||||
}
|
||||
if(!download && !fs.exists(_path) && fs.exists(_path+".gz")){
|
||||
_path = _path+".gz";
|
||||
addHeader("Content-Encoding", "gzip");
|
||||
_sendContentLength = true;
|
||||
_chunked = false;
|
||||
}
|
||||
|
||||
_content = fs.open(_path, "r");
|
||||
_contentLength = _content.size();
|
||||
_content = fs.open(_path, "r");
|
||||
_contentLength = _content.size();
|
||||
|
||||
if (contentType == "")
|
||||
_setContentType(path);
|
||||
else
|
||||
_contentType = contentType;
|
||||
setContentType(_contentType.c_str());
|
||||
if(contentType == "")
|
||||
_setContentType(path);
|
||||
else
|
||||
_contentType = contentType;
|
||||
setContentType(_contentType.c_str());
|
||||
|
||||
int filenameStart = path.lastIndexOf('/') + 1;
|
||||
char buf[26 + path.length() - filenameStart];
|
||||
char * filename = (char *)path.c_str() + filenameStart;
|
||||
int filenameStart = path.lastIndexOf('/') + 1;
|
||||
char buf[26+path.length()-filenameStart];
|
||||
char* filename = (char*)path.c_str() + filenameStart;
|
||||
|
||||
if (download) {
|
||||
// set filename and force download
|
||||
snprintf(buf, sizeof(buf), "attachment; filename=\"%s\"", filename);
|
||||
} else {
|
||||
// set filename and force rendering
|
||||
snprintf(buf, sizeof(buf), "inline; filename=\"%s\"", filename);
|
||||
}
|
||||
addHeader("Content-Disposition", buf);
|
||||
if(download) {
|
||||
// set filename and force download
|
||||
snprintf(buf, sizeof (buf), "attachment; filename=\"%s\"", filename);
|
||||
} else {
|
||||
// set filename and force rendering
|
||||
snprintf(buf, sizeof (buf), "inline; filename=\"%s\"", filename);
|
||||
}
|
||||
addHeader("Content-Disposition", buf);
|
||||
}
|
||||
|
||||
PsychicFileResponse::PsychicFileResponse(PsychicRequest * request, File content, const String & path, const String & contentType, bool download)
|
||||
: PsychicResponse(request) {
|
||||
_path = path;
|
||||
PsychicFileResponse::PsychicFileResponse(PsychicRequest *request, File content, const String& path, const String& contentType, bool download)
|
||||
: PsychicResponse(request) {
|
||||
_path = path;
|
||||
|
||||
if (!download && String(content.name()).endsWith(".gz") && !path.endsWith(".gz")) {
|
||||
addHeader("Content-Encoding", "gzip");
|
||||
_sendContentLength = true;
|
||||
_chunked = false;
|
||||
if(!download && String(content.name()).endsWith(".gz") && !path.endsWith(".gz")){
|
||||
addHeader("Content-Encoding", "gzip");
|
||||
_sendContentLength = true;
|
||||
_chunked = false;
|
||||
}
|
||||
|
||||
_content = content;
|
||||
_contentLength = _content.size();
|
||||
setContentLength(_contentLength);
|
||||
|
||||
if(contentType == "")
|
||||
_setContentType(path);
|
||||
else
|
||||
_contentType = contentType;
|
||||
setContentType(_contentType.c_str());
|
||||
|
||||
int filenameStart = path.lastIndexOf('/') + 1;
|
||||
char buf[26+path.length()-filenameStart];
|
||||
char* filename = (char*)path.c_str() + filenameStart;
|
||||
|
||||
if(download) {
|
||||
snprintf(buf, sizeof (buf), "attachment; filename=\"%s\"", filename);
|
||||
} else {
|
||||
snprintf(buf, sizeof (buf), "inline; filename=\"%s\"", filename);
|
||||
}
|
||||
addHeader("Content-Disposition", buf);
|
||||
}
|
||||
|
||||
PsychicFileResponse::~PsychicFileResponse()
|
||||
{
|
||||
if(_content)
|
||||
_content.close();
|
||||
}
|
||||
|
||||
void PsychicFileResponse::_setContentType(const String& path){
|
||||
if (path.endsWith(".html")) _contentType = "text/html";
|
||||
else if (path.endsWith(".htm")) _contentType = "text/html";
|
||||
else if (path.endsWith(".css")) _contentType = "text/css";
|
||||
else if (path.endsWith(".json")) _contentType = "application/json";
|
||||
else if (path.endsWith(".js")) _contentType = "application/javascript";
|
||||
else if (path.endsWith(".png")) _contentType = "image/png";
|
||||
else if (path.endsWith(".gif")) _contentType = "image/gif";
|
||||
else if (path.endsWith(".jpg")) _contentType = "image/jpeg";
|
||||
else if (path.endsWith(".ico")) _contentType = "image/x-icon";
|
||||
else if (path.endsWith(".svg")) _contentType = "image/svg+xml";
|
||||
else if (path.endsWith(".eot")) _contentType = "font/eot";
|
||||
else if (path.endsWith(".woff")) _contentType = "font/woff";
|
||||
else if (path.endsWith(".woff2")) _contentType = "font/woff2";
|
||||
else if (path.endsWith(".ttf")) _contentType = "font/ttf";
|
||||
else if (path.endsWith(".xml")) _contentType = "text/xml";
|
||||
else if (path.endsWith(".pdf")) _contentType = "application/pdf";
|
||||
else if (path.endsWith(".zip")) _contentType = "application/zip";
|
||||
else if(path.endsWith(".gz")) _contentType = "application/x-gzip";
|
||||
else _contentType = "text/plain";
|
||||
}
|
||||
|
||||
esp_err_t PsychicFileResponse::send()
|
||||
{
|
||||
esp_err_t err = ESP_OK;
|
||||
|
||||
//just send small files directly
|
||||
size_t size = getContentLength();
|
||||
if (size < FILE_CHUNK_SIZE)
|
||||
{
|
||||
uint8_t *buffer = (uint8_t *)malloc(size);
|
||||
int readSize = _content.readBytes((char *)buffer, size);
|
||||
|
||||
this->setContent(buffer, size);
|
||||
err = PsychicResponse::send();
|
||||
|
||||
free(buffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Retrieve the pointer to scratch buffer for temporary storage */
|
||||
char *chunk = (char *)malloc(FILE_CHUNK_SIZE);
|
||||
if (chunk == 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;
|
||||
}
|
||||
|
||||
_content = content;
|
||||
_contentLength = _content.size();
|
||||
setContentLength(_contentLength);
|
||||
this->sendHeaders();
|
||||
|
||||
if (contentType == "")
|
||||
_setContentType(path);
|
||||
else
|
||||
_contentType = contentType;
|
||||
setContentType(_contentType.c_str());
|
||||
|
||||
int filenameStart = path.lastIndexOf('/') + 1;
|
||||
char buf[26 + path.length() - filenameStart];
|
||||
char * filename = (char *)path.c_str() + filenameStart;
|
||||
|
||||
if (download) {
|
||||
snprintf(buf, sizeof(buf), "attachment; filename=\"%s\"", filename);
|
||||
} else {
|
||||
snprintf(buf, sizeof(buf), "inline; filename=\"%s\"", filename);
|
||||
}
|
||||
addHeader("Content-Disposition", buf);
|
||||
}
|
||||
|
||||
PsychicFileResponse::~PsychicFileResponse() {
|
||||
if (_content)
|
||||
_content.close();
|
||||
}
|
||||
|
||||
void PsychicFileResponse::_setContentType(const String & path) {
|
||||
if (path.endsWith(".html"))
|
||||
_contentType = "text/html";
|
||||
else if (path.endsWith(".htm"))
|
||||
_contentType = "text/html";
|
||||
else if (path.endsWith(".css"))
|
||||
_contentType = "text/css";
|
||||
else if (path.endsWith(".json"))
|
||||
_contentType = "application/json";
|
||||
else if (path.endsWith(".js"))
|
||||
_contentType = "application/javascript";
|
||||
else if (path.endsWith(".png"))
|
||||
_contentType = "image/png";
|
||||
else if (path.endsWith(".gif"))
|
||||
_contentType = "image/gif";
|
||||
else if (path.endsWith(".jpg"))
|
||||
_contentType = "image/jpeg";
|
||||
else if (path.endsWith(".ico"))
|
||||
_contentType = "image/x-icon";
|
||||
else if (path.endsWith(".svg"))
|
||||
_contentType = "image/svg+xml";
|
||||
else if (path.endsWith(".eot"))
|
||||
_contentType = "font/eot";
|
||||
else if (path.endsWith(".woff"))
|
||||
_contentType = "font/woff";
|
||||
else if (path.endsWith(".woff2"))
|
||||
_contentType = "font/woff2";
|
||||
else if (path.endsWith(".ttf"))
|
||||
_contentType = "font/ttf";
|
||||
else if (path.endsWith(".xml"))
|
||||
_contentType = "text/xml";
|
||||
else if (path.endsWith(".pdf"))
|
||||
_contentType = "application/pdf";
|
||||
else if (path.endsWith(".zip"))
|
||||
_contentType = "application/zip";
|
||||
else if (path.endsWith(".gz"))
|
||||
_contentType = "application/x-gzip";
|
||||
else
|
||||
_contentType = "text/plain";
|
||||
}
|
||||
|
||||
esp_err_t PsychicFileResponse::send() {
|
||||
esp_err_t err = ESP_OK;
|
||||
|
||||
//just send small files directly
|
||||
size_t size = getContentLength();
|
||||
if (size < FILE_CHUNK_SIZE) {
|
||||
uint8_t * buffer = (uint8_t *)malloc(size);
|
||||
int readSize = _content.readBytes((char *)buffer, size);
|
||||
|
||||
this->setContent(buffer, size);
|
||||
err = PsychicResponse::send();
|
||||
|
||||
free(buffer);
|
||||
} else {
|
||||
/* Retrieve the pointer to scratch buffer for temporary storage */
|
||||
char * chunk = (char *)malloc(FILE_CHUNK_SIZE);
|
||||
if (chunk == 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;
|
||||
size_t chunksize;
|
||||
do {
|
||||
/* Read file in chunks into the scratch buffer */
|
||||
chunksize = _content.readBytes(chunk, FILE_CHUNK_SIZE);
|
||||
if (chunksize > 0)
|
||||
{
|
||||
err = this->sendChunk((uint8_t *)chunk, chunksize);
|
||||
if (err != ESP_OK)
|
||||
break;
|
||||
}
|
||||
|
||||
this->sendHeaders();
|
||||
/* Keep looping till the whole file is sent */
|
||||
} while (chunksize != 0);
|
||||
|
||||
size_t chunksize;
|
||||
do {
|
||||
/* Read file in chunks into the scratch buffer */
|
||||
chunksize = _content.readBytes(chunk, FILE_CHUNK_SIZE);
|
||||
if (chunksize > 0) {
|
||||
err = this->sendChunk((uint8_t *)chunk, chunksize);
|
||||
if (err != ESP_OK)
|
||||
break;
|
||||
}
|
||||
//keep track of our memory
|
||||
free(chunk);
|
||||
|
||||
/* Keep looping till the whole file is sent */
|
||||
} while (chunksize != 0);
|
||||
|
||||
//keep track of our memory
|
||||
free(chunk);
|
||||
|
||||
if (err == ESP_OK) {
|
||||
ESP_LOGI(PH_TAG, "File sending complete");
|
||||
this->finishChunking();
|
||||
}
|
||||
|
||||
/* Close file after sending complete */
|
||||
_content.close();
|
||||
if (err == ESP_OK)
|
||||
{
|
||||
ESP_LOGI(PH_TAG, "File sending complete");
|
||||
this->finishChunking();
|
||||
}
|
||||
|
||||
return err;
|
||||
/* Close file after sending complete */
|
||||
_content.close();
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -6,21 +6,20 @@
|
||||
|
||||
class PsychicRequest;
|
||||
|
||||
class PsychicFileResponse : public PsychicResponse {
|
||||
using File = fs::File;
|
||||
using FS = fs::FS;
|
||||
|
||||
class PsychicFileResponse: public PsychicResponse
|
||||
{
|
||||
using File = fs::File;
|
||||
using FS = fs::FS;
|
||||
private:
|
||||
File _content;
|
||||
File _content;
|
||||
String _path;
|
||||
bool _sendContentLength;
|
||||
bool _chunked;
|
||||
bool _sendContentLength;
|
||||
bool _chunked;
|
||||
String _contentType;
|
||||
void _setContentType(const String & path);
|
||||
|
||||
void _setContentType(const String& path);
|
||||
public:
|
||||
PsychicFileResponse(PsychicRequest * request, FS & fs, const String & path, const String & contentType = String(), bool download = false);
|
||||
PsychicFileResponse(PsychicRequest * request, File content, const String & path, const String & contentType = String(), bool download = false);
|
||||
PsychicFileResponse(PsychicRequest *request, FS &fs, const String& path, const String& contentType=String(), bool download=false);
|
||||
PsychicFileResponse(PsychicRequest *request, File content, const String& path, const String& contentType=String(), bool download=false);
|
||||
~PsychicFileResponse();
|
||||
esp_err_t send();
|
||||
};
|
||||
|
||||
@@ -5,63 +5,57 @@
|
||||
#include "PsychicRequest.h"
|
||||
|
||||
class PsychicEndpoint;
|
||||
class PsychicHttpServer;
|
||||
|
||||
/*
|
||||
* HANDLER :: Can be attached to any endpoint or as a generic request handler.
|
||||
*/
|
||||
|
||||
class PsychicHandler {
|
||||
friend PsychicEndpoint;
|
||||
friend PsychicEndpoint;
|
||||
|
||||
protected:
|
||||
PsychicRequestFilterFunction _filter;
|
||||
PsychicHttpServer * _server;
|
||||
PsychicHttpServer *_server;
|
||||
|
||||
String _username;
|
||||
String _password;
|
||||
String _username;
|
||||
String _password;
|
||||
HTTPAuthMethod _method;
|
||||
String _realm;
|
||||
String _authFailMsg;
|
||||
String _realm;
|
||||
String _authFailMsg;
|
||||
|
||||
std::list<PsychicClient *> _clients;
|
||||
std::list<PsychicClient*> _clients;
|
||||
|
||||
public:
|
||||
PsychicHandler();
|
||||
~PsychicHandler();
|
||||
|
||||
PsychicHandler * setFilter(PsychicRequestFilterFunction fn);
|
||||
bool filter(PsychicRequest * request);
|
||||
PsychicHandler* setFilter(PsychicRequestFilterFunction fn);
|
||||
bool filter(PsychicRequest *request);
|
||||
|
||||
PsychicHandler *
|
||||
setAuthentication(const char * username, const char * password, HTTPAuthMethod method = BASIC_AUTH, const char * realm = "", const char * authFailMsg = "");
|
||||
bool needsAuthentication(PsychicRequest * request);
|
||||
esp_err_t authenticate(PsychicRequest * request);
|
||||
PsychicHandler* setAuthentication(const char *username, const char *password, HTTPAuthMethod method = BASIC_AUTH, const char *realm = "", const char *authFailMsg = "");
|
||||
bool needsAuthentication(PsychicRequest *request);
|
||||
esp_err_t authenticate(PsychicRequest *request);
|
||||
|
||||
virtual bool isWebSocket() {
|
||||
return false;
|
||||
};
|
||||
virtual bool isWebSocket() { return false; };
|
||||
|
||||
PsychicClient * checkForNewClient(PsychicClient * client);
|
||||
void checkForClosedClient(PsychicClient * client);
|
||||
PsychicClient * checkForNewClient(PsychicClient *client);
|
||||
void checkForClosedClient(PsychicClient *client);
|
||||
|
||||
virtual void addClient(PsychicClient * client);
|
||||
virtual void removeClient(PsychicClient * client);
|
||||
virtual void addClient(PsychicClient *client);
|
||||
virtual void removeClient(PsychicClient *client);
|
||||
virtual PsychicClient * getClient(int socket);
|
||||
virtual PsychicClient * getClient(PsychicClient * client);
|
||||
virtual void openCallback(PsychicClient * client){};
|
||||
virtual void closeCallback(PsychicClient * client){};
|
||||
virtual PsychicClient * getClient(PsychicClient *client);
|
||||
virtual void openCallback(PsychicClient *client) {};
|
||||
virtual void closeCallback(PsychicClient *client) {};
|
||||
|
||||
bool hasClient(PsychicClient * client);
|
||||
int count() {
|
||||
return _clients.size();
|
||||
};
|
||||
const std::list<PsychicClient *> & getClientList();
|
||||
bool hasClient(PsychicClient *client);
|
||||
int count() { return _clients.size(); };
|
||||
const std::list<PsychicClient*>& getClientList();
|
||||
|
||||
//derived classes must implement these functions
|
||||
virtual bool canHandle(PsychicRequest * request) {
|
||||
return true;
|
||||
};
|
||||
virtual esp_err_t handleRequest(PsychicRequest * request) = 0;
|
||||
virtual bool canHandle(PsychicRequest *request) { return true; };
|
||||
virtual esp_err_t handleRequest(PsychicRequest *request) = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,7 +1,7 @@
|
||||
#ifndef PsychicHttp_h
|
||||
#define PsychicHttp_h
|
||||
|
||||
//#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 <http_status.h>
|
||||
#include "PsychicHttpServer.h"
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "PsychicHandler.h"
|
||||
#include "PsychicStaticFileHandler.h"
|
||||
#include "PsychicFileResponse.h"
|
||||
#include "PsychicStreamResponse.h"
|
||||
#include "PsychicUploadHandler.h"
|
||||
#include "PsychicWebSocket.h"
|
||||
#include "PsychicEventSource.h"
|
||||
|
||||
@@ -4,28 +4,29 @@
|
||||
#include "PsychicWebHandler.h"
|
||||
#include "PsychicStaticFileHandler.h"
|
||||
#include "PsychicWebSocket.h"
|
||||
#include "PsychicJson.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;
|
||||
PsychicHttpServer::PsychicHttpServer() :
|
||||
_onOpen(NULL),
|
||||
_onClose(NULL)
|
||||
{
|
||||
maxRequestBodySize = MAX_REQUEST_BODY_SIZE;
|
||||
maxUploadSize = MAX_UPLOAD_SIZE;
|
||||
|
||||
defaultEndpoint = new PsychicEndpoint(this, HTTP_GET, "");
|
||||
onNotFound(PsychicHttpServer::defaultNotFoundHandler);
|
||||
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;
|
||||
//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
|
||||
#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
|
||||
@@ -33,297 +34,333 @@ PsychicHttpServer::PsychicHttpServer()
|
||||
// longer be responsive.
|
||||
config.max_open_sockets = ASYNC_WORKER_COUNT + 1;
|
||||
config.lru_purge_enable = true;
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
PsychicHttpServer::~PsychicHttpServer() {
|
||||
for (auto * client : _clients)
|
||||
delete (client);
|
||||
_clients.clear();
|
||||
PsychicHttpServer::~PsychicHttpServer()
|
||||
{
|
||||
for (auto *client : _clients)
|
||||
delete(client);
|
||||
_clients.clear();
|
||||
|
||||
for (auto * endpoint : _endpoints)
|
||||
delete (endpoint);
|
||||
_endpoints.clear();
|
||||
for (auto *endpoint : _endpoints)
|
||||
delete(endpoint);
|
||||
_endpoints.clear();
|
||||
|
||||
for (auto * handler : _handlers)
|
||||
delete (handler);
|
||||
_handlers.clear();
|
||||
for (auto *handler : _handlers)
|
||||
delete(handler);
|
||||
_handlers.clear();
|
||||
|
||||
delete defaultEndpoint;
|
||||
delete defaultEndpoint;
|
||||
}
|
||||
|
||||
void PsychicHttpServer::destroy(void * ctx) {
|
||||
PsychicHttpServer * temp = (PsychicHttpServer *)ctx;
|
||||
delete temp;
|
||||
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;
|
||||
esp_err_t PsychicHttpServer::listen(uint16_t port)
|
||||
{
|
||||
this->_use_ssl = false;
|
||||
this->config.server_port = port;
|
||||
|
||||
return this->_start();
|
||||
return this->_start();
|
||||
}
|
||||
|
||||
esp_err_t PsychicHttpServer::_start() {
|
||||
esp_err_t ret;
|
||||
esp_err_t PsychicHttpServer::_start()
|
||||
{
|
||||
esp_err_t ret;
|
||||
|
||||
#ifdef ENABLE_ASYNC
|
||||
#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));
|
||||
#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);
|
||||
return httpd_start(&this->server, &this->config);
|
||||
}
|
||||
|
||||
void PsychicHttpServer::stop() {
|
||||
httpd_stop(this->server);
|
||||
void PsychicHttpServer::stop()
|
||||
{
|
||||
httpd_stop(this->server);
|
||||
}
|
||||
|
||||
PsychicHandler & PsychicHttpServer::addHandler(PsychicHandler * handler) {
|
||||
_handlers.push_back(handler);
|
||||
return *handler;
|
||||
PsychicHandler& PsychicHttpServer::addHandler(PsychicHandler* handler){
|
||||
_handlers.push_back(handler);
|
||||
return *handler;
|
||||
}
|
||||
|
||||
void PsychicHttpServer::removeHandler(PsychicHandler * handler) {
|
||||
_handlers.remove(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) {
|
||||
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)
|
||||
{
|
||||
PsychicWebHandler *handler = new PsychicWebHandler();
|
||||
|
||||
return on(uri, method, handler);
|
||||
}
|
||||
|
||||
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);
|
||||
PsychicEndpoint* PsychicHttpServer::on(const char* uri, PsychicHandler *handler)
|
||||
{
|
||||
return on(uri, HTTP_GET, handler);
|
||||
}
|
||||
|
||||
// added by Proddy
|
||||
PsychicEndpoint * PsychicHttpServer::on(const char * uri, http_method method, PsychicJsonRequestCallback fn) {
|
||||
PsychicJsonHandler * handler = new PsychicJsonHandler(fn);
|
||||
PsychicEndpoint* PsychicHttpServer::on(const char* uri, http_method method, PsychicHandler *handler)
|
||||
{
|
||||
//make our endpoint
|
||||
PsychicEndpoint *endpoint = new PsychicEndpoint(this, method, uri);
|
||||
|
||||
return on(uri, method, handler);
|
||||
//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 failed (%s)", esp_err_to_name(ret));
|
||||
|
||||
//save it for later
|
||||
_endpoints.push_back(endpoint);
|
||||
|
||||
return endpoint;
|
||||
}
|
||||
|
||||
PsychicEndpoint * PsychicHttpServer::on(const char * uri, http_method method) {
|
||||
PsychicWebHandler * handler = new PsychicWebHandler();
|
||||
|
||||
return on(uri, method, handler);
|
||||
PsychicEndpoint* PsychicHttpServer::on(const char* uri, PsychicHttpRequestCallback fn)
|
||||
{
|
||||
return on(uri, HTTP_GET, fn);
|
||||
}
|
||||
|
||||
PsychicEndpoint * PsychicHttpServer::on(const char * uri, PsychicHandler * handler) {
|
||||
return on(uri, HTTP_GET, handler);
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
PsychicEndpoint* PsychicHttpServer::on(const char* uri, PsychicJsonRequestCallback fn)
|
||||
{
|
||||
return on(uri, HTTP_GET, fn);
|
||||
}
|
||||
|
||||
void PsychicHttpServer::onNotFound(PsychicHttpRequestCallback fn) {
|
||||
PsychicWebHandler * handler = new PsychicWebHandler();
|
||||
handler->onRequest(fn);
|
||||
PsychicEndpoint* PsychicHttpServer::on(const char* uri, http_method method, PsychicJsonRequestCallback fn)
|
||||
{
|
||||
//these basic requests need a basic web handler
|
||||
PsychicJsonHandler *handler = new PsychicJsonHandler();
|
||||
handler->onRequest(fn);
|
||||
|
||||
this->defaultEndpoint->setHandler(handler);
|
||||
return on(uri, method, 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);
|
||||
void PsychicHttpServer::onNotFound(PsychicHttpRequestCallback fn)
|
||||
{
|
||||
PsychicWebHandler *handler = new PsychicWebHandler();
|
||||
handler->onRequest(fn);
|
||||
|
||||
//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);
|
||||
}
|
||||
}
|
||||
this->defaultEndpoint->setHandler(handler);
|
||||
}
|
||||
|
||||
//nothing found, give it to our defaultEndpoint
|
||||
PsychicHandler * handler = server->defaultEndpoint->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);
|
||||
}
|
||||
}
|
||||
|
||||
//not sure how we got this far.
|
||||
return ESP_ERR_HTTPD_INVALID_REQ;
|
||||
//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.");
|
||||
esp_err_t PsychicHttpServer::defaultNotFoundHandler(PsychicRequest *request)
|
||||
{
|
||||
request->reply(404, "text/html", "That URI does not exist.");
|
||||
|
||||
return ESP_OK;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void PsychicHttpServer::onOpen(PsychicClientCallback handler) {
|
||||
this->_onOpen = handler;
|
||||
this->_onOpen = handler;
|
||||
}
|
||||
|
||||
esp_err_t PsychicHttpServer::openCallback(httpd_handle_t hd, int sockfd) {
|
||||
ESP_LOGI(PH_TAG, "New client connected %d", sockfd);
|
||||
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);
|
||||
//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);
|
||||
}
|
||||
//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);
|
||||
//user callback
|
||||
if (server->_onOpen != NULL)
|
||||
server->_onOpen(client);
|
||||
|
||||
return ESP_OK;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void PsychicHttpServer::onClose(PsychicClientCallback handler) {
|
||||
this->_onClose = handler;
|
||||
this->_onClose = handler;
|
||||
}
|
||||
|
||||
void PsychicHttpServer::closeCallback(httpd_handle_t hd, int sockfd) {
|
||||
ESP_LOGI(PH_TAG, "Client disconnected %d", sockfd);
|
||||
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);
|
||||
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);
|
||||
}
|
||||
//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);
|
||||
//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);
|
||||
//remove it from our list
|
||||
server->removeClient(client);
|
||||
}
|
||||
else
|
||||
ESP_LOGE(PH_TAG, "No client record %d", sockfd);
|
||||
|
||||
//finally close it out.
|
||||
close(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);
|
||||
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;
|
||||
return handler;
|
||||
}
|
||||
|
||||
void PsychicHttpServer::addClient(PsychicClient * client) {
|
||||
_clients.push_back(client);
|
||||
void PsychicHttpServer::addClient(PsychicClient *client) {
|
||||
_clients.push_back(client);
|
||||
}
|
||||
|
||||
void PsychicHttpServer::removeClient(PsychicClient * client) {
|
||||
_clients.remove(client);
|
||||
delete 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;
|
||||
for (PsychicClient * client : _clients)
|
||||
if (client->socket() == socket)
|
||||
return client;
|
||||
|
||||
return NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
PsychicClient * PsychicHttpServer::getClient(httpd_req_t * req) {
|
||||
return getClient(httpd_req_to_sockfd(req));
|
||||
PsychicClient * PsychicHttpServer::getClient(httpd_req_t *req) {
|
||||
return getClient(httpd_req_to_sockfd(req));
|
||||
}
|
||||
|
||||
bool PsychicHttpServer::hasClient(int socket) {
|
||||
return getClient(socket) != NULL;
|
||||
return getClient(socket) != NULL;
|
||||
}
|
||||
|
||||
const std::list<PsychicClient *> & PsychicHttpServer::getClientList() {
|
||||
return _clients;
|
||||
const std::list<PsychicClient*>& PsychicHttpServer::getClientList() {
|
||||
return _clients;
|
||||
}
|
||||
|
||||
bool ON_STA_FILTER(PsychicRequest * request) {
|
||||
return WiFi.localIP() == request->client()->localIP();
|
||||
bool ON_STA_FILTER(PsychicRequest *request) {
|
||||
return WiFi.localIP() == request->client()->localIP();
|
||||
}
|
||||
|
||||
bool ON_AP_FILTER(PsychicRequest * request) {
|
||||
return WiFi.softAPIP() == 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 "";
|
||||
}
|
||||
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];
|
||||
}
|
||||
}
|
||||
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
|
||||
decoded[j] = '\0'; // Null-terminate the decoded string
|
||||
|
||||
String output(decoded);
|
||||
free(decoded);
|
||||
String output(decoded);
|
||||
free(decoded);
|
||||
|
||||
return output;
|
||||
return output;
|
||||
}
|
||||
@@ -4,27 +4,23 @@
|
||||
#include "PsychicCore.h"
|
||||
#include "PsychicClient.h"
|
||||
#include "PsychicHandler.h"
|
||||
#include <ArduinoJson.h> // added by proddy
|
||||
|
||||
class PsychicEndpoint;
|
||||
class PsychicHandler;
|
||||
class PsychicStaticFileHandler;
|
||||
|
||||
//callback definitions
|
||||
typedef std::function<esp_err_t(PsychicRequest * request)> PsychicHttpRequestCallback;
|
||||
typedef std::function<esp_err_t(PsychicRequest * request, JsonVariant & json)> PsychicJsonRequestCallback; // added by proddy
|
||||
|
||||
class PsychicHttpServer {
|
||||
class PsychicHttpServer
|
||||
{
|
||||
protected:
|
||||
bool _use_ssl = false;
|
||||
std::list<PsychicEndpoint *> _endpoints;
|
||||
std::list<PsychicHandler *> _handlers;
|
||||
std::list<PsychicClient *> _clients;
|
||||
bool _use_ssl = false;
|
||||
std::list<PsychicEndpoint*> _endpoints;
|
||||
std::list<PsychicHandler*> _handlers;
|
||||
std::list<PsychicClient*> _clients;
|
||||
|
||||
PsychicClientCallback _onOpen;
|
||||
PsychicClientCallback _onClose;
|
||||
|
||||
esp_err_t _start();
|
||||
esp_err_t _start();
|
||||
virtual esp_err_t _startServer();
|
||||
|
||||
public:
|
||||
@@ -39,48 +35,47 @@ class PsychicHttpServer {
|
||||
unsigned long maxUploadSize;
|
||||
unsigned long maxRequestBodySize;
|
||||
|
||||
PsychicEndpoint * defaultEndpoint;
|
||||
PsychicEndpoint *defaultEndpoint;
|
||||
|
||||
static void destroy(void * ctx);
|
||||
static void destroy(void *ctx);
|
||||
|
||||
esp_err_t listen(uint16_t port);
|
||||
|
||||
virtual void stop();
|
||||
|
||||
PsychicHandler & addHandler(PsychicHandler * handler);
|
||||
void removeHandler(PsychicHandler * handler);
|
||||
PsychicHandler& addHandler(PsychicHandler* handler);
|
||||
void removeHandler(PsychicHandler* handler);
|
||||
|
||||
void addClient(PsychicClient * client);
|
||||
void removeClient(PsychicClient * client);
|
||||
PsychicClient * getClient(int socket);
|
||||
PsychicClient * getClient(httpd_req_t * req);
|
||||
bool hasClient(int socket);
|
||||
int count() {
|
||||
return _clients.size();
|
||||
};
|
||||
const std::list<PsychicClient *> & getClientList();
|
||||
void addClient(PsychicClient *client);
|
||||
void removeClient(PsychicClient *client);
|
||||
PsychicClient* getClient(int socket);
|
||||
PsychicClient* getClient(httpd_req_t *req);
|
||||
bool hasClient(int socket);
|
||||
int count() { return _clients.size(); };
|
||||
const std::list<PsychicClient*>& getClientList();
|
||||
|
||||
PsychicEndpoint * on(const char * uri);
|
||||
PsychicEndpoint * on(const char * uri, http_method method);
|
||||
PsychicEndpoint * on(const char * uri, PsychicHttpRequestCallback onRequest);
|
||||
PsychicEndpoint * on(const char * uri, http_method method, PsychicHttpRequestCallback onRequest);
|
||||
PsychicEndpoint * on(const char * uri, PsychicHandler * handler);
|
||||
PsychicEndpoint * on(const char * uri, http_method method, PsychicHandler * handler);
|
||||
PsychicEndpoint * on(const char * uri, http_method method, PsychicJsonRequestCallback onRequest); // added proddy
|
||||
PsychicEndpoint* on(const char* uri);
|
||||
PsychicEndpoint* on(const char* uri, http_method method);
|
||||
PsychicEndpoint* on(const char* uri, PsychicHandler *handler);
|
||||
PsychicEndpoint* on(const char* uri, http_method method, PsychicHandler *handler);
|
||||
PsychicEndpoint* on(const char* uri, PsychicHttpRequestCallback onRequest);
|
||||
PsychicEndpoint* on(const char* uri, http_method method, PsychicHttpRequestCallback onRequest);
|
||||
PsychicEndpoint* on(const char* uri, PsychicJsonRequestCallback onRequest);
|
||||
PsychicEndpoint* on(const char* uri, http_method method, PsychicJsonRequestCallback onRequest);
|
||||
|
||||
static esp_err_t notFoundHandler(httpd_req_t * req, httpd_err_code_t err);
|
||||
static esp_err_t defaultNotFoundHandler(PsychicRequest * request);
|
||||
void onNotFound(PsychicHttpRequestCallback fn);
|
||||
static esp_err_t notFoundHandler(httpd_req_t *req, httpd_err_code_t err);
|
||||
static esp_err_t defaultNotFoundHandler(PsychicRequest *request);
|
||||
void onNotFound(PsychicHttpRequestCallback fn);
|
||||
|
||||
void onOpen(PsychicClientCallback handler);
|
||||
void onClose(PsychicClientCallback handler);
|
||||
void onOpen(PsychicClientCallback handler);
|
||||
void onClose(PsychicClientCallback handler);
|
||||
static esp_err_t openCallback(httpd_handle_t hd, int sockfd);
|
||||
static void closeCallback(httpd_handle_t hd, int sockfd);
|
||||
static void closeCallback(httpd_handle_t hd, int sockfd);
|
||||
|
||||
PsychicStaticFileHandler * serveStatic(const char * uri, fs::FS & fs, const char * path, const char * cache_control = NULL);
|
||||
PsychicStaticFileHandler* serveStatic(const char* uri, fs::FS& fs, const char* path, const char* cache_control = NULL);
|
||||
};
|
||||
|
||||
bool ON_STA_FILTER(PsychicRequest * request);
|
||||
bool ON_AP_FILTER(PsychicRequest * request);
|
||||
bool ON_STA_FILTER(PsychicRequest *request);
|
||||
bool ON_AP_FILTER(PsychicRequest *request);
|
||||
|
||||
#endif // PsychicHttpServer_h
|
||||
@@ -1,48 +1,50 @@
|
||||
#include "PsychicHttpsServer.h"
|
||||
|
||||
PsychicHttpsServer::PsychicHttpsServer()
|
||||
: PsychicHttpServer() {
|
||||
//for a SSL server
|
||||
ssl_config = HTTPD_SSL_CONFIG_DEFAULT();
|
||||
ssl_config.httpd.open_fn = PsychicHttpServer::openCallback;
|
||||
ssl_config.httpd.close_fn = PsychicHttpServer::closeCallback;
|
||||
ssl_config.httpd.uri_match_fn = httpd_uri_match_wildcard;
|
||||
ssl_config.httpd.global_user_ctx = this;
|
||||
ssl_config.httpd.global_user_ctx_free_fn = destroy;
|
||||
ssl_config.httpd.max_uri_handlers = 20;
|
||||
PsychicHttpsServer::PsychicHttpsServer() : PsychicHttpServer()
|
||||
{
|
||||
//for a SSL server
|
||||
ssl_config = HTTPD_SSL_CONFIG_DEFAULT();
|
||||
ssl_config.httpd.open_fn = PsychicHttpServer::openCallback;
|
||||
ssl_config.httpd.close_fn = PsychicHttpServer::closeCallback;
|
||||
ssl_config.httpd.uri_match_fn = httpd_uri_match_wildcard;
|
||||
ssl_config.httpd.global_user_ctx = this;
|
||||
ssl_config.httpd.global_user_ctx_free_fn = destroy;
|
||||
ssl_config.httpd.max_uri_handlers = 20;
|
||||
|
||||
// each SSL connection takes about 45kb of heap
|
||||
// a barebones sketch with PsychicHttp has ~150kb of heap available
|
||||
// if we set it higher than 2 and use all the connections, we get lots of memory errors.
|
||||
// not to mention there is no heap left over for the program itself.
|
||||
ssl_config.httpd.max_open_sockets = 2;
|
||||
// each SSL connection takes about 45kb of heap
|
||||
// a barebones sketch with PsychicHttp has ~150kb of heap available
|
||||
// if we set it higher than 2 and use all the connections, we get lots of memory errors.
|
||||
// not to mention there is no heap left over for the program itself.
|
||||
ssl_config.httpd.max_open_sockets = 2;
|
||||
}
|
||||
|
||||
PsychicHttpsServer::~PsychicHttpsServer() {
|
||||
PsychicHttpsServer::~PsychicHttpsServer() {}
|
||||
|
||||
esp_err_t PsychicHttpsServer::listen(uint16_t port, const char *cert, const char *private_key)
|
||||
{
|
||||
this->_use_ssl = true;
|
||||
|
||||
this->ssl_config.port_secure = port;
|
||||
this->ssl_config.cacert_pem = (uint8_t *)cert;
|
||||
this->ssl_config.cacert_len = strlen(cert)+1;
|
||||
this->ssl_config.prvtkey_pem = (uint8_t *)private_key;
|
||||
this->ssl_config.prvtkey_len = strlen(private_key)+1;
|
||||
|
||||
return this->_start();
|
||||
}
|
||||
|
||||
esp_err_t PsychicHttpsServer::listen(uint16_t port, const char * cert, const char * private_key) {
|
||||
this->_use_ssl = true;
|
||||
|
||||
this->ssl_config.port_secure = port;
|
||||
this->ssl_config.cacert_pem = (uint8_t *)cert;
|
||||
this->ssl_config.cacert_len = strlen(cert) + 1;
|
||||
this->ssl_config.prvtkey_pem = (uint8_t *)private_key;
|
||||
this->ssl_config.prvtkey_len = strlen(private_key) + 1;
|
||||
|
||||
return this->_start();
|
||||
esp_err_t PsychicHttpsServer::_startServer()
|
||||
{
|
||||
if (this->_use_ssl)
|
||||
return httpd_ssl_start(&this->server, &this->ssl_config);
|
||||
else
|
||||
return httpd_start(&this->server, &this->config);
|
||||
}
|
||||
|
||||
esp_err_t PsychicHttpsServer::_startServer() {
|
||||
if (this->_use_ssl)
|
||||
return httpd_ssl_start(&this->server, &this->ssl_config);
|
||||
else
|
||||
return httpd_start(&this->server, &this->config);
|
||||
}
|
||||
|
||||
void PsychicHttpsServer::stop() {
|
||||
if (this->_use_ssl)
|
||||
httpd_ssl_stop(this->server);
|
||||
else
|
||||
httpd_stop(this->server);
|
||||
void PsychicHttpsServer::stop()
|
||||
{
|
||||
if (this->_use_ssl)
|
||||
httpd_ssl_stop(this->server);
|
||||
else
|
||||
httpd_stop(this->server);
|
||||
}
|
||||
@@ -6,12 +6,13 @@
|
||||
#include <esp_https_server.h>
|
||||
|
||||
#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
|
||||
|
||||
#define PSY_ENABLE_SSL //you can use this define in your code to enable/disable these features
|
||||
|
||||
class PsychicHttpsServer : public PsychicHttpServer {
|
||||
class PsychicHttpsServer : public PsychicHttpServer
|
||||
{
|
||||
protected:
|
||||
bool _use_ssl = false;
|
||||
|
||||
@@ -22,10 +23,10 @@ class PsychicHttpsServer : public PsychicHttpServer {
|
||||
httpd_ssl_config_t ssl_config;
|
||||
|
||||
using PsychicHttpServer::listen; //keep the regular version
|
||||
esp_err_t listen(uint16_t port, const char * cert, const char * private_key);
|
||||
esp_err_t listen(uint16_t port, const char *cert, const char *private_key);
|
||||
|
||||
virtual esp_err_t _startServer() override final;
|
||||
virtual void stop() override final;
|
||||
virtual void stop() override final;
|
||||
};
|
||||
|
||||
#endif // PsychicHttpsServer_h
|
||||
150
lib/PsychicHttp/src/PsychicJson.DELETEME
Normal file
150
lib/PsychicHttp/src/PsychicJson.DELETEME
Normal file
@@ -0,0 +1,150 @@
|
||||
#include "PsychicJson.h"
|
||||
|
||||
#ifdef ARDUINOJSON_5_COMPATIBILITY
|
||||
PsychicJsonResponse::PsychicJsonResponse(PsychicRequest *request, bool isArray) : PsychicResponse(request)
|
||||
{
|
||||
setContentType(JSON_MIMETYPE);
|
||||
if (isArray)
|
||||
_root = _jsonBuffer.createArray();
|
||||
else
|
||||
_root = _jsonBuffer.createObject();
|
||||
}
|
||||
#else
|
||||
PsychicJsonResponse::PsychicJsonResponse(PsychicRequest *request, bool isArray, size_t maxJsonBufferSize) :
|
||||
PsychicResponse(request),
|
||||
_jsonBuffer(maxJsonBufferSize)
|
||||
{
|
||||
setContentType(JSON_MIMETYPE);
|
||||
if (isArray)
|
||||
_root = _jsonBuffer.createNestedArray();
|
||||
else
|
||||
_root = _jsonBuffer.createNestedObject();
|
||||
}
|
||||
#endif
|
||||
|
||||
JsonVariant &PsychicJsonResponse::getRoot() { return _root; }
|
||||
|
||||
size_t PsychicJsonResponse::getLength()
|
||||
{
|
||||
#ifdef ARDUINOJSON_5_COMPATIBILITY
|
||||
return _root.measureLength();
|
||||
#else
|
||||
return measureJson(_root);
|
||||
#endif
|
||||
}
|
||||
|
||||
esp_err_t PsychicJsonResponse::send()
|
||||
{
|
||||
esp_err_t err = ESP_OK;
|
||||
size_t length = getLength();
|
||||
size_t buffer_size;
|
||||
char *buffer;
|
||||
|
||||
DUMP(length);
|
||||
|
||||
//how big of a buffer do we want?
|
||||
if (length < JSON_BUFFER_SIZE)
|
||||
buffer_size = length+1;
|
||||
else
|
||||
buffer_size = JSON_BUFFER_SIZE;
|
||||
|
||||
DUMP(buffer_size);
|
||||
|
||||
buffer = (char *)malloc(buffer_size);
|
||||
if (buffer == NULL) {
|
||||
httpd_resp_send_err(this->_request->request(), HTTPD_500_INTERNAL_SERVER_ERROR, "Unable to allocate memory.");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
//send it in one shot or no?
|
||||
if (length < JSON_BUFFER_SIZE)
|
||||
{
|
||||
TRACE();
|
||||
|
||||
#ifdef ARDUINOJSON_5_COMPATIBILITY
|
||||
_root.printTo(buffer, buffer_size);
|
||||
#else
|
||||
serializeJson(_root, buffer, buffer_size);
|
||||
#endif
|
||||
|
||||
this->setContent((uint8_t *)buffer, length);
|
||||
this->setContentType(JSON_MIMETYPE);
|
||||
|
||||
err = PsychicResponse::send();
|
||||
}
|
||||
else
|
||||
{
|
||||
//helper class that acts as a stream to print chunked responses
|
||||
ChunkPrinter dest(this, (uint8_t *)buffer, buffer_size);
|
||||
|
||||
//keep our headers
|
||||
this->sendHeaders();
|
||||
|
||||
//these commands write to the ChunkPrinter which does the sending
|
||||
#ifdef ARDUINOJSON_5_COMPATIBILITY
|
||||
_root.printTo(dest);
|
||||
#else
|
||||
serializeJson(_root, dest);
|
||||
#endif
|
||||
|
||||
//send the last bits
|
||||
dest.flush();
|
||||
|
||||
//done with our chunked response too
|
||||
err = this->finishChunking();
|
||||
}
|
||||
|
||||
//let the buffer go
|
||||
free(buffer);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
#ifdef ARDUINOJSON_5_COMPATIBILITY
|
||||
PsychicJsonHandler::PsychicJsonHandler() :
|
||||
_onRequest(NULL)
|
||||
{};
|
||||
|
||||
PsychicJsonHandler::PsychicJsonHandler(PsychicJsonRequestCallback onRequest) :
|
||||
_onRequest(onRequest)
|
||||
{}
|
||||
#else
|
||||
PsychicJsonHandler::PsychicJsonHandler(size_t maxJsonBufferSize) :
|
||||
_onRequest(NULL),
|
||||
_maxJsonBufferSize(maxJsonBufferSize)
|
||||
{};
|
||||
|
||||
PsychicJsonHandler::PsychicJsonHandler(PsychicJsonRequestCallback onRequest, size_t maxJsonBufferSize) :
|
||||
_onRequest(onRequest),
|
||||
_maxJsonBufferSize(maxJsonBufferSize)
|
||||
{}
|
||||
#endif
|
||||
|
||||
void PsychicJsonHandler::onRequest(PsychicJsonRequestCallback fn) { _onRequest = fn; }
|
||||
|
||||
esp_err_t PsychicJsonHandler::handleRequest(PsychicRequest *request)
|
||||
{
|
||||
//process basic stuff
|
||||
PsychicWebHandler::handleRequest(request);
|
||||
|
||||
if (_onRequest)
|
||||
{
|
||||
#ifdef ARDUINOJSON_5_COMPATIBILITY
|
||||
DynamicJsonBuffer jsonBuffer;
|
||||
JsonVariant json = jsonBuffer.parse();
|
||||
if (!json.success())
|
||||
return request->reply(400);
|
||||
#else
|
||||
DynamicJsonDocument jsonBuffer(this->_maxJsonBufferSize);
|
||||
DeserializationError error = deserializeJson(jsonBuffer, request->body());
|
||||
if (error)
|
||||
return request->reply(400);
|
||||
|
||||
JsonVariant json = jsonBuffer.as<JsonVariant>();
|
||||
#endif
|
||||
|
||||
return _onRequest(request, json);
|
||||
}
|
||||
else
|
||||
return request->reply(500);
|
||||
}
|
||||
@@ -98,10 +98,12 @@ class PsychicJsonResponse : public PsychicResponse {
|
||||
// }
|
||||
|
||||
virtual esp_err_t send() override {
|
||||
esp_err_t err = ESP_OK;
|
||||
size_t length = getLength();
|
||||
size_t buffer_size;
|
||||
char * buffer;
|
||||
esp_err_t err = ESP_OK;
|
||||
// size_t length = getLength();
|
||||
size_t length = JSON_BUFFER_SIZE; // TODO force chunking
|
||||
|
||||
size_t buffer_size;
|
||||
char * buffer;
|
||||
|
||||
DUMP(length);
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "PsychicRequest.h"
|
||||
#include "PsychicResponse.h"
|
||||
#include <http_status.h>
|
||||
#include "http_status.h"
|
||||
#include "PsychicHttpServer.h"
|
||||
|
||||
|
||||
PsychicRequest::PsychicRequest(PsychicHttpServer *server, httpd_req_t *req) :
|
||||
_server(server),
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "PsychicHttpServer.h"
|
||||
#include "PsychicClient.h"
|
||||
#include "PsychicWebParameter.h"
|
||||
#include "PsychicResponse.h"
|
||||
|
||||
typedef std::map<String, String> SessionData;
|
||||
|
||||
|
||||
@@ -2,137 +2,154 @@
|
||||
#include "PsychicRequest.h"
|
||||
#include <http_status.h>
|
||||
|
||||
PsychicResponse::PsychicResponse(PsychicRequest * request)
|
||||
: _request(request)
|
||||
, _code(200)
|
||||
, _status("")
|
||||
, _contentLength(0)
|
||||
, _body("") {
|
||||
PsychicResponse::PsychicResponse(PsychicRequest *request) :
|
||||
_request(request),
|
||||
_code(200),
|
||||
_status(""),
|
||||
_contentLength(0),
|
||||
_body("")
|
||||
{
|
||||
}
|
||||
|
||||
PsychicResponse::~PsychicResponse() {
|
||||
//clean up our header variables. we have to do this since httpd_resp_send doesn't store copies
|
||||
for (HTTPHeader header : _headers) {
|
||||
free(header.field);
|
||||
free(header.value);
|
||||
}
|
||||
_headers.clear();
|
||||
PsychicResponse::~PsychicResponse()
|
||||
{
|
||||
//clean up our header variables. we have to do this since httpd_resp_send doesn't store copies
|
||||
for (HTTPHeader header : _headers)
|
||||
{
|
||||
free(header.field);
|
||||
free(header.value);
|
||||
}
|
||||
_headers.clear();
|
||||
}
|
||||
|
||||
void PsychicResponse::addHeader(const char * field, const char * value) {
|
||||
//these get freed during send()
|
||||
HTTPHeader header;
|
||||
header.field = (char *)malloc(strlen(field) + 1);
|
||||
header.value = (char *)malloc(strlen(value) + 1);
|
||||
void PsychicResponse::addHeader(const char *field, const char *value)
|
||||
{
|
||||
//these get freed during send()
|
||||
HTTPHeader header;
|
||||
header.field =(char *)malloc(strlen(field)+1);
|
||||
header.value = (char *)malloc(strlen(value)+1);
|
||||
|
||||
strlcpy(header.field, field, strlen(field) + 1);
|
||||
strlcpy(header.value, value, strlen(value) + 1);
|
||||
strlcpy(header.field, field, strlen(field)+1);
|
||||
strlcpy(header.value, value, strlen(value)+1);
|
||||
|
||||
_headers.push_back(header);
|
||||
_headers.push_back(header);
|
||||
}
|
||||
|
||||
void PsychicResponse::setCookie(const char * name, const char * value, unsigned long secondsFromNow, const char * extras) {
|
||||
time_t now = time(nullptr);
|
||||
void PsychicResponse::setCookie(const char *name, const char *value, unsigned long secondsFromNow, const char *extras)
|
||||
{
|
||||
time_t now = time(nullptr);
|
||||
|
||||
String output;
|
||||
output = urlEncode(name) + "=" + urlEncode(value);
|
||||
String output;
|
||||
output = urlEncode(name) + "=" + urlEncode(value);
|
||||
|
||||
//if current time isn't modern, default to using max age
|
||||
if (now < 1700000000)
|
||||
output += "; Max-Age=" + String(secondsFromNow);
|
||||
//otherwise, set an expiration date
|
||||
else {
|
||||
time_t expirationTimestamp = now + secondsFromNow;
|
||||
//if current time isn't modern, default to using max age
|
||||
if (now < 1700000000)
|
||||
output += "; Max-Age=" + String(secondsFromNow);
|
||||
//otherwise, set an expiration date
|
||||
else
|
||||
{
|
||||
time_t expirationTimestamp = now + secondsFromNow;
|
||||
|
||||
// Convert the expiration timestamp to a formatted string for the "expires" attribute
|
||||
struct tm * tmInfo = gmtime(&expirationTimestamp);
|
||||
char expires[30];
|
||||
strftime(expires, sizeof(expires), "%a, %d %b %Y %H:%M:%S GMT", tmInfo);
|
||||
output += "; Expires=" + String(expires);
|
||||
}
|
||||
// Convert the expiration timestamp to a formatted string for the "expires" attribute
|
||||
struct tm* tmInfo = gmtime(&expirationTimestamp);
|
||||
char expires[30];
|
||||
strftime(expires, sizeof(expires), "%a, %d %b %Y %H:%M:%S GMT", tmInfo);
|
||||
output += "; Expires=" + String(expires);
|
||||
}
|
||||
|
||||
//did we get any extras?
|
||||
if (strlen(extras))
|
||||
output += "; " + String(extras);
|
||||
//did we get any extras?
|
||||
if (strlen(extras))
|
||||
output += "; " + String(extras);
|
||||
|
||||
//okay, add it in.
|
||||
addHeader("Set-Cookie", output.c_str());
|
||||
//okay, add it in.
|
||||
addHeader("Set-Cookie", output.c_str());
|
||||
}
|
||||
|
||||
// time_t now = time(nullptr);
|
||||
// // Set the cookie with the "expires" attribute
|
||||
// time_t now = time(nullptr);
|
||||
// // Set the cookie with the "expires" attribute
|
||||
|
||||
void PsychicResponse::setCode(int code) {
|
||||
_code = code;
|
||||
void PsychicResponse::setCode(int code)
|
||||
{
|
||||
_code = code;
|
||||
}
|
||||
|
||||
void PsychicResponse::setContentType(const char * contentType) {
|
||||
httpd_resp_set_type(_request->request(), contentType);
|
||||
void PsychicResponse::setContentType(const char *contentType)
|
||||
{
|
||||
httpd_resp_set_type(_request->request(), contentType);
|
||||
}
|
||||
|
||||
void PsychicResponse::setContent(const char * content) {
|
||||
_body = content;
|
||||
setContentLength(strlen(content));
|
||||
void PsychicResponse::setContent(const char *content)
|
||||
{
|
||||
_body = content;
|
||||
setContentLength(strlen(content));
|
||||
}
|
||||
|
||||
void PsychicResponse::setContent(const uint8_t * content, size_t len) {
|
||||
_body = (char *)content;
|
||||
setContentLength(len);
|
||||
void PsychicResponse::setContent(const uint8_t *content, size_t len)
|
||||
{
|
||||
_body = (char *)content;
|
||||
setContentLength(len);
|
||||
}
|
||||
|
||||
const char * PsychicResponse::getContent() {
|
||||
return _body;
|
||||
const char * PsychicResponse::getContent()
|
||||
{
|
||||
return _body;
|
||||
}
|
||||
|
||||
size_t PsychicResponse::getContentLength() {
|
||||
return _contentLength;
|
||||
size_t PsychicResponse::getContentLength()
|
||||
{
|
||||
return _contentLength;
|
||||
}
|
||||
|
||||
esp_err_t PsychicResponse::send() {
|
||||
//esp-idf makes you set the whole status.
|
||||
sprintf(_status, "%u %s", _code, http_status_reason(_code));
|
||||
httpd_resp_set_status(_request->request(), _status);
|
||||
esp_err_t PsychicResponse::send()
|
||||
{
|
||||
//esp-idf makes you set the whole status.
|
||||
sprintf(_status, "%u %s", _code, http_status_reason(_code));
|
||||
httpd_resp_set_status(_request->request(), _status);
|
||||
|
||||
//our headers too
|
||||
this->sendHeaders();
|
||||
//our headers too
|
||||
this->sendHeaders();
|
||||
|
||||
//now send it off
|
||||
esp_err_t err = httpd_resp_send(_request->request(), getContent(), getContentLength());
|
||||
//now send it off
|
||||
esp_err_t err = httpd_resp_send(_request->request(), getContent(), getContentLength());
|
||||
|
||||
//did something happen?
|
||||
if (err != ESP_OK)
|
||||
ESP_LOGE(PH_TAG, "Send response failed (%s)", esp_err_to_name(err));
|
||||
//did something happen?
|
||||
if (err != ESP_OK)
|
||||
ESP_LOGE(PH_TAG, "Send response failed (%s)", esp_err_to_name(err));
|
||||
|
||||
return err;
|
||||
return err;
|
||||
}
|
||||
|
||||
void PsychicResponse::sendHeaders() {
|
||||
//get our global headers out of the way first
|
||||
for (HTTPHeader header : DefaultHeaders::Instance().getHeaders())
|
||||
httpd_resp_set_hdr(_request->request(), header.field, header.value);
|
||||
void PsychicResponse::sendHeaders()
|
||||
{
|
||||
//get our global headers out of the way first
|
||||
for (HTTPHeader header : DefaultHeaders::Instance().getHeaders())
|
||||
httpd_resp_set_hdr(_request->request(), header.field, header.value);
|
||||
|
||||
//now do our individual headers
|
||||
for (HTTPHeader header : _headers)
|
||||
httpd_resp_set_hdr(this->_request->request(), header.field, header.value);
|
||||
//now do our individual headers
|
||||
for (HTTPHeader header : _headers)
|
||||
httpd_resp_set_hdr(this->_request->request(), header.field, header.value);
|
||||
}
|
||||
|
||||
esp_err_t PsychicResponse::sendChunk(uint8_t * chunk, size_t chunksize) {
|
||||
/* Send the buffer contents as HTTP response chunk */
|
||||
esp_err_t err = httpd_resp_send_chunk(this->_request->request(), (char *)chunk, chunksize);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(PH_TAG, "File sending failed (%s)", esp_err_to_name(err));
|
||||
esp_err_t PsychicResponse::sendChunk(uint8_t *chunk, size_t chunksize)
|
||||
{
|
||||
/* Send the buffer contents as HTTP response chunk */
|
||||
esp_err_t err = httpd_resp_send_chunk(this->_request->request(), (char *)chunk, chunksize);
|
||||
if (err != ESP_OK)
|
||||
{
|
||||
ESP_LOGE(PH_TAG, "File sending failed (%s)", esp_err_to_name(err));
|
||||
|
||||
/* Abort sending file */
|
||||
httpd_resp_sendstr_chunk(this->_request->request(), NULL);
|
||||
/* Abort sending file */
|
||||
httpd_resp_sendstr_chunk(this->_request->request(), NULL);
|
||||
|
||||
/* Respond with 500 Internal Server Error */
|
||||
httpd_resp_send_err(this->_request->request(), HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to send file");
|
||||
}
|
||||
/* Respond with 500 Internal Server Error */
|
||||
httpd_resp_send_err(this->_request->request(), HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to send file");
|
||||
}
|
||||
|
||||
return err;
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_err_t PsychicResponse::finishChunking() {
|
||||
/* Respond with an empty chunk to signal HTTP response completion */
|
||||
return httpd_resp_send_chunk(this->_request->request(), NULL, 0);
|
||||
esp_err_t PsychicResponse::finishChunking()
|
||||
{
|
||||
/* Respond with an empty chunk to signal HTTP response completion */
|
||||
return httpd_resp_send_chunk(this->_request->request(), NULL, 0);
|
||||
}
|
||||
@@ -6,44 +6,41 @@
|
||||
|
||||
class PsychicRequest;
|
||||
|
||||
class PsychicResponse {
|
||||
class PsychicResponse
|
||||
{
|
||||
protected:
|
||||
PsychicRequest * _request;
|
||||
PsychicRequest *_request;
|
||||
|
||||
int _code;
|
||||
char _status[60];
|
||||
int _code;
|
||||
char _status[60];
|
||||
std::list<HTTPHeader> _headers;
|
||||
int64_t _contentLength;
|
||||
const char * _body;
|
||||
int64_t _contentLength;
|
||||
const char * _body;
|
||||
|
||||
public:
|
||||
PsychicResponse(PsychicRequest * request);
|
||||
PsychicResponse(PsychicRequest *request);
|
||||
virtual ~PsychicResponse();
|
||||
|
||||
void setCode(int code);
|
||||
|
||||
void setContentType(const char * contentType);
|
||||
void setContentLength(int64_t contentLength) {
|
||||
_contentLength = contentLength;
|
||||
}
|
||||
int64_t getContentLength(int64_t contentLength) {
|
||||
return _contentLength;
|
||||
}
|
||||
void setContentType(const char *contentType);
|
||||
void setContentLength(int64_t contentLength) { _contentLength = contentLength; }
|
||||
int64_t getContentLength(int64_t contentLength) { return _contentLength; }
|
||||
|
||||
void addHeader(const char * field, const char * value);
|
||||
void addHeader(const char *field, const char *value);
|
||||
|
||||
void setCookie(const char * key, const char * value, unsigned long max_age = 60 * 60 * 24 * 30, const char * extras = "");
|
||||
void setCookie(const char *key, const char *value, unsigned long max_age = 60*60*24*30, const char *extras = "");
|
||||
|
||||
void setContent(const char * content);
|
||||
void setContent(const uint8_t * content, size_t len);
|
||||
void setContent(const char *content);
|
||||
void setContent(const uint8_t *content, size_t len);
|
||||
|
||||
const char * getContent();
|
||||
size_t getContentLength();
|
||||
size_t getContentLength();
|
||||
|
||||
virtual esp_err_t send();
|
||||
void sendHeaders();
|
||||
esp_err_t sendChunk(uint8_t * chunk, size_t chunksize);
|
||||
esp_err_t finishChunking();
|
||||
void sendHeaders();
|
||||
esp_err_t sendChunk(uint8_t *chunk, size_t chunksize);
|
||||
esp_err_t finishChunking();
|
||||
};
|
||||
|
||||
#endif // PsychicResponse_h
|
||||
@@ -4,192 +4,190 @@
|
||||
/* PsychicStaticFileHandler */
|
||||
/*************************************/
|
||||
|
||||
PsychicStaticFileHandler::PsychicStaticFileHandler(const char * uri, FS & fs, const char * path, const char * cache_control)
|
||||
: _fs(fs)
|
||||
, _uri(uri)
|
||||
, _path(path)
|
||||
, _default_file("index.html")
|
||||
, _cache_control(cache_control)
|
||||
, _last_modified("") {
|
||||
// Ensure leading '/'
|
||||
if (_uri.length() == 0 || _uri[0] != '/')
|
||||
_uri = "/" + _uri;
|
||||
if (_path.length() == 0 || _path[0] != '/')
|
||||
_path = "/" + _path;
|
||||
PsychicStaticFileHandler::PsychicStaticFileHandler(const char* uri, FS& fs, const char* path, const char* cache_control)
|
||||
: _fs(fs), _uri(uri), _path(path), _default_file("index.html"), _cache_control(cache_control), _last_modified("")
|
||||
{
|
||||
// Ensure leading '/'
|
||||
if (_uri.length() == 0 || _uri[0] != '/') _uri = "/" + _uri;
|
||||
if (_path.length() == 0 || _path[0] != '/') _path = "/" + _path;
|
||||
|
||||
// If path ends with '/' we assume a hint that this is a directory to improve performance.
|
||||
// However - if it does not end with '/' we, can't assume a file, path can still be a directory.
|
||||
_isDir = _path[_path.length() - 1] == '/';
|
||||
// If path ends with '/' we assume a hint that this is a directory to improve performance.
|
||||
// However - if it does not end with '/' we, can't assume a file, path can still be a directory.
|
||||
_isDir = _path[_path.length()-1] == '/';
|
||||
|
||||
// Remove the trailing '/' so we can handle default file
|
||||
// Notice that root will be "" not "/"
|
||||
if (_uri[_uri.length() - 1] == '/')
|
||||
_uri = _uri.substring(0, _uri.length() - 1);
|
||||
if (_path[_path.length() - 1] == '/')
|
||||
_path = _path.substring(0, _path.length() - 1);
|
||||
// Remove the trailing '/' so we can handle default file
|
||||
// Notice that root will be "" not "/"
|
||||
if (_uri[_uri.length()-1] == '/') _uri = _uri.substring(0, _uri.length()-1);
|
||||
if (_path[_path.length()-1] == '/') _path = _path.substring(0, _path.length()-1);
|
||||
|
||||
// Reset stats
|
||||
_gzipFirst = false;
|
||||
_gzipStats = 0xF8;
|
||||
// Reset stats
|
||||
_gzipFirst = false;
|
||||
_gzipStats = 0xF8;
|
||||
}
|
||||
|
||||
PsychicStaticFileHandler & PsychicStaticFileHandler::setIsDir(bool isDir) {
|
||||
_isDir = isDir;
|
||||
return *this;
|
||||
PsychicStaticFileHandler& PsychicStaticFileHandler::setIsDir(bool isDir){
|
||||
_isDir = isDir;
|
||||
return *this;
|
||||
}
|
||||
|
||||
PsychicStaticFileHandler & PsychicStaticFileHandler::setDefaultFile(const char * filename) {
|
||||
_default_file = String(filename);
|
||||
return *this;
|
||||
PsychicStaticFileHandler& PsychicStaticFileHandler::setDefaultFile(const char* filename){
|
||||
_default_file = String(filename);
|
||||
return *this;
|
||||
}
|
||||
|
||||
PsychicStaticFileHandler & PsychicStaticFileHandler::setCacheControl(const char * cache_control) {
|
||||
_cache_control = String(cache_control);
|
||||
return *this;
|
||||
PsychicStaticFileHandler& PsychicStaticFileHandler::setCacheControl(const char* cache_control){
|
||||
_cache_control = String(cache_control);
|
||||
return *this;
|
||||
}
|
||||
|
||||
PsychicStaticFileHandler & PsychicStaticFileHandler::setLastModified(const char * last_modified) {
|
||||
_last_modified = String(last_modified);
|
||||
return *this;
|
||||
PsychicStaticFileHandler& PsychicStaticFileHandler::setLastModified(const char* last_modified){
|
||||
_last_modified = String(last_modified);
|
||||
return *this;
|
||||
}
|
||||
|
||||
PsychicStaticFileHandler & PsychicStaticFileHandler::setLastModified(struct tm * last_modified) {
|
||||
char result[30];
|
||||
strftime(result, 30, "%a, %d %b %Y %H:%M:%S %Z", last_modified);
|
||||
return setLastModified((const char *)result);
|
||||
PsychicStaticFileHandler& PsychicStaticFileHandler::setLastModified(struct tm* last_modified){
|
||||
char result[30];
|
||||
strftime (result,30,"%a, %d %b %Y %H:%M:%S %Z", last_modified);
|
||||
return setLastModified((const char *)result);
|
||||
}
|
||||
|
||||
bool PsychicStaticFileHandler::canHandle(PsychicRequest * request) {
|
||||
if (request->method() != HTTP_GET || !request->uri().startsWith(_uri))
|
||||
return false;
|
||||
|
||||
if (_getFile(request))
|
||||
return true;
|
||||
|
||||
bool PsychicStaticFileHandler::canHandle(PsychicRequest *request)
|
||||
{
|
||||
if(request->method() != HTTP_GET || !request->uri().startsWith(_uri) )
|
||||
return false;
|
||||
|
||||
if (_getFile(request))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PsychicStaticFileHandler::_getFile(PsychicRequest * request) {
|
||||
// Remove the found uri
|
||||
String path = request->uri().substring(_uri.length());
|
||||
bool PsychicStaticFileHandler::_getFile(PsychicRequest *request)
|
||||
{
|
||||
// Remove the found uri
|
||||
String path = request->uri().substring(_uri.length());
|
||||
|
||||
// We can skip the file check and look for default if request is to the root of a directory or that request path ends with '/'
|
||||
bool canSkipFileCheck = (_isDir && path.length() == 0) || (path.length() && path[path.length() - 1] == '/');
|
||||
// We can skip the file check and look for default if request is to the root of a directory or that request path ends with '/'
|
||||
bool canSkipFileCheck = (_isDir && path.length() == 0) || (path.length() && path[path.length()-1] == '/');
|
||||
|
||||
path = _path + path;
|
||||
path = _path + path;
|
||||
|
||||
// Do we have a file or .gz file
|
||||
if (!canSkipFileCheck && _fileExists(path))
|
||||
return true;
|
||||
// Do we have a file or .gz file
|
||||
if (!canSkipFileCheck && _fileExists(path))
|
||||
return true;
|
||||
|
||||
// Can't handle if not default file
|
||||
if (_default_file.length() == 0)
|
||||
return false;
|
||||
// Can't handle if not default file
|
||||
if (_default_file.length() == 0)
|
||||
return false;
|
||||
|
||||
// Try to add default file, ensure there is a trailing '/' ot the path.
|
||||
if (path.length() == 0 || path[path.length() - 1] != '/')
|
||||
path += "/";
|
||||
path += _default_file;
|
||||
// Try to add default file, ensure there is a trailing '/' ot the path.
|
||||
if (path.length() == 0 || path[path.length()-1] != '/')
|
||||
path += "/";
|
||||
path += _default_file;
|
||||
|
||||
return _fileExists(path);
|
||||
return _fileExists(path);
|
||||
}
|
||||
|
||||
#define FILE_IS_REAL(f) (f == true && !f.isDirectory())
|
||||
|
||||
bool PsychicStaticFileHandler::_fileExists(const String & path) {
|
||||
bool fileFound = false;
|
||||
bool gzipFound = false;
|
||||
bool PsychicStaticFileHandler::_fileExists(const String& path)
|
||||
{
|
||||
bool fileFound = false;
|
||||
bool gzipFound = false;
|
||||
|
||||
String gzip = path + ".gz";
|
||||
String gzip = path + ".gz";
|
||||
|
||||
if (_gzipFirst) {
|
||||
_file = _fs.open(gzip, "r");
|
||||
gzipFound = FILE_IS_REAL(_file);
|
||||
if (!gzipFound) {
|
||||
_file = _fs.open(path, "r");
|
||||
fileFound = FILE_IS_REAL(_file);
|
||||
}
|
||||
} else {
|
||||
_file = _fs.open(path, "r");
|
||||
fileFound = FILE_IS_REAL(_file);
|
||||
if (!fileFound) {
|
||||
_file = _fs.open(gzip, "r");
|
||||
gzipFound = FILE_IS_REAL(_file);
|
||||
}
|
||||
if (_gzipFirst) {
|
||||
_file = _fs.open(gzip, "r");
|
||||
gzipFound = FILE_IS_REAL(_file);
|
||||
if (!gzipFound){
|
||||
_file = _fs.open(path, "r");
|
||||
fileFound = FILE_IS_REAL(_file);
|
||||
}
|
||||
|
||||
bool found = fileFound || gzipFound;
|
||||
|
||||
if (found) {
|
||||
_filename = path;
|
||||
|
||||
// Calculate gzip statistic
|
||||
_gzipStats = (_gzipStats << 1) + (gzipFound ? 1 : 0);
|
||||
if (_gzipStats == 0x00)
|
||||
_gzipFirst = false; // All files are not gzip
|
||||
else if (_gzipStats == 0xFF)
|
||||
_gzipFirst = true; // All files are gzip
|
||||
else
|
||||
_gzipFirst = _countBits(_gzipStats) > 4; // IF we have more gzip files - try gzip first
|
||||
} else {
|
||||
_file = _fs.open(path, "r");
|
||||
fileFound = FILE_IS_REAL(_file);
|
||||
if (!fileFound){
|
||||
_file = _fs.open(gzip, "r");
|
||||
gzipFound = FILE_IS_REAL(_file);
|
||||
}
|
||||
}
|
||||
|
||||
return found;
|
||||
bool found = fileFound || gzipFound;
|
||||
|
||||
if (found)
|
||||
{
|
||||
_filename = path;
|
||||
|
||||
// Calculate gzip statistic
|
||||
_gzipStats = (_gzipStats << 1) + (gzipFound ? 1 : 0);
|
||||
if (_gzipStats == 0x00) _gzipFirst = false; // All files are not gzip
|
||||
else if (_gzipStats == 0xFF) _gzipFirst = true; // All files are gzip
|
||||
else _gzipFirst = _countBits(_gzipStats) > 4; // IF we have more gzip files - try gzip first
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
uint8_t PsychicStaticFileHandler::_countBits(const uint8_t value) const {
|
||||
uint8_t w = value;
|
||||
uint8_t n;
|
||||
for (n = 0; w != 0; n++)
|
||||
w &= w - 1;
|
||||
return n;
|
||||
uint8_t PsychicStaticFileHandler::_countBits(const uint8_t value) const
|
||||
{
|
||||
uint8_t w = value;
|
||||
uint8_t n;
|
||||
for (n=0; w!=0; n++) w&=w-1;
|
||||
return n;
|
||||
}
|
||||
|
||||
esp_err_t PsychicStaticFileHandler::handleRequest(PsychicRequest * request) {
|
||||
if (_file == true) {
|
||||
DUMP(_filename);
|
||||
esp_err_t PsychicStaticFileHandler::handleRequest(PsychicRequest *request)
|
||||
{
|
||||
if (_file == true)
|
||||
{
|
||||
DUMP(_filename);
|
||||
|
||||
//is it not modified?
|
||||
String etag = String(_file.size());
|
||||
if (_last_modified.length() && _last_modified == request->header("If-Modified-Since")) {
|
||||
DUMP("Last Modified Hit");
|
||||
TRACE();
|
||||
_file.close();
|
||||
request->reply(304); // Not modified
|
||||
}
|
||||
//does our Etag match?
|
||||
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();
|
||||
|
||||
PsychicResponse response(request);
|
||||
response.addHeader("Cache-Control", _cache_control.c_str());
|
||||
response.addHeader("ETag", etag.c_str());
|
||||
response.setCode(304);
|
||||
response.send();
|
||||
}
|
||||
//nope, send them the full file.
|
||||
else {
|
||||
DUMP("No cache hit");
|
||||
DUMP(_last_modified);
|
||||
DUMP(_cache_control);
|
||||
|
||||
PsychicFileResponse response(request, _fs, _filename);
|
||||
|
||||
if (_last_modified.length())
|
||||
response.addHeader("Last-Modified", _last_modified.c_str());
|
||||
if (_cache_control.length()) {
|
||||
response.addHeader("Cache-Control", _cache_control.c_str());
|
||||
response.addHeader("ETag", etag.c_str());
|
||||
}
|
||||
|
||||
return response.send();
|
||||
}
|
||||
} else {
|
||||
return request->reply(404);
|
||||
//is it not modified?
|
||||
String etag = String(_file.size());
|
||||
if (_last_modified.length() && _last_modified == request->header("If-Modified-Since"))
|
||||
{
|
||||
DUMP("Last Modified Hit");
|
||||
TRACE();
|
||||
_file.close();
|
||||
request->reply(304); // Not modified
|
||||
}
|
||||
//does our Etag match?
|
||||
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);
|
||||
|
||||
return ESP_OK;
|
||||
_file.close();
|
||||
|
||||
PsychicResponse response(request);
|
||||
response.addHeader("Cache-Control", _cache_control.c_str());
|
||||
response.addHeader("ETag", etag.c_str());
|
||||
response.setCode(304);
|
||||
response.send();
|
||||
}
|
||||
//nope, send them the full file.
|
||||
else
|
||||
{
|
||||
DUMP("No cache hit");
|
||||
DUMP(_last_modified);
|
||||
DUMP(_cache_control);
|
||||
|
||||
PsychicFileResponse response(request, _fs, _filename);
|
||||
|
||||
if (_last_modified.length())
|
||||
response.addHeader("Last-Modified", _last_modified.c_str());
|
||||
if (_cache_control.length()) {
|
||||
response.addHeader("Cache-Control", _cache_control.c_str());
|
||||
response.addHeader("ETag", etag.c_str());
|
||||
}
|
||||
|
||||
return response.send();
|
||||
}
|
||||
} else {
|
||||
return request->reply(404);
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
@@ -8,36 +8,33 @@
|
||||
#include "PsychicFileResponse.h"
|
||||
|
||||
class PsychicStaticFileHandler : public PsychicWebHandler {
|
||||
using File = fs::File;
|
||||
using FS = fs::FS;
|
||||
|
||||
using File = fs::File;
|
||||
using FS = fs::FS;
|
||||
private:
|
||||
bool _getFile(PsychicRequest * request);
|
||||
bool _fileExists(const String & path);
|
||||
bool _getFile(PsychicRequest *request);
|
||||
bool _fileExists(const String& path);
|
||||
uint8_t _countBits(const uint8_t value) const;
|
||||
|
||||
protected:
|
||||
FS _fs;
|
||||
File _file;
|
||||
String _filename;
|
||||
String _uri;
|
||||
String _path;
|
||||
String _default_file;
|
||||
String _cache_control;
|
||||
String _last_modified;
|
||||
bool _isDir;
|
||||
bool _gzipFirst;
|
||||
FS _fs;
|
||||
File _file;
|
||||
String _filename;
|
||||
String _uri;
|
||||
String _path;
|
||||
String _default_file;
|
||||
String _cache_control;
|
||||
String _last_modified;
|
||||
bool _isDir;
|
||||
bool _gzipFirst;
|
||||
uint8_t _gzipStats;
|
||||
|
||||
public:
|
||||
PsychicStaticFileHandler(const char * uri, FS & fs, const char * path, const char * cache_control);
|
||||
bool canHandle(PsychicRequest * request) override;
|
||||
esp_err_t handleRequest(PsychicRequest * request) override;
|
||||
PsychicStaticFileHandler & setIsDir(bool isDir);
|
||||
PsychicStaticFileHandler & setDefaultFile(const char * filename);
|
||||
PsychicStaticFileHandler & setCacheControl(const char * cache_control);
|
||||
PsychicStaticFileHandler & setLastModified(const char * last_modified);
|
||||
PsychicStaticFileHandler & setLastModified(struct tm * last_modified);
|
||||
PsychicStaticFileHandler(const char* uri, FS& fs, const char* path, const char* cache_control);
|
||||
bool canHandle(PsychicRequest *request) override;
|
||||
esp_err_t handleRequest(PsychicRequest *request) override;
|
||||
PsychicStaticFileHandler& setIsDir(bool isDir);
|
||||
PsychicStaticFileHandler& setDefaultFile(const char* filename);
|
||||
PsychicStaticFileHandler& setCacheControl(const char* cache_control);
|
||||
PsychicStaticFileHandler& setLastModified(const char* last_modified);
|
||||
PsychicStaticFileHandler& setLastModified(struct tm* last_modified);
|
||||
//PsychicStaticFileHandler& setTemplateProcessor(AwsTemplateProcessor newCallback) {_callback = newCallback; return *this;}
|
||||
};
|
||||
|
||||
|
||||
89
lib/PsychicHttp/src/PsychicStreamResponse.cpp
Normal file
89
lib/PsychicHttp/src/PsychicStreamResponse.cpp
Normal file
@@ -0,0 +1,89 @@
|
||||
#include "PsychicStreamResponse.h"
|
||||
#include "PsychicResponse.h"
|
||||
#include "PsychicRequest.h"
|
||||
|
||||
PsychicStreamResponse::PsychicStreamResponse(PsychicRequest *request, const String& contentType)
|
||||
: PsychicResponse(request), _buffer(NULL) {
|
||||
|
||||
setContentType(contentType.c_str());
|
||||
addHeader("Content-Disposition", "inline");
|
||||
}
|
||||
|
||||
PsychicStreamResponse::PsychicStreamResponse(PsychicRequest *request, const String& contentType, const String& name)
|
||||
: PsychicResponse(request), _buffer(NULL) {
|
||||
|
||||
setContentType(contentType.c_str());
|
||||
|
||||
char buf[26+name.length()];
|
||||
snprintf(buf, sizeof (buf), "attachment; filename=\"%s\"", name);
|
||||
addHeader("Content-Disposition", buf);
|
||||
}
|
||||
|
||||
PsychicStreamResponse::~PsychicStreamResponse()
|
||||
{
|
||||
endSend();
|
||||
}
|
||||
|
||||
esp_err_t PsychicStreamResponse::beginSend()
|
||||
{
|
||||
if(_buffer)
|
||||
return ESP_OK;
|
||||
|
||||
//Buffer to hold ChunkPrinter and stream buffer. Using placement new will keep us at a single allocation.
|
||||
_buffer = (uint8_t*)malloc(STREAM_CHUNK_SIZE + sizeof(ChunkPrinter));
|
||||
|
||||
if(!_buffer)
|
||||
{
|
||||
/* Respond with 500 Internal Server Error */
|
||||
httpd_resp_send_err(_request->request(), HTTPD_500_INTERNAL_SERVER_ERROR, "Unable to allocate memory.");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
_printer = new (_buffer) ChunkPrinter(this, _buffer + sizeof(ChunkPrinter), STREAM_CHUNK_SIZE);
|
||||
|
||||
sendHeaders();
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t PsychicStreamResponse::endSend()
|
||||
{
|
||||
esp_err_t err = ESP_OK;
|
||||
|
||||
if(!_buffer)
|
||||
err = ESP_FAIL;
|
||||
else
|
||||
{
|
||||
//flush & send remaining data.
|
||||
_printer->flush();
|
||||
err = finishChunking();
|
||||
|
||||
//Free memory
|
||||
_printer->~ChunkPrinter();
|
||||
free(_buffer);
|
||||
_buffer = NULL;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
void PsychicStreamResponse::flush()
|
||||
{
|
||||
if(_buffer)
|
||||
_printer->flush();
|
||||
}
|
||||
|
||||
size_t PsychicStreamResponse::write(uint8_t data)
|
||||
{
|
||||
return _buffer ? _printer->write(data) : 0;
|
||||
}
|
||||
|
||||
size_t PsychicStreamResponse::copyFrom(Stream &stream)
|
||||
{
|
||||
size_t sentCount = 0;
|
||||
|
||||
if(_buffer)
|
||||
{
|
||||
while(stream.available())
|
||||
sentCount += _printer->write(stream.read());
|
||||
}
|
||||
return sentCount;
|
||||
}
|
||||
33
lib/PsychicHttp/src/PsychicStreamResponse.h
Normal file
33
lib/PsychicHttp/src/PsychicStreamResponse.h
Normal file
@@ -0,0 +1,33 @@
|
||||
#ifndef PsychicStreamResponse_h
|
||||
#define PsychicStreamResponse_h
|
||||
|
||||
#include "PsychicCore.h"
|
||||
#include "PsychicResponse.h"
|
||||
#include "ChunkPrinter.h"
|
||||
|
||||
class PsychicRequest;
|
||||
|
||||
class PsychicStreamResponse : public PsychicResponse, public Print
|
||||
{
|
||||
private:
|
||||
ChunkPrinter *_printer;
|
||||
uint8_t *_buffer;
|
||||
public:
|
||||
|
||||
PsychicStreamResponse(PsychicRequest *request, const String& contentType);
|
||||
PsychicStreamResponse(PsychicRequest *request, const String& contentType, const String& name); //Download
|
||||
|
||||
~PsychicStreamResponse();
|
||||
|
||||
esp_err_t beginSend();
|
||||
esp_err_t endSend();
|
||||
|
||||
virtual void flush() override;
|
||||
|
||||
size_t write(uint8_t data);
|
||||
size_t copyFrom(Stream &stream);
|
||||
|
||||
using Print::write;
|
||||
};
|
||||
|
||||
#endif // PsychicStreamResponse_h
|
||||
@@ -8,7 +8,7 @@
|
||||
#include "PsychicWebParameter.h"
|
||||
|
||||
//callback definitions
|
||||
typedef std::function<esp_err_t(PsychicRequest * request, const String & filename, uint64_t index, uint8_t * data, size_t len, bool final)> PsychicUploadCallback;
|
||||
typedef std::function<esp_err_t(PsychicRequest *request, const String& filename, uint64_t index, uint8_t *data, size_t len, bool final)> PsychicUploadCallback;
|
||||
|
||||
/*
|
||||
* HANDLER :: Can be attached to any endpoint or as a generic request handler.
|
||||
@@ -18,25 +18,25 @@ class PsychicUploadHandler : public PsychicWebHandler {
|
||||
protected:
|
||||
PsychicUploadCallback _uploadCallback;
|
||||
|
||||
PsychicRequest * _request;
|
||||
PsychicRequest *_request;
|
||||
|
||||
String _temp;
|
||||
size_t _parsedLength;
|
||||
uint8_t _multiParseState;
|
||||
String _boundary;
|
||||
uint8_t _boundaryPosition;
|
||||
size_t _itemStartIndex;
|
||||
size_t _itemSize;
|
||||
String _itemName;
|
||||
String _itemFilename;
|
||||
String _itemType;
|
||||
String _itemValue;
|
||||
uint8_t * _itemBuffer;
|
||||
size_t _itemBufferIndex;
|
||||
bool _itemIsFile;
|
||||
String _temp;
|
||||
size_t _parsedLength;
|
||||
uint8_t _multiParseState;
|
||||
String _boundary;
|
||||
uint8_t _boundaryPosition;
|
||||
size_t _itemStartIndex;
|
||||
size_t _itemSize;
|
||||
String _itemName;
|
||||
String _itemFilename;
|
||||
String _itemType;
|
||||
String _itemValue;
|
||||
uint8_t *_itemBuffer;
|
||||
size_t _itemBufferIndex;
|
||||
bool _itemIsFile;
|
||||
|
||||
esp_err_t _basicUploadHandler(PsychicRequest * request);
|
||||
esp_err_t _multipartUploadHandler(PsychicRequest * request);
|
||||
esp_err_t _basicUploadHandler(PsychicRequest *request);
|
||||
esp_err_t _multipartUploadHandler(PsychicRequest *request);
|
||||
|
||||
void _handleUploadByte(uint8_t data, bool last);
|
||||
void _parseMultipartPostByte(uint8_t data, bool last);
|
||||
@@ -45,24 +45,24 @@ class PsychicUploadHandler : public PsychicWebHandler {
|
||||
PsychicUploadHandler();
|
||||
~PsychicUploadHandler();
|
||||
|
||||
bool canHandle(PsychicRequest * request) override;
|
||||
esp_err_t handleRequest(PsychicRequest * request) override;
|
||||
bool canHandle(PsychicRequest *request) override;
|
||||
esp_err_t handleRequest(PsychicRequest *request) override;
|
||||
|
||||
PsychicUploadHandler * onUpload(PsychicUploadCallback fn);
|
||||
};
|
||||
|
||||
enum {
|
||||
EXPECT_BOUNDARY,
|
||||
PARSE_HEADERS,
|
||||
WAIT_FOR_RETURN1,
|
||||
EXPECT_FEED1,
|
||||
EXPECT_DASH1,
|
||||
EXPECT_DASH2,
|
||||
BOUNDARY_OR_DATA,
|
||||
DASH3_OR_RETURN2,
|
||||
EXPECT_FEED2,
|
||||
PARSING_FINISHED,
|
||||
PARSE_ERROR
|
||||
EXPECT_BOUNDARY,
|
||||
PARSE_HEADERS,
|
||||
WAIT_FOR_RETURN1,
|
||||
EXPECT_FEED1,
|
||||
EXPECT_DASH1,
|
||||
EXPECT_DASH2,
|
||||
BOUNDARY_OR_DATA,
|
||||
DASH3_OR_RETURN2,
|
||||
EXPECT_FEED2,
|
||||
PARSING_FINISHED,
|
||||
PARSE_ERROR
|
||||
};
|
||||
|
||||
#endif // PsychicUploadHandler_h
|
||||
@@ -1,46 +1,74 @@
|
||||
#include "PsychicWebHandler.h"
|
||||
|
||||
PsychicWebHandler::PsychicWebHandler()
|
||||
: PsychicHandler()
|
||||
, _requestCallback(NULL) {
|
||||
}
|
||||
PsychicWebHandler::~PsychicWebHandler() {
|
||||
PsychicWebHandler::PsychicWebHandler() :
|
||||
PsychicHandler(),
|
||||
_requestCallback(NULL),
|
||||
_onOpen(NULL),
|
||||
_onClose(NULL)
|
||||
{}
|
||||
PsychicWebHandler::~PsychicWebHandler() {}
|
||||
|
||||
bool PsychicWebHandler::canHandle(PsychicRequest *request) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PsychicWebHandler::canHandle(PsychicRequest * request) {
|
||||
return true;
|
||||
}
|
||||
esp_err_t PsychicWebHandler::handleRequest(PsychicRequest *request)
|
||||
{
|
||||
//lookup our client
|
||||
PsychicClient *client = checkForNewClient(request->client());
|
||||
if (client->isNew)
|
||||
openCallback(client);
|
||||
|
||||
esp_err_t PsychicWebHandler::handleRequest(PsychicRequest * request) {
|
||||
/* Request body cannot be larger than a limit */
|
||||
if (request->contentLength() > request->server()->maxRequestBodySize) {
|
||||
ESP_LOGE(PH_TAG, "Request body too large : %d bytes", request->contentLength());
|
||||
/* Request body cannot be larger than a limit */
|
||||
if (request->contentLength() > request->server()->maxRequestBodySize)
|
||||
{
|
||||
ESP_LOGE(PH_TAG, "Request body too large : %d bytes", request->contentLength());
|
||||
|
||||
/* Respond with 400 Bad Request */
|
||||
char error[60];
|
||||
sprintf(error, "Request body must be less than %u bytes!", request->server()->maxRequestBodySize);
|
||||
httpd_resp_send_err(request->request(), HTTPD_400_BAD_REQUEST, error);
|
||||
/* Respond with 400 Bad Request */
|
||||
char error[60];
|
||||
sprintf(error, "Request body must be less than %u bytes!", request->server()->maxRequestBodySize);
|
||||
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 ESP_FAIL;
|
||||
}
|
||||
|
||||
//get our body loaded up.
|
||||
esp_err_t err = request->loadBody();
|
||||
if (err != ESP_OK)
|
||||
return err;
|
||||
|
||||
//load our params in.
|
||||
request->loadParams();
|
||||
|
||||
//okay, pass on to our callback.
|
||||
if (this->_requestCallback != NULL)
|
||||
err = this->_requestCallback(request);
|
||||
/* Return failure to close underlying connection else the incoming file content will keep the socket busy */
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
//get our body loaded up.
|
||||
esp_err_t err = request->loadBody();
|
||||
if (err != ESP_OK)
|
||||
return err;
|
||||
|
||||
//load our params in.
|
||||
request->loadParams();
|
||||
|
||||
//okay, pass on to our callback.
|
||||
if (this->_requestCallback != NULL)
|
||||
err = this->_requestCallback(request);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
PsychicWebHandler * PsychicWebHandler::onRequest(PsychicHttpRequestCallback fn) {
|
||||
_requestCallback = fn;
|
||||
return this;
|
||||
_requestCallback = fn;
|
||||
return this;
|
||||
}
|
||||
|
||||
void PsychicWebHandler::openCallback(PsychicClient *client) {
|
||||
if (_onOpen != NULL)
|
||||
_onOpen(client);
|
||||
}
|
||||
|
||||
void PsychicWebHandler::closeCallback(PsychicClient *client) {
|
||||
if (_onClose != NULL)
|
||||
_onClose(getClient(client));
|
||||
}
|
||||
|
||||
PsychicWebHandler * PsychicWebHandler::onOpen(PsychicClientCallback fn) {
|
||||
_onOpen = fn;
|
||||
return this;
|
||||
}
|
||||
|
||||
PsychicWebHandler * PsychicWebHandler::onClose(PsychicClientCallback fn) {
|
||||
_onClose = fn;
|
||||
return this;
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
#ifndef PsychicWebHandler_h
|
||||
#define PsychicWebHandler_h
|
||||
|
||||
#include "PsychicCore.h"
|
||||
#include "PsychicHttpServer.h"
|
||||
#include "PsychicRequest.h"
|
||||
// #include "PsychicCore.h"
|
||||
// #include "PsychicHttpServer.h"
|
||||
// #include "PsychicRequest.h"
|
||||
#include "PsychicHandler.h"
|
||||
|
||||
/*
|
||||
@@ -13,14 +13,22 @@
|
||||
class PsychicWebHandler : public PsychicHandler {
|
||||
protected:
|
||||
PsychicHttpRequestCallback _requestCallback;
|
||||
PsychicClientCallback _onOpen;
|
||||
PsychicClientCallback _onClose;
|
||||
|
||||
public:
|
||||
PsychicWebHandler();
|
||||
~PsychicWebHandler();
|
||||
|
||||
virtual bool canHandle(PsychicRequest * request) override;
|
||||
virtual esp_err_t handleRequest(PsychicRequest * request) override;
|
||||
virtual bool canHandle(PsychicRequest *request) override;
|
||||
virtual esp_err_t handleRequest(PsychicRequest *request) override;
|
||||
PsychicWebHandler * onRequest(PsychicHttpRequestCallback fn);
|
||||
|
||||
virtual void openCallback(PsychicClient *client);
|
||||
virtual void closeCallback(PsychicClient *client);
|
||||
|
||||
PsychicWebHandler *onOpen(PsychicClientCallback fn);
|
||||
PsychicWebHandler *onClose(PsychicClientCallback fn);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -10,32 +10,16 @@ class PsychicWebParameter {
|
||||
String _name;
|
||||
String _value;
|
||||
size_t _size;
|
||||
bool _isForm;
|
||||
bool _isFile;
|
||||
bool _isForm;
|
||||
bool _isFile;
|
||||
|
||||
public:
|
||||
PsychicWebParameter(const String & name, const String & value, bool form = false, bool file = false, size_t size = 0)
|
||||
: _name(name)
|
||||
, _value(value)
|
||||
, _size(size)
|
||||
, _isForm(form)
|
||||
, _isFile(file) {
|
||||
}
|
||||
const String & name() const {
|
||||
return _name;
|
||||
}
|
||||
const String & value() const {
|
||||
return _value;
|
||||
}
|
||||
size_t size() const {
|
||||
return _size;
|
||||
}
|
||||
bool isPost() const {
|
||||
return _isForm;
|
||||
}
|
||||
bool isFile() const {
|
||||
return _isFile;
|
||||
}
|
||||
PsychicWebParameter(const String& name, const String& value, bool form=false, bool file=false, size_t size=0): _name(name), _value(value), _size(size), _isForm(form), _isFile(file){}
|
||||
const String& name() const { return _name; }
|
||||
const String& value() const { return _value; }
|
||||
size_t size() const { return _size; }
|
||||
bool isPost() const { return _isForm; }
|
||||
bool isFile() const { return _isFile; }
|
||||
};
|
||||
|
||||
#endif //PsychicWebParameter_h
|
||||
@@ -4,240 +4,257 @@
|
||||
/* PsychicWebSocketRequest */
|
||||
/*************************************/
|
||||
|
||||
PsychicWebSocketRequest::PsychicWebSocketRequest(PsychicRequest * req)
|
||||
: PsychicRequest(req->server(), req->request())
|
||||
, _client(req->client()) {
|
||||
PsychicWebSocketRequest::PsychicWebSocketRequest(PsychicRequest *req) :
|
||||
PsychicRequest(req->server(), req->request()),
|
||||
_client(req->client())
|
||||
{
|
||||
}
|
||||
|
||||
PsychicWebSocketRequest::~PsychicWebSocketRequest() {
|
||||
PsychicWebSocketRequest::~PsychicWebSocketRequest()
|
||||
{
|
||||
}
|
||||
|
||||
PsychicWebSocketClient * PsychicWebSocketRequest::client() {
|
||||
return &_client;
|
||||
return &_client;
|
||||
}
|
||||
|
||||
esp_err_t PsychicWebSocketRequest::reply(httpd_ws_frame_t * ws_pkt) {
|
||||
return httpd_ws_send_frame(this->_req, ws_pkt);
|
||||
esp_err_t PsychicWebSocketRequest::reply(httpd_ws_frame_t * ws_pkt)
|
||||
{
|
||||
return httpd_ws_send_frame(this->_req, ws_pkt);
|
||||
}
|
||||
|
||||
esp_err_t PsychicWebSocketRequest::reply(httpd_ws_type_t op, const void * data, size_t len) {
|
||||
httpd_ws_frame_t ws_pkt;
|
||||
memset(&ws_pkt, 0, sizeof(httpd_ws_frame_t));
|
||||
esp_err_t PsychicWebSocketRequest::reply(httpd_ws_type_t op, const void *data, size_t len)
|
||||
{
|
||||
httpd_ws_frame_t ws_pkt;
|
||||
memset(&ws_pkt, 0, sizeof(httpd_ws_frame_t));
|
||||
|
||||
ws_pkt.payload = (uint8_t *)data;
|
||||
ws_pkt.len = len;
|
||||
ws_pkt.type = op;
|
||||
ws_pkt.payload = (uint8_t*)data;
|
||||
ws_pkt.len = len;
|
||||
ws_pkt.type = op;
|
||||
|
||||
return this->reply(&ws_pkt);
|
||||
return this->reply(&ws_pkt);
|
||||
}
|
||||
|
||||
esp_err_t PsychicWebSocketRequest::reply(const char * buf) {
|
||||
return this->reply(HTTPD_WS_TYPE_TEXT, buf, strlen(buf));
|
||||
esp_err_t PsychicWebSocketRequest::reply(const char *buf)
|
||||
{
|
||||
return this->reply(HTTPD_WS_TYPE_TEXT, buf, strlen(buf));
|
||||
}
|
||||
|
||||
/*************************************/
|
||||
/* PsychicWebSocketClient */
|
||||
/*************************************/
|
||||
|
||||
PsychicWebSocketClient::PsychicWebSocketClient(PsychicClient * client)
|
||||
: PsychicClient(client->server(), client->socket()) {
|
||||
PsychicWebSocketClient::PsychicWebSocketClient(PsychicClient *client)
|
||||
: PsychicClient(client->server(), client->socket())
|
||||
{
|
||||
}
|
||||
|
||||
PsychicWebSocketClient::~PsychicWebSocketClient() {
|
||||
}
|
||||
|
||||
esp_err_t PsychicWebSocketClient::sendMessage(httpd_ws_frame_t * ws_pkt) {
|
||||
return httpd_ws_send_frame_async(this->server(), this->socket(), 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);
|
||||
}
|
||||
|
||||
esp_err_t PsychicWebSocketClient::sendMessage(httpd_ws_type_t op, const void * data, size_t len) {
|
||||
httpd_ws_frame_t ws_pkt;
|
||||
memset(&ws_pkt, 0, sizeof(httpd_ws_frame_t));
|
||||
esp_err_t PsychicWebSocketClient::sendMessage(httpd_ws_type_t op, const void *data, size_t len)
|
||||
{
|
||||
httpd_ws_frame_t ws_pkt;
|
||||
memset(&ws_pkt, 0, sizeof(httpd_ws_frame_t));
|
||||
|
||||
ws_pkt.payload = (uint8_t *)data;
|
||||
ws_pkt.len = len;
|
||||
ws_pkt.type = op;
|
||||
ws_pkt.payload = (uint8_t*)data;
|
||||
ws_pkt.len = len;
|
||||
ws_pkt.type = op;
|
||||
|
||||
return this->sendMessage(&ws_pkt);
|
||||
return this->sendMessage(&ws_pkt);
|
||||
}
|
||||
|
||||
esp_err_t PsychicWebSocketClient::sendMessage(const char * buf) {
|
||||
return this->sendMessage(HTTPD_WS_TYPE_TEXT, buf, strlen(buf));
|
||||
esp_err_t PsychicWebSocketClient::sendMessage(const char *buf)
|
||||
{
|
||||
return this->sendMessage(HTTPD_WS_TYPE_TEXT, buf, strlen(buf));
|
||||
}
|
||||
|
||||
PsychicWebSocketHandler::PsychicWebSocketHandler()
|
||||
: PsychicHandler()
|
||||
, _onOpen(NULL)
|
||||
, _onFrame(NULL)
|
||||
, _onClose(NULL) {
|
||||
}
|
||||
PsychicWebSocketHandler::PsychicWebSocketHandler() :
|
||||
PsychicHandler(),
|
||||
_onOpen(NULL),
|
||||
_onFrame(NULL),
|
||||
_onClose(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
PsychicWebSocketHandler::~PsychicWebSocketHandler() {
|
||||
}
|
||||
|
||||
PsychicWebSocketClient * PsychicWebSocketHandler::getClient(int socket) {
|
||||
PsychicClient * client = PsychicHandler::getClient(socket);
|
||||
if (client == NULL)
|
||||
return NULL;
|
||||
PsychicWebSocketClient * PsychicWebSocketHandler::getClient(int socket)
|
||||
{
|
||||
PsychicClient *client = PsychicHandler::getClient(socket);
|
||||
if (client == NULL)
|
||||
return NULL;
|
||||
|
||||
if (client->_friend == NULL) {
|
||||
DUMP(socket);
|
||||
return NULL;
|
||||
}
|
||||
if (client->_friend == NULL)
|
||||
{
|
||||
DUMP(socket);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return (PsychicWebSocketClient *)client->_friend;
|
||||
return (PsychicWebSocketClient *)client->_friend;
|
||||
}
|
||||
|
||||
PsychicWebSocketClient * PsychicWebSocketHandler::getClient(PsychicClient * client) {
|
||||
return getClient(client->socket());
|
||||
PsychicWebSocketClient * PsychicWebSocketHandler::getClient(PsychicClient *client) {
|
||||
return getClient(client->socket());
|
||||
}
|
||||
|
||||
void PsychicWebSocketHandler::addClient(PsychicClient * client) {
|
||||
client->_friend = new PsychicWebSocketClient(client);
|
||||
PsychicHandler::addClient(client);
|
||||
void PsychicWebSocketHandler::addClient(PsychicClient *client) {
|
||||
client->_friend = new PsychicWebSocketClient(client);
|
||||
PsychicHandler::addClient(client);
|
||||
}
|
||||
|
||||
void PsychicWebSocketHandler::removeClient(PsychicClient * client) {
|
||||
PsychicHandler::removeClient(client);
|
||||
delete (PsychicWebSocketClient *)client->_friend;
|
||||
client->_friend = NULL;
|
||||
void PsychicWebSocketHandler::removeClient(PsychicClient *client) {
|
||||
PsychicHandler::removeClient(client);
|
||||
delete (PsychicWebSocketClient*)client->_friend;
|
||||
client->_friend = NULL;
|
||||
}
|
||||
|
||||
void PsychicWebSocketHandler::openCallback(PsychicClient * client) {
|
||||
PsychicWebSocketClient * buddy = getClient(client);
|
||||
if (buddy == NULL) {
|
||||
TRACE();
|
||||
return;
|
||||
}
|
||||
void PsychicWebSocketHandler::openCallback(PsychicClient *client) {
|
||||
PsychicWebSocketClient *buddy = getClient(client);
|
||||
if (buddy == NULL)
|
||||
{
|
||||
TRACE();
|
||||
return;
|
||||
}
|
||||
|
||||
if (_onOpen != NULL)
|
||||
_onOpen(getClient(buddy));
|
||||
if (_onOpen != NULL)
|
||||
_onOpen(getClient(buddy));
|
||||
}
|
||||
|
||||
void PsychicWebSocketHandler::closeCallback(PsychicClient * client) {
|
||||
PsychicWebSocketClient * buddy = getClient(client);
|
||||
if (buddy == NULL) {
|
||||
TRACE();
|
||||
return;
|
||||
}
|
||||
void PsychicWebSocketHandler::closeCallback(PsychicClient *client) {
|
||||
PsychicWebSocketClient *buddy = getClient(client);
|
||||
if (buddy == NULL)
|
||||
{
|
||||
TRACE();
|
||||
return;
|
||||
}
|
||||
|
||||
if (_onClose != NULL)
|
||||
_onClose(getClient(buddy));
|
||||
if (_onClose != NULL)
|
||||
_onClose(getClient(buddy));
|
||||
}
|
||||
|
||||
bool PsychicWebSocketHandler::isWebSocket() {
|
||||
return true;
|
||||
}
|
||||
bool PsychicWebSocketHandler::isWebSocket() { return true; }
|
||||
|
||||
esp_err_t PsychicWebSocketHandler::handleRequest(PsychicRequest * request) {
|
||||
//lookup our client
|
||||
PsychicClient * client = checkForNewClient(request->client());
|
||||
esp_err_t PsychicWebSocketHandler::handleRequest(PsychicRequest *request)
|
||||
{
|
||||
//lookup our client
|
||||
PsychicClient *client = checkForNewClient(request->client());
|
||||
|
||||
// beginning of the ws URI handler and our onConnect hook
|
||||
if (request->method() == HTTP_GET) {
|
||||
if (client->isNew)
|
||||
openCallback(client);
|
||||
// beginning of the ws URI handler and our onConnect hook
|
||||
if (request->method() == HTTP_GET)
|
||||
{
|
||||
if (client->isNew)
|
||||
openCallback(client);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
//prep our request
|
||||
PsychicWebSocketRequest wsRequest(request);
|
||||
//prep our request
|
||||
PsychicWebSocketRequest wsRequest(request);
|
||||
|
||||
//init our memory for storing the packet
|
||||
httpd_ws_frame_t ws_pkt;
|
||||
memset(&ws_pkt, 0, sizeof(httpd_ws_frame_t));
|
||||
ws_pkt.type = HTTPD_WS_TYPE_TEXT;
|
||||
uint8_t * buf = NULL;
|
||||
|
||||
/* Set max_len = 0 to get the frame len */
|
||||
esp_err_t ret = httpd_ws_recv_frame(wsRequest.request(), &ws_pkt, 0);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(PH_TAG, "httpd_ws_recv_frame failed to get frame len with %s", esp_err_to_name(ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
//okay, now try to load the packet
|
||||
ESP_LOGI(PH_TAG, "frame len is %d", ws_pkt.len);
|
||||
if (ws_pkt.len) {
|
||||
/* ws_pkt.len + 1 is for NULL termination as we are expecting a string */
|
||||
buf = (uint8_t *)calloc(1, ws_pkt.len + 1);
|
||||
if (buf == NULL) {
|
||||
ESP_LOGE(PH_TAG, "Failed to calloc memory for buf");
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
ws_pkt.payload = buf;
|
||||
/* Set max_len = ws_pkt.len to get the frame payload */
|
||||
ret = httpd_ws_recv_frame(wsRequest.request(), &ws_pkt, ws_pkt.len);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(PH_TAG, "httpd_ws_recv_frame failed with %s", esp_err_to_name(ret));
|
||||
free(buf);
|
||||
return ret;
|
||||
}
|
||||
ESP_LOGI(PH_TAG, "Got packet with message: %s", ws_pkt.payload);
|
||||
}
|
||||
|
||||
// Text messages are our payload.
|
||||
if (ws_pkt.type == HTTPD_WS_TYPE_TEXT) {
|
||||
if (this->_onFrame != NULL)
|
||||
ret = this->_onFrame(&wsRequest, &ws_pkt);
|
||||
}
|
||||
|
||||
//logging housekeeping
|
||||
if (ret != ESP_OK)
|
||||
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(),
|
||||
httpd_req_to_sockfd(request->request()),
|
||||
httpd_ws_get_fd_info(request->server(), httpd_req_to_sockfd(request->request())));
|
||||
|
||||
//dont forget to release our buffer memory
|
||||
free(buf);
|
||||
//init our memory for storing the packet
|
||||
httpd_ws_frame_t ws_pkt;
|
||||
memset(&ws_pkt, 0, sizeof(httpd_ws_frame_t));
|
||||
ws_pkt.type = HTTPD_WS_TYPE_TEXT;
|
||||
uint8_t *buf = NULL;
|
||||
|
||||
/* Set max_len = 0 to get the frame len */
|
||||
esp_err_t ret = httpd_ws_recv_frame(wsRequest.request(), &ws_pkt, 0);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(PH_TAG, "httpd_ws_recv_frame failed to get frame len with %s", esp_err_to_name(ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
//okay, now try to load the packet
|
||||
ESP_LOGI(PH_TAG, "frame len is %d", ws_pkt.len);
|
||||
if (ws_pkt.len) {
|
||||
/* ws_pkt.len + 1 is for NULL termination as we are expecting a string */
|
||||
buf = (uint8_t*) calloc(1, ws_pkt.len + 1);
|
||||
if (buf == NULL) {
|
||||
ESP_LOGE(PH_TAG, "Failed to calloc memory for buf");
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
ws_pkt.payload = buf;
|
||||
/* Set max_len = ws_pkt.len to get the frame payload */
|
||||
ret = httpd_ws_recv_frame(wsRequest.request(), &ws_pkt, ws_pkt.len);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(PH_TAG, "httpd_ws_recv_frame failed with %s", esp_err_to_name(ret));
|
||||
free(buf);
|
||||
return ret;
|
||||
}
|
||||
ESP_LOGI(PH_TAG, "Got packet with message: %s", ws_pkt.payload);
|
||||
}
|
||||
|
||||
// Text messages are our payload.
|
||||
if (ws_pkt.type == HTTPD_WS_TYPE_TEXT)
|
||||
{
|
||||
if (this->_onFrame != NULL)
|
||||
ret = this->_onFrame(&wsRequest, &ws_pkt);
|
||||
}
|
||||
|
||||
//logging housekeeping
|
||||
if (ret != ESP_OK)
|
||||
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(),
|
||||
httpd_req_to_sockfd(request->request()), httpd_ws_get_fd_info(request->server(), httpd_req_to_sockfd(request->request())));
|
||||
|
||||
//dont forget to release our buffer memory
|
||||
free(buf);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
PsychicWebSocketHandler * PsychicWebSocketHandler::onOpen(PsychicWebSocketClientCallback fn) {
|
||||
_onOpen = fn;
|
||||
return this;
|
||||
_onOpen = fn;
|
||||
return this;
|
||||
}
|
||||
|
||||
PsychicWebSocketHandler * PsychicWebSocketHandler::onFrame(PsychicWebSocketFrameCallback fn) {
|
||||
_onFrame = fn;
|
||||
return this;
|
||||
_onFrame = fn;
|
||||
return this;
|
||||
}
|
||||
|
||||
PsychicWebSocketHandler * PsychicWebSocketHandler::onClose(PsychicWebSocketClientCallback fn) {
|
||||
_onClose = fn;
|
||||
return this;
|
||||
_onClose = fn;
|
||||
return this;
|
||||
}
|
||||
|
||||
void PsychicWebSocketHandler::sendAll(httpd_ws_frame_t * ws_pkt) {
|
||||
for (PsychicClient * client : _clients) {
|
||||
ESP_LOGI(PH_TAG, "Active client (fd=%d) -> sending async message", client->socket());
|
||||
void PsychicWebSocketHandler::sendAll(httpd_ws_frame_t * ws_pkt)
|
||||
{
|
||||
for (PsychicClient *client : _clients)
|
||||
{
|
||||
ESP_LOGI(PH_TAG, "Active client (fd=%d) -> sending async message", client->socket());
|
||||
|
||||
if (client->_friend == NULL) {
|
||||
TRACE();
|
||||
return;
|
||||
}
|
||||
|
||||
if (((PsychicWebSocketClient *)client->_friend)->sendMessage(ws_pkt) != ESP_OK)
|
||||
break;
|
||||
if (client->_friend == NULL)
|
||||
{
|
||||
TRACE();
|
||||
return;
|
||||
}
|
||||
|
||||
if (((PsychicWebSocketClient*)client->_friend)->sendMessage(ws_pkt) != ESP_OK)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void PsychicWebSocketHandler::sendAll(httpd_ws_type_t op, const void * data, size_t len) {
|
||||
httpd_ws_frame_t ws_pkt;
|
||||
memset(&ws_pkt, 0, sizeof(httpd_ws_frame_t));
|
||||
void PsychicWebSocketHandler::sendAll(httpd_ws_type_t op, const void *data, size_t len)
|
||||
{
|
||||
httpd_ws_frame_t ws_pkt;
|
||||
memset(&ws_pkt, 0, sizeof(httpd_ws_frame_t));
|
||||
|
||||
ws_pkt.payload = (uint8_t *)data;
|
||||
ws_pkt.len = len;
|
||||
ws_pkt.type = op;
|
||||
ws_pkt.payload = (uint8_t*)data;
|
||||
ws_pkt.len = len;
|
||||
ws_pkt.type = op;
|
||||
|
||||
this->sendAll(&ws_pkt);
|
||||
this->sendAll(&ws_pkt);
|
||||
}
|
||||
|
||||
void PsychicWebSocketHandler::sendAll(const char * buf) {
|
||||
this->sendAll(HTTPD_WS_TYPE_TEXT, buf, strlen(buf));
|
||||
void PsychicWebSocketHandler::sendAll(const char *buf)
|
||||
{
|
||||
this->sendAll(HTTPD_WS_TYPE_TEXT, buf, strlen(buf));
|
||||
}
|
||||
@@ -8,38 +8,40 @@ class PsychicWebSocketRequest;
|
||||
class PsychicWebSocketClient;
|
||||
|
||||
//callback function definitions
|
||||
typedef std::function<void(PsychicWebSocketClient * client)> PsychicWebSocketClientCallback;
|
||||
typedef std::function<esp_err_t(PsychicWebSocketRequest * request, httpd_ws_frame * frame)> PsychicWebSocketFrameCallback;
|
||||
typedef std::function<void(PsychicWebSocketClient *client)> PsychicWebSocketClientCallback;
|
||||
typedef std::function<esp_err_t(PsychicWebSocketRequest *request, httpd_ws_frame *frame)> PsychicWebSocketFrameCallback;
|
||||
|
||||
class PsychicWebSocketClient : public PsychicClient {
|
||||
class PsychicWebSocketClient : public PsychicClient
|
||||
{
|
||||
public:
|
||||
PsychicWebSocketClient(PsychicClient * client);
|
||||
PsychicWebSocketClient(PsychicClient *client);
|
||||
~PsychicWebSocketClient();
|
||||
|
||||
esp_err_t sendMessage(httpd_ws_frame_t * ws_pkt);
|
||||
esp_err_t sendMessage(httpd_ws_type_t op, const void * data, size_t len);
|
||||
esp_err_t sendMessage(const char * buf);
|
||||
esp_err_t sendMessage(httpd_ws_type_t op, const void *data, size_t len);
|
||||
esp_err_t sendMessage(const char *buf);
|
||||
};
|
||||
|
||||
class PsychicWebSocketRequest : public PsychicRequest {
|
||||
class PsychicWebSocketRequest : public PsychicRequest
|
||||
{
|
||||
private:
|
||||
PsychicWebSocketClient _client;
|
||||
|
||||
public:
|
||||
PsychicWebSocketRequest(PsychicRequest * req);
|
||||
PsychicWebSocketRequest(PsychicRequest *req);
|
||||
virtual ~PsychicWebSocketRequest();
|
||||
|
||||
PsychicWebSocketClient * client() override;
|
||||
|
||||
esp_err_t reply(httpd_ws_frame_t * ws_pkt);
|
||||
esp_err_t reply(httpd_ws_type_t op, const void * data, size_t len);
|
||||
esp_err_t reply(const char * buf);
|
||||
esp_err_t reply(httpd_ws_type_t op, const void *data, size_t len);
|
||||
esp_err_t reply(const char *buf);
|
||||
};
|
||||
|
||||
class PsychicWebSocketHandler : public PsychicHandler {
|
||||
protected:
|
||||
PsychicWebSocketClientCallback _onOpen;
|
||||
PsychicWebSocketFrameCallback _onFrame;
|
||||
PsychicWebSocketFrameCallback _onFrame;
|
||||
PsychicWebSocketClientCallback _onClose;
|
||||
|
||||
public:
|
||||
@@ -47,22 +49,22 @@ class PsychicWebSocketHandler : public PsychicHandler {
|
||||
~PsychicWebSocketHandler();
|
||||
|
||||
PsychicWebSocketClient * getClient(int socket) override;
|
||||
PsychicWebSocketClient * getClient(PsychicClient * client) override;
|
||||
void addClient(PsychicClient * client) override;
|
||||
void removeClient(PsychicClient * client) override;
|
||||
void openCallback(PsychicClient * client) override;
|
||||
void closeCallback(PsychicClient * client) override;
|
||||
PsychicWebSocketClient * getClient(PsychicClient *client) override;
|
||||
void addClient(PsychicClient *client) override;
|
||||
void removeClient(PsychicClient *client) override;
|
||||
void openCallback(PsychicClient *client) override;
|
||||
void closeCallback(PsychicClient *client) override;
|
||||
|
||||
bool isWebSocket() override final;
|
||||
esp_err_t handleRequest(PsychicRequest * request) override;
|
||||
bool isWebSocket() override final;
|
||||
esp_err_t handleRequest(PsychicRequest *request) override;
|
||||
|
||||
PsychicWebSocketHandler * onOpen(PsychicWebSocketClientCallback fn);
|
||||
PsychicWebSocketHandler * onFrame(PsychicWebSocketFrameCallback fn);
|
||||
PsychicWebSocketHandler * onClose(PsychicWebSocketClientCallback fn);
|
||||
PsychicWebSocketHandler *onOpen(PsychicWebSocketClientCallback fn);
|
||||
PsychicWebSocketHandler *onFrame(PsychicWebSocketFrameCallback fn);
|
||||
PsychicWebSocketHandler *onClose(PsychicWebSocketClientCallback fn);
|
||||
|
||||
void sendAll(httpd_ws_frame_t * ws_pkt);
|
||||
void sendAll(httpd_ws_type_t op, const void * data, size_t len);
|
||||
void sendAll(const char * buf);
|
||||
void sendAll(httpd_ws_type_t op, const void *data, size_t len);
|
||||
void sendAll(const char *buf);
|
||||
};
|
||||
|
||||
#endif // PsychicWebSocket_h
|
||||
@@ -1,30 +1,37 @@
|
||||
#include "http_status.h"
|
||||
|
||||
bool http_informational(int code) {
|
||||
bool http_informational(int code)
|
||||
{
|
||||
return code >= 100 && code < 200;
|
||||
}
|
||||
|
||||
bool http_success(int code) {
|
||||
bool http_success(int code)
|
||||
{
|
||||
return code >= 200 && code < 300;
|
||||
}
|
||||
|
||||
bool http_redirection(int code) {
|
||||
bool http_redirection(int code)
|
||||
{
|
||||
return code >= 300 && code < 400;
|
||||
}
|
||||
|
||||
bool http_client_error(int code) {
|
||||
bool http_client_error(int code)
|
||||
{
|
||||
return code >= 400 && code < 500;
|
||||
}
|
||||
|
||||
bool http_server_error(int code) {
|
||||
bool http_server_error(int code)
|
||||
{
|
||||
return code >= 500 && code < 600;
|
||||
}
|
||||
|
||||
bool http_failure(int code) {
|
||||
bool http_failure(int code)
|
||||
{
|
||||
return code >= 400 && code < 600;
|
||||
}
|
||||
|
||||
const char * http_status_group(int code) {
|
||||
const char *http_status_group(int code)
|
||||
{
|
||||
if (http_informational(code))
|
||||
return "Informational";
|
||||
|
||||
@@ -43,8 +50,10 @@ const char * http_status_group(int code) {
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
const char * http_status_reason(int code) {
|
||||
switch (code) {
|
||||
const char *http_status_reason(int code)
|
||||
{
|
||||
switch (code)
|
||||
{
|
||||
/*####### 1xx - Informational #######*/
|
||||
case 100:
|
||||
return "Continue";
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
bool http_informational(int code);
|
||||
bool http_success(int code);
|
||||
bool http_redirection(int code);
|
||||
bool http_client_error(int code);
|
||||
bool http_server_error(int code);
|
||||
bool http_failure(int code);
|
||||
const char * http_status_group(int code);
|
||||
const char * http_status_reason(int code);
|
||||
bool http_informational(int code);
|
||||
bool http_success(int code);
|
||||
bool http_redirection(int code);
|
||||
bool http_client_error(int code);
|
||||
bool http_server_error(int code);
|
||||
bool http_failure(int code);
|
||||
const char *http_status_group(int code);
|
||||
const char *http_status_reason(int code);
|
||||
|
||||
#endif // MICRO_HTTP_STATUS_H
|
||||
52
lib/PsychicHttp/src_old/ChunkPrinter.h
Normal file
52
lib/PsychicHttp/src_old/ChunkPrinter.h
Normal file
@@ -0,0 +1,52 @@
|
||||
#ifndef ChunkPrinter_h
|
||||
#define ChunkPrinter_h
|
||||
|
||||
#include "PsychicCore.h"
|
||||
#include "PsychicResponse.h"
|
||||
#include <Print.h>
|
||||
|
||||
class ChunkPrinter : public Print {
|
||||
private:
|
||||
PsychicResponse * _response;
|
||||
uint8_t * _buffer;
|
||||
size_t _length;
|
||||
size_t _pos;
|
||||
|
||||
public:
|
||||
ChunkPrinter(PsychicResponse * response, uint8_t * buffer, size_t len)
|
||||
: _response(response)
|
||||
, _buffer(buffer)
|
||||
, _length(len)
|
||||
, _pos(0) {
|
||||
}
|
||||
|
||||
virtual ~ChunkPrinter() {
|
||||
}
|
||||
|
||||
size_t write(uint8_t c) {
|
||||
esp_err_t err;
|
||||
|
||||
_buffer[_pos] = c;
|
||||
_pos++;
|
||||
|
||||
//if we're full, send a chunk
|
||||
if (_pos == _length) {
|
||||
_pos = 0;
|
||||
|
||||
err = _response->sendChunk(_buffer, _length);
|
||||
if (err != ESP_OK)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
virtual void flush() override {
|
||||
if (_pos) {
|
||||
_response->sendChunk(_buffer, _pos);
|
||||
_pos = 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
67
lib/PsychicHttp/src_old/PsychicClient.cpp
Normal file
67
lib/PsychicHttp/src_old/PsychicClient.cpp
Normal file
@@ -0,0 +1,67 @@
|
||||
#include "PsychicClient.h"
|
||||
|
||||
PsychicClient::PsychicClient(httpd_handle_t server, int socket)
|
||||
: _server(server)
|
||||
, _socket(socket)
|
||||
, _friend(NULL)
|
||||
, isNew(false) {
|
||||
}
|
||||
|
||||
PsychicClient::~PsychicClient() {
|
||||
}
|
||||
|
||||
httpd_handle_t PsychicClient::server() {
|
||||
return _server;
|
||||
}
|
||||
|
||||
int PsychicClient::socket() {
|
||||
return _socket;
|
||||
}
|
||||
|
||||
// I'm not sure this is entirely safe to call. I was having issues with race conditions when highly loaded using this.
|
||||
esp_err_t PsychicClient::close() {
|
||||
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.
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
IPAddress PsychicClient::localIP() {
|
||||
IPAddress address(0, 0, 0, 0);
|
||||
|
||||
char ipstr[INET6_ADDRSTRLEN];
|
||||
struct sockaddr_in6 addr; // esp_http_server uses IPv6 addressing
|
||||
socklen_t addr_size = sizeof(addr);
|
||||
|
||||
if (getsockname(_socket, (struct sockaddr *)&addr, &addr_size) < 0) {
|
||||
ESP_LOGE(PH_TAG, "Error getting client IP");
|
||||
return address;
|
||||
}
|
||||
|
||||
// Convert to IPv4 string
|
||||
inet_ntop(AF_INET, &addr.sin6_addr.un.u32_addr[3], ipstr, sizeof(ipstr));
|
||||
ESP_LOGI(PH_TAG, "Client Local IP => %s", ipstr);
|
||||
address.fromString(ipstr);
|
||||
|
||||
return address;
|
||||
}
|
||||
|
||||
IPAddress PsychicClient::remoteIP() {
|
||||
IPAddress address(0, 0, 0, 0);
|
||||
|
||||
char ipstr[INET6_ADDRSTRLEN];
|
||||
struct sockaddr_in6 addr; // esp_http_server uses IPv6 addressing
|
||||
socklen_t addr_size = sizeof(addr);
|
||||
|
||||
if (getpeername(_socket, (struct sockaddr *)&addr, &addr_size) < 0) {
|
||||
ESP_LOGE(PH_TAG, "Error getting client IP");
|
||||
return address;
|
||||
}
|
||||
|
||||
// Convert to IPv4 string
|
||||
inet_ntop(AF_INET, &addr.sin6_addr.un.u32_addr[3], ipstr, sizeof(ipstr));
|
||||
ESP_LOGI(PH_TAG, "Client Remote IP => %s", ipstr);
|
||||
address.fromString(ipstr);
|
||||
|
||||
return address;
|
||||
}
|
||||
39
lib/PsychicHttp/src_old/PsychicClient.h
Normal file
39
lib/PsychicHttp/src_old/PsychicClient.h
Normal file
@@ -0,0 +1,39 @@
|
||||
#ifndef PsychicClient_h
|
||||
#define PsychicClient_h
|
||||
|
||||
#include "PsychicCore.h"
|
||||
#include "PsychicHttpServer.h"
|
||||
#include <lwip/sockets.h>
|
||||
|
||||
/*
|
||||
* PsychicClient :: Generic wrapper around the ESP-IDF socket
|
||||
*/
|
||||
|
||||
class PsychicClient {
|
||||
protected:
|
||||
httpd_handle_t _server;
|
||||
int _socket;
|
||||
|
||||
public:
|
||||
PsychicClient(httpd_handle_t server, int socket);
|
||||
~PsychicClient();
|
||||
|
||||
//no idea if this is the right way to do it or not, but lets see.
|
||||
//pointer to our derived class (eg. PsychicWebSocketConnection)
|
||||
void * _friend;
|
||||
|
||||
bool isNew = false;
|
||||
|
||||
bool operator==(PsychicClient & rhs) const {
|
||||
return _socket == rhs.socket();
|
||||
}
|
||||
|
||||
httpd_handle_t server();
|
||||
int socket();
|
||||
esp_err_t close();
|
||||
|
||||
IPAddress localIP();
|
||||
IPAddress remoteIP();
|
||||
};
|
||||
|
||||
#endif
|
||||
101
lib/PsychicHttp/src_old/PsychicCore.h
Normal file
101
lib/PsychicHttp/src_old/PsychicCore.h
Normal file
@@ -0,0 +1,101 @@
|
||||
#ifndef PsychicCore_h
|
||||
#define PsychicCore_h
|
||||
|
||||
#define PH_TAG "psychic"
|
||||
|
||||
//version numbers
|
||||
#define PSYCHIC_HTTP_VERSION_MAJOR 1
|
||||
#define PSYCHIC_HTTP_VERSION_MINOR 1
|
||||
#define PSYCHIC_HTTP_VERSION_PATCH 0
|
||||
|
||||
#ifndef MAX_COOKIE_SIZE
|
||||
#define MAX_COOKIE_SIZE 512
|
||||
#endif
|
||||
|
||||
#ifndef FILE_CHUNK_SIZE
|
||||
#define FILE_CHUNK_SIZE 8 * 1024
|
||||
#endif
|
||||
|
||||
#ifndef MAX_UPLOAD_SIZE
|
||||
#define MAX_UPLOAD_SIZE (2048 * 1024) // 2MB
|
||||
#endif
|
||||
|
||||
#ifndef MAX_REQUEST_BODY_SIZE
|
||||
#define MAX_REQUEST_BODY_SIZE (16 * 1024) //16K
|
||||
#endif
|
||||
|
||||
#ifdef ARDUINO
|
||||
#include <Arduino.h>
|
||||
#include <ArduinoTrace.h>
|
||||
#endif
|
||||
|
||||
#include <esp_http_server.h>
|
||||
#include <map>
|
||||
#include <list>
|
||||
#include <libb64/cencode.h>
|
||||
#include "esp_random.h"
|
||||
#include "MD5Builder.h"
|
||||
#include <UrlEncode.h>
|
||||
#include "FS.h"
|
||||
|
||||
enum HTTPAuthMethod { BASIC_AUTH, DIGEST_AUTH };
|
||||
|
||||
String urlDecode(const char * encoded);
|
||||
|
||||
class PsychicHttpServer;
|
||||
class PsychicRequest;
|
||||
class PsychicWebSocketRequest;
|
||||
class PsychicClient;
|
||||
|
||||
//filter function definition
|
||||
typedef std::function<bool(PsychicRequest * request)> PsychicRequestFilterFunction;
|
||||
|
||||
//client connect callback
|
||||
typedef std::function<void(PsychicClient * client)> PsychicClientCallback;
|
||||
|
||||
|
||||
struct HTTPHeader {
|
||||
char * field;
|
||||
char * value;
|
||||
};
|
||||
|
||||
class DefaultHeaders {
|
||||
std::list<HTTPHeader> _headers;
|
||||
|
||||
public:
|
||||
DefaultHeaders() {
|
||||
}
|
||||
|
||||
void addHeader(const String & field, const String & value) {
|
||||
addHeader(field.c_str(), value.c_str());
|
||||
}
|
||||
|
||||
void addHeader(const char * field, const char * value) {
|
||||
HTTPHeader header;
|
||||
|
||||
//these are just going to stick around forever.
|
||||
header.field = (char *)malloc(strlen(field) + 1);
|
||||
header.value = (char *)malloc(strlen(value) + 1);
|
||||
|
||||
strlcpy(header.field, field, strlen(field) + 1);
|
||||
strlcpy(header.value, value, strlen(value) + 1);
|
||||
|
||||
_headers.push_back(header);
|
||||
}
|
||||
|
||||
const std::list<HTTPHeader> & getHeaders() {
|
||||
return _headers;
|
||||
}
|
||||
|
||||
//delete the copy constructor, singleton class
|
||||
DefaultHeaders(DefaultHeaders const &) = delete;
|
||||
DefaultHeaders & operator=(DefaultHeaders const &) = delete;
|
||||
|
||||
//single static class interface
|
||||
static DefaultHeaders & Instance() {
|
||||
static DefaultHeaders instance;
|
||||
return instance;
|
||||
}
|
||||
};
|
||||
|
||||
#endif //PsychicCore_h
|
||||
82
lib/PsychicHttp/src_old/PsychicEndpoint.cpp
Normal file
82
lib/PsychicHttp/src_old/PsychicEndpoint.cpp
Normal file
@@ -0,0 +1,82 @@
|
||||
#include "PsychicEndpoint.h"
|
||||
|
||||
PsychicEndpoint::PsychicEndpoint()
|
||||
: _server(NULL)
|
||||
, _uri("")
|
||||
, _method(HTTP_GET)
|
||||
, _handler(NULL) {
|
||||
}
|
||||
|
||||
PsychicEndpoint::PsychicEndpoint(PsychicHttpServer * server, http_method method, const char * uri)
|
||||
: _server(server)
|
||||
, _uri(uri)
|
||||
, _method(method)
|
||||
, _handler(NULL) {
|
||||
}
|
||||
|
||||
PsychicEndpoint * PsychicEndpoint::setHandler(PsychicHandler * handler) {
|
||||
//clean up old / default handler
|
||||
if (_handler != NULL)
|
||||
delete _handler;
|
||||
|
||||
//get our new pointer
|
||||
_handler = handler;
|
||||
|
||||
//keep a pointer to the server
|
||||
_handler->_server = _server;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
PsychicHandler * PsychicEndpoint::handler() {
|
||||
return _handler;
|
||||
}
|
||||
|
||||
String PsychicEndpoint::uri() {
|
||||
return _uri;
|
||||
}
|
||||
|
||||
esp_err_t PsychicEndpoint::requestCallback(httpd_req_t * req) {
|
||||
#ifdef ENABLE_ASYNC
|
||||
if (is_on_async_worker_thread() == false) {
|
||||
if (submit_async_req(req, PsychicEndpoint::requestCallback) == ESP_OK) {
|
||||
return ESP_OK;
|
||||
} else {
|
||||
httpd_resp_set_status(req, "503 Busy");
|
||||
httpd_resp_sendstr(req, "No workers available. Server busy.</div>");
|
||||
return ESP_OK;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
PsychicEndpoint * self = (PsychicEndpoint *)req->user_ctx;
|
||||
PsychicHandler * handler = self->handler();
|
||||
PsychicRequest request(self->_server, req);
|
||||
|
||||
//make sure we have a handler
|
||||
if (handler != NULL) {
|
||||
if (handler->filter(&request) && handler->canHandle(&request)) {
|
||||
//check our credentials
|
||||
if (handler->needsAuthentication(&request))
|
||||
return handler->authenticate(&request);
|
||||
|
||||
//pass it to our handler
|
||||
return handler->handleRequest(&request);
|
||||
}
|
||||
//pass it to our generic handlers
|
||||
else
|
||||
return PsychicHttpServer::notFoundHandler(req, HTTPD_500_INTERNAL_SERVER_ERROR);
|
||||
} else
|
||||
return request.reply(500, "text/html", "No handler registered.");
|
||||
}
|
||||
|
||||
PsychicEndpoint * PsychicEndpoint::setFilter(PsychicRequestFilterFunction fn) {
|
||||
_handler->setFilter(fn);
|
||||
return this;
|
||||
}
|
||||
|
||||
PsychicEndpoint *
|
||||
PsychicEndpoint::setAuthentication(const char * username, const char * password, HTTPAuthMethod method, const char * realm, const char * authFailMsg) {
|
||||
_handler->setAuthentication(username, password, method, realm, authFailMsg);
|
||||
return this;
|
||||
};
|
||||
36
lib/PsychicHttp/src_old/PsychicEndpoint.h
Normal file
36
lib/PsychicHttp/src_old/PsychicEndpoint.h
Normal file
@@ -0,0 +1,36 @@
|
||||
#ifndef PsychicEndpoint_h
|
||||
#define PsychicEndpoint_h
|
||||
|
||||
#include "PsychicCore.h"
|
||||
#include "PsychicHttpServer.h"
|
||||
|
||||
#ifdef ENABLE_ASYNC
|
||||
#include "async_worker.h"
|
||||
#endif
|
||||
|
||||
class PsychicEndpoint {
|
||||
friend PsychicHttpServer;
|
||||
|
||||
private:
|
||||
PsychicHttpServer * _server;
|
||||
String _uri;
|
||||
http_method _method;
|
||||
PsychicHandler * _handler;
|
||||
|
||||
public:
|
||||
PsychicEndpoint();
|
||||
PsychicEndpoint(PsychicHttpServer * server, http_method method, const char * uri);
|
||||
|
||||
PsychicEndpoint * setHandler(PsychicHandler * handler);
|
||||
PsychicHandler * handler();
|
||||
|
||||
PsychicEndpoint * setFilter(PsychicRequestFilterFunction fn);
|
||||
PsychicEndpoint *
|
||||
setAuthentication(const char * username, const char * password, HTTPAuthMethod method = BASIC_AUTH, const char * realm = "", const char * authFailMsg = "");
|
||||
|
||||
String uri();
|
||||
|
||||
static esp_err_t requestCallback(httpd_req_t * req);
|
||||
};
|
||||
|
||||
#endif // PsychicEndpoint_h
|
||||
217
lib/PsychicHttp/src_old/PsychicEventSource.cpp
Normal file
217
lib/PsychicHttp/src_old/PsychicEventSource.cpp
Normal file
@@ -0,0 +1,217 @@
|
||||
/*
|
||||
Asynchronous WebServer library for Espressif MCUs
|
||||
|
||||
Copyright (c) 2016 Hristo Gochkov. All rights reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "PsychicEventSource.h"
|
||||
|
||||
/*****************************************/
|
||||
// PsychicEventSource - Handler
|
||||
/*****************************************/
|
||||
|
||||
PsychicEventSource::PsychicEventSource()
|
||||
: PsychicHandler()
|
||||
, _onOpen(NULL)
|
||||
, _onClose(NULL) {
|
||||
}
|
||||
|
||||
PsychicEventSource::~PsychicEventSource() {
|
||||
}
|
||||
|
||||
PsychicEventSourceClient * PsychicEventSource::getClient(int socket) {
|
||||
PsychicClient * client = PsychicHandler::getClient(socket);
|
||||
|
||||
if (client == NULL)
|
||||
return NULL;
|
||||
|
||||
return (PsychicEventSourceClient *)client->_friend;
|
||||
}
|
||||
|
||||
PsychicEventSourceClient * PsychicEventSource::getClient(PsychicClient * client) {
|
||||
return getClient(client->socket());
|
||||
}
|
||||
|
||||
esp_err_t PsychicEventSource::handleRequest(PsychicRequest * request) {
|
||||
//start our open ended HTTP response
|
||||
PsychicEventSourceResponse response(request);
|
||||
esp_err_t err = response.send();
|
||||
|
||||
//lookup our client
|
||||
PsychicClient * client = checkForNewClient(request->client());
|
||||
if (client->isNew) {
|
||||
//did we get our last id?
|
||||
if (request->hasHeader("Last-Event-ID")) {
|
||||
PsychicEventSourceClient * buddy = getClient(client);
|
||||
buddy->_lastId = atoi(request->header("Last-Event-ID").c_str());
|
||||
}
|
||||
|
||||
//let our handler know.
|
||||
openCallback(client);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
PsychicEventSource * PsychicEventSource::onOpen(PsychicEventSourceClientCallback fn) {
|
||||
_onOpen = fn;
|
||||
return this;
|
||||
}
|
||||
|
||||
PsychicEventSource * PsychicEventSource::onClose(PsychicEventSourceClientCallback fn) {
|
||||
_onClose = fn;
|
||||
return this;
|
||||
}
|
||||
|
||||
void PsychicEventSource::addClient(PsychicClient * client) {
|
||||
client->_friend = new PsychicEventSourceClient(client);
|
||||
PsychicHandler::addClient(client);
|
||||
}
|
||||
|
||||
void PsychicEventSource::removeClient(PsychicClient * client) {
|
||||
PsychicHandler::removeClient(client);
|
||||
delete (PsychicEventSourceClient *)client->_friend;
|
||||
client->_friend = NULL;
|
||||
}
|
||||
|
||||
void PsychicEventSource::openCallback(PsychicClient * client) {
|
||||
PsychicEventSourceClient * buddy = getClient(client);
|
||||
if (buddy == NULL) {
|
||||
TRACE();
|
||||
return;
|
||||
}
|
||||
|
||||
if (_onOpen != NULL)
|
||||
_onOpen(buddy);
|
||||
}
|
||||
|
||||
void PsychicEventSource::closeCallback(PsychicClient * client) {
|
||||
PsychicEventSourceClient * buddy = getClient(client);
|
||||
if (buddy == NULL) {
|
||||
TRACE();
|
||||
return;
|
||||
}
|
||||
|
||||
if (_onClose != NULL)
|
||||
_onClose(getClient(buddy));
|
||||
}
|
||||
|
||||
void PsychicEventSource::send(const char * message, const char * event, uint32_t id, uint32_t reconnect) {
|
||||
String ev = generateEventMessage(message, event, id, reconnect);
|
||||
for (PsychicClient * c : _clients) {
|
||||
((PsychicEventSourceClient *)c->_friend)->sendEvent(ev.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
/*****************************************/
|
||||
// PsychicEventSourceClient
|
||||
/*****************************************/
|
||||
|
||||
PsychicEventSourceClient::PsychicEventSourceClient(PsychicClient * client)
|
||||
: PsychicClient(client->server(), client->socket())
|
||||
, _lastId(0) {
|
||||
}
|
||||
|
||||
PsychicEventSourceClient::~PsychicEventSourceClient() {
|
||||
}
|
||||
|
||||
void PsychicEventSourceClient::send(const char * message, const char * event, uint32_t id, uint32_t reconnect) {
|
||||
String ev = generateEventMessage(message, event, id, reconnect);
|
||||
sendEvent(ev.c_str());
|
||||
}
|
||||
|
||||
void PsychicEventSourceClient::sendEvent(const char * event) {
|
||||
int result;
|
||||
do {
|
||||
result = httpd_socket_send(this->server(), this->socket(), event, strlen(event), 0);
|
||||
} while (result == HTTPD_SOCK_ERR_TIMEOUT);
|
||||
|
||||
//if (result < 0)
|
||||
//error log here
|
||||
}
|
||||
|
||||
/*****************************************/
|
||||
// PsychicEventSourceResponse
|
||||
/*****************************************/
|
||||
|
||||
PsychicEventSourceResponse::PsychicEventSourceResponse(PsychicRequest * request)
|
||||
: PsychicResponse(request) {
|
||||
}
|
||||
|
||||
esp_err_t PsychicEventSourceResponse::send() {
|
||||
//build our main header
|
||||
String out = String();
|
||||
out.concat("HTTP/1.1 200 OK\r\n");
|
||||
out.concat("Content-Type: text/event-stream\r\n");
|
||||
out.concat("Cache-Control: no-cache\r\n");
|
||||
out.concat("Connection: keep-alive\r\n");
|
||||
|
||||
//get our global headers out of the way first
|
||||
for (HTTPHeader header : DefaultHeaders::Instance().getHeaders())
|
||||
out.concat(String(header.field) + ": " + String(header.value) + "\r\n");
|
||||
|
||||
//separator
|
||||
out.concat("\r\n");
|
||||
|
||||
int result;
|
||||
do {
|
||||
result = httpd_send(_request->request(), out.c_str(), out.length());
|
||||
} while (result == HTTPD_SOCK_ERR_TIMEOUT);
|
||||
|
||||
if (result < 0)
|
||||
ESP_LOGE(PH_TAG, "EventSource send failed with %s", esp_err_to_name(result));
|
||||
|
||||
if (result > 0)
|
||||
return ESP_OK;
|
||||
else
|
||||
return ESP_ERR_HTTPD_RESP_SEND;
|
||||
}
|
||||
|
||||
/*****************************************/
|
||||
// Event Message Generator
|
||||
/*****************************************/
|
||||
|
||||
String generateEventMessage(const char * message, const char * event, uint32_t id, uint32_t reconnect) {
|
||||
String ev = "";
|
||||
|
||||
if (reconnect) {
|
||||
ev += "retry: ";
|
||||
ev += String(reconnect);
|
||||
ev += "\r\n";
|
||||
}
|
||||
|
||||
if (id) {
|
||||
ev += "id: ";
|
||||
ev += String(id);
|
||||
ev += "\r\n";
|
||||
}
|
||||
|
||||
if (event != NULL) {
|
||||
ev += "event: ";
|
||||
ev += String(event);
|
||||
ev += "\r\n";
|
||||
}
|
||||
|
||||
if (message != NULL) {
|
||||
ev += "data: ";
|
||||
ev += String(message);
|
||||
ev += "\r\n";
|
||||
}
|
||||
ev += "\r\n";
|
||||
|
||||
return ev;
|
||||
}
|
||||
84
lib/PsychicHttp/src_old/PsychicEventSource.h
Normal file
84
lib/PsychicHttp/src_old/PsychicEventSource.h
Normal file
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
Asynchronous WebServer library for Espressif MCUs
|
||||
|
||||
Copyright (c) 2016 Hristo Gochkov. All rights reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#ifndef PsychicEventSource_H_
|
||||
#define PsychicEventSource_H_
|
||||
|
||||
#include "PsychicCore.h"
|
||||
#include "PsychicClient.h"
|
||||
#include "PsychicHandler.h"
|
||||
#include "PsychicResponse.h"
|
||||
|
||||
class PsychicEventSource;
|
||||
class PsychicEventSourceResponse;
|
||||
class PsychicEventSourceClient;
|
||||
class PsychicResponse;
|
||||
|
||||
typedef std::function<void(PsychicEventSourceClient * client)> PsychicEventSourceClientCallback;
|
||||
|
||||
class PsychicEventSourceClient : public PsychicClient {
|
||||
friend PsychicEventSource;
|
||||
|
||||
protected:
|
||||
uint32_t _lastId;
|
||||
|
||||
public:
|
||||
PsychicEventSourceClient(PsychicClient * client);
|
||||
~PsychicEventSourceClient();
|
||||
|
||||
uint32_t lastId() const {
|
||||
return _lastId;
|
||||
}
|
||||
void send(const char * message, const char * event = NULL, uint32_t id = 0, uint32_t reconnect = 0);
|
||||
void sendEvent(const char * event);
|
||||
};
|
||||
|
||||
class PsychicEventSource : public PsychicHandler {
|
||||
private:
|
||||
PsychicEventSourceClientCallback _onOpen;
|
||||
PsychicEventSourceClientCallback _onClose;
|
||||
|
||||
public:
|
||||
PsychicEventSource();
|
||||
~PsychicEventSource();
|
||||
|
||||
PsychicEventSourceClient * getClient(int socket) override;
|
||||
PsychicEventSourceClient * getClient(PsychicClient * client) override;
|
||||
void addClient(PsychicClient * client) override;
|
||||
void removeClient(PsychicClient * client) override;
|
||||
void openCallback(PsychicClient * client) override;
|
||||
void closeCallback(PsychicClient * client) override;
|
||||
|
||||
PsychicEventSource * onOpen(PsychicEventSourceClientCallback fn);
|
||||
PsychicEventSource * onClose(PsychicEventSourceClientCallback fn);
|
||||
|
||||
esp_err_t handleRequest(PsychicRequest * request) override final;
|
||||
|
||||
void send(const char * message, const char * event = NULL, uint32_t id = 0, uint32_t reconnect = 0);
|
||||
};
|
||||
|
||||
class PsychicEventSourceResponse : public PsychicResponse {
|
||||
public:
|
||||
PsychicEventSourceResponse(PsychicRequest * request);
|
||||
virtual esp_err_t send() override;
|
||||
};
|
||||
|
||||
String generateEventMessage(const char * message, const char * event, uint32_t id, uint32_t reconnect);
|
||||
|
||||
#endif /* PsychicEventSource_H_ */
|
||||
169
lib/PsychicHttp/src_old/PsychicFileResponse.cpp
Normal file
169
lib/PsychicHttp/src_old/PsychicFileResponse.cpp
Normal file
@@ -0,0 +1,169 @@
|
||||
#include "PsychicFileResponse.h"
|
||||
#include "PsychicResponse.h"
|
||||
#include "PsychicRequest.h"
|
||||
|
||||
|
||||
PsychicFileResponse::PsychicFileResponse(PsychicRequest * request, FS & fs, const String & path, const String & contentType, bool download)
|
||||
: PsychicResponse(request) {
|
||||
//_code = 200;
|
||||
_path = path;
|
||||
|
||||
if (!download && !fs.exists(_path) && fs.exists(_path + ".gz")) {
|
||||
_path = _path + ".gz";
|
||||
addHeader("Content-Encoding", "gzip");
|
||||
_sendContentLength = true;
|
||||
_chunked = false;
|
||||
}
|
||||
|
||||
_content = fs.open(_path, "r");
|
||||
_contentLength = _content.size();
|
||||
|
||||
if (contentType == "")
|
||||
_setContentType(path);
|
||||
else
|
||||
_contentType = contentType;
|
||||
setContentType(_contentType.c_str());
|
||||
|
||||
int filenameStart = path.lastIndexOf('/') + 1;
|
||||
char buf[26 + path.length() - filenameStart];
|
||||
char * filename = (char *)path.c_str() + filenameStart;
|
||||
|
||||
if (download) {
|
||||
// set filename and force download
|
||||
snprintf(buf, sizeof(buf), "attachment; filename=\"%s\"", filename);
|
||||
} else {
|
||||
// set filename and force rendering
|
||||
snprintf(buf, sizeof(buf), "inline; filename=\"%s\"", filename);
|
||||
}
|
||||
addHeader("Content-Disposition", buf);
|
||||
}
|
||||
|
||||
PsychicFileResponse::PsychicFileResponse(PsychicRequest * request, File content, const String & path, const String & contentType, bool download)
|
||||
: PsychicResponse(request) {
|
||||
_path = path;
|
||||
|
||||
if (!download && String(content.name()).endsWith(".gz") && !path.endsWith(".gz")) {
|
||||
addHeader("Content-Encoding", "gzip");
|
||||
_sendContentLength = true;
|
||||
_chunked = false;
|
||||
}
|
||||
|
||||
_content = content;
|
||||
_contentLength = _content.size();
|
||||
setContentLength(_contentLength);
|
||||
|
||||
if (contentType == "")
|
||||
_setContentType(path);
|
||||
else
|
||||
_contentType = contentType;
|
||||
setContentType(_contentType.c_str());
|
||||
|
||||
int filenameStart = path.lastIndexOf('/') + 1;
|
||||
char buf[26 + path.length() - filenameStart];
|
||||
char * filename = (char *)path.c_str() + filenameStart;
|
||||
|
||||
if (download) {
|
||||
snprintf(buf, sizeof(buf), "attachment; filename=\"%s\"", filename);
|
||||
} else {
|
||||
snprintf(buf, sizeof(buf), "inline; filename=\"%s\"", filename);
|
||||
}
|
||||
addHeader("Content-Disposition", buf);
|
||||
}
|
||||
|
||||
PsychicFileResponse::~PsychicFileResponse() {
|
||||
if (_content)
|
||||
_content.close();
|
||||
}
|
||||
|
||||
void PsychicFileResponse::_setContentType(const String & path) {
|
||||
if (path.endsWith(".html"))
|
||||
_contentType = "text/html";
|
||||
else if (path.endsWith(".htm"))
|
||||
_contentType = "text/html";
|
||||
else if (path.endsWith(".css"))
|
||||
_contentType = "text/css";
|
||||
else if (path.endsWith(".json"))
|
||||
_contentType = "application/json";
|
||||
else if (path.endsWith(".js"))
|
||||
_contentType = "application/javascript";
|
||||
else if (path.endsWith(".png"))
|
||||
_contentType = "image/png";
|
||||
else if (path.endsWith(".gif"))
|
||||
_contentType = "image/gif";
|
||||
else if (path.endsWith(".jpg"))
|
||||
_contentType = "image/jpeg";
|
||||
else if (path.endsWith(".ico"))
|
||||
_contentType = "image/x-icon";
|
||||
else if (path.endsWith(".svg"))
|
||||
_contentType = "image/svg+xml";
|
||||
else if (path.endsWith(".eot"))
|
||||
_contentType = "font/eot";
|
||||
else if (path.endsWith(".woff"))
|
||||
_contentType = "font/woff";
|
||||
else if (path.endsWith(".woff2"))
|
||||
_contentType = "font/woff2";
|
||||
else if (path.endsWith(".ttf"))
|
||||
_contentType = "font/ttf";
|
||||
else if (path.endsWith(".xml"))
|
||||
_contentType = "text/xml";
|
||||
else if (path.endsWith(".pdf"))
|
||||
_contentType = "application/pdf";
|
||||
else if (path.endsWith(".zip"))
|
||||
_contentType = "application/zip";
|
||||
else if (path.endsWith(".gz"))
|
||||
_contentType = "application/x-gzip";
|
||||
else
|
||||
_contentType = "text/plain";
|
||||
}
|
||||
|
||||
esp_err_t PsychicFileResponse::send() {
|
||||
esp_err_t err = ESP_OK;
|
||||
|
||||
//just send small files directly
|
||||
size_t size = getContentLength();
|
||||
if (size < FILE_CHUNK_SIZE) {
|
||||
uint8_t * buffer = (uint8_t *)malloc(size);
|
||||
int readSize = _content.readBytes((char *)buffer, size);
|
||||
|
||||
this->setContent(buffer, size);
|
||||
err = PsychicResponse::send();
|
||||
|
||||
free(buffer);
|
||||
} else {
|
||||
/* Retrieve the pointer to scratch buffer for temporary storage */
|
||||
char * chunk = (char *)malloc(FILE_CHUNK_SIZE);
|
||||
if (chunk == 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->sendHeaders();
|
||||
|
||||
size_t chunksize;
|
||||
do {
|
||||
/* Read file in chunks into the scratch buffer */
|
||||
chunksize = _content.readBytes(chunk, FILE_CHUNK_SIZE);
|
||||
if (chunksize > 0) {
|
||||
err = this->sendChunk((uint8_t *)chunk, chunksize);
|
||||
if (err != ESP_OK)
|
||||
break;
|
||||
}
|
||||
|
||||
/* Keep looping till the whole file is sent */
|
||||
} while (chunksize != 0);
|
||||
|
||||
//keep track of our memory
|
||||
free(chunk);
|
||||
|
||||
if (err == ESP_OK) {
|
||||
ESP_LOGI(PH_TAG, "File sending complete");
|
||||
this->finishChunking();
|
||||
}
|
||||
|
||||
/* Close file after sending complete */
|
||||
_content.close();
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
28
lib/PsychicHttp/src_old/PsychicFileResponse.h
Normal file
28
lib/PsychicHttp/src_old/PsychicFileResponse.h
Normal file
@@ -0,0 +1,28 @@
|
||||
#ifndef PsychicFileResponse_h
|
||||
#define PsychicFileResponse_h
|
||||
|
||||
#include "PsychicCore.h"
|
||||
#include "PsychicResponse.h"
|
||||
|
||||
class PsychicRequest;
|
||||
|
||||
class PsychicFileResponse : public PsychicResponse {
|
||||
using File = fs::File;
|
||||
using FS = fs::FS;
|
||||
|
||||
private:
|
||||
File _content;
|
||||
String _path;
|
||||
bool _sendContentLength;
|
||||
bool _chunked;
|
||||
String _contentType;
|
||||
void _setContentType(const String & path);
|
||||
|
||||
public:
|
||||
PsychicFileResponse(PsychicRequest * request, FS & fs, const String & path, const String & contentType = String(), bool download = false);
|
||||
PsychicFileResponse(PsychicRequest * request, File content, const String & path, const String & contentType = String(), bool download = false);
|
||||
~PsychicFileResponse();
|
||||
esp_err_t send();
|
||||
};
|
||||
|
||||
#endif // PsychicFileResponse_h
|
||||
103
lib/PsychicHttp/src_old/PsychicHandler.cpp
Normal file
103
lib/PsychicHttp/src_old/PsychicHandler.cpp
Normal file
@@ -0,0 +1,103 @@
|
||||
#include "PsychicHandler.h"
|
||||
|
||||
PsychicHandler::PsychicHandler() :
|
||||
_filter(NULL),
|
||||
_server(NULL),
|
||||
_username(""),
|
||||
_password(""),
|
||||
_method(DIGEST_AUTH),
|
||||
_realm(""),
|
||||
_authFailMsg("")
|
||||
{}
|
||||
|
||||
PsychicHandler::~PsychicHandler() {
|
||||
// actual PsychicClient deletion handled by PsychicServer
|
||||
// for (PsychicClient *client : _clients)
|
||||
// delete(client);
|
||||
_clients.clear();
|
||||
}
|
||||
|
||||
PsychicHandler* PsychicHandler::setFilter(PsychicRequestFilterFunction fn) {
|
||||
_filter = fn;
|
||||
return this;
|
||||
}
|
||||
|
||||
bool PsychicHandler::filter(PsychicRequest *request){
|
||||
return _filter == NULL || _filter(request);
|
||||
}
|
||||
|
||||
PsychicHandler* PsychicHandler::setAuthentication(const char *username, const char *password, HTTPAuthMethod method, const char *realm, const char *authFailMsg) {
|
||||
_username = String(username);
|
||||
_password = String(password);
|
||||
_method = method;
|
||||
_realm = String(realm);
|
||||
_authFailMsg = String(authFailMsg);
|
||||
return this;
|
||||
};
|
||||
|
||||
bool PsychicHandler::needsAuthentication(PsychicRequest *request) {
|
||||
return (_username != "" && _password != "") && !request->authenticate(_username.c_str(), _password.c_str());
|
||||
}
|
||||
|
||||
esp_err_t PsychicHandler::authenticate(PsychicRequest *request) {
|
||||
return request->requestAuthentication(_method, _realm.c_str(), _authFailMsg.c_str());
|
||||
}
|
||||
|
||||
PsychicClient * PsychicHandler::checkForNewClient(PsychicClient *client)
|
||||
{
|
||||
PsychicClient *c = PsychicHandler::getClient(client);
|
||||
if (c == NULL)
|
||||
{
|
||||
c = client;
|
||||
addClient(c);
|
||||
c->isNew = true;
|
||||
}
|
||||
else
|
||||
c->isNew = false;
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
void PsychicHandler::checkForClosedClient(PsychicClient *client)
|
||||
{
|
||||
if (hasClient(client))
|
||||
{
|
||||
closeCallback(client);
|
||||
removeClient(client);
|
||||
}
|
||||
}
|
||||
|
||||
void PsychicHandler::addClient(PsychicClient *client) {
|
||||
_clients.push_back(client);
|
||||
}
|
||||
|
||||
void PsychicHandler::removeClient(PsychicClient *client) {
|
||||
_clients.remove(client);
|
||||
}
|
||||
|
||||
PsychicClient * PsychicHandler::getClient(int socket)
|
||||
{
|
||||
//make sure the server has it too.
|
||||
if (!_server->hasClient(socket))
|
||||
return NULL;
|
||||
|
||||
//what about us?
|
||||
for (PsychicClient *client : _clients)
|
||||
if (client->socket() == socket)
|
||||
return client;
|
||||
|
||||
//nothing found.
|
||||
return NULL;
|
||||
}
|
||||
|
||||
PsychicClient * PsychicHandler::getClient(PsychicClient *client) {
|
||||
return PsychicHandler::getClient(client->socket());
|
||||
}
|
||||
|
||||
bool PsychicHandler::hasClient(PsychicClient *socket) {
|
||||
return PsychicHandler::getClient(socket) != NULL;
|
||||
}
|
||||
|
||||
const std::list<PsychicClient*>& PsychicHandler::getClientList() {
|
||||
return _clients;
|
||||
}
|
||||
67
lib/PsychicHttp/src_old/PsychicHandler.h
Normal file
67
lib/PsychicHttp/src_old/PsychicHandler.h
Normal file
@@ -0,0 +1,67 @@
|
||||
#ifndef PsychicHandler_h
|
||||
#define PsychicHandler_h
|
||||
|
||||
#include "PsychicCore.h"
|
||||
#include "PsychicRequest.h"
|
||||
|
||||
class PsychicEndpoint;
|
||||
|
||||
/*
|
||||
* HANDLER :: Can be attached to any endpoint or as a generic request handler.
|
||||
*/
|
||||
|
||||
class PsychicHandler {
|
||||
friend PsychicEndpoint;
|
||||
|
||||
protected:
|
||||
PsychicRequestFilterFunction _filter;
|
||||
PsychicHttpServer * _server;
|
||||
|
||||
String _username;
|
||||
String _password;
|
||||
HTTPAuthMethod _method;
|
||||
String _realm;
|
||||
String _authFailMsg;
|
||||
|
||||
std::list<PsychicClient *> _clients;
|
||||
|
||||
public:
|
||||
PsychicHandler();
|
||||
~PsychicHandler();
|
||||
|
||||
PsychicHandler * setFilter(PsychicRequestFilterFunction fn);
|
||||
bool filter(PsychicRequest * request);
|
||||
|
||||
PsychicHandler *
|
||||
setAuthentication(const char * username, const char * password, HTTPAuthMethod method = BASIC_AUTH, const char * realm = "", const char * authFailMsg = "");
|
||||
bool needsAuthentication(PsychicRequest * request);
|
||||
esp_err_t authenticate(PsychicRequest * request);
|
||||
|
||||
virtual bool isWebSocket() {
|
||||
return false;
|
||||
};
|
||||
|
||||
PsychicClient * checkForNewClient(PsychicClient * client);
|
||||
void checkForClosedClient(PsychicClient * client);
|
||||
|
||||
virtual void addClient(PsychicClient * client);
|
||||
virtual void removeClient(PsychicClient * client);
|
||||
virtual PsychicClient * getClient(int socket);
|
||||
virtual PsychicClient * getClient(PsychicClient * client);
|
||||
virtual void openCallback(PsychicClient * client){};
|
||||
virtual void closeCallback(PsychicClient * client){};
|
||||
|
||||
bool hasClient(PsychicClient * client);
|
||||
int count() {
|
||||
return _clients.size();
|
||||
};
|
||||
const std::list<PsychicClient *> & getClientList();
|
||||
|
||||
//derived classes must implement these functions
|
||||
virtual bool canHandle(PsychicRequest * request) {
|
||||
return true;
|
||||
};
|
||||
virtual esp_err_t handleRequest(PsychicRequest * request) = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
23
lib/PsychicHttp/src_old/PsychicHttp.h
Normal file
23
lib/PsychicHttp/src_old/PsychicHttp.h
Normal file
@@ -0,0 +1,23 @@
|
||||
#ifndef PsychicHttp_h
|
||||
#define PsychicHttp_h
|
||||
|
||||
//#define ENABLE_ASYNC // This is something added in ESP-IDF 5.1.x where each request can be handled in its own thread
|
||||
|
||||
#include <http_status.h>
|
||||
#include "PsychicHttpServer.h"
|
||||
#include "PsychicRequest.h"
|
||||
#include "PsychicResponse.h"
|
||||
#include "PsychicEndpoint.h"
|
||||
#include "PsychicHandler.h"
|
||||
#include "PsychicStaticFileHandler.h"
|
||||
#include "PsychicFileResponse.h"
|
||||
#include "PsychicUploadHandler.h"
|
||||
#include "PsychicWebSocket.h"
|
||||
#include "PsychicEventSource.h"
|
||||
#include "PsychicJson.h"
|
||||
|
||||
#ifdef ENABLE_ASYNC
|
||||
#include "async_worker.h"
|
||||
#endif
|
||||
|
||||
#endif /* PsychicHttp_h */
|
||||
329
lib/PsychicHttp/src_old/PsychicHttpServer.cpp
Normal file
329
lib/PsychicHttp/src_old/PsychicHttpServer.cpp
Normal file
@@ -0,0 +1,329 @@
|
||||
#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;
|
||||
}
|
||||
86
lib/PsychicHttp/src_old/PsychicHttpServer.h
Normal file
86
lib/PsychicHttp/src_old/PsychicHttpServer.h
Normal file
@@ -0,0 +1,86 @@
|
||||
#ifndef PsychicHttpServer_h
|
||||
#define PsychicHttpServer_h
|
||||
|
||||
#include "PsychicCore.h"
|
||||
#include "PsychicClient.h"
|
||||
#include "PsychicHandler.h"
|
||||
#include <ArduinoJson.h> // added by proddy
|
||||
|
||||
class PsychicEndpoint;
|
||||
class PsychicHandler;
|
||||
class PsychicStaticFileHandler;
|
||||
|
||||
//callback definitions
|
||||
typedef std::function<esp_err_t(PsychicRequest * request)> PsychicHttpRequestCallback;
|
||||
typedef std::function<esp_err_t(PsychicRequest * request, JsonVariant & json)> PsychicJsonRequestCallback; // added by proddy
|
||||
|
||||
class PsychicHttpServer {
|
||||
protected:
|
||||
bool _use_ssl = false;
|
||||
std::list<PsychicEndpoint *> _endpoints;
|
||||
std::list<PsychicHandler *> _handlers;
|
||||
std::list<PsychicClient *> _clients;
|
||||
|
||||
PsychicClientCallback _onOpen;
|
||||
PsychicClientCallback _onClose;
|
||||
|
||||
esp_err_t _start();
|
||||
virtual esp_err_t _startServer();
|
||||
|
||||
public:
|
||||
PsychicHttpServer();
|
||||
~PsychicHttpServer();
|
||||
|
||||
//esp-idf specific stuff
|
||||
httpd_handle_t server;
|
||||
httpd_config_t config;
|
||||
|
||||
//some limits on what we will accept
|
||||
unsigned long maxUploadSize;
|
||||
unsigned long maxRequestBodySize;
|
||||
|
||||
PsychicEndpoint * defaultEndpoint;
|
||||
|
||||
static void destroy(void * ctx);
|
||||
|
||||
esp_err_t listen(uint16_t port);
|
||||
|
||||
virtual void stop();
|
||||
|
||||
PsychicHandler & addHandler(PsychicHandler * handler);
|
||||
void removeHandler(PsychicHandler * handler);
|
||||
|
||||
void addClient(PsychicClient * client);
|
||||
void removeClient(PsychicClient * client);
|
||||
PsychicClient * getClient(int socket);
|
||||
PsychicClient * getClient(httpd_req_t * req);
|
||||
bool hasClient(int socket);
|
||||
int count() {
|
||||
return _clients.size();
|
||||
};
|
||||
const std::list<PsychicClient *> & getClientList();
|
||||
|
||||
PsychicEndpoint * on(const char * uri);
|
||||
PsychicEndpoint * on(const char * uri, http_method method);
|
||||
PsychicEndpoint * on(const char * uri, PsychicHttpRequestCallback onRequest);
|
||||
PsychicEndpoint * on(const char * uri, http_method method, PsychicHttpRequestCallback onRequest);
|
||||
PsychicEndpoint * on(const char * uri, PsychicHandler * handler);
|
||||
PsychicEndpoint * on(const char * uri, http_method method, PsychicHandler * handler);
|
||||
PsychicEndpoint * on(const char * uri, http_method method, PsychicJsonRequestCallback onRequest); // added proddy
|
||||
|
||||
static esp_err_t notFoundHandler(httpd_req_t * req, httpd_err_code_t err);
|
||||
static esp_err_t defaultNotFoundHandler(PsychicRequest * request);
|
||||
void onNotFound(PsychicHttpRequestCallback fn);
|
||||
|
||||
void onOpen(PsychicClientCallback handler);
|
||||
void onClose(PsychicClientCallback handler);
|
||||
static esp_err_t openCallback(httpd_handle_t hd, int sockfd);
|
||||
static void closeCallback(httpd_handle_t hd, int sockfd);
|
||||
|
||||
PsychicStaticFileHandler * serveStatic(const char * uri, fs::FS & fs, const char * path, const char * cache_control = NULL);
|
||||
};
|
||||
|
||||
bool ON_STA_FILTER(PsychicRequest * request);
|
||||
bool ON_AP_FILTER(PsychicRequest * request);
|
||||
|
||||
#endif // PsychicHttpServer_h
|
||||
48
lib/PsychicHttp/src_old/PsychicHttpsServer.cpp
Normal file
48
lib/PsychicHttp/src_old/PsychicHttpsServer.cpp
Normal file
@@ -0,0 +1,48 @@
|
||||
#include "PsychicHttpsServer.h"
|
||||
|
||||
PsychicHttpsServer::PsychicHttpsServer()
|
||||
: PsychicHttpServer() {
|
||||
//for a SSL server
|
||||
ssl_config = HTTPD_SSL_CONFIG_DEFAULT();
|
||||
ssl_config.httpd.open_fn = PsychicHttpServer::openCallback;
|
||||
ssl_config.httpd.close_fn = PsychicHttpServer::closeCallback;
|
||||
ssl_config.httpd.uri_match_fn = httpd_uri_match_wildcard;
|
||||
ssl_config.httpd.global_user_ctx = this;
|
||||
ssl_config.httpd.global_user_ctx_free_fn = destroy;
|
||||
ssl_config.httpd.max_uri_handlers = 20;
|
||||
|
||||
// each SSL connection takes about 45kb of heap
|
||||
// a barebones sketch with PsychicHttp has ~150kb of heap available
|
||||
// if we set it higher than 2 and use all the connections, we get lots of memory errors.
|
||||
// not to mention there is no heap left over for the program itself.
|
||||
ssl_config.httpd.max_open_sockets = 2;
|
||||
}
|
||||
|
||||
PsychicHttpsServer::~PsychicHttpsServer() {
|
||||
}
|
||||
|
||||
esp_err_t PsychicHttpsServer::listen(uint16_t port, const char * cert, const char * private_key) {
|
||||
this->_use_ssl = true;
|
||||
|
||||
this->ssl_config.port_secure = port;
|
||||
this->ssl_config.cacert_pem = (uint8_t *)cert;
|
||||
this->ssl_config.cacert_len = strlen(cert) + 1;
|
||||
this->ssl_config.prvtkey_pem = (uint8_t *)private_key;
|
||||
this->ssl_config.prvtkey_len = strlen(private_key) + 1;
|
||||
|
||||
return this->_start();
|
||||
}
|
||||
|
||||
esp_err_t PsychicHttpsServer::_startServer() {
|
||||
if (this->_use_ssl)
|
||||
return httpd_ssl_start(&this->server, &this->ssl_config);
|
||||
else
|
||||
return httpd_start(&this->server, &this->config);
|
||||
}
|
||||
|
||||
void PsychicHttpsServer::stop() {
|
||||
if (this->_use_ssl)
|
||||
httpd_ssl_stop(this->server);
|
||||
else
|
||||
httpd_stop(this->server);
|
||||
}
|
||||
31
lib/PsychicHttp/src_old/PsychicHttpsServer.h
Normal file
31
lib/PsychicHttp/src_old/PsychicHttpsServer.h
Normal file
@@ -0,0 +1,31 @@
|
||||
#ifndef PsychicHttpsServer_h
|
||||
#define PsychicHttpsServer_h
|
||||
|
||||
#include "PsychicCore.h"
|
||||
#include "PsychicHttpServer.h"
|
||||
#include <esp_https_server.h>
|
||||
|
||||
#if !CONFIG_HTTPD_WS_SUPPORT
|
||||
#error PsychicHttpsServer cannot be used unless HTTPD_WS_SUPPORT is enabled in esp-http-server component configuration
|
||||
#endif
|
||||
|
||||
#define PSY_ENABLE_SSL //you can use this define in your code to enable/disable these features
|
||||
|
||||
class PsychicHttpsServer : public PsychicHttpServer {
|
||||
protected:
|
||||
bool _use_ssl = false;
|
||||
|
||||
public:
|
||||
PsychicHttpsServer();
|
||||
~PsychicHttpsServer();
|
||||
|
||||
httpd_ssl_config_t ssl_config;
|
||||
|
||||
using PsychicHttpServer::listen; //keep the regular version
|
||||
esp_err_t listen(uint16_t port, const char * cert, const char * private_key);
|
||||
|
||||
virtual esp_err_t _startServer() override final;
|
||||
virtual void stop() override final;
|
||||
};
|
||||
|
||||
#endif // PsychicHttpsServer_h
|
||||
247
lib/PsychicHttp/src_old/PsychicJson.h
Normal file
247
lib/PsychicHttp/src_old/PsychicJson.h
Normal file
@@ -0,0 +1,247 @@
|
||||
// PsychicJson.h
|
||||
/*
|
||||
Async Response to use with ArduinoJson and AsyncWebServer
|
||||
Written by Andrew Melvin (SticilFace) with help from me-no-dev and BBlanchon.
|
||||
Ported to PsychicHttp by Zach Hoeken
|
||||
|
||||
*/
|
||||
#ifndef PSYCHIC_JSON_H_
|
||||
#define PSYCHIC_JSON_H_
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include "PsychicCore.h"
|
||||
#include "PsychicResponse.h"
|
||||
#include "ChunkPrinter.h"
|
||||
|
||||
#if ARDUINOJSON_VERSION_MAJOR == 5
|
||||
#define ARDUINOJSON_5_COMPATIBILITY
|
||||
#else
|
||||
#ifndef DYNAMIC_JSON_DOCUMENT_SIZE
|
||||
#define DYNAMIC_JSON_DOCUMENT_SIZE 4096
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef JSON_BUFFER_SIZE
|
||||
//#define JSON_BUFFER_SIZE 256
|
||||
#define JSON_BUFFER_SIZE 4 * 1024
|
||||
#endif
|
||||
|
||||
constexpr const char * JSON_MIMETYPE = "application/json";
|
||||
|
||||
/*
|
||||
* Json Response
|
||||
* */
|
||||
|
||||
class PsychicJsonResponse : public PsychicResponse {
|
||||
protected:
|
||||
#ifdef ARDUINOJSON_5_COMPATIBILITY
|
||||
DynamicJsonBuffer _jsonBuffer;
|
||||
#else
|
||||
DynamicJsonDocument _jsonBuffer;
|
||||
#endif
|
||||
|
||||
JsonVariant _root;
|
||||
size_t _contentLength;
|
||||
bool _msgPack; // added by proddy
|
||||
|
||||
public:
|
||||
#ifdef ARDUINOJSON_5_COMPATIBILITY
|
||||
PsychicJsonResponse(PsychicRequest * request, bool isArray = false)
|
||||
: PsychicResponse(request) {
|
||||
setContentType(JSON_MIMETYPE);
|
||||
if (isArray)
|
||||
_root = _jsonBuffer.createArray();
|
||||
else
|
||||
_root = _jsonBuffer.createObject();
|
||||
}
|
||||
#else
|
||||
PsychicJsonResponse(PsychicRequest * request, bool isArray = false, size_t maxJsonBufferSize = DYNAMIC_JSON_DOCUMENT_SIZE, bool msgPack = false) // added by proddy
|
||||
: PsychicResponse(request)
|
||||
, _jsonBuffer(maxJsonBufferSize)
|
||||
, _msgPack(msgPack) // added by proddy
|
||||
{
|
||||
setContentType(JSON_MIMETYPE);
|
||||
if (isArray)
|
||||
_root = _jsonBuffer.createNestedArray();
|
||||
else
|
||||
_root = _jsonBuffer.createNestedObject();
|
||||
}
|
||||
#endif
|
||||
|
||||
~PsychicJsonResponse() {
|
||||
}
|
||||
|
||||
JsonVariant & getRoot() {
|
||||
return _root;
|
||||
}
|
||||
|
||||
size_t getLength() {
|
||||
#ifdef ARDUINOJSON_5_COMPATIBILITY
|
||||
return _root.measureLength();
|
||||
#else
|
||||
// return measureJson(_root);
|
||||
return (_msgPack) ? measureMsgPack(_root) : measureJson(_root); // added by proddy
|
||||
#endif
|
||||
}
|
||||
|
||||
// size_t _fillBuffer(uint8_t *data, size_t len)
|
||||
// {
|
||||
// //TODO: fix
|
||||
// //ChunkPrint dest(data, _sentLength, len);
|
||||
|
||||
// #ifdef ARDUINOJSON_5_COMPATIBILITY
|
||||
// _root.printTo(dest);
|
||||
// #else
|
||||
// //serializeJson(_root, dest);
|
||||
// #endif
|
||||
// return len;
|
||||
// }
|
||||
|
||||
virtual esp_err_t send() override {
|
||||
esp_err_t err = ESP_OK;
|
||||
size_t length = getLength();
|
||||
size_t buffer_size;
|
||||
char * buffer;
|
||||
|
||||
DUMP(length);
|
||||
|
||||
//how big of a buffer do we want?
|
||||
if (length < JSON_BUFFER_SIZE)
|
||||
buffer_size = length + 1;
|
||||
else
|
||||
buffer_size = JSON_BUFFER_SIZE;
|
||||
|
||||
DUMP(buffer_size);
|
||||
|
||||
buffer = (char *)malloc(buffer_size);
|
||||
if (buffer == NULL) {
|
||||
httpd_resp_send_err(this->_request->request(), HTTPD_500_INTERNAL_SERVER_ERROR, "Unable to allocate memory.");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
//send it in one shot or no?
|
||||
if (length < JSON_BUFFER_SIZE) {
|
||||
TRACE();
|
||||
|
||||
#ifdef ARDUINOJSON_5_COMPATIBILITY
|
||||
_root.printTo(buffer, buffer_size);
|
||||
#else
|
||||
// serializeJson(_root, buffer, buffer_size)
|
||||
(_msgPack) ? serializeMsgPack(_root, buffer, buffer_size) : serializeJson(_root, buffer, buffer_size); // added by proddy
|
||||
#endif
|
||||
|
||||
this->setContent((uint8_t *)buffer, length);
|
||||
this->setContentType(JSON_MIMETYPE);
|
||||
|
||||
err = PsychicResponse::send();
|
||||
} else {
|
||||
//helper class that acts as a stream to print chunked responses
|
||||
ChunkPrinter dest(this, (uint8_t *)buffer, buffer_size);
|
||||
|
||||
//keep our headers
|
||||
this->sendHeaders();
|
||||
|
||||
//these commands write to the ChunkPrinter which does the sending
|
||||
#ifdef ARDUINOJSON_5_COMPATIBILITY
|
||||
_root.printTo(dest);
|
||||
#else
|
||||
// serializeJson(_root, dest); // added by proddy
|
||||
(_msgPack) ? serializeMsgPack(_root, dest) : serializeJson(_root, dest); // added by proddy
|
||||
|
||||
#endif
|
||||
|
||||
//send the last bits
|
||||
dest.flush();
|
||||
|
||||
//done with our chunked response too
|
||||
err = this->finishChunking();
|
||||
}
|
||||
|
||||
//let the buffer go
|
||||
free(buffer);
|
||||
|
||||
return err;
|
||||
}
|
||||
};
|
||||
|
||||
// class PrettyPsychicJsonResponse : public PsychicJsonResponse
|
||||
// {
|
||||
// public:
|
||||
// #ifdef ARDUINOJSON_5_COMPATIBILITY
|
||||
// PrettyPsychicJsonResponse(PsychicRequest *request, bool isArray = false) : PsychicJsonResponse(request, isArray) {}
|
||||
// #else
|
||||
// PrettyPsychicJsonResponse(
|
||||
// PsychicRequest *request,
|
||||
// bool isArray = false,
|
||||
// size_t maxJsonBufferSize = DYNAMIC_JSON_DOCUMENT_SIZE)
|
||||
// : PsychicJsonResponse{request, isArray, maxJsonBufferSize} {}
|
||||
// #endif
|
||||
|
||||
// size_t setLength()
|
||||
// {
|
||||
// #ifdef ARDUINOJSON_5_COMPATIBILITY
|
||||
// _contentLength = _root.measurePrettyLength();
|
||||
// #else
|
||||
// _contentLength = measureJsonPretty(_root);
|
||||
// #endif
|
||||
// return _contentLength;
|
||||
// }
|
||||
// };
|
||||
|
||||
typedef std::function<esp_err_t(PsychicRequest * request, JsonVariant & json)> PsychicJsonRequestCallback;
|
||||
|
||||
class PsychicJsonHandler : public PsychicWebHandler {
|
||||
protected:
|
||||
PsychicJsonRequestCallback _onRequest;
|
||||
#ifndef ARDUINOJSON_5_COMPATIBILITY
|
||||
const size_t _maxJsonBufferSize = DYNAMIC_JSON_DOCUMENT_SIZE;
|
||||
#endif
|
||||
|
||||
public:
|
||||
#ifdef ARDUINOJSON_5_COMPATIBILITY
|
||||
PsychicJsonHandler()
|
||||
: _onRequest(NULL){};
|
||||
|
||||
PsychicJsonHandler(PsychicJsonRequestCallback onRequest)
|
||||
: _onRequest(onRequest) {
|
||||
}
|
||||
#else
|
||||
PsychicJsonHandler(size_t maxJsonBufferSize = DYNAMIC_JSON_DOCUMENT_SIZE)
|
||||
: _onRequest(NULL)
|
||||
, _maxJsonBufferSize(maxJsonBufferSize){};
|
||||
|
||||
PsychicJsonHandler(PsychicJsonRequestCallback onRequest, size_t maxJsonBufferSize = DYNAMIC_JSON_DOCUMENT_SIZE)
|
||||
: _onRequest(onRequest)
|
||||
, _maxJsonBufferSize(maxJsonBufferSize) {
|
||||
}
|
||||
#endif
|
||||
|
||||
void onRequest(PsychicJsonRequestCallback fn) {
|
||||
_onRequest = fn;
|
||||
}
|
||||
|
||||
virtual esp_err_t handleRequest(PsychicRequest * request) override {
|
||||
//process basic stuff
|
||||
PsychicWebHandler::handleRequest(request);
|
||||
|
||||
if (_onRequest) {
|
||||
#ifdef ARDUINOJSON_5_COMPATIBILITY
|
||||
DynamicJsonBuffer jsonBuffer;
|
||||
JsonVariant json = jsonBuffer.parse();
|
||||
if (!json.success())
|
||||
return request->reply(400);
|
||||
#else
|
||||
DynamicJsonDocument jsonBuffer(this->_maxJsonBufferSize);
|
||||
DeserializationError error = deserializeJson(jsonBuffer, request->body());
|
||||
if (error)
|
||||
return request->reply(400);
|
||||
|
||||
JsonVariant json = jsonBuffer.as<JsonVariant>();
|
||||
#endif
|
||||
|
||||
return _onRequest(request, json);
|
||||
} else
|
||||
return request->reply(500);
|
||||
}
|
||||
};
|
||||
#endif
|
||||
548
lib/PsychicHttp/src_old/PsychicRequest.cpp
Normal file
548
lib/PsychicHttp/src_old/PsychicRequest.cpp
Normal file
@@ -0,0 +1,548 @@
|
||||
#include "PsychicRequest.h"
|
||||
#include "PsychicResponse.h"
|
||||
#include <http_status.h>
|
||||
|
||||
PsychicRequest::PsychicRequest(PsychicHttpServer *server, httpd_req_t *req) :
|
||||
_server(server),
|
||||
_req(req),
|
||||
_method(HTTP_GET),
|
||||
_query(""),
|
||||
_body(""),
|
||||
_tempObject(NULL)
|
||||
{
|
||||
//load up our client.
|
||||
this->_client = server->getClient(req);
|
||||
|
||||
//handle our session data
|
||||
if (req->sess_ctx != NULL)
|
||||
this->_session = (SessionData *)req->sess_ctx;
|
||||
else
|
||||
{
|
||||
this->_session = new SessionData();
|
||||
req->sess_ctx = this->_session;
|
||||
}
|
||||
|
||||
//callback for freeing the session later
|
||||
req->free_ctx = this->freeSession;
|
||||
|
||||
//load up some data
|
||||
this->_uri = String(this->_req->uri);
|
||||
}
|
||||
|
||||
PsychicRequest::~PsychicRequest()
|
||||
{
|
||||
//temorary user object
|
||||
if (_tempObject != NULL)
|
||||
free(_tempObject);
|
||||
|
||||
//our web parameters
|
||||
for (auto *param : _params)
|
||||
delete(param);
|
||||
_params.clear();
|
||||
}
|
||||
|
||||
void PsychicRequest::freeSession(void *ctx)
|
||||
{
|
||||
if (ctx != NULL)
|
||||
{
|
||||
SessionData *session = (SessionData*)ctx;
|
||||
delete session;
|
||||
}
|
||||
}
|
||||
|
||||
PsychicHttpServer * PsychicRequest::server() {
|
||||
return _server;
|
||||
}
|
||||
|
||||
httpd_req_t * PsychicRequest::request() {
|
||||
return _req;
|
||||
}
|
||||
|
||||
PsychicClient * PsychicRequest::client() {
|
||||
return _client;
|
||||
}
|
||||
|
||||
const String PsychicRequest::getFilename()
|
||||
{
|
||||
//parse the content-disposition header
|
||||
if (this->hasHeader("Content-Disposition"))
|
||||
{
|
||||
ContentDisposition cd = this->getContentDisposition();
|
||||
if (cd.filename != "")
|
||||
return cd.filename;
|
||||
}
|
||||
|
||||
//fall back to passed in query string
|
||||
PsychicWebParameter *param = getParam("_filename");
|
||||
if (param != NULL)
|
||||
return param->name();
|
||||
|
||||
//fall back to parsing it from url (useful for wildcard uploads)
|
||||
String uri = this->uri();
|
||||
int filenameStart = uri.lastIndexOf('/') + 1;
|
||||
String filename = uri.substring(filenameStart);
|
||||
if (filename != "")
|
||||
return filename;
|
||||
|
||||
//finally, unknown.
|
||||
ESP_LOGE(PH_TAG, "Did not get a valid filename from the upload.");
|
||||
return "unknown.txt";
|
||||
}
|
||||
|
||||
const ContentDisposition PsychicRequest::getContentDisposition()
|
||||
{
|
||||
ContentDisposition cd;
|
||||
String header = this->header("Content-Disposition");
|
||||
int start;
|
||||
int end;
|
||||
|
||||
if (header.indexOf("form-data") == 0)
|
||||
cd.disposition = FORM_DATA;
|
||||
else if (header.indexOf("attachment") == 0)
|
||||
cd.disposition = ATTACHMENT;
|
||||
else if (header.indexOf("inline") == 0)
|
||||
cd.disposition = INLINE;
|
||||
else
|
||||
cd.disposition = NONE;
|
||||
|
||||
start = header.indexOf("filename=");
|
||||
if (start)
|
||||
{
|
||||
end = header.indexOf('"', start+10);
|
||||
cd.filename = header.substring(start+10, end-1);
|
||||
}
|
||||
|
||||
start = header.indexOf("name=");
|
||||
if (start)
|
||||
{
|
||||
end = header.indexOf('"', start+6);
|
||||
cd.name = header.substring(start+6, end-1);
|
||||
}
|
||||
|
||||
return cd;
|
||||
}
|
||||
|
||||
esp_err_t PsychicRequest::loadBody()
|
||||
{
|
||||
esp_err_t err = ESP_OK;
|
||||
|
||||
this->_body = String();
|
||||
|
||||
//Get header value string length and allocate memory for length + 1, extra byte for null termination
|
||||
size_t remaining = this->_req->content_len;
|
||||
char *buf = (char *)malloc(remaining+1);
|
||||
|
||||
//while loop for retries
|
||||
while (remaining > 0)
|
||||
{
|
||||
//read our data from the socket
|
||||
int received = httpd_req_recv(this->_req, buf, this->_req->content_len);
|
||||
|
||||
//Retry if timeout occurred
|
||||
if (received == HTTPD_SOCK_ERR_TIMEOUT)
|
||||
continue;
|
||||
//bail if we got an error
|
||||
else if (received == HTTPD_SOCK_ERR_FAIL)
|
||||
{
|
||||
err = ESP_FAIL;
|
||||
break;
|
||||
}
|
||||
|
||||
//keep track of our
|
||||
remaining -= received;
|
||||
}
|
||||
|
||||
//null terminate and make our string
|
||||
buf[this->_req->content_len] = '\0';
|
||||
this->_body = String(buf);
|
||||
|
||||
//keep track of that pesky memory
|
||||
free(buf);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
http_method PsychicRequest::method() {
|
||||
return (http_method)this->_req->method;
|
||||
}
|
||||
|
||||
const String PsychicRequest::methodStr() {
|
||||
return String(http_method_str((http_method)this->_req->method));
|
||||
}
|
||||
|
||||
const String PsychicRequest::path() {
|
||||
int index = _uri.indexOf("?");
|
||||
if(index == -1)
|
||||
return _uri;
|
||||
else
|
||||
return _uri.substring(0, index);
|
||||
}
|
||||
|
||||
const String& PsychicRequest::uri() {
|
||||
return this->_uri;
|
||||
}
|
||||
|
||||
const String& PsychicRequest::query() {
|
||||
return this->_query;
|
||||
}
|
||||
|
||||
// no way to get list of headers yet....
|
||||
// int PsychicRequest::headers()
|
||||
// {
|
||||
// }
|
||||
|
||||
const String PsychicRequest::header(const char *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 (header_len)
|
||||
{
|
||||
char header[header_len+1];
|
||||
httpd_req_get_hdr_value_str(this->_req, name, header, sizeof(header));
|
||||
return String(header);
|
||||
}
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
bool PsychicRequest::hasHeader(const char *name)
|
||||
{
|
||||
return httpd_req_get_hdr_value_len(this->_req, name) > 0;
|
||||
}
|
||||
|
||||
const String PsychicRequest::host() {
|
||||
return this->header("Host");
|
||||
}
|
||||
|
||||
const String PsychicRequest::contentType() {
|
||||
return header("Content-Type");
|
||||
}
|
||||
|
||||
size_t PsychicRequest::contentLength() {
|
||||
return this->_req->content_len;
|
||||
}
|
||||
|
||||
const String& PsychicRequest::body()
|
||||
{
|
||||
return this->_body;
|
||||
}
|
||||
|
||||
bool PsychicRequest::isMultipart()
|
||||
{
|
||||
const String& type = this->contentType();
|
||||
|
||||
return (this->contentType().indexOf("multipart/form-data") >= 0);
|
||||
}
|
||||
|
||||
esp_err_t PsychicRequest::redirect(const char *url)
|
||||
{
|
||||
PsychicResponse response(this);
|
||||
response.setCode(301);
|
||||
response.addHeader("Location", url);
|
||||
|
||||
return response.send();
|
||||
}
|
||||
|
||||
bool PsychicRequest::hasCookie(const char *key)
|
||||
{
|
||||
char cookie[MAX_COOKIE_SIZE];
|
||||
size_t cookieSize = MAX_COOKIE_SIZE;
|
||||
esp_err_t err = httpd_req_get_cookie_val(this->_req, key, cookie, &cookieSize);
|
||||
|
||||
//did we get anything?
|
||||
if (err == ESP_OK)
|
||||
return true;
|
||||
else if (err == ESP_ERR_HTTPD_RESULT_TRUNC)
|
||||
ESP_LOGE(PH_TAG, "cookie too large (%d bytes).\n", cookieSize);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
const String PsychicRequest::getCookie(const char *key)
|
||||
{
|
||||
char cookie[MAX_COOKIE_SIZE];
|
||||
size_t cookieSize = MAX_COOKIE_SIZE;
|
||||
esp_err_t err = httpd_req_get_cookie_val(this->_req, key, cookie, &cookieSize);
|
||||
|
||||
//did we get anything?
|
||||
if (err == ESP_OK)
|
||||
return String(cookie);
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
void PsychicRequest::loadParams()
|
||||
{
|
||||
//did we get a query string?
|
||||
size_t query_len = httpd_req_get_url_query_len(_req);
|
||||
if (query_len)
|
||||
{
|
||||
char query[query_len+1];
|
||||
httpd_req_get_url_query_str(_req, query, sizeof(query));
|
||||
_query.concat(query);
|
||||
|
||||
//parse them.
|
||||
_addParams(_query);
|
||||
}
|
||||
|
||||
//did we get form data as body?
|
||||
if (this->method() == HTTP_POST && this->contentType() == "application/x-www-form-urlencoded")
|
||||
{
|
||||
_addParams(_body);
|
||||
}
|
||||
}
|
||||
|
||||
void PsychicRequest::_addParams(const String& params){
|
||||
size_t start = 0;
|
||||
while (start < params.length()){
|
||||
int end = params.indexOf('&', start);
|
||||
if (end < 0) end = params.length();
|
||||
int equal = params.indexOf('=', start);
|
||||
if (equal < 0 || equal > end) equal = end;
|
||||
String name = params.substring(start, equal);
|
||||
String value = equal + 1 < end ? params.substring(equal + 1, end) : String();
|
||||
addParam(name, value);
|
||||
start = end + 1;
|
||||
}
|
||||
}
|
||||
|
||||
PsychicWebParameter * PsychicRequest::addParam(const String &name, const String &value, bool decode)
|
||||
{
|
||||
if (decode)
|
||||
return addParam(new PsychicWebParameter(urlDecode(name.c_str()), urlDecode(value.c_str())));
|
||||
else
|
||||
return addParam(new PsychicWebParameter(name, value));
|
||||
}
|
||||
|
||||
PsychicWebParameter * PsychicRequest::addParam(PsychicWebParameter *param) {
|
||||
_params.push_back(param);
|
||||
return param;
|
||||
}
|
||||
|
||||
bool PsychicRequest::hasParam(const char *key)
|
||||
{
|
||||
return getParam(key) != NULL;
|
||||
}
|
||||
|
||||
PsychicWebParameter * PsychicRequest::getParam(const char *key)
|
||||
{
|
||||
for (auto *param : _params)
|
||||
if (param->name().equals(key))
|
||||
return param;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool PsychicRequest::hasSessionKey(const String& key)
|
||||
{
|
||||
return this->_session->find(key) != this->_session->end();
|
||||
}
|
||||
|
||||
const String PsychicRequest::getSessionKey(const String& key)
|
||||
{
|
||||
auto it = this->_session->find(key);
|
||||
if (it != this->_session->end())
|
||||
return it->second;
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
void PsychicRequest::setSessionKey(const String& key, const String& value)
|
||||
{
|
||||
this->_session->insert(std::pair<String, String>(key, value));
|
||||
}
|
||||
|
||||
static const String md5str(const String &in){
|
||||
MD5Builder md5 = MD5Builder();
|
||||
md5.begin();
|
||||
md5.add(in);
|
||||
md5.calculate();
|
||||
return md5.toString();
|
||||
}
|
||||
|
||||
bool PsychicRequest::authenticate(const char * username, const char * password)
|
||||
{
|
||||
if(hasHeader("Authorization"))
|
||||
{
|
||||
String authReq = header("Authorization");
|
||||
if(authReq.startsWith("Basic")){
|
||||
authReq = authReq.substring(6);
|
||||
authReq.trim();
|
||||
char toencodeLen = strlen(username)+strlen(password)+1;
|
||||
char *toencode = new char[toencodeLen + 1];
|
||||
if(toencode == NULL){
|
||||
authReq = "";
|
||||
return false;
|
||||
}
|
||||
char *encoded = new char[base64_encode_expected_len(toencodeLen)+1];
|
||||
if(encoded == NULL){
|
||||
authReq = "";
|
||||
delete[] toencode;
|
||||
return false;
|
||||
}
|
||||
sprintf(toencode, "%s:%s", username, password);
|
||||
if(base64_encode_chars(toencode, toencodeLen, encoded) > 0 && authReq.equalsConstantTime(encoded)) {
|
||||
authReq = "";
|
||||
delete[] toencode;
|
||||
delete[] encoded;
|
||||
return true;
|
||||
}
|
||||
delete[] toencode;
|
||||
delete[] encoded;
|
||||
}
|
||||
else if(authReq.startsWith(F("Digest")))
|
||||
{
|
||||
authReq = authReq.substring(7);
|
||||
String _username = _extractParam(authReq,F("username=\""),'\"');
|
||||
if(!_username.length() || _username != String(username)) {
|
||||
authReq = "";
|
||||
return false;
|
||||
}
|
||||
// extracting required parameters for RFC 2069 simpler Digest
|
||||
String _realm = _extractParam(authReq, F("realm=\""),'\"');
|
||||
String _nonce = _extractParam(authReq, F("nonce=\""),'\"');
|
||||
String _uri = _extractParam(authReq, F("uri=\""),'\"');
|
||||
String _resp = _extractParam(authReq, F("response=\""),'\"');
|
||||
String _opaque = _extractParam(authReq, F("opaque=\""),'\"');
|
||||
|
||||
if((!_realm.length()) || (!_nonce.length()) || (!_uri.length()) || (!_resp.length()) || (!_opaque.length())) {
|
||||
authReq = "";
|
||||
return false;
|
||||
}
|
||||
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 = "";
|
||||
return false;
|
||||
}
|
||||
// parameters for the RFC 2617 newer Digest
|
||||
String _nc,_cnonce;
|
||||
if(authReq.indexOf("qop=auth") != -1 || authReq.indexOf("qop=\"auth\"") != -1) {
|
||||
_nc = _extractParam(authReq, F("nc="), ',');
|
||||
_cnonce = _extractParam(authReq, F("cnonce=\""),'\"');
|
||||
}
|
||||
String _H1 = md5str(String(username) + ':' + _realm + ':' + String(password));
|
||||
ESP_LOGD(PH_TAG, "Hash of user:realm:pass=%s", _H1);
|
||||
String _H2 = "";
|
||||
if(_method == HTTP_GET){
|
||||
_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);
|
||||
}
|
||||
ESP_LOGD(PH_TAG, "Hash of GET:uri=%s", _H2);
|
||||
String _responsecheck = "";
|
||||
if(authReq.indexOf("qop=auth") != -1 || authReq.indexOf("qop=\"auth\"") != -1) {
|
||||
_responsecheck = md5str(_H1 + ':' + _nonce + ':' + _nc + ':' + _cnonce + F(":auth:") + _H2);
|
||||
} else {
|
||||
_responsecheck = md5str(_H1 + ':' + _nonce + ':' + _H2);
|
||||
}
|
||||
ESP_LOGD(PH_TAG, "The Proper response=%s", _responsecheck);
|
||||
if(_resp == _responsecheck){
|
||||
authReq = "";
|
||||
return true;
|
||||
}
|
||||
}
|
||||
authReq = "";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const String PsychicRequest::_extractParam(const String& authReq, const String& param, const char delimit)
|
||||
{
|
||||
int _begin = authReq.indexOf(param);
|
||||
if (_begin == -1)
|
||||
return "";
|
||||
return authReq.substring(_begin+param.length(),authReq.indexOf(delimit,_begin+param.length()));
|
||||
}
|
||||
|
||||
const String PsychicRequest::_getRandomHexString()
|
||||
{
|
||||
char buffer[33]; // buffer to hold 32 Hex Digit + /0
|
||||
int i;
|
||||
for(i = 0; i < 4; i++) {
|
||||
sprintf (buffer + (i*8), "%08lx", esp_random());
|
||||
}
|
||||
return String(buffer);
|
||||
}
|
||||
|
||||
esp_err_t PsychicRequest::requestAuthentication(HTTPAuthMethod mode, const char* realm, const char* authFailMsg)
|
||||
{
|
||||
//what is thy realm, sire?
|
||||
if(!strcmp(realm, ""))
|
||||
this->setSessionKey("realm", "Login Required");
|
||||
else
|
||||
this->setSessionKey("realm", realm);
|
||||
|
||||
PsychicResponse response(this);
|
||||
String authStr;
|
||||
|
||||
//what kind of auth?
|
||||
if(mode == BASIC_AUTH)
|
||||
{
|
||||
authStr = "Basic realm=\"" + this->getSessionKey("realm") + "\"";
|
||||
response.addHeader("WWW-Authenticate", authStr.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
//only make new ones if we havent sent them yet
|
||||
if (this->getSessionKey("nonce").isEmpty())
|
||||
this->setSessionKey("nonce", _getRandomHexString());
|
||||
if (this->getSessionKey("opaque").isEmpty())
|
||||
this->setSessionKey("opaque", _getRandomHexString());
|
||||
|
||||
authStr = "Digest realm=\"" + this->getSessionKey("realm") + "\", qop=\"auth\", nonce=\"" + this->getSessionKey("nonce") + "\", opaque=\"" + this->getSessionKey("opaque") + "\"";
|
||||
response.addHeader("WWW-Authenticate", authStr.c_str());
|
||||
}
|
||||
|
||||
//DUMP(authStr);
|
||||
|
||||
response.setCode(401);
|
||||
response.setContentType("text/html");
|
||||
response.setContent(authStr.c_str());
|
||||
return response.send();
|
||||
}
|
||||
|
||||
esp_err_t PsychicRequest::reply(int code)
|
||||
{
|
||||
PsychicResponse response(this);
|
||||
|
||||
response.setCode(code);
|
||||
response.setContentType("text/plain");
|
||||
response.setContent(http_status_reason(code));
|
||||
|
||||
return response.send();
|
||||
}
|
||||
|
||||
esp_err_t PsychicRequest::reply(const char *content)
|
||||
{
|
||||
PsychicResponse response(this);
|
||||
|
||||
response.setCode(200);
|
||||
response.setContentType("text/html");
|
||||
response.setContent(content);
|
||||
|
||||
return response.send();
|
||||
}
|
||||
|
||||
esp_err_t PsychicRequest::reply(int code, const char *contentType, const char *content)
|
||||
{
|
||||
PsychicResponse response(this);
|
||||
|
||||
response.setCode(code);
|
||||
response.setContentType(contentType);
|
||||
response.setContent(content);
|
||||
|
||||
return response.send();
|
||||
}
|
||||
97
lib/PsychicHttp/src_old/PsychicRequest.h
Normal file
97
lib/PsychicHttp/src_old/PsychicRequest.h
Normal file
@@ -0,0 +1,97 @@
|
||||
#ifndef PsychicRequest_h
|
||||
#define PsychicRequest_h
|
||||
|
||||
#include "PsychicCore.h"
|
||||
#include "PsychicHttpServer.h"
|
||||
#include "PsychicClient.h"
|
||||
#include "PsychicWebParameter.h"
|
||||
|
||||
typedef std::map<String, String> SessionData;
|
||||
|
||||
enum Disposition { NONE, INLINE, ATTACHMENT, FORM_DATA};
|
||||
|
||||
struct ContentDisposition {
|
||||
Disposition disposition;
|
||||
String filename;
|
||||
String name;
|
||||
};
|
||||
|
||||
class PsychicRequest {
|
||||
friend PsychicHttpServer;
|
||||
|
||||
protected:
|
||||
PsychicHttpServer *_server;
|
||||
httpd_req_t *_req;
|
||||
SessionData *_session;
|
||||
PsychicClient *_client;
|
||||
|
||||
http_method _method;
|
||||
String _uri;
|
||||
String _query;
|
||||
String _body;
|
||||
|
||||
std::list<PsychicWebParameter*> _params;
|
||||
|
||||
void _addParams(const String& params);
|
||||
void _parseGETParams();
|
||||
void _parsePOSTParams();
|
||||
|
||||
const String _extractParam(const String& authReq, const String& param, const char delimit);
|
||||
const String _getRandomHexString();
|
||||
|
||||
public:
|
||||
PsychicRequest(PsychicHttpServer *server, httpd_req_t *req);
|
||||
virtual ~PsychicRequest();
|
||||
|
||||
void *_tempObject;
|
||||
|
||||
PsychicHttpServer * server();
|
||||
httpd_req_t * request();
|
||||
virtual PsychicClient * client();
|
||||
|
||||
bool isMultipart();
|
||||
esp_err_t loadBody();
|
||||
|
||||
const String header(const char *name);
|
||||
bool hasHeader(const char *name);
|
||||
|
||||
static void freeSession(void *ctx);
|
||||
bool hasSessionKey(const String& key);
|
||||
const String getSessionKey(const String& key);
|
||||
void setSessionKey(const String& key, const String& value);
|
||||
|
||||
bool hasCookie(const char * key);
|
||||
const String getCookie(const char * key);
|
||||
|
||||
http_method method(); // returns the HTTP method used as enum value (eg. HTTP_GET)
|
||||
const String methodStr(); // returns the HTTP method used as a string (eg. "GET")
|
||||
const String path(); // returns the request path (eg /page?foo=bar returns "/page")
|
||||
const String& uri(); // returns the full request uri (eg /page?foo=bar)
|
||||
const String& query(); // returns the request query data (eg /page?foo=bar returns "foo=bar")
|
||||
const String host(); // returns the requested host (request to http://psychic.local/foo will return "psychic.local")
|
||||
const String contentType(); // returns the Content-Type header value
|
||||
size_t contentLength(); // returns the Content-Length header value
|
||||
const String& body(); // returns the body of the request
|
||||
const ContentDisposition getContentDisposition();
|
||||
|
||||
const String& queryString() { return query(); } //compatability function. same as query()
|
||||
const String& url() { return uri(); } //compatability function. same as uri()
|
||||
|
||||
void loadParams();
|
||||
PsychicWebParameter * addParam(PsychicWebParameter *param);
|
||||
PsychicWebParameter * addParam(const String &name, const String &value, bool decode = true);
|
||||
bool hasParam(const char *key);
|
||||
PsychicWebParameter * getParam(const char *name);
|
||||
|
||||
const String getFilename();
|
||||
|
||||
bool authenticate(const char * username, const char * password);
|
||||
esp_err_t requestAuthentication(HTTPAuthMethod mode, const char* realm, const char* authFailMsg);
|
||||
|
||||
esp_err_t redirect(const char *url);
|
||||
esp_err_t reply(int code);
|
||||
esp_err_t reply(const char *content);
|
||||
esp_err_t reply(int code, const char *contentType, const char *content);
|
||||
};
|
||||
|
||||
#endif // PsychicRequest_h
|
||||
138
lib/PsychicHttp/src_old/PsychicResponse.cpp
Normal file
138
lib/PsychicHttp/src_old/PsychicResponse.cpp
Normal file
@@ -0,0 +1,138 @@
|
||||
#include "PsychicResponse.h"
|
||||
#include "PsychicRequest.h"
|
||||
#include <http_status.h>
|
||||
|
||||
PsychicResponse::PsychicResponse(PsychicRequest * request)
|
||||
: _request(request)
|
||||
, _code(200)
|
||||
, _status("")
|
||||
, _contentLength(0)
|
||||
, _body("") {
|
||||
}
|
||||
|
||||
PsychicResponse::~PsychicResponse() {
|
||||
//clean up our header variables. we have to do this since httpd_resp_send doesn't store copies
|
||||
for (HTTPHeader header : _headers) {
|
||||
free(header.field);
|
||||
free(header.value);
|
||||
}
|
||||
_headers.clear();
|
||||
}
|
||||
|
||||
void PsychicResponse::addHeader(const char * field, const char * value) {
|
||||
//these get freed during send()
|
||||
HTTPHeader header;
|
||||
header.field = (char *)malloc(strlen(field) + 1);
|
||||
header.value = (char *)malloc(strlen(value) + 1);
|
||||
|
||||
strlcpy(header.field, field, strlen(field) + 1);
|
||||
strlcpy(header.value, value, strlen(value) + 1);
|
||||
|
||||
_headers.push_back(header);
|
||||
}
|
||||
|
||||
void PsychicResponse::setCookie(const char * name, const char * value, unsigned long secondsFromNow, const char * extras) {
|
||||
time_t now = time(nullptr);
|
||||
|
||||
String output;
|
||||
output = urlEncode(name) + "=" + urlEncode(value);
|
||||
|
||||
//if current time isn't modern, default to using max age
|
||||
if (now < 1700000000)
|
||||
output += "; Max-Age=" + String(secondsFromNow);
|
||||
//otherwise, set an expiration date
|
||||
else {
|
||||
time_t expirationTimestamp = now + secondsFromNow;
|
||||
|
||||
// Convert the expiration timestamp to a formatted string for the "expires" attribute
|
||||
struct tm * tmInfo = gmtime(&expirationTimestamp);
|
||||
char expires[30];
|
||||
strftime(expires, sizeof(expires), "%a, %d %b %Y %H:%M:%S GMT", tmInfo);
|
||||
output += "; Expires=" + String(expires);
|
||||
}
|
||||
|
||||
//did we get any extras?
|
||||
if (strlen(extras))
|
||||
output += "; " + String(extras);
|
||||
|
||||
//okay, add it in.
|
||||
addHeader("Set-Cookie", output.c_str());
|
||||
}
|
||||
|
||||
// time_t now = time(nullptr);
|
||||
// // Set the cookie with the "expires" attribute
|
||||
|
||||
void PsychicResponse::setCode(int code) {
|
||||
_code = code;
|
||||
}
|
||||
|
||||
void PsychicResponse::setContentType(const char * contentType) {
|
||||
httpd_resp_set_type(_request->request(), contentType);
|
||||
}
|
||||
|
||||
void PsychicResponse::setContent(const char * content) {
|
||||
_body = content;
|
||||
setContentLength(strlen(content));
|
||||
}
|
||||
|
||||
void PsychicResponse::setContent(const uint8_t * content, size_t len) {
|
||||
_body = (char *)content;
|
||||
setContentLength(len);
|
||||
}
|
||||
|
||||
const char * PsychicResponse::getContent() {
|
||||
return _body;
|
||||
}
|
||||
|
||||
size_t PsychicResponse::getContentLength() {
|
||||
return _contentLength;
|
||||
}
|
||||
|
||||
esp_err_t PsychicResponse::send() {
|
||||
//esp-idf makes you set the whole status.
|
||||
sprintf(_status, "%u %s", _code, http_status_reason(_code));
|
||||
httpd_resp_set_status(_request->request(), _status);
|
||||
|
||||
//our headers too
|
||||
this->sendHeaders();
|
||||
|
||||
//now send it off
|
||||
esp_err_t err = httpd_resp_send(_request->request(), getContent(), getContentLength());
|
||||
|
||||
//did something happen?
|
||||
if (err != ESP_OK)
|
||||
ESP_LOGE(PH_TAG, "Send response failed (%s)", esp_err_to_name(err));
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
void PsychicResponse::sendHeaders() {
|
||||
//get our global headers out of the way first
|
||||
for (HTTPHeader header : DefaultHeaders::Instance().getHeaders())
|
||||
httpd_resp_set_hdr(_request->request(), header.field, header.value);
|
||||
|
||||
//now do our individual headers
|
||||
for (HTTPHeader header : _headers)
|
||||
httpd_resp_set_hdr(this->_request->request(), header.field, header.value);
|
||||
}
|
||||
|
||||
esp_err_t PsychicResponse::sendChunk(uint8_t * chunk, size_t chunksize) {
|
||||
/* Send the buffer contents as HTTP response chunk */
|
||||
esp_err_t err = httpd_resp_send_chunk(this->_request->request(), (char *)chunk, chunksize);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(PH_TAG, "File sending failed (%s)", esp_err_to_name(err));
|
||||
|
||||
/* Abort sending file */
|
||||
httpd_resp_sendstr_chunk(this->_request->request(), NULL);
|
||||
|
||||
/* Respond with 500 Internal Server Error */
|
||||
httpd_resp_send_err(this->_request->request(), HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to send file");
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_err_t PsychicResponse::finishChunking() {
|
||||
/* Respond with an empty chunk to signal HTTP response completion */
|
||||
return httpd_resp_send_chunk(this->_request->request(), NULL, 0);
|
||||
}
|
||||
49
lib/PsychicHttp/src_old/PsychicResponse.h
Normal file
49
lib/PsychicHttp/src_old/PsychicResponse.h
Normal file
@@ -0,0 +1,49 @@
|
||||
#ifndef PsychicResponse_h
|
||||
#define PsychicResponse_h
|
||||
|
||||
#include "PsychicCore.h"
|
||||
#include "time.h"
|
||||
|
||||
class PsychicRequest;
|
||||
|
||||
class PsychicResponse {
|
||||
protected:
|
||||
PsychicRequest * _request;
|
||||
|
||||
int _code;
|
||||
char _status[60];
|
||||
std::list<HTTPHeader> _headers;
|
||||
int64_t _contentLength;
|
||||
const char * _body;
|
||||
|
||||
public:
|
||||
PsychicResponse(PsychicRequest * request);
|
||||
virtual ~PsychicResponse();
|
||||
|
||||
void setCode(int code);
|
||||
|
||||
void setContentType(const char * contentType);
|
||||
void setContentLength(int64_t contentLength) {
|
||||
_contentLength = contentLength;
|
||||
}
|
||||
int64_t getContentLength(int64_t contentLength) {
|
||||
return _contentLength;
|
||||
}
|
||||
|
||||
void addHeader(const char * field, const char * value);
|
||||
|
||||
void setCookie(const char * key, const char * value, unsigned long max_age = 60 * 60 * 24 * 30, const char * extras = "");
|
||||
|
||||
void setContent(const char * content);
|
||||
void setContent(const uint8_t * content, size_t len);
|
||||
|
||||
const char * getContent();
|
||||
size_t getContentLength();
|
||||
|
||||
virtual esp_err_t send();
|
||||
void sendHeaders();
|
||||
esp_err_t sendChunk(uint8_t * chunk, size_t chunksize);
|
||||
esp_err_t finishChunking();
|
||||
};
|
||||
|
||||
#endif // PsychicResponse_h
|
||||
195
lib/PsychicHttp/src_old/PsychicStaticFileHander.cpp
Normal file
195
lib/PsychicHttp/src_old/PsychicStaticFileHander.cpp
Normal file
@@ -0,0 +1,195 @@
|
||||
#include "PsychicStaticFileHandler.h"
|
||||
|
||||
/*************************************/
|
||||
/* PsychicStaticFileHandler */
|
||||
/*************************************/
|
||||
|
||||
PsychicStaticFileHandler::PsychicStaticFileHandler(const char * uri, FS & fs, const char * path, const char * cache_control)
|
||||
: _fs(fs)
|
||||
, _uri(uri)
|
||||
, _path(path)
|
||||
, _default_file("index.html")
|
||||
, _cache_control(cache_control)
|
||||
, _last_modified("") {
|
||||
// Ensure leading '/'
|
||||
if (_uri.length() == 0 || _uri[0] != '/')
|
||||
_uri = "/" + _uri;
|
||||
if (_path.length() == 0 || _path[0] != '/')
|
||||
_path = "/" + _path;
|
||||
|
||||
// If path ends with '/' we assume a hint that this is a directory to improve performance.
|
||||
// However - if it does not end with '/' we, can't assume a file, path can still be a directory.
|
||||
_isDir = _path[_path.length() - 1] == '/';
|
||||
|
||||
// Remove the trailing '/' so we can handle default file
|
||||
// Notice that root will be "" not "/"
|
||||
if (_uri[_uri.length() - 1] == '/')
|
||||
_uri = _uri.substring(0, _uri.length() - 1);
|
||||
if (_path[_path.length() - 1] == '/')
|
||||
_path = _path.substring(0, _path.length() - 1);
|
||||
|
||||
// Reset stats
|
||||
_gzipFirst = false;
|
||||
_gzipStats = 0xF8;
|
||||
}
|
||||
|
||||
PsychicStaticFileHandler & PsychicStaticFileHandler::setIsDir(bool isDir) {
|
||||
_isDir = isDir;
|
||||
return *this;
|
||||
}
|
||||
|
||||
PsychicStaticFileHandler & PsychicStaticFileHandler::setDefaultFile(const char * filename) {
|
||||
_default_file = String(filename);
|
||||
return *this;
|
||||
}
|
||||
|
||||
PsychicStaticFileHandler & PsychicStaticFileHandler::setCacheControl(const char * cache_control) {
|
||||
_cache_control = String(cache_control);
|
||||
return *this;
|
||||
}
|
||||
|
||||
PsychicStaticFileHandler & PsychicStaticFileHandler::setLastModified(const char * last_modified) {
|
||||
_last_modified = String(last_modified);
|
||||
return *this;
|
||||
}
|
||||
|
||||
PsychicStaticFileHandler & PsychicStaticFileHandler::setLastModified(struct tm * last_modified) {
|
||||
char result[30];
|
||||
strftime(result, 30, "%a, %d %b %Y %H:%M:%S %Z", last_modified);
|
||||
return setLastModified((const char *)result);
|
||||
}
|
||||
|
||||
bool PsychicStaticFileHandler::canHandle(PsychicRequest * request) {
|
||||
if (request->method() != HTTP_GET || !request->uri().startsWith(_uri))
|
||||
return false;
|
||||
|
||||
if (_getFile(request))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PsychicStaticFileHandler::_getFile(PsychicRequest * request) {
|
||||
// Remove the found uri
|
||||
String path = request->uri().substring(_uri.length());
|
||||
|
||||
// We can skip the file check and look for default if request is to the root of a directory or that request path ends with '/'
|
||||
bool canSkipFileCheck = (_isDir && path.length() == 0) || (path.length() && path[path.length() - 1] == '/');
|
||||
|
||||
path = _path + path;
|
||||
|
||||
// Do we have a file or .gz file
|
||||
if (!canSkipFileCheck && _fileExists(path))
|
||||
return true;
|
||||
|
||||
// Can't handle if not default file
|
||||
if (_default_file.length() == 0)
|
||||
return false;
|
||||
|
||||
// Try to add default file, ensure there is a trailing '/' ot the path.
|
||||
if (path.length() == 0 || path[path.length() - 1] != '/')
|
||||
path += "/";
|
||||
path += _default_file;
|
||||
|
||||
return _fileExists(path);
|
||||
}
|
||||
|
||||
#define FILE_IS_REAL(f) (f == true && !f.isDirectory())
|
||||
|
||||
bool PsychicStaticFileHandler::_fileExists(const String & path) {
|
||||
bool fileFound = false;
|
||||
bool gzipFound = false;
|
||||
|
||||
String gzip = path + ".gz";
|
||||
|
||||
if (_gzipFirst) {
|
||||
_file = _fs.open(gzip, "r");
|
||||
gzipFound = FILE_IS_REAL(_file);
|
||||
if (!gzipFound) {
|
||||
_file = _fs.open(path, "r");
|
||||
fileFound = FILE_IS_REAL(_file);
|
||||
}
|
||||
} else {
|
||||
_file = _fs.open(path, "r");
|
||||
fileFound = FILE_IS_REAL(_file);
|
||||
if (!fileFound) {
|
||||
_file = _fs.open(gzip, "r");
|
||||
gzipFound = FILE_IS_REAL(_file);
|
||||
}
|
||||
}
|
||||
|
||||
bool found = fileFound || gzipFound;
|
||||
|
||||
if (found) {
|
||||
_filename = path;
|
||||
|
||||
// Calculate gzip statistic
|
||||
_gzipStats = (_gzipStats << 1) + (gzipFound ? 1 : 0);
|
||||
if (_gzipStats == 0x00)
|
||||
_gzipFirst = false; // All files are not gzip
|
||||
else if (_gzipStats == 0xFF)
|
||||
_gzipFirst = true; // All files are gzip
|
||||
else
|
||||
_gzipFirst = _countBits(_gzipStats) > 4; // IF we have more gzip files - try gzip first
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
uint8_t PsychicStaticFileHandler::_countBits(const uint8_t value) const {
|
||||
uint8_t w = value;
|
||||
uint8_t n;
|
||||
for (n = 0; w != 0; n++)
|
||||
w &= w - 1;
|
||||
return n;
|
||||
}
|
||||
|
||||
esp_err_t PsychicStaticFileHandler::handleRequest(PsychicRequest * request) {
|
||||
if (_file == true) {
|
||||
DUMP(_filename);
|
||||
|
||||
//is it not modified?
|
||||
String etag = String(_file.size());
|
||||
if (_last_modified.length() && _last_modified == request->header("If-Modified-Since")) {
|
||||
DUMP("Last Modified Hit");
|
||||
TRACE();
|
||||
_file.close();
|
||||
request->reply(304); // Not modified
|
||||
}
|
||||
//does our Etag match?
|
||||
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();
|
||||
|
||||
PsychicResponse response(request);
|
||||
response.addHeader("Cache-Control", _cache_control.c_str());
|
||||
response.addHeader("ETag", etag.c_str());
|
||||
response.setCode(304);
|
||||
response.send();
|
||||
}
|
||||
//nope, send them the full file.
|
||||
else {
|
||||
DUMP("No cache hit");
|
||||
DUMP(_last_modified);
|
||||
DUMP(_cache_control);
|
||||
|
||||
PsychicFileResponse response(request, _fs, _filename);
|
||||
|
||||
if (_last_modified.length())
|
||||
response.addHeader("Last-Modified", _last_modified.c_str());
|
||||
if (_cache_control.length()) {
|
||||
response.addHeader("Cache-Control", _cache_control.c_str());
|
||||
response.addHeader("ETag", etag.c_str());
|
||||
}
|
||||
|
||||
return response.send();
|
||||
}
|
||||
} else {
|
||||
return request->reply(404);
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
44
lib/PsychicHttp/src_old/PsychicStaticFileHandler.h
Normal file
44
lib/PsychicHttp/src_old/PsychicStaticFileHandler.h
Normal file
@@ -0,0 +1,44 @@
|
||||
#ifndef PsychicStaticFileHandler_h
|
||||
#define PsychicStaticFileHandler_h
|
||||
|
||||
#include "PsychicCore.h"
|
||||
#include "PsychicWebHandler.h"
|
||||
#include "PsychicRequest.h"
|
||||
#include "PsychicResponse.h"
|
||||
#include "PsychicFileResponse.h"
|
||||
|
||||
class PsychicStaticFileHandler : public PsychicWebHandler {
|
||||
using File = fs::File;
|
||||
using FS = fs::FS;
|
||||
|
||||
private:
|
||||
bool _getFile(PsychicRequest * request);
|
||||
bool _fileExists(const String & path);
|
||||
uint8_t _countBits(const uint8_t value) const;
|
||||
|
||||
protected:
|
||||
FS _fs;
|
||||
File _file;
|
||||
String _filename;
|
||||
String _uri;
|
||||
String _path;
|
||||
String _default_file;
|
||||
String _cache_control;
|
||||
String _last_modified;
|
||||
bool _isDir;
|
||||
bool _gzipFirst;
|
||||
uint8_t _gzipStats;
|
||||
|
||||
public:
|
||||
PsychicStaticFileHandler(const char * uri, FS & fs, const char * path, const char * cache_control);
|
||||
bool canHandle(PsychicRequest * request) override;
|
||||
esp_err_t handleRequest(PsychicRequest * request) override;
|
||||
PsychicStaticFileHandler & setIsDir(bool isDir);
|
||||
PsychicStaticFileHandler & setDefaultFile(const char * filename);
|
||||
PsychicStaticFileHandler & setCacheControl(const char * cache_control);
|
||||
PsychicStaticFileHandler & setLastModified(const char * last_modified);
|
||||
PsychicStaticFileHandler & setLastModified(struct tm * last_modified);
|
||||
//PsychicStaticFileHandler& setTemplateProcessor(AwsTemplateProcessor newCallback) {_callback = newCallback; return *this;}
|
||||
};
|
||||
|
||||
#endif /* PsychicHttp_h */
|
||||
395
lib/PsychicHttp/src_old/PsychicUploadHandler.cpp
Normal file
395
lib/PsychicHttp/src_old/PsychicUploadHandler.cpp
Normal file
@@ -0,0 +1,395 @@
|
||||
#include "PsychicUploadHandler.h"
|
||||
|
||||
PsychicUploadHandler::PsychicUploadHandler() :
|
||||
PsychicWebHandler()
|
||||
, _temp()
|
||||
, _parsedLength(0)
|
||||
, _multiParseState(EXPECT_BOUNDARY)
|
||||
, _boundaryPosition(0)
|
||||
, _itemStartIndex(0)
|
||||
, _itemSize(0)
|
||||
, _itemName()
|
||||
, _itemFilename()
|
||||
, _itemType()
|
||||
, _itemValue()
|
||||
, _itemBuffer(0)
|
||||
, _itemBufferIndex(0)
|
||||
, _itemIsFile(false)
|
||||
{}
|
||||
PsychicUploadHandler::~PsychicUploadHandler() {}
|
||||
|
||||
bool PsychicUploadHandler::canHandle(PsychicRequest *request) {
|
||||
return true;
|
||||
}
|
||||
|
||||
esp_err_t PsychicUploadHandler::handleRequest(PsychicRequest *request)
|
||||
{
|
||||
esp_err_t err = ESP_OK;
|
||||
|
||||
//save it for later (multipart)
|
||||
_request = request;
|
||||
|
||||
/* File cannot be larger than a limit */
|
||||
if (request->contentLength() > request->server()->maxUploadSize)
|
||||
{
|
||||
ESP_LOGE(PH_TAG, "File too large : %d bytes", request->contentLength());
|
||||
|
||||
/* Respond with 400 Bad Request */
|
||||
char error[50];
|
||||
sprintf(error, "File size must be less than %u bytes!", request->server()->maxUploadSize);
|
||||
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 ESP_FAIL;
|
||||
}
|
||||
|
||||
//we might want to access some of these params
|
||||
request->loadParams();
|
||||
|
||||
//TODO: support for the 100 header. not sure if we can do it.
|
||||
// if (request->header("Expect").equals("100-continue"))
|
||||
// {
|
||||
// char response[] = "100 Continue";
|
||||
// httpd_socket_send(self->server, httpd_req_to_sockfd(req), response, strlen(response), 0);
|
||||
// }
|
||||
|
||||
//2 types of upload requests
|
||||
if (request->isMultipart())
|
||||
err = _multipartUploadHandler(request);
|
||||
else
|
||||
err = _basicUploadHandler(request);
|
||||
|
||||
//we can also call onRequest for some final processing and response
|
||||
if (err == ESP_OK)
|
||||
{
|
||||
if (_requestCallback != NULL)
|
||||
err = _requestCallback(request);
|
||||
else
|
||||
err = request->reply("Upload Successful.");
|
||||
}
|
||||
else
|
||||
request->reply(500, "text/html", "Error processing upload.");
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_err_t PsychicUploadHandler::_basicUploadHandler(PsychicRequest *request)
|
||||
{
|
||||
esp_err_t err = ESP_OK;
|
||||
|
||||
String filename = request->getFilename();
|
||||
|
||||
/* Retrieve the pointer to scratch buffer for temporary storage */
|
||||
char *buf = (char *)malloc(FILE_CHUNK_SIZE);
|
||||
int received;
|
||||
unsigned long index = 0;
|
||||
|
||||
/* Content length of the request gives the size of the file being uploaded */
|
||||
int remaining = request->contentLength();
|
||||
|
||||
while (remaining > 0)
|
||||
{
|
||||
#ifdef ENABLE_ASYNC
|
||||
httpd_sess_update_lru_counter(request->server()->server, request->client()->socket());
|
||||
#endif
|
||||
|
||||
ESP_LOGI(PH_TAG, "Remaining size : %d", remaining);
|
||||
|
||||
/* Receive the file part by part into a buffer */
|
||||
if ((received = httpd_req_recv(request->request(), buf, min(remaining, FILE_CHUNK_SIZE))) <= 0)
|
||||
{
|
||||
/* Retry if timeout occurred */
|
||||
if (received == HTTPD_SOCK_ERR_TIMEOUT)
|
||||
continue;
|
||||
//bail if we got an error
|
||||
else if (received == HTTPD_SOCK_ERR_FAIL)
|
||||
{
|
||||
ESP_LOGE(PH_TAG, "Socket error");
|
||||
err = ESP_FAIL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//call our upload callback here.
|
||||
if (_uploadCallback != NULL)
|
||||
{
|
||||
err = _uploadCallback(request, filename, index, (uint8_t *)buf, received, (remaining - received == 0));
|
||||
if (err != ESP_OK)
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
ESP_LOGE(PH_TAG, "No upload callback specified!");
|
||||
err = ESP_FAIL;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Keep track of remaining size of the file left to be uploaded */
|
||||
remaining -= received;
|
||||
index += received;
|
||||
}
|
||||
|
||||
//dont forget to free our buffer
|
||||
free(buf);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_err_t PsychicUploadHandler::_multipartUploadHandler(PsychicRequest *request)
|
||||
{
|
||||
esp_err_t err = ESP_OK;
|
||||
|
||||
String value = request->header("Content-Type");
|
||||
if (value.startsWith("multipart/")){
|
||||
_boundary = value.substring(value.indexOf('=')+1);
|
||||
_boundary.replace("\"","");
|
||||
} else {
|
||||
ESP_LOGE(PH_TAG, "No multipart boundary found.");
|
||||
return request->reply(400, "text/html", "No multipart boundary found.");
|
||||
}
|
||||
|
||||
char *buf = (char *)malloc(FILE_CHUNK_SIZE);
|
||||
int received;
|
||||
unsigned long index = 0;
|
||||
|
||||
/* Content length of the request gives the size of the file being uploaded */
|
||||
int remaining = request->contentLength();
|
||||
|
||||
while (remaining > 0)
|
||||
{
|
||||
#ifdef ENABLE_ASYNC
|
||||
httpd_sess_update_lru_counter(request->server()->server, request->client()->socket());
|
||||
#endif
|
||||
|
||||
ESP_LOGI(PH_TAG, "Remaining size : %d", remaining);
|
||||
|
||||
/* Receive the file part by part into a buffer */
|
||||
if ((received = httpd_req_recv(request->request(), buf, min(remaining, FILE_CHUNK_SIZE))) <= 0)
|
||||
{
|
||||
/* Retry if timeout occurred */
|
||||
if (received == HTTPD_SOCK_ERR_TIMEOUT)
|
||||
continue;
|
||||
//bail if we got an error
|
||||
else if (received == HTTPD_SOCK_ERR_FAIL)
|
||||
{
|
||||
ESP_LOGE(PH_TAG, "Socket error");
|
||||
err = ESP_FAIL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//parse it 1 byte at a time.
|
||||
for (int i=0; i<received; i++)
|
||||
{
|
||||
/* Keep track of remaining size of the file left to be uploaded */
|
||||
remaining--;
|
||||
index++;
|
||||
|
||||
//send it to our parser
|
||||
_parseMultipartPostByte(buf[i], !remaining);
|
||||
_parsedLength++;
|
||||
}
|
||||
}
|
||||
|
||||
//dont forget to free our buffer
|
||||
free(buf);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
PsychicUploadHandler * PsychicUploadHandler::onUpload(PsychicUploadCallback fn) {
|
||||
_uploadCallback = fn;
|
||||
return this;
|
||||
}
|
||||
|
||||
void PsychicUploadHandler::_handleUploadByte(uint8_t data, bool last)
|
||||
{
|
||||
_itemBuffer[_itemBufferIndex++] = data;
|
||||
|
||||
if(last || _itemBufferIndex == FILE_CHUNK_SIZE)
|
||||
{
|
||||
if(_uploadCallback)
|
||||
_uploadCallback(_request, _itemFilename, _itemSize - _itemBufferIndex, _itemBuffer, _itemBufferIndex, last);
|
||||
_itemBufferIndex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
#define itemWriteByte(b) do { _itemSize++; if(_itemIsFile) _handleUploadByte(b, last); else _itemValue+=(char)(b); } while(0)
|
||||
|
||||
void PsychicUploadHandler::_parseMultipartPostByte(uint8_t data, bool last)
|
||||
{
|
||||
if (_multiParseState == PARSE_ERROR)
|
||||
{
|
||||
// not sure we can end up with an error during buffer fill, but jsut to be safe
|
||||
if (_itemBuffer != NULL)
|
||||
{
|
||||
free(_itemBuffer);
|
||||
_itemBuffer = NULL;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if(!_parsedLength){
|
||||
_multiParseState = EXPECT_BOUNDARY;
|
||||
_temp = String();
|
||||
_itemName = String();
|
||||
_itemFilename = String();
|
||||
_itemType = String();
|
||||
}
|
||||
|
||||
if(_multiParseState == WAIT_FOR_RETURN1){
|
||||
if(data != '\r'){
|
||||
itemWriteByte(data);
|
||||
} else {
|
||||
_multiParseState = EXPECT_FEED1;
|
||||
}
|
||||
} else if(_multiParseState == EXPECT_BOUNDARY){
|
||||
if(_parsedLength < 2 && data != '-'){
|
||||
ESP_LOGE(PH_TAG, "Multipart: No boundary");
|
||||
_multiParseState = PARSE_ERROR;
|
||||
return;
|
||||
} else if(_parsedLength - 2 < _boundary.length() && _boundary.c_str()[_parsedLength - 2] != data){
|
||||
ESP_LOGE(PH_TAG, "Multipart: Multipart malformed");
|
||||
_multiParseState = PARSE_ERROR;
|
||||
return;
|
||||
} else if(_parsedLength - 2 == _boundary.length() && data != '\r'){
|
||||
ESP_LOGE(PH_TAG, "Multipart: Multipart missing carriage return");
|
||||
_multiParseState = PARSE_ERROR;
|
||||
return;
|
||||
} else if(_parsedLength - 3 == _boundary.length()){
|
||||
if(data != '\n'){
|
||||
ESP_LOGE(PH_TAG, "Multipart: Multipart missing newline");
|
||||
_multiParseState = PARSE_ERROR;
|
||||
return;
|
||||
}
|
||||
_multiParseState = PARSE_HEADERS;
|
||||
_itemIsFile = false;
|
||||
}
|
||||
} else if(_multiParseState == PARSE_HEADERS){
|
||||
if((char)data != '\r' && (char)data != '\n')
|
||||
_temp += (char)data;
|
||||
if((char)data == '\n'){
|
||||
if(_temp.length()){
|
||||
if(_temp.length() > 12 && _temp.substring(0, 12).equalsIgnoreCase("Content-Type")){
|
||||
_itemType = _temp.substring(14);
|
||||
_itemIsFile = true;
|
||||
} else if(_temp.length() > 19 && _temp.substring(0, 19).equalsIgnoreCase("Content-Disposition")){
|
||||
_temp = _temp.substring(_temp.indexOf(';') + 2);
|
||||
while(_temp.indexOf(';') > 0){
|
||||
String name = _temp.substring(0, _temp.indexOf('='));
|
||||
String nameVal = _temp.substring(_temp.indexOf('=') + 2, _temp.indexOf(';') - 1);
|
||||
if(name == "name"){
|
||||
_itemName = nameVal;
|
||||
} else if(name == "filename"){
|
||||
_itemFilename = nameVal;
|
||||
_itemIsFile = true;
|
||||
}
|
||||
_temp = _temp.substring(_temp.indexOf(';') + 2);
|
||||
}
|
||||
String name = _temp.substring(0, _temp.indexOf('='));
|
||||
String nameVal = _temp.substring(_temp.indexOf('=') + 2, _temp.length() - 1);
|
||||
if(name == "name"){
|
||||
_itemName = nameVal;
|
||||
} else if(name == "filename"){
|
||||
_itemFilename = nameVal;
|
||||
_itemIsFile = true;
|
||||
}
|
||||
}
|
||||
_temp = String();
|
||||
} else {
|
||||
_multiParseState = WAIT_FOR_RETURN1;
|
||||
//value starts from here
|
||||
_itemSize = 0;
|
||||
_itemStartIndex = _parsedLength;
|
||||
_itemValue = String();
|
||||
if(_itemIsFile){
|
||||
if(_itemBuffer)
|
||||
free(_itemBuffer);
|
||||
_itemBuffer = (uint8_t*)malloc(FILE_CHUNK_SIZE);
|
||||
if(_itemBuffer == NULL){
|
||||
ESP_LOGE(PH_TAG, "Multipart: Failed to allocate buffer");
|
||||
_multiParseState = PARSE_ERROR;
|
||||
return;
|
||||
}
|
||||
_itemBufferIndex = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if(_multiParseState == EXPECT_FEED1){
|
||||
if(data != '\n'){
|
||||
_multiParseState = WAIT_FOR_RETURN1;
|
||||
itemWriteByte('\r'); _parseMultipartPostByte(data, last);
|
||||
} else {
|
||||
_multiParseState = EXPECT_DASH1;
|
||||
}
|
||||
} else if(_multiParseState == EXPECT_DASH1){
|
||||
if(data != '-'){
|
||||
_multiParseState = WAIT_FOR_RETURN1;
|
||||
itemWriteByte('\r'); itemWriteByte('\n'); _parseMultipartPostByte(data, last);
|
||||
} else {
|
||||
_multiParseState = EXPECT_DASH2;
|
||||
}
|
||||
} else if(_multiParseState == EXPECT_DASH2){
|
||||
if(data != '-'){
|
||||
_multiParseState = WAIT_FOR_RETURN1;
|
||||
itemWriteByte('\r'); itemWriteByte('\n'); itemWriteByte('-'); _parseMultipartPostByte(data, last);
|
||||
} else {
|
||||
_multiParseState = BOUNDARY_OR_DATA;
|
||||
_boundaryPosition = 0;
|
||||
}
|
||||
} else if(_multiParseState == BOUNDARY_OR_DATA){
|
||||
if(_boundaryPosition < _boundary.length() && _boundary.c_str()[_boundaryPosition] != data){
|
||||
_multiParseState = WAIT_FOR_RETURN1;
|
||||
itemWriteByte('\r'); itemWriteByte('\n'); itemWriteByte('-'); itemWriteByte('-');
|
||||
uint8_t i;
|
||||
for(i=0; i<_boundaryPosition; i++)
|
||||
itemWriteByte(_boundary.c_str()[i]);
|
||||
_parseMultipartPostByte(data, last);
|
||||
} else if(_boundaryPosition == _boundary.length() - 1){
|
||||
_multiParseState = DASH3_OR_RETURN2;
|
||||
if(!_itemIsFile){
|
||||
_request->addParam(_itemName, _itemValue);
|
||||
//_addParam(new AsyncWebParameter(_itemName, _itemValue, true));
|
||||
} else {
|
||||
if(_itemSize){
|
||||
if(_uploadCallback)
|
||||
_uploadCallback(_request, _itemFilename, _itemSize - _itemBufferIndex, _itemBuffer, _itemBufferIndex, true);
|
||||
_itemBufferIndex = 0;
|
||||
_request->addParam(new PsychicWebParameter(_itemName, _itemFilename, true, true, _itemSize));
|
||||
}
|
||||
free(_itemBuffer);
|
||||
_itemBuffer = NULL;
|
||||
}
|
||||
|
||||
} else {
|
||||
_boundaryPosition++;
|
||||
}
|
||||
} else if(_multiParseState == DASH3_OR_RETURN2){
|
||||
if(data == '-' && (_request->contentLength() - _parsedLength - 4) != 0){
|
||||
ESP_LOGE(PH_TAG, "ERROR: The parser got to the end of the POST but is expecting more bytes!");
|
||||
_multiParseState = PARSE_ERROR;
|
||||
return;
|
||||
}
|
||||
if(data == '\r'){
|
||||
_multiParseState = EXPECT_FEED2;
|
||||
} else if(data == '-' && _request->contentLength() == (_parsedLength + 4)){
|
||||
_multiParseState = PARSING_FINISHED;
|
||||
} else {
|
||||
_multiParseState = WAIT_FOR_RETURN1;
|
||||
itemWriteByte('\r'); itemWriteByte('\n'); itemWriteByte('-'); itemWriteByte('-');
|
||||
uint8_t i; for(i=0; i<_boundary.length(); i++) itemWriteByte(_boundary.c_str()[i]);
|
||||
_parseMultipartPostByte(data, last);
|
||||
}
|
||||
} else if(_multiParseState == EXPECT_FEED2){
|
||||
if(data == '\n'){
|
||||
_multiParseState = PARSE_HEADERS;
|
||||
_itemIsFile = false;
|
||||
} else {
|
||||
_multiParseState = WAIT_FOR_RETURN1;
|
||||
itemWriteByte('\r'); itemWriteByte('\n'); itemWriteByte('-'); itemWriteByte('-');
|
||||
uint8_t i; for(i=0; i<_boundary.length(); i++) itemWriteByte(_boundary.c_str()[i]);
|
||||
itemWriteByte('\r'); _parseMultipartPostByte(data, last);
|
||||
}
|
||||
}
|
||||
}
|
||||
68
lib/PsychicHttp/src_old/PsychicUploadHandler.h
Normal file
68
lib/PsychicHttp/src_old/PsychicUploadHandler.h
Normal file
@@ -0,0 +1,68 @@
|
||||
#ifndef PsychicUploadHandler_h
|
||||
#define PsychicUploadHandler_h
|
||||
|
||||
#include "PsychicCore.h"
|
||||
#include "PsychicHttpServer.h"
|
||||
#include "PsychicRequest.h"
|
||||
#include "PsychicWebHandler.h"
|
||||
#include "PsychicWebParameter.h"
|
||||
|
||||
//callback definitions
|
||||
typedef std::function<esp_err_t(PsychicRequest * request, const String & filename, uint64_t index, uint8_t * data, size_t len, bool final)> PsychicUploadCallback;
|
||||
|
||||
/*
|
||||
* HANDLER :: Can be attached to any endpoint or as a generic request handler.
|
||||
*/
|
||||
|
||||
class PsychicUploadHandler : public PsychicWebHandler {
|
||||
protected:
|
||||
PsychicUploadCallback _uploadCallback;
|
||||
|
||||
PsychicRequest * _request;
|
||||
|
||||
String _temp;
|
||||
size_t _parsedLength;
|
||||
uint8_t _multiParseState;
|
||||
String _boundary;
|
||||
uint8_t _boundaryPosition;
|
||||
size_t _itemStartIndex;
|
||||
size_t _itemSize;
|
||||
String _itemName;
|
||||
String _itemFilename;
|
||||
String _itemType;
|
||||
String _itemValue;
|
||||
uint8_t * _itemBuffer;
|
||||
size_t _itemBufferIndex;
|
||||
bool _itemIsFile;
|
||||
|
||||
esp_err_t _basicUploadHandler(PsychicRequest * request);
|
||||
esp_err_t _multipartUploadHandler(PsychicRequest * request);
|
||||
|
||||
void _handleUploadByte(uint8_t data, bool last);
|
||||
void _parseMultipartPostByte(uint8_t data, bool last);
|
||||
|
||||
public:
|
||||
PsychicUploadHandler();
|
||||
~PsychicUploadHandler();
|
||||
|
||||
bool canHandle(PsychicRequest * request) override;
|
||||
esp_err_t handleRequest(PsychicRequest * request) override;
|
||||
|
||||
PsychicUploadHandler * onUpload(PsychicUploadCallback fn);
|
||||
};
|
||||
|
||||
enum {
|
||||
EXPECT_BOUNDARY,
|
||||
PARSE_HEADERS,
|
||||
WAIT_FOR_RETURN1,
|
||||
EXPECT_FEED1,
|
||||
EXPECT_DASH1,
|
||||
EXPECT_DASH2,
|
||||
BOUNDARY_OR_DATA,
|
||||
DASH3_OR_RETURN2,
|
||||
EXPECT_FEED2,
|
||||
PARSING_FINISHED,
|
||||
PARSE_ERROR
|
||||
};
|
||||
|
||||
#endif // PsychicUploadHandler_h
|
||||
46
lib/PsychicHttp/src_old/PsychicWebHandler.cpp
Normal file
46
lib/PsychicHttp/src_old/PsychicWebHandler.cpp
Normal file
@@ -0,0 +1,46 @@
|
||||
#include "PsychicWebHandler.h"
|
||||
|
||||
PsychicWebHandler::PsychicWebHandler()
|
||||
: PsychicHandler()
|
||||
, _requestCallback(NULL) {
|
||||
}
|
||||
PsychicWebHandler::~PsychicWebHandler() {
|
||||
}
|
||||
|
||||
bool PsychicWebHandler::canHandle(PsychicRequest * request) {
|
||||
return true;
|
||||
}
|
||||
|
||||
esp_err_t PsychicWebHandler::handleRequest(PsychicRequest * request) {
|
||||
/* Request body cannot be larger than a limit */
|
||||
if (request->contentLength() > request->server()->maxRequestBodySize) {
|
||||
ESP_LOGE(PH_TAG, "Request body too large : %d bytes", request->contentLength());
|
||||
|
||||
/* Respond with 400 Bad Request */
|
||||
char error[60];
|
||||
sprintf(error, "Request body must be less than %u bytes!", request->server()->maxRequestBodySize);
|
||||
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 ESP_FAIL;
|
||||
}
|
||||
|
||||
//get our body loaded up.
|
||||
esp_err_t err = request->loadBody();
|
||||
if (err != ESP_OK)
|
||||
return err;
|
||||
|
||||
//load our params in.
|
||||
request->loadParams();
|
||||
|
||||
//okay, pass on to our callback.
|
||||
if (this->_requestCallback != NULL)
|
||||
err = this->_requestCallback(request);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
PsychicWebHandler * PsychicWebHandler::onRequest(PsychicHttpRequestCallback fn) {
|
||||
_requestCallback = fn;
|
||||
return this;
|
||||
}
|
||||
26
lib/PsychicHttp/src_old/PsychicWebHandler.h
Normal file
26
lib/PsychicHttp/src_old/PsychicWebHandler.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#ifndef PsychicWebHandler_h
|
||||
#define PsychicWebHandler_h
|
||||
|
||||
#include "PsychicCore.h"
|
||||
#include "PsychicHttpServer.h"
|
||||
#include "PsychicRequest.h"
|
||||
#include "PsychicHandler.h"
|
||||
|
||||
/*
|
||||
* HANDLER :: Can be attached to any endpoint or as a generic request handler.
|
||||
*/
|
||||
|
||||
class PsychicWebHandler : public PsychicHandler {
|
||||
protected:
|
||||
PsychicHttpRequestCallback _requestCallback;
|
||||
|
||||
public:
|
||||
PsychicWebHandler();
|
||||
~PsychicWebHandler();
|
||||
|
||||
virtual bool canHandle(PsychicRequest * request) override;
|
||||
virtual esp_err_t handleRequest(PsychicRequest * request) override;
|
||||
PsychicWebHandler * onRequest(PsychicHttpRequestCallback fn);
|
||||
};
|
||||
|
||||
#endif
|
||||
41
lib/PsychicHttp/src_old/PsychicWebParameter.h
Normal file
41
lib/PsychicHttp/src_old/PsychicWebParameter.h
Normal file
@@ -0,0 +1,41 @@
|
||||
#ifndef PsychicWebParameter_h
|
||||
#define PsychicWebParameter_h
|
||||
|
||||
/*
|
||||
* PARAMETER :: Chainable object to hold GET/POST and FILE parameters
|
||||
* */
|
||||
|
||||
class PsychicWebParameter {
|
||||
private:
|
||||
String _name;
|
||||
String _value;
|
||||
size_t _size;
|
||||
bool _isForm;
|
||||
bool _isFile;
|
||||
|
||||
public:
|
||||
PsychicWebParameter(const String & name, const String & value, bool form = false, bool file = false, size_t size = 0)
|
||||
: _name(name)
|
||||
, _value(value)
|
||||
, _size(size)
|
||||
, _isForm(form)
|
||||
, _isFile(file) {
|
||||
}
|
||||
const String & name() const {
|
||||
return _name;
|
||||
}
|
||||
const String & value() const {
|
||||
return _value;
|
||||
}
|
||||
size_t size() const {
|
||||
return _size;
|
||||
}
|
||||
bool isPost() const {
|
||||
return _isForm;
|
||||
}
|
||||
bool isFile() const {
|
||||
return _isFile;
|
||||
}
|
||||
};
|
||||
|
||||
#endif //PsychicWebParameter_h
|
||||
243
lib/PsychicHttp/src_old/PsychicWebSocket.cpp
Normal file
243
lib/PsychicHttp/src_old/PsychicWebSocket.cpp
Normal file
@@ -0,0 +1,243 @@
|
||||
#include "PsychicWebSocket.h"
|
||||
|
||||
/*************************************/
|
||||
/* PsychicWebSocketRequest */
|
||||
/*************************************/
|
||||
|
||||
PsychicWebSocketRequest::PsychicWebSocketRequest(PsychicRequest * req)
|
||||
: PsychicRequest(req->server(), req->request())
|
||||
, _client(req->client()) {
|
||||
}
|
||||
|
||||
PsychicWebSocketRequest::~PsychicWebSocketRequest() {
|
||||
}
|
||||
|
||||
PsychicWebSocketClient * PsychicWebSocketRequest::client() {
|
||||
return &_client;
|
||||
}
|
||||
|
||||
esp_err_t PsychicWebSocketRequest::reply(httpd_ws_frame_t * ws_pkt) {
|
||||
return httpd_ws_send_frame(this->_req, ws_pkt);
|
||||
}
|
||||
|
||||
esp_err_t PsychicWebSocketRequest::reply(httpd_ws_type_t op, const void * data, size_t len) {
|
||||
httpd_ws_frame_t ws_pkt;
|
||||
memset(&ws_pkt, 0, sizeof(httpd_ws_frame_t));
|
||||
|
||||
ws_pkt.payload = (uint8_t *)data;
|
||||
ws_pkt.len = len;
|
||||
ws_pkt.type = op;
|
||||
|
||||
return this->reply(&ws_pkt);
|
||||
}
|
||||
|
||||
esp_err_t PsychicWebSocketRequest::reply(const char * buf) {
|
||||
return this->reply(HTTPD_WS_TYPE_TEXT, buf, strlen(buf));
|
||||
}
|
||||
|
||||
/*************************************/
|
||||
/* PsychicWebSocketClient */
|
||||
/*************************************/
|
||||
|
||||
PsychicWebSocketClient::PsychicWebSocketClient(PsychicClient * client)
|
||||
: PsychicClient(client->server(), client->socket()) {
|
||||
}
|
||||
|
||||
PsychicWebSocketClient::~PsychicWebSocketClient() {
|
||||
}
|
||||
|
||||
esp_err_t PsychicWebSocketClient::sendMessage(httpd_ws_frame_t * ws_pkt) {
|
||||
return httpd_ws_send_frame_async(this->server(), this->socket(), ws_pkt);
|
||||
}
|
||||
|
||||
esp_err_t PsychicWebSocketClient::sendMessage(httpd_ws_type_t op, const void * data, size_t len) {
|
||||
httpd_ws_frame_t ws_pkt;
|
||||
memset(&ws_pkt, 0, sizeof(httpd_ws_frame_t));
|
||||
|
||||
ws_pkt.payload = (uint8_t *)data;
|
||||
ws_pkt.len = len;
|
||||
ws_pkt.type = op;
|
||||
|
||||
return this->sendMessage(&ws_pkt);
|
||||
}
|
||||
|
||||
esp_err_t PsychicWebSocketClient::sendMessage(const char * buf) {
|
||||
return this->sendMessage(HTTPD_WS_TYPE_TEXT, buf, strlen(buf));
|
||||
}
|
||||
|
||||
PsychicWebSocketHandler::PsychicWebSocketHandler()
|
||||
: PsychicHandler()
|
||||
, _onOpen(NULL)
|
||||
, _onFrame(NULL)
|
||||
, _onClose(NULL) {
|
||||
}
|
||||
|
||||
PsychicWebSocketHandler::~PsychicWebSocketHandler() {
|
||||
}
|
||||
|
||||
PsychicWebSocketClient * PsychicWebSocketHandler::getClient(int socket) {
|
||||
PsychicClient * client = PsychicHandler::getClient(socket);
|
||||
if (client == NULL)
|
||||
return NULL;
|
||||
|
||||
if (client->_friend == NULL) {
|
||||
DUMP(socket);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return (PsychicWebSocketClient *)client->_friend;
|
||||
}
|
||||
|
||||
PsychicWebSocketClient * PsychicWebSocketHandler::getClient(PsychicClient * client) {
|
||||
return getClient(client->socket());
|
||||
}
|
||||
|
||||
void PsychicWebSocketHandler::addClient(PsychicClient * client) {
|
||||
client->_friend = new PsychicWebSocketClient(client);
|
||||
PsychicHandler::addClient(client);
|
||||
}
|
||||
|
||||
void PsychicWebSocketHandler::removeClient(PsychicClient * client) {
|
||||
PsychicHandler::removeClient(client);
|
||||
delete (PsychicWebSocketClient *)client->_friend;
|
||||
client->_friend = NULL;
|
||||
}
|
||||
|
||||
void PsychicWebSocketHandler::openCallback(PsychicClient * client) {
|
||||
PsychicWebSocketClient * buddy = getClient(client);
|
||||
if (buddy == NULL) {
|
||||
TRACE();
|
||||
return;
|
||||
}
|
||||
|
||||
if (_onOpen != NULL)
|
||||
_onOpen(getClient(buddy));
|
||||
}
|
||||
|
||||
void PsychicWebSocketHandler::closeCallback(PsychicClient * client) {
|
||||
PsychicWebSocketClient * buddy = getClient(client);
|
||||
if (buddy == NULL) {
|
||||
TRACE();
|
||||
return;
|
||||
}
|
||||
|
||||
if (_onClose != NULL)
|
||||
_onClose(getClient(buddy));
|
||||
}
|
||||
|
||||
bool PsychicWebSocketHandler::isWebSocket() {
|
||||
return true;
|
||||
}
|
||||
|
||||
esp_err_t PsychicWebSocketHandler::handleRequest(PsychicRequest * request) {
|
||||
//lookup our client
|
||||
PsychicClient * client = checkForNewClient(request->client());
|
||||
|
||||
// beginning of the ws URI handler and our onConnect hook
|
||||
if (request->method() == HTTP_GET) {
|
||||
if (client->isNew)
|
||||
openCallback(client);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
//prep our request
|
||||
PsychicWebSocketRequest wsRequest(request);
|
||||
|
||||
//init our memory for storing the packet
|
||||
httpd_ws_frame_t ws_pkt;
|
||||
memset(&ws_pkt, 0, sizeof(httpd_ws_frame_t));
|
||||
ws_pkt.type = HTTPD_WS_TYPE_TEXT;
|
||||
uint8_t * buf = NULL;
|
||||
|
||||
/* Set max_len = 0 to get the frame len */
|
||||
esp_err_t ret = httpd_ws_recv_frame(wsRequest.request(), &ws_pkt, 0);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(PH_TAG, "httpd_ws_recv_frame failed to get frame len with %s", esp_err_to_name(ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
//okay, now try to load the packet
|
||||
ESP_LOGI(PH_TAG, "frame len is %d", ws_pkt.len);
|
||||
if (ws_pkt.len) {
|
||||
/* ws_pkt.len + 1 is for NULL termination as we are expecting a string */
|
||||
buf = (uint8_t *)calloc(1, ws_pkt.len + 1);
|
||||
if (buf == NULL) {
|
||||
ESP_LOGE(PH_TAG, "Failed to calloc memory for buf");
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
ws_pkt.payload = buf;
|
||||
/* Set max_len = ws_pkt.len to get the frame payload */
|
||||
ret = httpd_ws_recv_frame(wsRequest.request(), &ws_pkt, ws_pkt.len);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(PH_TAG, "httpd_ws_recv_frame failed with %s", esp_err_to_name(ret));
|
||||
free(buf);
|
||||
return ret;
|
||||
}
|
||||
ESP_LOGI(PH_TAG, "Got packet with message: %s", ws_pkt.payload);
|
||||
}
|
||||
|
||||
// Text messages are our payload.
|
||||
if (ws_pkt.type == HTTPD_WS_TYPE_TEXT) {
|
||||
if (this->_onFrame != NULL)
|
||||
ret = this->_onFrame(&wsRequest, &ws_pkt);
|
||||
}
|
||||
|
||||
//logging housekeeping
|
||||
if (ret != ESP_OK)
|
||||
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(),
|
||||
httpd_req_to_sockfd(request->request()),
|
||||
httpd_ws_get_fd_info(request->server(), httpd_req_to_sockfd(request->request())));
|
||||
|
||||
//dont forget to release our buffer memory
|
||||
free(buf);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
PsychicWebSocketHandler * PsychicWebSocketHandler::onOpen(PsychicWebSocketClientCallback fn) {
|
||||
_onOpen = fn;
|
||||
return this;
|
||||
}
|
||||
|
||||
PsychicWebSocketHandler * PsychicWebSocketHandler::onFrame(PsychicWebSocketFrameCallback fn) {
|
||||
_onFrame = fn;
|
||||
return this;
|
||||
}
|
||||
|
||||
PsychicWebSocketHandler * PsychicWebSocketHandler::onClose(PsychicWebSocketClientCallback fn) {
|
||||
_onClose = fn;
|
||||
return this;
|
||||
}
|
||||
|
||||
void PsychicWebSocketHandler::sendAll(httpd_ws_frame_t * ws_pkt) {
|
||||
for (PsychicClient * client : _clients) {
|
||||
ESP_LOGI(PH_TAG, "Active client (fd=%d) -> sending async message", client->socket());
|
||||
|
||||
if (client->_friend == NULL) {
|
||||
TRACE();
|
||||
return;
|
||||
}
|
||||
|
||||
if (((PsychicWebSocketClient *)client->_friend)->sendMessage(ws_pkt) != ESP_OK)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void PsychicWebSocketHandler::sendAll(httpd_ws_type_t op, const void * data, size_t len) {
|
||||
httpd_ws_frame_t ws_pkt;
|
||||
memset(&ws_pkt, 0, sizeof(httpd_ws_frame_t));
|
||||
|
||||
ws_pkt.payload = (uint8_t *)data;
|
||||
ws_pkt.len = len;
|
||||
ws_pkt.type = op;
|
||||
|
||||
this->sendAll(&ws_pkt);
|
||||
}
|
||||
|
||||
void PsychicWebSocketHandler::sendAll(const char * buf) {
|
||||
this->sendAll(HTTPD_WS_TYPE_TEXT, buf, strlen(buf));
|
||||
}
|
||||
68
lib/PsychicHttp/src_old/PsychicWebSocket.h
Normal file
68
lib/PsychicHttp/src_old/PsychicWebSocket.h
Normal file
@@ -0,0 +1,68 @@
|
||||
#ifndef PsychicWebSocket_h
|
||||
#define PsychicWebSocket_h
|
||||
|
||||
#include "PsychicCore.h"
|
||||
#include "PsychicRequest.h"
|
||||
|
||||
class PsychicWebSocketRequest;
|
||||
class PsychicWebSocketClient;
|
||||
|
||||
//callback function definitions
|
||||
typedef std::function<void(PsychicWebSocketClient * client)> PsychicWebSocketClientCallback;
|
||||
typedef std::function<esp_err_t(PsychicWebSocketRequest * request, httpd_ws_frame * frame)> PsychicWebSocketFrameCallback;
|
||||
|
||||
class PsychicWebSocketClient : public PsychicClient {
|
||||
public:
|
||||
PsychicWebSocketClient(PsychicClient * client);
|
||||
~PsychicWebSocketClient();
|
||||
|
||||
esp_err_t sendMessage(httpd_ws_frame_t * ws_pkt);
|
||||
esp_err_t sendMessage(httpd_ws_type_t op, const void * data, size_t len);
|
||||
esp_err_t sendMessage(const char * buf);
|
||||
};
|
||||
|
||||
class PsychicWebSocketRequest : public PsychicRequest {
|
||||
private:
|
||||
PsychicWebSocketClient _client;
|
||||
|
||||
public:
|
||||
PsychicWebSocketRequest(PsychicRequest * req);
|
||||
virtual ~PsychicWebSocketRequest();
|
||||
|
||||
PsychicWebSocketClient * client() override;
|
||||
|
||||
esp_err_t reply(httpd_ws_frame_t * ws_pkt);
|
||||
esp_err_t reply(httpd_ws_type_t op, const void * data, size_t len);
|
||||
esp_err_t reply(const char * buf);
|
||||
};
|
||||
|
||||
class PsychicWebSocketHandler : public PsychicHandler {
|
||||
protected:
|
||||
PsychicWebSocketClientCallback _onOpen;
|
||||
PsychicWebSocketFrameCallback _onFrame;
|
||||
PsychicWebSocketClientCallback _onClose;
|
||||
|
||||
public:
|
||||
PsychicWebSocketHandler();
|
||||
~PsychicWebSocketHandler();
|
||||
|
||||
PsychicWebSocketClient * getClient(int socket) override;
|
||||
PsychicWebSocketClient * getClient(PsychicClient * client) override;
|
||||
void addClient(PsychicClient * client) override;
|
||||
void removeClient(PsychicClient * client) override;
|
||||
void openCallback(PsychicClient * client) override;
|
||||
void closeCallback(PsychicClient * client) override;
|
||||
|
||||
bool isWebSocket() override final;
|
||||
esp_err_t handleRequest(PsychicRequest * request) override;
|
||||
|
||||
PsychicWebSocketHandler * onOpen(PsychicWebSocketClientCallback fn);
|
||||
PsychicWebSocketHandler * onFrame(PsychicWebSocketFrameCallback fn);
|
||||
PsychicWebSocketHandler * onClose(PsychicWebSocketClientCallback fn);
|
||||
|
||||
void sendAll(httpd_ws_frame_t * ws_pkt);
|
||||
void sendAll(httpd_ws_type_t op, const void * data, size_t len);
|
||||
void sendAll(const char * buf);
|
||||
};
|
||||
|
||||
#endif // PsychicWebSocket_h
|
||||
203
lib/PsychicHttp/src_old/async_worker.cpp
Normal file
203
lib/PsychicHttp/src_old/async_worker.cpp
Normal file
@@ -0,0 +1,203 @@
|
||||
#include "async_worker.h"
|
||||
|
||||
bool is_on_async_worker_thread(void)
|
||||
{
|
||||
// is our handle one of the known async handles?
|
||||
TaskHandle_t handle = xTaskGetCurrentTaskHandle();
|
||||
for (int i = 0; i < ASYNC_WORKER_COUNT; i++) {
|
||||
if (worker_handles[i] == handle) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Submit an HTTP req to the async worker queue
|
||||
esp_err_t submit_async_req(httpd_req_t *req, httpd_req_handler_t handler)
|
||||
{
|
||||
// must create a copy of the request that we own
|
||||
httpd_req_t* copy = NULL;
|
||||
esp_err_t err = httpd_req_async_handler_begin(req, ©);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
httpd_async_req_t async_req = {
|
||||
.req = copy,
|
||||
.handler = handler,
|
||||
};
|
||||
|
||||
// How should we handle resource exhaustion?
|
||||
// In this example, we immediately respond with an
|
||||
// http error if no workers are available.
|
||||
int ticks = 0;
|
||||
|
||||
// counting semaphore: if success, we know 1 or
|
||||
// more asyncReqTaskWorkers are available.
|
||||
if (xSemaphoreTake(worker_ready_count, ticks) == false) {
|
||||
ESP_LOGE(PH_TAG, "No workers are available");
|
||||
httpd_req_async_handler_complete(copy); // cleanup
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
// Since worker_ready_count > 0 the queue should already have space.
|
||||
// But lets wait up to 100ms just to be safe.
|
||||
if (xQueueSend(async_req_queue, &async_req, pdMS_TO_TICKS(100)) == false) {
|
||||
ESP_LOGE(PH_TAG, "worker queue is full");
|
||||
httpd_req_async_handler_complete(copy); // cleanup
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void async_req_worker_task(void *p)
|
||||
{
|
||||
ESP_LOGI(PH_TAG, "starting async req task worker");
|
||||
|
||||
while (true) {
|
||||
|
||||
// counting semaphore - this signals that a worker
|
||||
// is ready to accept work
|
||||
xSemaphoreGive(worker_ready_count);
|
||||
|
||||
// wait for a request
|
||||
httpd_async_req_t async_req;
|
||||
if (xQueueReceive(async_req_queue, &async_req, portMAX_DELAY)) {
|
||||
|
||||
ESP_LOGI(PH_TAG, "invoking %s", async_req.req->uri);
|
||||
|
||||
// call the handler
|
||||
async_req.handler(async_req.req);
|
||||
|
||||
// Inform the server that it can purge the socket used for
|
||||
// this request, if needed.
|
||||
if (httpd_req_async_handler_complete(async_req.req) != ESP_OK) {
|
||||
ESP_LOGE(PH_TAG, "failed to complete async req");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ESP_LOGW(PH_TAG, "worker stopped");
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
void start_async_req_workers(void)
|
||||
{
|
||||
|
||||
// counting semaphore keeps track of available workers
|
||||
worker_ready_count = xSemaphoreCreateCounting(
|
||||
ASYNC_WORKER_COUNT, // Max Count
|
||||
0); // Initial Count
|
||||
if (worker_ready_count == NULL) {
|
||||
ESP_LOGE(PH_TAG, "Failed to create workers counting Semaphore");
|
||||
return;
|
||||
}
|
||||
|
||||
// create queue
|
||||
async_req_queue = xQueueCreate(1, sizeof(httpd_async_req_t));
|
||||
if (async_req_queue == NULL){
|
||||
ESP_LOGE(PH_TAG, "Failed to create async_req_queue");
|
||||
vSemaphoreDelete(worker_ready_count);
|
||||
return;
|
||||
}
|
||||
|
||||
// start worker tasks
|
||||
for (int i = 0; i < ASYNC_WORKER_COUNT; i++) {
|
||||
|
||||
bool success = xTaskCreate(async_req_worker_task, "async_req_worker",
|
||||
ASYNC_WORKER_TASK_STACK_SIZE, // stack size
|
||||
(void *)0, // argument
|
||||
ASYNC_WORKER_TASK_PRIORITY, // priority
|
||||
&worker_handles[i]);
|
||||
|
||||
if (!success) {
|
||||
ESP_LOGE(PH_TAG, "Failed to start asyncReqWorker");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/****
|
||||
*
|
||||
* This code is backported from the 5.1.x branch
|
||||
*
|
||||
****/
|
||||
|
||||
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
|
||||
|
||||
/* Calculate the maximum size needed for the scratch buffer */
|
||||
#define HTTPD_SCRATCH_BUF MAX(HTTPD_MAX_REQ_HDR_LEN, HTTPD_MAX_URI_LEN)
|
||||
|
||||
/**
|
||||
* @brief Auxiliary data structure for use during reception and processing
|
||||
* of requests and temporarily keeping responses
|
||||
*/
|
||||
struct httpd_req_aux {
|
||||
struct sock_db *sd; /*!< Pointer to socket database */
|
||||
char scratch[HTTPD_SCRATCH_BUF + 1]; /*!< Temporary buffer for our operations (1 byte extra for null termination) */
|
||||
size_t remaining_len; /*!< Amount of data remaining to be fetched */
|
||||
char *status; /*!< HTTP response's status code */
|
||||
char *content_type; /*!< HTTP response's content type */
|
||||
bool first_chunk_sent; /*!< Used to indicate if first chunk sent */
|
||||
unsigned req_hdrs_count; /*!< Count of total headers in request packet */
|
||||
unsigned resp_hdrs_count; /*!< Count of additional headers in response packet */
|
||||
struct resp_hdr {
|
||||
const char *field;
|
||||
const char *value;
|
||||
} *resp_hdrs; /*!< Additional headers in response packet */
|
||||
struct http_parser_url url_parse_res; /*!< URL parsing result, used for retrieving URL elements */
|
||||
#ifdef CONFIG_HTTPD_WS_SUPPORT
|
||||
bool ws_handshake_detect; /*!< WebSocket handshake detection flag */
|
||||
httpd_ws_type_t ws_type; /*!< WebSocket frame type */
|
||||
bool ws_final; /*!< WebSocket FIN bit (final frame or not) */
|
||||
uint8_t mask_key[4]; /*!< WebSocket mask key for this payload */
|
||||
#endif
|
||||
};
|
||||
|
||||
esp_err_t httpd_req_async_handler_begin(httpd_req_t *r, httpd_req_t **out)
|
||||
{
|
||||
if (r == NULL || out == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
// alloc async req
|
||||
httpd_req_t *async = (httpd_req_t *)malloc(sizeof(httpd_req_t));
|
||||
if (async == NULL) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
memcpy(async, r, sizeof(httpd_req_t));
|
||||
|
||||
// alloc async aux
|
||||
async->aux = (httpd_req_aux *)malloc(sizeof(struct httpd_req_aux));
|
||||
if (async->aux == NULL) {
|
||||
free(async);
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
memcpy(async->aux, r->aux, sizeof(struct httpd_req_aux));
|
||||
|
||||
// not available in 4.4.x
|
||||
// mark socket as "in use"
|
||||
// struct httpd_req_aux *ra = r->aux;
|
||||
//ra->sd->for_async_req = true;
|
||||
|
||||
*out = async;
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t httpd_req_async_handler_complete(httpd_req_t *r)
|
||||
{
|
||||
if (r == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
// not available in 4.4.x
|
||||
// struct httpd_req_aux *ra = (httpd_req_aux *)r->aux;
|
||||
// ra->sd->for_async_req = false;
|
||||
|
||||
free(r->aux);
|
||||
free(r);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
36
lib/PsychicHttp/src_old/async_worker.h
Normal file
36
lib/PsychicHttp/src_old/async_worker.h
Normal file
@@ -0,0 +1,36 @@
|
||||
#ifndef async_worker_h
|
||||
#define async_worker_h
|
||||
|
||||
#include "PsychicCore.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/semphr.h"
|
||||
|
||||
#define ASYNC_WORKER_TASK_PRIORITY 5
|
||||
#define ASYNC_WORKER_TASK_STACK_SIZE (4*1024)
|
||||
#define ASYNC_WORKER_COUNT 8
|
||||
|
||||
// Async requests are queued here while they wait to be processed by the workers
|
||||
static QueueHandle_t async_req_queue;
|
||||
|
||||
// Track the number of free workers at any given time
|
||||
static SemaphoreHandle_t worker_ready_count;
|
||||
|
||||
// Each worker has its own thread
|
||||
static TaskHandle_t worker_handles[ASYNC_WORKER_COUNT];
|
||||
|
||||
typedef esp_err_t (*httpd_req_handler_t)(httpd_req_t *req);
|
||||
|
||||
typedef struct {
|
||||
httpd_req_t* req;
|
||||
httpd_req_handler_t handler;
|
||||
} httpd_async_req_t;
|
||||
|
||||
bool is_on_async_worker_thread(void);
|
||||
esp_err_t submit_async_req(httpd_req_t *req, httpd_req_handler_t handler);
|
||||
void async_req_worker_task(void *p);
|
||||
void start_async_req_workers(void);
|
||||
|
||||
esp_err_t httpd_req_async_handler_begin(httpd_req_t *r, httpd_req_t **out);
|
||||
esp_err_t httpd_req_async_handler_complete(httpd_req_t *r);
|
||||
|
||||
#endif //async_worker_h
|
||||
185
lib/PsychicHttp/src_old/http_status.cpp
Normal file
185
lib/PsychicHttp/src_old/http_status.cpp
Normal file
@@ -0,0 +1,185 @@
|
||||
#include "http_status.h"
|
||||
|
||||
bool http_informational(int code) {
|
||||
return code >= 100 && code < 200;
|
||||
}
|
||||
|
||||
bool http_success(int code) {
|
||||
return code >= 200 && code < 300;
|
||||
}
|
||||
|
||||
bool http_redirection(int code) {
|
||||
return code >= 300 && code < 400;
|
||||
}
|
||||
|
||||
bool http_client_error(int code) {
|
||||
return code >= 400 && code < 500;
|
||||
}
|
||||
|
||||
bool http_server_error(int code) {
|
||||
return code >= 500 && code < 600;
|
||||
}
|
||||
|
||||
bool http_failure(int code) {
|
||||
return code >= 400 && code < 600;
|
||||
}
|
||||
|
||||
const char * http_status_group(int code) {
|
||||
if (http_informational(code))
|
||||
return "Informational";
|
||||
|
||||
if (http_success(code))
|
||||
return "Success";
|
||||
|
||||
if (http_redirection(code))
|
||||
return "Redirection";
|
||||
|
||||
if (http_client_error(code))
|
||||
return "Client Error";
|
||||
|
||||
if (http_server_error(code))
|
||||
return "Server Error";
|
||||
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
const char * http_status_reason(int code) {
|
||||
switch (code) {
|
||||
/*####### 1xx - Informational #######*/
|
||||
case 100:
|
||||
return "Continue";
|
||||
case 101:
|
||||
return "Switching Protocols";
|
||||
case 102:
|
||||
return "Processing";
|
||||
case 103:
|
||||
return "Early Hints";
|
||||
|
||||
/*####### 2xx - Successful #######*/
|
||||
case 200:
|
||||
return "OK";
|
||||
case 201:
|
||||
return "Created";
|
||||
case 202:
|
||||
return "Accepted";
|
||||
case 203:
|
||||
return "Non-Authoritative Information";
|
||||
case 204:
|
||||
return "No Content";
|
||||
case 205:
|
||||
return "Reset Content";
|
||||
case 206:
|
||||
return "Partial Content";
|
||||
case 207:
|
||||
return "Multi-Status";
|
||||
case 208:
|
||||
return "Already Reported";
|
||||
case 226:
|
||||
return "IM Used";
|
||||
|
||||
/*####### 3xx - Redirection #######*/
|
||||
case 300:
|
||||
return "Multiple Choices";
|
||||
case 301:
|
||||
return "Moved Permanently";
|
||||
case 302:
|
||||
return "Found";
|
||||
case 303:
|
||||
return "See Other";
|
||||
case 304:
|
||||
return "Not Modified";
|
||||
case 305:
|
||||
return "Use Proxy";
|
||||
case 307:
|
||||
return "Temporary Redirect";
|
||||
case 308:
|
||||
return "Permanent Redirect";
|
||||
|
||||
/*####### 4xx - Client Error #######*/
|
||||
case 400:
|
||||
return "Bad Request";
|
||||
case 401:
|
||||
return "Unauthorized";
|
||||
case 402:
|
||||
return "Payment Required";
|
||||
case 403:
|
||||
return "Forbidden";
|
||||
case 404:
|
||||
return "Not Found";
|
||||
case 405:
|
||||
return "Method Not Allowed";
|
||||
case 406:
|
||||
return "Not Acceptable";
|
||||
case 407:
|
||||
return "Proxy Authentication Required";
|
||||
case 408:
|
||||
return "Request Timeout";
|
||||
case 409:
|
||||
return "Conflict";
|
||||
case 410:
|
||||
return "Gone";
|
||||
case 411:
|
||||
return "Length Required";
|
||||
case 412:
|
||||
return "Precondition Failed";
|
||||
case 413:
|
||||
return "Content Too Large";
|
||||
case 414:
|
||||
return "URI Too Long";
|
||||
case 415:
|
||||
return "Unsupported Media Type";
|
||||
case 416:
|
||||
return "Range Not Satisfiable";
|
||||
case 417:
|
||||
return "Expectation Failed";
|
||||
case 418:
|
||||
return "I'm a teapot";
|
||||
case 421:
|
||||
return "Misdirected Request";
|
||||
case 422:
|
||||
return "Unprocessable Content";
|
||||
case 423:
|
||||
return "Locked";
|
||||
case 424:
|
||||
return "Failed Dependency";
|
||||
case 425:
|
||||
return "Too Early";
|
||||
case 426:
|
||||
return "Upgrade Required";
|
||||
case 428:
|
||||
return "Precondition Required";
|
||||
case 429:
|
||||
return "Too Many Requests";
|
||||
case 431:
|
||||
return "Request Header Fields Too Large";
|
||||
case 451:
|
||||
return "Unavailable For Legal Reasons";
|
||||
|
||||
/*####### 5xx - Server Error #######*/
|
||||
case 500:
|
||||
return "Internal Server Error";
|
||||
case 501:
|
||||
return "Not Implemented";
|
||||
case 502:
|
||||
return "Bad Gateway";
|
||||
case 503:
|
||||
return "Service Unavailable";
|
||||
case 504:
|
||||
return "Gateway Timeout";
|
||||
case 505:
|
||||
return "HTTP Version Not Supported";
|
||||
case 506:
|
||||
return "Variant Also Negotiates";
|
||||
case 507:
|
||||
return "Insufficient Storage";
|
||||
case 508:
|
||||
return "Loop Detected";
|
||||
case 510:
|
||||
return "Not Extended";
|
||||
case 511:
|
||||
return "Network Authentication Required";
|
||||
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
15
lib/PsychicHttp/src_old/http_status.h
Normal file
15
lib/PsychicHttp/src_old/http_status.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#ifndef MICRO_HTTP_STATUS_H
|
||||
#define MICRO_HTTP_STATUS_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
bool http_informational(int code);
|
||||
bool http_success(int code);
|
||||
bool http_redirection(int code);
|
||||
bool http_client_error(int code);
|
||||
bool http_server_error(int code);
|
||||
bool http_failure(int code);
|
||||
const char * http_status_group(int code);
|
||||
const char * http_status_reason(int code);
|
||||
|
||||
#endif // MICRO_HTTP_STATUS_H
|
||||
Reference in New Issue
Block a user