diff --git a/interface/package.json b/interface/package.json index 756f32dbb..06dbffd06 100644 --- a/interface/package.json +++ b/interface/package.json @@ -52,8 +52,8 @@ "devDependencies": { "@preact/compat": "^17.1.2", "@preact/preset-vite": "^2.7.0", - "@typescript-eslint/eslint-plugin": "^6.16.0", - "@typescript-eslint/parser": "^6.16.0", + "@typescript-eslint/eslint-plugin": "^6.17.0", + "@typescript-eslint/parser": "^6.17.0", "concurrently": "^8.2.2", "eslint": "^8.56.0", "eslint-config-airbnb": "^19.0.4", diff --git a/interface/progmem-generator.js b/interface/progmem-generator.js index f95fee287..9cf56ed14 100644 --- a/interface/progmem-generator.js +++ b/interface/progmem-generator.js @@ -11,7 +11,8 @@ const bytesPerLine = 20; var totalSize = 0; const generateWWWClass = () => - `typedef std::function RouteRegistrationHandler; + `typedef std::function RouteRegistrationHandler; +// Total size is ${totalSize} bytes class WWWData { ${indent}public: diff --git a/interface/src/utils/useWs.ts b/interface/src/utils/useWs.ts index 3471c691e..c2b3806ca 100644 --- a/interface/src/utils/useWs.ts +++ b/interface/src/utils/useWs.ts @@ -1,3 +1,4 @@ +/* import { debounce } from 'lodash-es'; import { useCallback, useEffect, useRef, useState } from 'react'; import Sockette from 'sockette'; @@ -89,3 +90,4 @@ export const useWs = (wsUrl: string, wsThrottle = 100) => { return { connected, data, updateData } as const; }; +*/ diff --git a/interface/vite.config.ts b/interface/vite.config.ts index bb76b59ab..b5b69c560 100644 --- a/interface/vite.config.ts +++ b/interface/vite.config.ts @@ -1,4 +1,4 @@ -import { defineConfig } from 'vite'; +import { defineConfig, splitVendorChunkPlugin } from 'vite'; import viteTsconfigPaths from 'vite-tsconfig-paths'; import preact from '@preact/preset-vite'; import viteImagemin from 'vite-plugin-imagemin'; @@ -44,6 +44,7 @@ export default defineConfig(({ command, mode }) => { plugins: [ preact(), viteTsconfigPaths(), + splitVendorChunkPlugin(), { ...viteImagemin({ verbose: false, @@ -112,6 +113,20 @@ export default defineConfig(({ command, mode }) => { nameCache: null, safari10: false, toplevel: false + }, + + rollupOptions: { + output: { + manualChunks(id: string) { + if (id.includes('node_modules')) { + // creating a chunk to react routes deps. Reducing the vendor chunk size + if (id.includes('react-router-dom') || id.includes('@remix-run') || id.includes('react-router')) { + return '@react-router'; + } + return 'vendor'; + } + } + } } } }; diff --git a/interface/yarn.lock b/interface/yarn.lock index c91ac531c..d67e93a60 100644 --- a/interface/yarn.lock +++ b/interface/yarn.lock @@ -1689,15 +1689,15 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/eslint-plugin@npm:^6.16.0": - version: 6.16.0 - resolution: "@typescript-eslint/eslint-plugin@npm:6.16.0" +"@typescript-eslint/eslint-plugin@npm:^6.17.0": + version: 6.17.0 + resolution: "@typescript-eslint/eslint-plugin@npm:6.17.0" dependencies: "@eslint-community/regexpp": "npm:^4.5.1" - "@typescript-eslint/scope-manager": "npm:6.16.0" - "@typescript-eslint/type-utils": "npm:6.16.0" - "@typescript-eslint/utils": "npm:6.16.0" - "@typescript-eslint/visitor-keys": "npm:6.16.0" + "@typescript-eslint/scope-manager": "npm:6.17.0" + "@typescript-eslint/type-utils": "npm:6.17.0" + "@typescript-eslint/utils": "npm:6.17.0" + "@typescript-eslint/visitor-keys": "npm:6.17.0" debug: "npm:^4.3.4" graphemer: "npm:^1.4.0" ignore: "npm:^5.2.4" @@ -1710,44 +1710,44 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 4bedce948ac3c20492a59813ee5d4f1f2306310857864dfaac2736f6c38e18785002c36844fd64c9fbdf3059fc390b29412be105fd7a118177f1eeeb1eb533f7 + checksum: f2a5774e9cc03e491a5a488501e5622c7eebd766f5a4fc2c30642864a3b89b0807946bde33a678f326ba7032f3f6a51aa0bf9c2d10adc823804fc9fb47db55a6 languageName: node linkType: hard -"@typescript-eslint/parser@npm:^6.16.0": - version: 6.16.0 - resolution: "@typescript-eslint/parser@npm:6.16.0" +"@typescript-eslint/parser@npm:^6.17.0": + version: 6.17.0 + resolution: "@typescript-eslint/parser@npm:6.17.0" dependencies: - "@typescript-eslint/scope-manager": "npm:6.16.0" - "@typescript-eslint/types": "npm:6.16.0" - "@typescript-eslint/typescript-estree": "npm:6.16.0" - "@typescript-eslint/visitor-keys": "npm:6.16.0" + "@typescript-eslint/scope-manager": "npm:6.17.0" + "@typescript-eslint/types": "npm:6.17.0" + "@typescript-eslint/typescript-estree": "npm:6.17.0" + "@typescript-eslint/visitor-keys": "npm:6.17.0" debug: "npm:^4.3.4" peerDependencies: eslint: ^7.0.0 || ^8.0.0 peerDependenciesMeta: typescript: optional: true - checksum: 3d941ce345dc2ce29957e2110957662873d514b094b8939923c3281d858c11cd1f9058db862644afe14f68d087770f39a0a1f9e523a2013ed5d2fdf3421b34d0 + checksum: 2ed0ed4a5b30e953430ce3279df3655af09fa1caed2abf11804d239717daefc32a22864f6620ef57bb9c684c74a99a13241384fea5096e961385e3678fc2e920 languageName: node linkType: hard -"@typescript-eslint/scope-manager@npm:6.16.0": - version: 6.16.0 - resolution: "@typescript-eslint/scope-manager@npm:6.16.0" +"@typescript-eslint/scope-manager@npm:6.17.0": + version: 6.17.0 + resolution: "@typescript-eslint/scope-manager@npm:6.17.0" dependencies: - "@typescript-eslint/types": "npm:6.16.0" - "@typescript-eslint/visitor-keys": "npm:6.16.0" - checksum: 3360aae4b85f5c31d20ad48d771cc09a6f8f6b1811b00d94f06e55b5a09c610ac75631b1c4edecb3bec682d41351b87e7d14d42bee84aa032064d0e13463035b + "@typescript-eslint/types": "npm:6.17.0" + "@typescript-eslint/visitor-keys": "npm:6.17.0" + checksum: fe09c628553c9336e6a36d32c1d34e78ebd20aa02130a6bf535329621ba5a98aaac171f607bc6e4d17b3478c42e7de6476376636897ce3f227c754eb99acd07e languageName: node linkType: hard -"@typescript-eslint/type-utils@npm:6.16.0": - version: 6.16.0 - resolution: "@typescript-eslint/type-utils@npm:6.16.0" +"@typescript-eslint/type-utils@npm:6.17.0": + version: 6.17.0 + resolution: "@typescript-eslint/type-utils@npm:6.17.0" dependencies: - "@typescript-eslint/typescript-estree": "npm:6.16.0" - "@typescript-eslint/utils": "npm:6.16.0" + "@typescript-eslint/typescript-estree": "npm:6.17.0" + "@typescript-eslint/utils": "npm:6.17.0" debug: "npm:^4.3.4" ts-api-utils: "npm:^1.0.1" peerDependencies: @@ -1755,23 +1755,23 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 5964b87a87252bed278a248eb568902babd7c34defd3af8c3df371926d96aec716f33f1dc14bde170e93f73ed1b0af6e591e647853d0f33f378e2c7b3b73fc5b + checksum: dc7938429193acfda61b7282197ec046039e2c4da41cdcddf4daaf300d10229e4e23bb0fcf0503b19c0b99a874849c8a9f5bb35ce106260f56a14106d2b41d8c languageName: node linkType: hard -"@typescript-eslint/types@npm:6.16.0": - version: 6.16.0 - resolution: "@typescript-eslint/types@npm:6.16.0" - checksum: 236ca318c2440c95068e5d4d147e2bfed62447775e18695e21c8ca04a341a74d01c37ed2b417629b7bf2fb91ad4fd5e2a6570215d16fc24dd1507ce6973b4e22 +"@typescript-eslint/types@npm:6.17.0": + version: 6.17.0 + resolution: "@typescript-eslint/types@npm:6.17.0" + checksum: 87ab1b5a3270ab34b917c22a2fb90a9ad7d9f3b19d73a337bc9efbe65f924da13482c97e8ccbe3bd3d081aa96039eeff50de41c1da2a2128066429b931cdb21d languageName: node linkType: hard -"@typescript-eslint/typescript-estree@npm:6.16.0": - version: 6.16.0 - resolution: "@typescript-eslint/typescript-estree@npm:6.16.0" +"@typescript-eslint/typescript-estree@npm:6.17.0": + version: 6.17.0 + resolution: "@typescript-eslint/typescript-estree@npm:6.17.0" dependencies: - "@typescript-eslint/types": "npm:6.16.0" - "@typescript-eslint/visitor-keys": "npm:6.16.0" + "@typescript-eslint/types": "npm:6.17.0" + "@typescript-eslint/visitor-keys": "npm:6.17.0" debug: "npm:^4.3.4" globby: "npm:^11.1.0" is-glob: "npm:^4.0.3" @@ -1781,34 +1781,34 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 8e1ef03ecabaf3791b11240a51217836dbb74850e458258db77ac5eab5508cd9c63fb671924993d1e7654718c0c857c3550d51ecba0845fe489d143bb858e1b1 + checksum: 1671b0d2f2fdf07074fb1e2524d61935cec173bd8db6e482cc5b2dcc77aed3ffa831396736ffa0ee2fdbddd8585ae9ca8d6c97bcaea1385b23907a1ec0508f83 languageName: node linkType: hard -"@typescript-eslint/utils@npm:6.16.0": - version: 6.16.0 - resolution: "@typescript-eslint/utils@npm:6.16.0" +"@typescript-eslint/utils@npm:6.17.0": + version: 6.17.0 + resolution: "@typescript-eslint/utils@npm:6.17.0" dependencies: "@eslint-community/eslint-utils": "npm:^4.4.0" "@types/json-schema": "npm:^7.0.12" "@types/semver": "npm:^7.5.0" - "@typescript-eslint/scope-manager": "npm:6.16.0" - "@typescript-eslint/types": "npm:6.16.0" - "@typescript-eslint/typescript-estree": "npm:6.16.0" + "@typescript-eslint/scope-manager": "npm:6.17.0" + "@typescript-eslint/types": "npm:6.17.0" + "@typescript-eslint/typescript-estree": "npm:6.17.0" semver: "npm:^7.5.4" peerDependencies: eslint: ^7.0.0 || ^8.0.0 - checksum: 84dd02f7c8e47fae699cc222da5cbea08b28c6e1cc7827860430bc86c2a17ee3f86e198a4356902b95930f85785aa662266ea9c476f69bf80c6a5f648e55f9f4 + checksum: 37c63afcf66124bf84808699997953b8c84a378aa2c490a771b611d82cdac8499c58fac8eeb8378528e97660b59563d99297bfec4b982cd68760b0ffe54aa714 languageName: node linkType: hard -"@typescript-eslint/visitor-keys@npm:6.16.0": - version: 6.16.0 - resolution: "@typescript-eslint/visitor-keys@npm:6.16.0" +"@typescript-eslint/visitor-keys@npm:6.17.0": + version: 6.17.0 + resolution: "@typescript-eslint/visitor-keys@npm:6.17.0" dependencies: - "@typescript-eslint/types": "npm:6.16.0" + "@typescript-eslint/types": "npm:6.17.0" eslint-visitor-keys: "npm:^3.4.1" - checksum: 19e559f14ea0092585a374b8c5f1aca9b6b271fc23909d9857de9cf71a1e1d3abc0afd237e9c02d7a5fbdfe8e3be7853cf9fedf40a6f16bac3495cb7f4e67982 + checksum: a2aed0e1437fdab8858ab9c7c8e355f8b72a5fa4b0adc54f28b8a2bbc29d4bb93214968ee940f83d013d0a4b83d00cd4eeeb05fb4c2c7d0ead324c6793f7d6d4 languageName: node linkType: hard @@ -1838,8 +1838,8 @@ __metadata: "@types/react": "npm:^18.2.46" "@types/react-dom": "npm:^18.2.18" "@types/react-router-dom": "npm:^5.3.3" - "@typescript-eslint/eslint-plugin": "npm:^6.16.0" - "@typescript-eslint/parser": "npm:^6.16.0" + "@typescript-eslint/eslint-plugin": "npm:^6.17.0" + "@typescript-eslint/parser": "npm:^6.17.0" alova: "npm:^2.16.2" async-validator: "npm:^4.2.5" concurrently: "npm:^8.2.2" diff --git a/lib/PsychicHttp/src/ChunkPrinter.h b/lib/PsychicHttp/src/ChunkPrinter.h index f453e43b9..4021949c7 100644 --- a/lib/PsychicHttp/src/ChunkPrinter.h +++ b/lib/PsychicHttp/src/ChunkPrinter.h @@ -1,51 +1,55 @@ #ifndef ChunkPrinter_h #define ChunkPrinter_h -#include "PsychicCore.h" +// #include "PsychicCore.h" #include "PsychicResponse.h" #include -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; + } } }; diff --git a/lib/PsychicHttp/src/PsychicClient.cpp b/lib/PsychicHttp/src/PsychicClient.cpp index bbee4e269..1e59d70e7 100644 --- a/lib/PsychicHttp/src/PsychicClient.cpp +++ b/lib/PsychicHttp/src/PsychicClient.cpp @@ -1,67 +1,72 @@ #include "PsychicClient.h" +#include "PsychicHttpServer.h" +#include -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; } \ No newline at end of file diff --git a/lib/PsychicHttp/src/PsychicClient.h b/lib/PsychicHttp/src/PsychicClient.h index 3b7b79e6d..b823df77e 100644 --- a/lib/PsychicHttp/src/PsychicClient.h +++ b/lib/PsychicHttp/src/PsychicClient.h @@ -2,8 +2,6 @@ #define PsychicClient_h #include "PsychicCore.h" -#include "PsychicHttpServer.h" -#include /* * 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(); diff --git a/lib/PsychicHttp/src/PsychicCore.h b/lib/PsychicHttp/src/PsychicCore.h index 5cf8232be..0d8c422c8 100644 --- a/lib/PsychicHttp/src/PsychicCore.h +++ b/lib/PsychicHttp/src/PsychicCore.h @@ -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 #include "FS.h" +#include -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 PsychicRequestFilterFunction; +// filter function definition +typedef std::function PsychicRequestFilterFunction; -//client connect callback -typedef std::function PsychicClientCallback; +// client connect callback +typedef std::function PsychicClientCallback; +// callback definitions +typedef std::function PsychicHttpRequestCallback; +typedef std::function PsychicJsonRequestCallback; -struct HTTPHeader { - char * field; - char * value; +struct HTTPHeader +{ + char *field; + char *value; }; -class DefaultHeaders { - std::list _headers; +class DefaultHeaders +{ + std::list _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 & getHeaders() { - return _headers; - } + const std::list &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 \ No newline at end of file +#endif // PsychicCore_h \ No newline at end of file diff --git a/lib/PsychicHttp/src/PsychicEndpoint.cpp b/lib/PsychicHttp/src/PsychicEndpoint.cpp index e17ddef13..e6926583e 100644 --- a/lib/PsychicHttp/src/PsychicEndpoint.cpp +++ b/lib/PsychicHttp/src/PsychicEndpoint.cpp @@ -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."); - 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."); + 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; }; \ No newline at end of file diff --git a/lib/PsychicHttp/src/PsychicEndpoint.h b/lib/PsychicHttp/src/PsychicEndpoint.h index 3df414794..540b29413 100644 --- a/lib/PsychicHttp/src/PsychicEndpoint.h +++ b/lib/PsychicHttp/src/PsychicEndpoint.h @@ -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 \ No newline at end of file diff --git a/lib/PsychicHttp/src/PsychicEventSource.cpp b/lib/PsychicHttp/src/PsychicEventSource.cpp index d457e4754..044ebbac0 100644 --- a/lib/PsychicHttp/src/PsychicEventSource.cpp +++ b/lib/PsychicHttp/src/PsychicEventSource.cpp @@ -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; } \ No newline at end of file diff --git a/lib/PsychicHttp/src/PsychicEventSource.h b/lib/PsychicHttp/src/PsychicEventSource.h index 1406e5c30..ab3198043 100644 --- a/lib/PsychicHttp/src/PsychicEventSource.h +++ b/lib/PsychicHttp/src/PsychicEventSource.h @@ -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 PsychicEventSourceClientCallback; +typedef std::function 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_ */ \ No newline at end of file diff --git a/lib/PsychicHttp/src/PsychicFileResponse.cpp b/lib/PsychicHttp/src/PsychicFileResponse.cpp index 51827cc49..ed031560a 100644 --- a/lib/PsychicHttp/src/PsychicFileResponse.cpp +++ b/lib/PsychicHttp/src/PsychicFileResponse.cpp @@ -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; } diff --git a/lib/PsychicHttp/src/PsychicFileResponse.h b/lib/PsychicHttp/src/PsychicFileResponse.h index 918e81bb7..85a8300b0 100644 --- a/lib/PsychicHttp/src/PsychicFileResponse.h +++ b/lib/PsychicHttp/src/PsychicFileResponse.h @@ -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(); }; diff --git a/lib/PsychicHttp/src/PsychicHandler.h b/lib/PsychicHttp/src/PsychicHandler.h index 071238124..4d832f955 100644 --- a/lib/PsychicHttp/src/PsychicHandler.h +++ b/lib/PsychicHttp/src/PsychicHandler.h @@ -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 _clients; + std::list _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 & getClientList(); + bool hasClient(PsychicClient *client); + int count() { return _clients.size(); }; + const std::list& 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 \ No newline at end of file diff --git a/lib/PsychicHttp/src/PsychicHttp.h b/lib/PsychicHttp/src/PsychicHttp.h index dd863162a..ac633858a 100644 --- a/lib/PsychicHttp/src/PsychicHttp.h +++ b/lib/PsychicHttp/src/PsychicHttp.h @@ -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 #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" diff --git a/lib/PsychicHttp/src/PsychicHttpServer.cpp b/lib/PsychicHttp/src/PsychicHttpServer.cpp index c7938ad63..e48a73b16 100644 --- a/lib/PsychicHttp/src/PsychicHttpServer.cpp +++ b/lib/PsychicHttp/src/PsychicHttpServer.cpp @@ -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 & PsychicHttpServer::getClientList() { - return _clients; +const std::list& 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; } \ No newline at end of file diff --git a/lib/PsychicHttp/src/PsychicHttpServer.h b/lib/PsychicHttp/src/PsychicHttpServer.h index 0407ceb19..e572c1b2d 100644 --- a/lib/PsychicHttp/src/PsychicHttpServer.h +++ b/lib/PsychicHttp/src/PsychicHttpServer.h @@ -4,27 +4,23 @@ #include "PsychicCore.h" #include "PsychicClient.h" #include "PsychicHandler.h" -#include // added by proddy class PsychicEndpoint; class PsychicHandler; class PsychicStaticFileHandler; -//callback definitions -typedef std::function PsychicHttpRequestCallback; -typedef std::function PsychicJsonRequestCallback; // added by proddy - -class PsychicHttpServer { +class PsychicHttpServer +{ protected: - bool _use_ssl = false; - std::list _endpoints; - std::list _handlers; - std::list _clients; + bool _use_ssl = false; + std::list _endpoints; + std::list _handlers; + std::list _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 & 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& 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 \ No newline at end of file diff --git a/lib/PsychicHttp/src/PsychicHttpsServer.cpp b/lib/PsychicHttp/src/PsychicHttpsServer.cpp index 489beb6a3..e62dd4ecd 100644 --- a/lib/PsychicHttp/src/PsychicHttpsServer.cpp +++ b/lib/PsychicHttp/src/PsychicHttpsServer.cpp @@ -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); } \ No newline at end of file diff --git a/lib/PsychicHttp/src/PsychicHttpsServer.h b/lib/PsychicHttp/src/PsychicHttpsServer.h index fa20c81e8..f61043d56 100644 --- a/lib/PsychicHttp/src/PsychicHttpsServer.h +++ b/lib/PsychicHttp/src/PsychicHttpsServer.h @@ -6,12 +6,13 @@ #include #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 \ No newline at end of file diff --git a/lib/PsychicHttp/src/PsychicJson.DELETEME b/lib/PsychicHttp/src/PsychicJson.DELETEME new file mode 100644 index 000000000..dd6830a53 --- /dev/null +++ b/lib/PsychicHttp/src/PsychicJson.DELETEME @@ -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(); + #endif + + return _onRequest(request, json); + } + else + return request->reply(500); +} \ No newline at end of file diff --git a/lib/PsychicHttp/src/PsychicJson.h b/lib/PsychicHttp/src/PsychicJson.h index 5f98a404d..b01abd666 100644 --- a/lib/PsychicHttp/src/PsychicJson.h +++ b/lib/PsychicHttp/src/PsychicJson.h @@ -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); diff --git a/lib/PsychicHttp/src/PsychicRequest.cpp b/lib/PsychicHttp/src/PsychicRequest.cpp index 50a163ca8..aa54b330b 100644 --- a/lib/PsychicHttp/src/PsychicRequest.cpp +++ b/lib/PsychicHttp/src/PsychicRequest.cpp @@ -1,6 +1,7 @@ #include "PsychicRequest.h" -#include "PsychicResponse.h" -#include +#include "http_status.h" +#include "PsychicHttpServer.h" + PsychicRequest::PsychicRequest(PsychicHttpServer *server, httpd_req_t *req) : _server(server), diff --git a/lib/PsychicHttp/src/PsychicRequest.h b/lib/PsychicHttp/src/PsychicRequest.h index 8ab6340ca..e4393c78a 100644 --- a/lib/PsychicHttp/src/PsychicRequest.h +++ b/lib/PsychicHttp/src/PsychicRequest.h @@ -5,6 +5,7 @@ #include "PsychicHttpServer.h" #include "PsychicClient.h" #include "PsychicWebParameter.h" +#include "PsychicResponse.h" typedef std::map SessionData; diff --git a/lib/PsychicHttp/src/PsychicResponse.cpp b/lib/PsychicHttp/src/PsychicResponse.cpp index 0aa3ae8fc..515674e56 100644 --- a/lib/PsychicHttp/src/PsychicResponse.cpp +++ b/lib/PsychicHttp/src/PsychicResponse.cpp @@ -2,137 +2,154 @@ #include "PsychicRequest.h" #include -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); } \ No newline at end of file diff --git a/lib/PsychicHttp/src/PsychicResponse.h b/lib/PsychicHttp/src/PsychicResponse.h index 9fd11f420..a36de6577 100644 --- a/lib/PsychicHttp/src/PsychicResponse.h +++ b/lib/PsychicHttp/src/PsychicResponse.h @@ -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 _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 \ No newline at end of file diff --git a/lib/PsychicHttp/src/PsychicStaticFileHander.cpp b/lib/PsychicHttp/src/PsychicStaticFileHander.cpp index 20d6d6b61..e58f3ad88 100644 --- a/lib/PsychicHttp/src/PsychicStaticFileHander.cpp +++ b/lib/PsychicHttp/src/PsychicStaticFileHander.cpp @@ -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; } \ No newline at end of file diff --git a/lib/PsychicHttp/src/PsychicStaticFileHandler.h b/lib/PsychicHttp/src/PsychicStaticFileHandler.h index 126b9beb7..f7571113f 100644 --- a/lib/PsychicHttp/src/PsychicStaticFileHandler.h +++ b/lib/PsychicHttp/src/PsychicStaticFileHandler.h @@ -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;} }; diff --git a/lib/PsychicHttp/src/PsychicStreamResponse.cpp b/lib/PsychicHttp/src/PsychicStreamResponse.cpp new file mode 100644 index 000000000..4b369b69f --- /dev/null +++ b/lib/PsychicHttp/src/PsychicStreamResponse.cpp @@ -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; +} diff --git a/lib/PsychicHttp/src/PsychicStreamResponse.h b/lib/PsychicHttp/src/PsychicStreamResponse.h new file mode 100644 index 000000000..25339e9d6 --- /dev/null +++ b/lib/PsychicHttp/src/PsychicStreamResponse.h @@ -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 diff --git a/lib/PsychicHttp/src/PsychicUploadHandler.h b/lib/PsychicHttp/src/PsychicUploadHandler.h index df8497334..59e62b6a4 100644 --- a/lib/PsychicHttp/src/PsychicUploadHandler.h +++ b/lib/PsychicHttp/src/PsychicUploadHandler.h @@ -8,7 +8,7 @@ #include "PsychicWebParameter.h" //callback definitions -typedef std::function PsychicUploadCallback; +typedef std::function 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 \ No newline at end of file diff --git a/lib/PsychicHttp/src/PsychicWebHandler.cpp b/lib/PsychicHttp/src/PsychicWebHandler.cpp index dbe8ee051..d1b79168c 100644 --- a/lib/PsychicHttp/src/PsychicWebHandler.cpp +++ b/lib/PsychicHttp/src/PsychicWebHandler.cpp @@ -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; } \ No newline at end of file diff --git a/lib/PsychicHttp/src/PsychicWebHandler.h b/lib/PsychicHttp/src/PsychicWebHandler.h index 5562dfd10..07a6780a6 100644 --- a/lib/PsychicHttp/src/PsychicWebHandler.h +++ b/lib/PsychicHttp/src/PsychicWebHandler.h @@ -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 \ No newline at end of file diff --git a/lib/PsychicHttp/src/PsychicWebParameter.h b/lib/PsychicHttp/src/PsychicWebParameter.h index fe6efc7e0..e09136b1e 100644 --- a/lib/PsychicHttp/src/PsychicWebParameter.h +++ b/lib/PsychicHttp/src/PsychicWebParameter.h @@ -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 \ No newline at end of file diff --git a/lib/PsychicHttp/src/PsychicWebSocket.cpp b/lib/PsychicHttp/src/PsychicWebSocket.cpp index 24c99ca73..45f004b90 100644 --- a/lib/PsychicHttp/src/PsychicWebSocket.cpp +++ b/lib/PsychicHttp/src/PsychicWebSocket.cpp @@ -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)); + + ws_pkt.payload = (uint8_t*)data; + ws_pkt.len = len; + ws_pkt.type = op; + + return this->reply(&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)); +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)); + + ws_pkt.payload = (uint8_t*)data; + ws_pkt.len = len; + ws_pkt.type = op; + + return this->sendMessage(&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)); } -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)); } \ No newline at end of file diff --git a/lib/PsychicHttp/src/PsychicWebSocket.h b/lib/PsychicHttp/src/PsychicWebSocket.h index bea702197..01917de25 100644 --- a/lib/PsychicHttp/src/PsychicWebSocket.h +++ b/lib/PsychicHttp/src/PsychicWebSocket.h @@ -8,38 +8,40 @@ class PsychicWebSocketRequest; class PsychicWebSocketClient; //callback function definitions -typedef std::function PsychicWebSocketClientCallback; -typedef std::function PsychicWebSocketFrameCallback; +typedef std::function PsychicWebSocketClientCallback; +typedef std::function 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 \ No newline at end of file diff --git a/lib/PsychicHttp/src/http_status.cpp b/lib/PsychicHttp/src/http_status.cpp index ae4b0ee5a..64e0c0708 100644 --- a/lib/PsychicHttp/src/http_status.cpp +++ b/lib/PsychicHttp/src/http_status.cpp @@ -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"; diff --git a/lib/PsychicHttp/src/http_status.h b/lib/PsychicHttp/src/http_status.h index 12ad33281..e03b7357d 100644 --- a/lib/PsychicHttp/src/http_status.h +++ b/lib/PsychicHttp/src/http_status.h @@ -3,13 +3,13 @@ #include -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 \ No newline at end of file diff --git a/lib/framework/ESP8266React.cpp b/lib/framework/ESP8266React.cpp index 61490b777..311889aac 100644 --- a/lib/framework/ESP8266React.cpp +++ b/lib/framework/ESP8266React.cpp @@ -49,6 +49,7 @@ void ESP8266React::registerURI() { _mqttStatus.registerURI(); _securitySettingsService.registerURI(); + _otaSettingsService.registerURI(); _restartService.registerURI(); diff --git a/lib/framework/HttpEndpoint.h b/lib/framework/HttpEndpoint.h index aa8bdea54..0c744f5ad 100644 --- a/lib/framework/HttpEndpoint.h +++ b/lib/framework/HttpEndpoint.h @@ -46,6 +46,7 @@ class HttpEndpoint { // register the web server on() endpoints void registerURI() { + // GET _server->on(_servicePath, HTTP_GET, _securityManager->wrapRequest( @@ -57,6 +58,7 @@ class HttpEndpoint { }, _authenticationPredicate)); + // POST _server->on(_servicePath, HTTP_POST, _securityManager->wrapCallback( diff --git a/lib/framework/NetworkSettingsService.cpp b/lib/framework/NetworkSettingsService.cpp index 962384df3..ff3cf25d8 100644 --- a/lib/framework/NetworkSettingsService.cpp +++ b/lib/framework/NetworkSettingsService.cpp @@ -17,12 +17,14 @@ void NetworkSettingsService::begin() { // WiFi.mode(WIFI_OFF); // } + WiFi.mode(WIFI_MODE_STA); // TODO added otherwise crashes. this is the default. + // WiFi.useStaticBuffers(true); // uses 40kb more heap and max alloc, so not recommended WiFi.persistent(false); WiFi.setAutoReconnect(false); - WiFi.mode(WIFI_MODE_MAX); - WiFi.mode(WIFI_MODE_NULL); + // WiFi.mode(WIFI_MODE_MAX); // TODO commented out, not sure what use it has + // WiFi.mode(WIFI_MODE_NULL); // WiFi.setScanMethod(WIFI_ALL_CHANNEL_SCAN); // default is FAST_SCAN, connect issues in 2.0.14 // WiFi.setSortMethod(WIFI_CONNECT_AP_BY_SIGNAL); // is default, no need to set diff --git a/lib/framework/WebSocketTxRx.h b/lib/framework/WebSocketTxRx.h index 066670d2e..b80e28c54 100644 --- a/lib/framework/WebSocketTxRx.h +++ b/lib/framework/WebSocketTxRx.h @@ -1,3 +1,4 @@ +/* #ifndef WebSocketTxRx_h #define WebSocketTxRx_h @@ -107,13 +108,12 @@ class WebSocketTx : virtual public WebSocketConnector { } } - /** - * Broadcasts the payload to the destination, if provided. Otherwise broadcasts to all clients except the origin, if - * specified. - * - * Original implementation sent clients their own IDs so they could ignore updates they initiated. This approach - * simplifies the client and the server implementation but may not be sufficent for all use-cases. - */ + // Broadcasts the payload to the destination, if provided. Otherwise broadcasts to all clients except the origin, if + // specified. + // + // Original implementation sent clients their own IDs so they could ignore updates they initiated. This approach + // simplifies the client and the server implementation but may not be sufficent for all use-cases. + // void transmitData(AsyncWebSocketClient * client, const String & originId) { DynamicJsonDocument jsonDocument = DynamicJsonDocument(WebSocketConnector::_bufferSize); JsonObject root = jsonDocument.to(); @@ -214,3 +214,4 @@ class WebSocketTxRx : public WebSocketTx, public WebSocketRx { }; #endif +*/ \ No newline at end of file diff --git a/platformio.ini b/platformio.ini index eec50c32d..4f913de87 100644 --- a/platformio.ini +++ b/platformio.ini @@ -1,71 +1,60 @@ -; PlatformIO Project Configuration File for EMS-ESP -; override any settings with your own local ones in pio_local.ini +; PlatformIO Project Configuration File +; +; Build options: build flags, source filter +; Upload options: custom upload port, speed and extra flags +; Library options: dependencies, extra library storages +; Advanced options: extra scripting +; +; Please visit documentation for the other options and examples +; https://docs.platformio.org/page/projectconf.html [platformio] -; default_envs = esp32_4M default_envs = lolin_s3 -; default_envs = esp32_16M -; default_envs = standalone -; default_envs = https - -extra_configs = - factory_settings.ini - pio_local.ini +extra_configs = + factory_settings.ini + pio_local.ini [common] -core_build_flags = - -D ARDUINO_ARCH_ESP32=1 - -D ESP32=1 - ; -std=gnu++17 - -; core_unbuild_flags = -std=gnu++11 -core_unbuild_flags = - -; my_build_flags is set in pio_local.ini -my_build_flags = - -build_flags = - ${common.core_build_flags} - ${factory_settings.build_flags} - ${common.my_build_flags} - -D ONEWIRE_CRC16=0 - -D NO_GLOBAL_ARDUINOOTA - -D ARDUINOJSON_ENABLE_STD_STRING=1 - -D ARDUINOJSON_USE_DOUBLE=0 - -D ARDUINOTRACE_ENABLE=0 - ; -D CONFIG_UART_ISR_IN_IRAM - -unbuild_flags = - ${common.core_unbuild_flags} +core_build_flags = + -D ARDUINO_ARCH_ESP32=1 + -D ESP32=1 +core_unbuild_flags = +my_build_flags = +build_flags = + ${common.core_build_flags} + ${factory_settings.build_flags} + ${common.my_build_flags} + -D ONEWIRE_CRC16=0 + -D NO_GLOBAL_ARDUINOOTA + -D ARDUINOJSON_ENABLE_STD_STRING=1 + -D ARDUINOJSON_USE_DOUBLE=0 + -D ARDUINOTRACE_ENABLE=0 + -D CONFIG_ETH_ENABLED + -D ARDUINOTRACE_ENABLE=0 +unbuild_flags = + ${common.core_unbuild_flags} [espressi32_base] -platform = espressif32@6.5.0 +platform = espressif32 framework = arduino board_build.filesystem = littlefs build_flags = ${common.build_flags} build_unflags = ${common.unbuild_flags} -extra_scripts = - pre:scripts/build_interface.py - scripts/rename_fw.py +extra_scripts = + pre:scripts/build_interface.py + scripts/rename_fw.py [espressi32_base_tasmota] -; use Tasmota's libary which removes some libs (like mbedtsl) and increases available heap -; platform = https://github.com/tasmota/platform-espressif32.git ; latest development -; latest release with WiFi_secure.h -platform = https://github.com/tasmota/platform-espressif32/releases/download/2023.10.03/platform-espressif32-2023.10.03.zip ; latest stable -; latest arduino 2.xx release: -; platform = https://github.com/tasmota/platform-espressif32/releases/download/2023.11.01/platform-espressif32.zip -; latest arduino 3.0/IDF 5.1.(alpha 3): -; platform = https://github.com/tasmota/platform-espressif32/releases/download/2023.12.10/platform-espressif32.zip +platform = https://github.com/tasmota/platform-espressif32/releases/download/2023.10.03/platform-espressif32-2023.10.03.zip framework = arduino board_build.filesystem = littlefs -build_flags = - ${common.build_flags} - -DTASMOTA_SDK +build_flags = + ${common.build_flags} + -DTASMOTA_SDK build_unflags = ${common.unbuild_flags} -extra_scripts = - pre:scripts/build_interface.py - scripts/rename_fw.py +extra_scripts = + pre:scripts/build_interface.py + scripts/rename_fw.py [env] monitor_speed = 115200 @@ -73,16 +62,12 @@ monitor_filters = esp32_exception_decoder upload_speed = 921600 build_type = release lib_ldf_mode = chain+ -; board_build.flash_mode = qio - check_tool = cppcheck, clangtidy check_severity = high, medium -check_flags = - cppcheck: --std=c++11 -v - clangtidy: --checks=-*,clang-analyzer-*,performance-* +check_flags = + cppcheck: --std=c++11 -v + clangtidy: --checks=-*,clang-analyzer-*,performance-* -; build for GitHub Actions CI -; the Web interface is built seperately [env:ci] extends = espressi32_base_tasmota extra_scripts = scripts/rename_fw.py @@ -98,19 +83,19 @@ board_build.f_cpu = 240000000L board_upload.flash_size = 16MB board_build.partitions = esp32_partition_16M.csv build_unflags = ${common.unbuild_flags} -build_flags = - ${espressi32_base.build_flags} - -O2 - '-DEMSESP_DEFAULT_BOARD_PROFILE="S32S3"' +build_flags = + ${espressi32_base.build_flags} + -O2 + '-DEMSESP_DEFAULT_BOARD_PROFILE="S32S3"' [env:esp32_4M] extends = espressi32_base_tasmota board = esp32dev board_upload.flash_size = 4MB board_build.partitions = esp32_partition_4M.csv -build_flags = - ${espressi32_base_tasmota.build_flags} - -Os +build_flags = + ${espressi32_base_tasmota.build_flags} + -Os [env:esp32_4Mplus] extends = espressi32_base_tasmota @@ -130,30 +115,28 @@ extends = espressi32_base_tasmota board = lolin_c3_mini board_upload.flash_size = 4MB board_build.partitions = esp32_partition_4M.csv -build_flags = - ${espressi32_base_tasmota.build_flags} - '-DEMSESP_DEFAULT_BOARD_PROFILE="C3MINI"' +build_flags = + ${espressi32_base_tasmota.build_flags} + '-DEMSESP_DEFAULT_BOARD_PROFILE="C3MINI"' -; lolin C3 mini v1 needs special wifi init. -; https://www.wemos.cc/en/latest/c3/c3_mini_1_0_0.html#about-wifi [env:lolin_c3_mini_v1] extends = espressi32_base_tasmota board = lolin_c3_mini board_upload.flash_size = 4MB board_build.partitions = esp32_partition_4M.csv -build_flags = - ${espressi32_base_tasmota.build_flags} - -DBOARD_C3_MINI_V1 - '-DEMSESP_DEFAULT_BOARD_PROFILE="C3MINI"' +build_flags = + ${espressi32_base_tasmota.build_flags} + -DBOARD_C3_MINI_V1 + '-DEMSESP_DEFAULT_BOARD_PROFILE="C3MINI"' [env:lolin_s2_mini] extends = espressi32_base_tasmota board = lolin_s2_mini board_upload.flash_size = 4MB board_build.partitions = esp32_partition_4M.csv -build_flags = - ${espressi32_base_tasmota.build_flags} - '-DEMSESP_DEFAULT_BOARD_PROFILE="S2MINI"' +build_flags = + ${espressi32_base_tasmota.build_flags} + '-DEMSESP_DEFAULT_BOARD_PROFILE="S2MINI"' [env:lolin_s3] extends = espressi32_base @@ -163,91 +146,110 @@ board_upload.flash_size = 16MB board_build.partitions = esp32_partition_16M.csv board_upload.use_1200bps_touch = false board_upload.wait_for_upload_port = false -build_flags = - ${espressi32_base.build_flags} - -O2 - '-DEMSESP_DEFAULT_BOARD_PROFILE="S32S3"' +build_flags = + ${espressi32_base.build_flags} + -O2 + '-DEMSESP_DEFAULT_BOARD_PROFILE="S32S3"' [env:https] -; use Tasmota's libary which removes some libs (like mbedtsl) and increases available heap -; platform = https://github.com/tasmota/platform-espressif32.git ; latest development -; -; latest release with WiFi_secure.h -; platform = https://github.com/tasmota/platform-espressif32/releases/download/2023.10.03/platform-espressif32-2023.10.03.zip ; latest stable -; -; latest arduino 2.xx release: -; platform = https://github.com/tasmota/platform-espressif32/releases/download/2023.11.01/platform-espressif32.zip -; -; latest arduino 3.0/IDF 5.1.(alpha 3): -; platform = https://github.com/platformio/platform-espressif32.git -; platform = https://github.com/Jason2866/platform-espressif32.git#Arduino/IDF5 -; platform_packages = https://github.com/espressif/arduino-esp32.git#3.0.0-alpha2 -platform = espressif32@6.5.0 +platform = espressif32 framework = arduino board = esp32dev board_build.filesystem = littlefs board_build.f_cpu = 240000000L board_upload.flash_size = 4MB board_build.partitions = esp32_partition_4M.csv -; board_upload.flash_size = 16MB -; board_build.partitions = esp32_partition_16M.csv board_upload.use_1200bps_touch = false board_upload.wait_for_upload_port = true upload_port = /dev/ttyUSB0 -extra_scripts = - ; pre:scripts/build_interface.py - scripts/rename_fw.py +extra_scripts = + pre:scripts/build_interface.py + scripts/rename_fw.py build_unflags = ${common.unbuild_flags} -build_flags = - ${common.core_build_flags} - ${factory_settings.build_flags} - ${common.my_build_flags} - -D ONEWIRE_CRC16=0 - -D NO_GLOBAL_ARDUINOOTA - -D ARDUINOJSON_ENABLE_STD_STRING=1 - -D ARDUINOJSON_USE_DOUBLE=0 - ; -D ARDUINOTRACE_ENABLE=1 - -D ARDUINOTRACE_ENABLE=0 - -D TASMOTA_SDK - ; -D CORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_WARN - -D EMSESP_TEST - -D EMSESP_DEBUG - -D CONFIG_ETH_ENABLED - ; -D BOARD_HAS_PSRAM - '-DEMSESP_DEFAULT_BOARD_PROFILE="Test"' +build_flags = + ${common.core_build_flags} + ${factory_settings.build_flags} + ${common.my_build_flags} + -D ONEWIRE_CRC16=0 + -D NO_GLOBAL_ARDUINOOTA + -D ARDUINOJSON_ENABLE_STD_STRING=1 + -D ARDUINOJSON_USE_DOUBLE=0 + -D ARDUINOTRACE_ENABLE=1 + -D TASMOTA_SDK + -D CORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_WARN + -D EMSESP_TEST + -D EMSESP_DEBUG + -D CONFIG_ETH_ENABLED + '-DEMSESP_DEFAULT_BOARD_PROFILE="Test"' -; to build and run: pio run -e standalone -t exec [env:standalone] platform = native -build_flags = - -DARDUINOJSON_ENABLE_STD_STRING=1 -DARDUINOJSON_ENABLE_PROGMEM=1 -DARDUINOJSON_ENABLE_ARDUINO_STRING -DARDUINOJSON_USE_DOUBLE=0 - -DEMSESP_DEBUG -DEMSESP_STANDALONE -DEMSESP_TEST -D__linux__ - -DEMSESP_DEFAULT_LOCALE=\"en\" -DEMSESP_DEFAULT_TX_MODE=8 -DEMSESP_DEFAULT_VERSION=\"3.6.4-dev\" -DEMSESP_DEFAULT_BOARD_PROFILE=\"S32\" +build_flags = + -DARDUINOJSON_ENABLE_STD_STRING=1 -DARDUINOJSON_ENABLE_PROGMEM=1 -DARDUINOJSON_ENABLE_ARDUINO_STRING -DARDUINOJSON_USE_DOUBLE=0 + -DEMSESP_DEBUG -DEMSESP_STANDALONE -DEMSESP_TEST -D__linux__ + -DEMSESP_DEFAULT_LOCALE=\"en\" -DEMSESP_DEFAULT_TX_MODE=8 -DEMSESP_DEFAULT_VERSION=\"3.6.4-dev\" -DEMSESP_DEFAULT_BOARD_PROFILE=\"S32\" -lpthread - -std=gnu++11 -Og -ggdb -build_src_flags = - ; -Wall -Wextra -Werror -Wswitch-enum -Wno-unused-parameter -Wno-inconsistent-missing-override -Wno-unused-lambda-capture -Wno-sign-compare - ; -Wall -Wextra -Werror - -Wno-missing-braces - -I./lib_standalone - -I./lib/ArduinoJson/src - -I./lib/uuid-common/src - -I./lib/uuid-console/src - -I./lib/uuid-log/src - -I./lib/semver - -I./lib/PButton - -I./lib/espMqttClient/src - -I./lib/espMqttClient/src/Transport -build_src_filter = + -std=gnu++11 -Og -ggdb +build_src_flags = + -Wno-missing-braces + -I./lib_standalone + -I./lib/ArduinoJson/src + -I./lib/uuid-common/src + -I./lib/uuid-console/src + -I./lib/uuid-log/src + -I./lib/semver + -I./lib/PButton + -I./lib/espMqttClient/src + -I./lib/espMqttClient/src/Transport +build_src_filter = +<*> - -<.git/> - +<../lib_standalone> - +<../lib/uuid-common> - +<../lib/uuid-console> - +<../lib/uuid-log> - +<../lib/semver> - +<../lib/PButton> - +<../lib/espMqttClient/src> - +<../lib/espMqttClient/src/Transport> + -<.git/> + +<../lib_standalone> + +<../lib/uuid-common> + +<../lib/uuid-console> + +<../lib/uuid-log> + +<../lib/semver> + +<../lib/PButton> + +<../lib/espMqttClient/src> + +<../lib/espMqttClient/src/Transport> lib_compat_mode = off -lib_ldf_mode = off \ No newline at end of file +lib_ldf_mode = off + +[factory_settings] +build_flags = + -D FACTORY_WIFI_SSID=\"\" + -D FACTORY_WIFI_PASSWORD=\"\" + -D FACTORY_WIFI_HOSTNAME=\"ems-esp\" + + -D FACTORY_AP_PROVISION_MODE=AP_MODE_DISCONNECTED + -D FACTORY_AP_SSID=\"ems-esp\" + -D FACTORY_AP_PASSWORD=\"ems-esp-neo\" + -D FACTORY_AP_LOCAL_IP=\"192.168.4.1\" + -D FACTORY_AP_GATEWAY_IP=\"192.168.4.1\" + -D FACTORY_AP_SUBNET_MASK=\"255.255.255.0\" + + -D FACTORY_ADMIN_USERNAME=\"admin\" + -D FACTORY_ADMIN_PASSWORD=\"admin\" + -D FACTORY_GUEST_USERNAME=\"guest\" + -D FACTORY_GUEST_PASSWORD=\"guest\" + + -D FACTORY_NTP_ENABLED=false + -D FACTORY_NTP_TIME_ZONE_LABEL=\"Europe/Amsterdam\" + -D FACTORY_NTP_TIME_ZONE_FORMAT=\"CET-1CEST,M3.5.0,M10.5.0/3\" + -D FACTORY_NTP_SERVER=\"time.google.com\" + + -D FACTORY_OTA_PORT=8266 + -D FACTORY_OTA_PASSWORD=\"ems-esp-neo\" + -D FACTORY_OTA_ENABLED=false + + -D FACTORY_MQTT_ENABLED=false + -D FACTORY_MQTT_HOST=\"\" + -D FACTORY_MQTT_PORT=1883 + -D FACTORY_MQTT_USERNAME=\"\" + -D FACTORY_MQTT_PASSWORD=\"\" + -D FACTORY_MQTT_CLIENT_ID=\"ems-esp\" + -D FACTORY_MQTT_KEEP_ALIVE=60 + -D FACTORY_MQTT_CLEAN_SESSION=false + -D FACTORY_MQTT_MAX_TOPIC_LENGTH=128 + + -D FACTORY_JWT_SECRET=\"ems-esp-neo\" diff --git a/src/emsesp.cpp b/src/emsesp.cpp index 6054adf92..cdb4c9ebc 100644 --- a/src/emsesp.cpp +++ b/src/emsesp.cpp @@ -1405,24 +1405,26 @@ EMSESP::EMSESP() } // add web server endpoint -void EMSESP::handler(const char * uri, const char * contentType, const uint8_t * content, size_t len) { +void EMSESP::handler(const String & uri, const String & contentType, const uint8_t * content, size_t len) { PsychicHttpRequestCallback fn = [contentType, content, len](PsychicRequest * request) { PsychicResponse response(request); response.setCode(200); - response.setContentType(contentType); - response.addHeader("Content-Encoding", "gzip"); + response.setContentType(contentType.c_str()); // response.addHeader("Content-Encoding", "br"); // Brotli - only works over HTTPS + response.addHeader("Content-Encoding", "gzip"); + response.addHeader("Cache-Control", "public, immutable, max-age=31536000"); response.setContent(content, len); return response.send(); }; PsychicWebHandler * handler = new PsychicWebHandler(); handler->onRequest(fn); - webServer.on(uri, HTTP_GET, handler); + webServer.on(uri.c_str(), HTTP_GET, handler); // Set default end-point for all non matching requests // this is easier than using webServer.onNotFound() - if (strcmp(uri, "/index.html") == 0) { + if (uri.equals("/index.html")) { + // if (strcmp(uri, "/index.html") == 0) { webServer.defaultEndpoint->setHandler(handler); } }; @@ -1435,14 +1437,43 @@ void EMSESP::setupWeb() { // esp8266React services has 13 // custom projects has around 23 webServer.config.max_uri_handlers = 80; - // webServer.config.uri_match_fn = NULL; // don't use wildcards + /* + #define HTTPD_DEFAULT_CONFIG() { \ + .task_priority = tskIDLE_PRIORITY+5, \ + .stack_size = 4096, \ + .core_id = tskNO_AFFINITY, \ + .server_port = 80, \ + .ctrl_port = 32768, \ + .max_open_sockets = 7, \ + .max_uri_handlers = 8, \ + .max_resp_headers = 8, \ + .backlog_conn = 5, \ + .lru_purge_enable = false, \ + .recv_wait_timeout = 5, \ + .send_wait_timeout = 5, \ + .global_user_ctx = NULL, \ + .global_user_ctx_free_fn = NULL, \ + .global_transport_ctx = NULL, \ + .global_transport_ctx_free_fn = NULL, \ + .enable_so_linger = false, \ + .linger_timeout = 0, \ + .open_fn = NULL, \ + .close_fn = NULL, \ + .uri_match_fn = NULL \ + */ + + // TODO remove experimental stuff + // webServer.config.uri_match_fn = NULL; // don't use wildcards + // webServer.config.stack_size = 8192; // default is 4096, we had 2x8192 in AsyncTCP + // webServer.config.max_open_sockets = 3; // webServer.config.task_priority = uxTaskPriorityGet(nullptr); // seems to make it slightly slower + // webServer.config.task_priority = 5; // same as AsyncTCP, but looks like even slower + // webServer.config.lru_purge_enable = true; // makes no diff. is as in https://github.com/espressif/esp-idf/blob/master/examples/protocols/http_server/simple/main/main.c // TODO add support for https webServer.listen(80); // start the web server - DefaultHeaders::Instance().addHeader("Server", "EMS-ESP"); #ifndef EMSESP_STANDALONE WWWData::registerRoutes(handler); // add webServer.on() endpoints from the generated web code @@ -1562,6 +1593,9 @@ void EMSESP::start() { analogsensor_.start(); // Analog external sensors webLogService.start(); // apply settings to weblog service + // set hostname on web server + DefaultHeaders::Instance().addHeader("Server", system_.hostname().c_str()); // TODO use hostname + // Load our library of known devices into stack mem. Names are stored in Flash memory device_library_ = { #include "device_library.h" diff --git a/src/emsesp.h b/src/emsesp.h index ea02110b6..80c2e6859 100644 --- a/src/emsesp.h +++ b/src/emsesp.h @@ -241,7 +241,8 @@ class EMSESP { static WebSchedulerService webSchedulerService; static WebCustomEntityService webCustomEntityService; - static void handler(const char * uri, const char * contentType, const uint8_t * content, size_t len); + static void handler(const String & uri, const String & contentType, const uint8_t * content, size_t len); + // static void handler(const char * uri, const char * contentType, const uint8_t * content, size_t len); private: static std::string device_tostring(const uint8_t device_id); diff --git a/src/version.h b/src/version.h index 986808d84..38b8b3d1e 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define EMSESP_APP_VERSION "3.6.5-https.1" +#define EMSESP_APP_VERSION "3.6.5-https.2" diff --git a/src/web/WebLogService.cpp b/src/web/WebLogService.cpp index 291eefa40..facb1f12c 100644 --- a/src/web/WebLogService.cpp +++ b/src/web/WebLogService.cpp @@ -23,27 +23,19 @@ using namespace std::placeholders; namespace emsesp { WebLogService::WebLogService(PsychicHttpServer * server, SecurityManager * securityManager) - // TODO fix event source - : // : _events(EVENT_SOURCE_LOG_PATH) - _server(server) + : _server(server) , _securityManager(securityManager) { } - void WebLogService::registerURI() { - // TODO fix event source - /* - _events_.onOpen([](PsychicEventSourceClient * client) { - Serial.printf("[eventsource] connection #%u connected from %s\n", client->socket(), client->remoteIP().toString()); - client->send("Hello user!", NULL, millis(), 1000); - }); - _events_.onClose([](PsychicEventSourceClient * client) { - Serial.printf("[eventsource] connection #%u closed from %s\n", client->socket(), client->remoteIP().toString()); - }); - _server->on(EVENT_SOURCE_LOG_PATH, &events_); - */ + // eventsource + _server->on(EVENT_SOURCE_LOG_PATH, &_events); - // post + // TODO this doesn't work with PsychicEventSource + // _server->on(EVENT_SOURCE_LOG_PATH, &_events)->setFilter(_securityManager->filterRequest(AuthenticationPredicates::IS_ADMIN)); + // _events.setFilter(_securityManager->filterRequest(AuthenticationPredicates::IS_ADMIN)); + + // POST _server->on(LOG_SETTINGS_PATH, HTTP_POST, [this](PsychicRequest * request, JsonVariant & json) { auto && body = json.as(); @@ -59,9 +51,7 @@ void WebLogService::registerURI() { return request->reply(200); // OK }); - _events.setFilter(_securityManager->filterRequest(AuthenticationPredicates::IS_ADMIN)); - - // get settings + // GET settings _server->on(LOG_SETTINGS_PATH, HTTP_GET, [this](PsychicRequest * request) { PsychicJsonResponse response = PsychicJsonResponse(request, false, EMSESP_JSON_SIZE_SMALL); JsonObject root = response.getRoot(); @@ -72,6 +62,7 @@ void WebLogService::registerURI() { return response.send(); }); + // /rest/fetchLog // for bring back the whole log - is a command, hence a POST // send the complete log buffer to the API, not filtering on log level // done by resetting the pointer @@ -79,9 +70,6 @@ void WebLogService::registerURI() { log_message_id_tail_ = 0; return request->reply(200); }); - - // TODO this can be removed when ported over - // server->addHandler(&events_); } // start the log service with INFO level @@ -232,9 +220,9 @@ char * WebLogService::messagetime(char * out, const uint64_t t, const size_t buf // send to web eventsource void WebLogService::transmit(const QueuedLogMessage & message) { DynamicJsonDocument jsonDocument(EMSESP_JSON_SIZE_LARGE); - if (jsonDocument.capacity() == 0) { - return; - } + // if (jsonDocument.capacity() == 0) { + // return; + // } JsonObject logEvent = jsonDocument.to(); char time_string[25]; @@ -244,47 +232,13 @@ void WebLogService::transmit(const QueuedLogMessage & message) { logEvent["n"] = message.content_->name; logEvent["m"] = message.content_->text; - size_t len = measureJson(jsonDocument); - char * buffer = new char[len + 1]; - if (buffer) { - serializeJson(jsonDocument, buffer, len + 1); + size_t len = measureJson(jsonDocument) + 1; + char * buffer = (char *)malloc(len); + if (buffer != NULL) { + serializeJson(jsonDocument, buffer, len); _events.send(buffer, "message", message.id_); } - delete[] buffer; + free(buffer); } -// send the complete log buffer to the API, not filtering on log level -// done by resetting the pointer -// esp_err_t WebLogService::fetchLog(PsychicRequest * request) { -// log_message_id_tail_ = 0; -// request->send(200); -// } - -// return the current value settings after a GET -// esp_err_t WebLogService::getValues(PsychicRequest * request) { -// auto * response = new AsyncJsonResponse(false, EMSESP_JSON_SIZE_SMALL); -// JsonObject root = response->getRoot(); -// root["level"] = log_level(); -// root["max_messages"] = maximum_log_messages(); -// root["compact"] = compact(); -// response->setLength(); -// request->send(response); -// } - -// sets the values like level after a POST -// esp_err_t WebLogService::setValues(PsychicRequest * request, JsonVariant & json) { -// auto && body = json.as(); - -// uuid::log::Level level = body["level"]; -// log_level(level); - -// uint8_t max_messages = body["max_messages"]; -// maximum_log_messages(max_messages); - -// bool comp = body["compact"]; -// compact(comp); - -// return request->reply(200); // OK -// } - -} // namespace emsesp \ No newline at end of file +} // namespace emsesp diff --git a/src/web/WebLogService.h b/src/web/WebLogService.h index 66913425e..36856eff2 100644 --- a/src/web/WebLogService.h +++ b/src/web/WebLogService.h @@ -50,8 +50,7 @@ class WebLogService : public uuid::log::Handler { private: SecurityManager * _securityManager; PsychicHttpServer * _server; - - PsychicEventSource _events; + PsychicEventSource _events; class QueuedLogMessage { public: