mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-06 07:49:52 +03:00
Merge pull request #1533 from proddy/https_36
keeping branch update. still memory and performance issues
This commit is contained in:
@@ -52,8 +52,8 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@preact/compat": "^17.1.2",
|
"@preact/compat": "^17.1.2",
|
||||||
"@preact/preset-vite": "^2.7.0",
|
"@preact/preset-vite": "^2.7.0",
|
||||||
"@typescript-eslint/eslint-plugin": "^6.16.0",
|
"@typescript-eslint/eslint-plugin": "^6.17.0",
|
||||||
"@typescript-eslint/parser": "^6.16.0",
|
"@typescript-eslint/parser": "^6.17.0",
|
||||||
"concurrently": "^8.2.2",
|
"concurrently": "^8.2.2",
|
||||||
"eslint": "^8.56.0",
|
"eslint": "^8.56.0",
|
||||||
"eslint-config-airbnb": "^19.0.4",
|
"eslint-config-airbnb": "^19.0.4",
|
||||||
|
|||||||
@@ -11,7 +11,8 @@ const bytesPerLine = 20;
|
|||||||
var totalSize = 0;
|
var totalSize = 0;
|
||||||
|
|
||||||
const generateWWWClass = () =>
|
const generateWWWClass = () =>
|
||||||
`typedef std::function<void(const char *, const char * contentType, const uint8_t * content, size_t len)> RouteRegistrationHandler;
|
`typedef std::function<void(const String &, const String & contentType, const uint8_t * content, size_t len)> RouteRegistrationHandler;
|
||||||
|
// Total size is ${totalSize} bytes
|
||||||
|
|
||||||
class WWWData {
|
class WWWData {
|
||||||
${indent}public:
|
${indent}public:
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
/*
|
||||||
import { debounce } from 'lodash-es';
|
import { debounce } from 'lodash-es';
|
||||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||||
import Sockette from 'sockette';
|
import Sockette from 'sockette';
|
||||||
@@ -89,3 +90,4 @@ export const useWs = <D>(wsUrl: string, wsThrottle = 100) => {
|
|||||||
|
|
||||||
return { connected, data, updateData } as const;
|
return { connected, data, updateData } as const;
|
||||||
};
|
};
|
||||||
|
*/
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { defineConfig } from 'vite';
|
import { defineConfig, splitVendorChunkPlugin } from 'vite';
|
||||||
import viteTsconfigPaths from 'vite-tsconfig-paths';
|
import viteTsconfigPaths from 'vite-tsconfig-paths';
|
||||||
import preact from '@preact/preset-vite';
|
import preact from '@preact/preset-vite';
|
||||||
import viteImagemin from 'vite-plugin-imagemin';
|
import viteImagemin from 'vite-plugin-imagemin';
|
||||||
@@ -44,6 +44,7 @@ export default defineConfig(({ command, mode }) => {
|
|||||||
plugins: [
|
plugins: [
|
||||||
preact(),
|
preact(),
|
||||||
viteTsconfigPaths(),
|
viteTsconfigPaths(),
|
||||||
|
splitVendorChunkPlugin(),
|
||||||
{
|
{
|
||||||
...viteImagemin({
|
...viteImagemin({
|
||||||
verbose: false,
|
verbose: false,
|
||||||
@@ -112,6 +113,20 @@ export default defineConfig(({ command, mode }) => {
|
|||||||
nameCache: null,
|
nameCache: null,
|
||||||
safari10: false,
|
safari10: false,
|
||||||
toplevel: 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';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1689,15 +1689,15 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@typescript-eslint/eslint-plugin@npm:^6.16.0":
|
"@typescript-eslint/eslint-plugin@npm:^6.17.0":
|
||||||
version: 6.16.0
|
version: 6.17.0
|
||||||
resolution: "@typescript-eslint/eslint-plugin@npm:6.16.0"
|
resolution: "@typescript-eslint/eslint-plugin@npm:6.17.0"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@eslint-community/regexpp": "npm:^4.5.1"
|
"@eslint-community/regexpp": "npm:^4.5.1"
|
||||||
"@typescript-eslint/scope-manager": "npm:6.16.0"
|
"@typescript-eslint/scope-manager": "npm:6.17.0"
|
||||||
"@typescript-eslint/type-utils": "npm:6.16.0"
|
"@typescript-eslint/type-utils": "npm:6.17.0"
|
||||||
"@typescript-eslint/utils": "npm:6.16.0"
|
"@typescript-eslint/utils": "npm:6.17.0"
|
||||||
"@typescript-eslint/visitor-keys": "npm:6.16.0"
|
"@typescript-eslint/visitor-keys": "npm:6.17.0"
|
||||||
debug: "npm:^4.3.4"
|
debug: "npm:^4.3.4"
|
||||||
graphemer: "npm:^1.4.0"
|
graphemer: "npm:^1.4.0"
|
||||||
ignore: "npm:^5.2.4"
|
ignore: "npm:^5.2.4"
|
||||||
@@ -1710,44 +1710,44 @@ __metadata:
|
|||||||
peerDependenciesMeta:
|
peerDependenciesMeta:
|
||||||
typescript:
|
typescript:
|
||||||
optional: true
|
optional: true
|
||||||
checksum: 4bedce948ac3c20492a59813ee5d4f1f2306310857864dfaac2736f6c38e18785002c36844fd64c9fbdf3059fc390b29412be105fd7a118177f1eeeb1eb533f7
|
checksum: f2a5774e9cc03e491a5a488501e5622c7eebd766f5a4fc2c30642864a3b89b0807946bde33a678f326ba7032f3f6a51aa0bf9c2d10adc823804fc9fb47db55a6
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@typescript-eslint/parser@npm:^6.16.0":
|
"@typescript-eslint/parser@npm:^6.17.0":
|
||||||
version: 6.16.0
|
version: 6.17.0
|
||||||
resolution: "@typescript-eslint/parser@npm:6.16.0"
|
resolution: "@typescript-eslint/parser@npm:6.17.0"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@typescript-eslint/scope-manager": "npm:6.16.0"
|
"@typescript-eslint/scope-manager": "npm:6.17.0"
|
||||||
"@typescript-eslint/types": "npm:6.16.0"
|
"@typescript-eslint/types": "npm:6.17.0"
|
||||||
"@typescript-eslint/typescript-estree": "npm:6.16.0"
|
"@typescript-eslint/typescript-estree": "npm:6.17.0"
|
||||||
"@typescript-eslint/visitor-keys": "npm:6.16.0"
|
"@typescript-eslint/visitor-keys": "npm:6.17.0"
|
||||||
debug: "npm:^4.3.4"
|
debug: "npm:^4.3.4"
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
eslint: ^7.0.0 || ^8.0.0
|
eslint: ^7.0.0 || ^8.0.0
|
||||||
peerDependenciesMeta:
|
peerDependenciesMeta:
|
||||||
typescript:
|
typescript:
|
||||||
optional: true
|
optional: true
|
||||||
checksum: 3d941ce345dc2ce29957e2110957662873d514b094b8939923c3281d858c11cd1f9058db862644afe14f68d087770f39a0a1f9e523a2013ed5d2fdf3421b34d0
|
checksum: 2ed0ed4a5b30e953430ce3279df3655af09fa1caed2abf11804d239717daefc32a22864f6620ef57bb9c684c74a99a13241384fea5096e961385e3678fc2e920
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@typescript-eslint/scope-manager@npm:6.16.0":
|
"@typescript-eslint/scope-manager@npm:6.17.0":
|
||||||
version: 6.16.0
|
version: 6.17.0
|
||||||
resolution: "@typescript-eslint/scope-manager@npm:6.16.0"
|
resolution: "@typescript-eslint/scope-manager@npm:6.17.0"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@typescript-eslint/types": "npm:6.16.0"
|
"@typescript-eslint/types": "npm:6.17.0"
|
||||||
"@typescript-eslint/visitor-keys": "npm:6.16.0"
|
"@typescript-eslint/visitor-keys": "npm:6.17.0"
|
||||||
checksum: 3360aae4b85f5c31d20ad48d771cc09a6f8f6b1811b00d94f06e55b5a09c610ac75631b1c4edecb3bec682d41351b87e7d14d42bee84aa032064d0e13463035b
|
checksum: fe09c628553c9336e6a36d32c1d34e78ebd20aa02130a6bf535329621ba5a98aaac171f607bc6e4d17b3478c42e7de6476376636897ce3f227c754eb99acd07e
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@typescript-eslint/type-utils@npm:6.16.0":
|
"@typescript-eslint/type-utils@npm:6.17.0":
|
||||||
version: 6.16.0
|
version: 6.17.0
|
||||||
resolution: "@typescript-eslint/type-utils@npm:6.16.0"
|
resolution: "@typescript-eslint/type-utils@npm:6.17.0"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@typescript-eslint/typescript-estree": "npm:6.16.0"
|
"@typescript-eslint/typescript-estree": "npm:6.17.0"
|
||||||
"@typescript-eslint/utils": "npm:6.16.0"
|
"@typescript-eslint/utils": "npm:6.17.0"
|
||||||
debug: "npm:^4.3.4"
|
debug: "npm:^4.3.4"
|
||||||
ts-api-utils: "npm:^1.0.1"
|
ts-api-utils: "npm:^1.0.1"
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@@ -1755,23 +1755,23 @@ __metadata:
|
|||||||
peerDependenciesMeta:
|
peerDependenciesMeta:
|
||||||
typescript:
|
typescript:
|
||||||
optional: true
|
optional: true
|
||||||
checksum: 5964b87a87252bed278a248eb568902babd7c34defd3af8c3df371926d96aec716f33f1dc14bde170e93f73ed1b0af6e591e647853d0f33f378e2c7b3b73fc5b
|
checksum: dc7938429193acfda61b7282197ec046039e2c4da41cdcddf4daaf300d10229e4e23bb0fcf0503b19c0b99a874849c8a9f5bb35ce106260f56a14106d2b41d8c
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@typescript-eslint/types@npm:6.16.0":
|
"@typescript-eslint/types@npm:6.17.0":
|
||||||
version: 6.16.0
|
version: 6.17.0
|
||||||
resolution: "@typescript-eslint/types@npm:6.16.0"
|
resolution: "@typescript-eslint/types@npm:6.17.0"
|
||||||
checksum: 236ca318c2440c95068e5d4d147e2bfed62447775e18695e21c8ca04a341a74d01c37ed2b417629b7bf2fb91ad4fd5e2a6570215d16fc24dd1507ce6973b4e22
|
checksum: 87ab1b5a3270ab34b917c22a2fb90a9ad7d9f3b19d73a337bc9efbe65f924da13482c97e8ccbe3bd3d081aa96039eeff50de41c1da2a2128066429b931cdb21d
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@typescript-eslint/typescript-estree@npm:6.16.0":
|
"@typescript-eslint/typescript-estree@npm:6.17.0":
|
||||||
version: 6.16.0
|
version: 6.17.0
|
||||||
resolution: "@typescript-eslint/typescript-estree@npm:6.16.0"
|
resolution: "@typescript-eslint/typescript-estree@npm:6.17.0"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@typescript-eslint/types": "npm:6.16.0"
|
"@typescript-eslint/types": "npm:6.17.0"
|
||||||
"@typescript-eslint/visitor-keys": "npm:6.16.0"
|
"@typescript-eslint/visitor-keys": "npm:6.17.0"
|
||||||
debug: "npm:^4.3.4"
|
debug: "npm:^4.3.4"
|
||||||
globby: "npm:^11.1.0"
|
globby: "npm:^11.1.0"
|
||||||
is-glob: "npm:^4.0.3"
|
is-glob: "npm:^4.0.3"
|
||||||
@@ -1781,34 +1781,34 @@ __metadata:
|
|||||||
peerDependenciesMeta:
|
peerDependenciesMeta:
|
||||||
typescript:
|
typescript:
|
||||||
optional: true
|
optional: true
|
||||||
checksum: 8e1ef03ecabaf3791b11240a51217836dbb74850e458258db77ac5eab5508cd9c63fb671924993d1e7654718c0c857c3550d51ecba0845fe489d143bb858e1b1
|
checksum: 1671b0d2f2fdf07074fb1e2524d61935cec173bd8db6e482cc5b2dcc77aed3ffa831396736ffa0ee2fdbddd8585ae9ca8d6c97bcaea1385b23907a1ec0508f83
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@typescript-eslint/utils@npm:6.16.0":
|
"@typescript-eslint/utils@npm:6.17.0":
|
||||||
version: 6.16.0
|
version: 6.17.0
|
||||||
resolution: "@typescript-eslint/utils@npm:6.16.0"
|
resolution: "@typescript-eslint/utils@npm:6.17.0"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@eslint-community/eslint-utils": "npm:^4.4.0"
|
"@eslint-community/eslint-utils": "npm:^4.4.0"
|
||||||
"@types/json-schema": "npm:^7.0.12"
|
"@types/json-schema": "npm:^7.0.12"
|
||||||
"@types/semver": "npm:^7.5.0"
|
"@types/semver": "npm:^7.5.0"
|
||||||
"@typescript-eslint/scope-manager": "npm:6.16.0"
|
"@typescript-eslint/scope-manager": "npm:6.17.0"
|
||||||
"@typescript-eslint/types": "npm:6.16.0"
|
"@typescript-eslint/types": "npm:6.17.0"
|
||||||
"@typescript-eslint/typescript-estree": "npm:6.16.0"
|
"@typescript-eslint/typescript-estree": "npm:6.17.0"
|
||||||
semver: "npm:^7.5.4"
|
semver: "npm:^7.5.4"
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
eslint: ^7.0.0 || ^8.0.0
|
eslint: ^7.0.0 || ^8.0.0
|
||||||
checksum: 84dd02f7c8e47fae699cc222da5cbea08b28c6e1cc7827860430bc86c2a17ee3f86e198a4356902b95930f85785aa662266ea9c476f69bf80c6a5f648e55f9f4
|
checksum: 37c63afcf66124bf84808699997953b8c84a378aa2c490a771b611d82cdac8499c58fac8eeb8378528e97660b59563d99297bfec4b982cd68760b0ffe54aa714
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@typescript-eslint/visitor-keys@npm:6.16.0":
|
"@typescript-eslint/visitor-keys@npm:6.17.0":
|
||||||
version: 6.16.0
|
version: 6.17.0
|
||||||
resolution: "@typescript-eslint/visitor-keys@npm:6.16.0"
|
resolution: "@typescript-eslint/visitor-keys@npm:6.17.0"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@typescript-eslint/types": "npm:6.16.0"
|
"@typescript-eslint/types": "npm:6.17.0"
|
||||||
eslint-visitor-keys: "npm:^3.4.1"
|
eslint-visitor-keys: "npm:^3.4.1"
|
||||||
checksum: 19e559f14ea0092585a374b8c5f1aca9b6b271fc23909d9857de9cf71a1e1d3abc0afd237e9c02d7a5fbdfe8e3be7853cf9fedf40a6f16bac3495cb7f4e67982
|
checksum: a2aed0e1437fdab8858ab9c7c8e355f8b72a5fa4b0adc54f28b8a2bbc29d4bb93214968ee940f83d013d0a4b83d00cd4eeeb05fb4c2c7d0ead324c6793f7d6d4
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
@@ -1838,8 +1838,8 @@ __metadata:
|
|||||||
"@types/react": "npm:^18.2.46"
|
"@types/react": "npm:^18.2.46"
|
||||||
"@types/react-dom": "npm:^18.2.18"
|
"@types/react-dom": "npm:^18.2.18"
|
||||||
"@types/react-router-dom": "npm:^5.3.3"
|
"@types/react-router-dom": "npm:^5.3.3"
|
||||||
"@typescript-eslint/eslint-plugin": "npm:^6.16.0"
|
"@typescript-eslint/eslint-plugin": "npm:^6.17.0"
|
||||||
"@typescript-eslint/parser": "npm:^6.16.0"
|
"@typescript-eslint/parser": "npm:^6.17.0"
|
||||||
alova: "npm:^2.16.2"
|
alova: "npm:^2.16.2"
|
||||||
async-validator: "npm:^4.2.5"
|
async-validator: "npm:^4.2.5"
|
||||||
concurrently: "npm:^8.2.2"
|
concurrently: "npm:^8.2.2"
|
||||||
|
|||||||
@@ -1,51 +1,55 @@
|
|||||||
#ifndef ChunkPrinter_h
|
#ifndef ChunkPrinter_h
|
||||||
#define ChunkPrinter_h
|
#define ChunkPrinter_h
|
||||||
|
|
||||||
#include "PsychicCore.h"
|
// #include "PsychicCore.h"
|
||||||
#include "PsychicResponse.h"
|
#include "PsychicResponse.h"
|
||||||
#include <Print.h>
|
#include <Print.h>
|
||||||
|
|
||||||
class ChunkPrinter : public Print {
|
class ChunkPrinter : public Print
|
||||||
|
{
|
||||||
private:
|
private:
|
||||||
PsychicResponse * _response;
|
PsychicResponse *_response;
|
||||||
uint8_t * _buffer;
|
uint8_t *_buffer;
|
||||||
size_t _length;
|
size_t _length;
|
||||||
size_t _pos;
|
size_t _pos;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ChunkPrinter(PsychicResponse * response, uint8_t * buffer, size_t len)
|
ChunkPrinter(PsychicResponse *response, uint8_t *buffer, size_t len) :
|
||||||
: _response(response)
|
_response(response),
|
||||||
, _buffer(buffer)
|
_buffer(buffer),
|
||||||
, _length(len)
|
_length(len),
|
||||||
, _pos(0) {
|
_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() {
|
virtual void flush() override
|
||||||
}
|
{
|
||||||
|
if (_pos)
|
||||||
size_t write(uint8_t c) {
|
{
|
||||||
esp_err_t err;
|
_response->sendChunk(_buffer, _pos);
|
||||||
|
_pos = 0;
|
||||||
_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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,67 +1,72 @@
|
|||||||
#include "PsychicClient.h"
|
#include "PsychicClient.h"
|
||||||
|
#include "PsychicHttpServer.h"
|
||||||
|
#include <lwip/sockets.h>
|
||||||
|
|
||||||
PsychicClient::PsychicClient(httpd_handle_t server, int socket)
|
PsychicClient::PsychicClient(httpd_handle_t server, int socket) :
|
||||||
: _server(server)
|
_server(server),
|
||||||
, _socket(socket)
|
_socket(socket),
|
||||||
, _friend(NULL)
|
_friend(NULL),
|
||||||
, isNew(false) {
|
isNew(false)
|
||||||
}
|
{}
|
||||||
|
|
||||||
PsychicClient::~PsychicClient() {
|
PsychicClient::~PsychicClient() {
|
||||||
}
|
}
|
||||||
|
|
||||||
httpd_handle_t PsychicClient::server() {
|
httpd_handle_t PsychicClient::server() {
|
||||||
return _server;
|
return _server;
|
||||||
}
|
}
|
||||||
|
|
||||||
int PsychicClient::socket() {
|
int PsychicClient::socket() {
|
||||||
return _socket;
|
return _socket;
|
||||||
}
|
}
|
||||||
|
|
||||||
// I'm not sure this is entirely safe to call. I was having issues with race conditions when highly loaded using this.
|
// 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 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 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 PsychicClient::localIP()
|
||||||
IPAddress address(0, 0, 0, 0);
|
{
|
||||||
|
IPAddress address(0,0,0,0);
|
||||||
|
|
||||||
char ipstr[INET6_ADDRSTRLEN];
|
char ipstr[INET6_ADDRSTRLEN];
|
||||||
struct sockaddr_in6 addr; // esp_http_server uses IPv6 addressing
|
struct sockaddr_in6 addr; // esp_http_server uses IPv6 addressing
|
||||||
socklen_t addr_size = sizeof(addr);
|
socklen_t addr_size = sizeof(addr);
|
||||||
|
|
||||||
if (getsockname(_socket, (struct sockaddr *)&addr, &addr_size) < 0) {
|
|
||||||
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);
|
|
||||||
|
|
||||||
|
if (getsockname(_socket, (struct sockaddr *)&addr, &addr_size) < 0) {
|
||||||
|
ESP_LOGE(PH_TAG, "Error getting client IP");
|
||||||
return address;
|
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 PsychicClient::remoteIP()
|
||||||
IPAddress address(0, 0, 0, 0);
|
{
|
||||||
|
IPAddress address(0,0,0,0);
|
||||||
|
|
||||||
char ipstr[INET6_ADDRSTRLEN];
|
char ipstr[INET6_ADDRSTRLEN];
|
||||||
struct sockaddr_in6 addr; // esp_http_server uses IPv6 addressing
|
struct sockaddr_in6 addr; // esp_http_server uses IPv6 addressing
|
||||||
socklen_t addr_size = sizeof(addr);
|
socklen_t addr_size = sizeof(addr);
|
||||||
|
|
||||||
if (getpeername(_socket, (struct sockaddr *)&addr, &addr_size) < 0) {
|
|
||||||
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);
|
|
||||||
|
|
||||||
|
if (getpeername(_socket, (struct sockaddr *)&addr, &addr_size) < 0) {
|
||||||
|
ESP_LOGE(PH_TAG, "Error getting client IP");
|
||||||
return address;
|
return address;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert to IPv4 string
|
||||||
|
inet_ntop(AF_INET, &addr.sin6_addr.un.u32_addr[3], ipstr, sizeof(ipstr));
|
||||||
|
ESP_LOGI(PH_TAG, "Client Remote IP => %s", ipstr);
|
||||||
|
address.fromString(ipstr);
|
||||||
|
|
||||||
|
return address;
|
||||||
}
|
}
|
||||||
@@ -2,8 +2,6 @@
|
|||||||
#define PsychicClient_h
|
#define PsychicClient_h
|
||||||
|
|
||||||
#include "PsychicCore.h"
|
#include "PsychicCore.h"
|
||||||
#include "PsychicHttpServer.h"
|
|
||||||
#include <lwip/sockets.h>
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* PsychicClient :: Generic wrapper around the ESP-IDF socket
|
* PsychicClient :: Generic wrapper around the ESP-IDF socket
|
||||||
@@ -12,7 +10,7 @@
|
|||||||
class PsychicClient {
|
class PsychicClient {
|
||||||
protected:
|
protected:
|
||||||
httpd_handle_t _server;
|
httpd_handle_t _server;
|
||||||
int _socket;
|
int _socket;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
PsychicClient(httpd_handle_t server, int socket);
|
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.
|
//no idea if this is the right way to do it or not, but lets see.
|
||||||
//pointer to our derived class (eg. PsychicWebSocketConnection)
|
//pointer to our derived class (eg. PsychicWebSocketConnection)
|
||||||
void * _friend;
|
void *_friend;
|
||||||
|
|
||||||
bool isNew = false;
|
bool isNew = false;
|
||||||
|
|
||||||
bool operator==(PsychicClient & rhs) const {
|
bool operator==(PsychicClient& rhs) const { return _socket == rhs.socket(); }
|
||||||
return _socket == rhs.socket();
|
|
||||||
}
|
|
||||||
|
|
||||||
httpd_handle_t server();
|
httpd_handle_t server();
|
||||||
int socket();
|
int socket();
|
||||||
esp_err_t close();
|
esp_err_t close();
|
||||||
|
|
||||||
IPAddress localIP();
|
IPAddress localIP();
|
||||||
IPAddress remoteIP();
|
IPAddress remoteIP();
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
#define PH_TAG "psychic"
|
#define PH_TAG "psychic"
|
||||||
|
|
||||||
//version numbers
|
// version numbers
|
||||||
#define PSYCHIC_HTTP_VERSION_MAJOR 1
|
#define PSYCHIC_HTTP_VERSION_MAJOR 1
|
||||||
#define PSYCHIC_HTTP_VERSION_MINOR 1
|
#define PSYCHIC_HTTP_VERSION_MINOR 1
|
||||||
#define PSYCHIC_HTTP_VERSION_PATCH 0
|
#define PSYCHIC_HTTP_VERSION_PATCH 0
|
||||||
@@ -16,12 +16,16 @@
|
|||||||
#define FILE_CHUNK_SIZE 8 * 1024
|
#define FILE_CHUNK_SIZE 8 * 1024
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef STREAM_CHUNK_SIZE
|
||||||
|
#define STREAM_CHUNK_SIZE 1024
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef MAX_UPLOAD_SIZE
|
#ifndef MAX_UPLOAD_SIZE
|
||||||
#define MAX_UPLOAD_SIZE (2048 * 1024) // 2MB
|
#define MAX_UPLOAD_SIZE (2048 * 1024) // 2MB
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef MAX_REQUEST_BODY_SIZE
|
#ifndef MAX_REQUEST_BODY_SIZE
|
||||||
#define MAX_REQUEST_BODY_SIZE (16 * 1024) //16K
|
#define MAX_REQUEST_BODY_SIZE (16 * 1024) // 16K
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef ARDUINO
|
#ifdef ARDUINO
|
||||||
@@ -37,65 +41,75 @@
|
|||||||
#include "MD5Builder.h"
|
#include "MD5Builder.h"
|
||||||
#include <UrlEncode.h>
|
#include <UrlEncode.h>
|
||||||
#include "FS.h"
|
#include "FS.h"
|
||||||
|
#include <ArduinoJson.h>
|
||||||
|
|
||||||
enum HTTPAuthMethod { BASIC_AUTH, DIGEST_AUTH };
|
enum HTTPAuthMethod
|
||||||
|
{
|
||||||
|
BASIC_AUTH,
|
||||||
|
DIGEST_AUTH
|
||||||
|
};
|
||||||
|
|
||||||
String urlDecode(const char * encoded);
|
String urlDecode(const char *encoded);
|
||||||
|
|
||||||
class PsychicHttpServer;
|
class PsychicHttpServer;
|
||||||
class PsychicRequest;
|
class PsychicRequest;
|
||||||
class PsychicWebSocketRequest;
|
class PsychicWebSocketRequest;
|
||||||
class PsychicClient;
|
class PsychicClient;
|
||||||
|
|
||||||
//filter function definition
|
// filter function definition
|
||||||
typedef std::function<bool(PsychicRequest * request)> PsychicRequestFilterFunction;
|
typedef std::function<bool(PsychicRequest *request)> PsychicRequestFilterFunction;
|
||||||
|
|
||||||
//client connect callback
|
// client connect callback
|
||||||
typedef std::function<void(PsychicClient * client)> PsychicClientCallback;
|
typedef std::function<void(PsychicClient *client)> PsychicClientCallback;
|
||||||
|
|
||||||
|
// callback definitions
|
||||||
|
typedef std::function<esp_err_t(PsychicRequest *request)> PsychicHttpRequestCallback;
|
||||||
|
typedef std::function<esp_err_t(PsychicRequest *request, JsonVariant &json)> PsychicJsonRequestCallback;
|
||||||
|
|
||||||
struct HTTPHeader {
|
struct HTTPHeader
|
||||||
char * field;
|
{
|
||||||
char * value;
|
char *field;
|
||||||
|
char *value;
|
||||||
};
|
};
|
||||||
|
|
||||||
class DefaultHeaders {
|
class DefaultHeaders
|
||||||
std::list<HTTPHeader> _headers;
|
{
|
||||||
|
std::list<HTTPHeader> _headers;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
DefaultHeaders() {
|
DefaultHeaders() {}
|
||||||
}
|
|
||||||
|
|
||||||
void addHeader(const String & field, const String & value) {
|
void addHeader(const String &field, const String &value)
|
||||||
addHeader(field.c_str(), value.c_str());
|
{
|
||||||
}
|
addHeader(field.c_str(), value.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
void addHeader(const char * field, const char * value) {
|
void addHeader(const char *field, const char *value)
|
||||||
HTTPHeader header;
|
{
|
||||||
|
HTTPHeader header;
|
||||||
|
|
||||||
//these are just going to stick around forever.
|
// these are just going to stick around forever.
|
||||||
header.field = (char *)malloc(strlen(field) + 1);
|
header.field = (char *)malloc(strlen(field) + 1);
|
||||||
header.value = (char *)malloc(strlen(value) + 1);
|
header.value = (char *)malloc(strlen(value) + 1);
|
||||||
|
|
||||||
strlcpy(header.field, field, strlen(field) + 1);
|
strlcpy(header.field, field, strlen(field) + 1);
|
||||||
strlcpy(header.value, value, strlen(value) + 1);
|
strlcpy(header.value, value, strlen(value) + 1);
|
||||||
|
|
||||||
_headers.push_back(header);
|
_headers.push_back(header);
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::list<HTTPHeader> & getHeaders() {
|
const std::list<HTTPHeader> &getHeaders() { return _headers; }
|
||||||
return _headers;
|
|
||||||
}
|
|
||||||
|
|
||||||
//delete the copy constructor, singleton class
|
// delete the copy constructor, singleton class
|
||||||
DefaultHeaders(DefaultHeaders const &) = delete;
|
DefaultHeaders(DefaultHeaders const &) = delete;
|
||||||
DefaultHeaders & operator=(DefaultHeaders const &) = delete;
|
DefaultHeaders &operator=(DefaultHeaders const &) = delete;
|
||||||
|
|
||||||
//single static class interface
|
// single static class interface
|
||||||
static DefaultHeaders & Instance() {
|
static DefaultHeaders &Instance()
|
||||||
static DefaultHeaders instance;
|
{
|
||||||
return instance;
|
static DefaultHeaders instance;
|
||||||
}
|
return instance;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif //PsychicCore_h
|
#endif // PsychicCore_h
|
||||||
@@ -1,82 +1,90 @@
|
|||||||
#include "PsychicEndpoint.h"
|
#include "PsychicEndpoint.h"
|
||||||
|
#include "PsychicHttpServer.h"
|
||||||
|
|
||||||
PsychicEndpoint::PsychicEndpoint()
|
PsychicEndpoint::PsychicEndpoint() :
|
||||||
: _server(NULL)
|
_server(NULL),
|
||||||
, _uri("")
|
_uri(""),
|
||||||
, _method(HTTP_GET)
|
_method(HTTP_GET),
|
||||||
, _handler(NULL) {
|
_handler(NULL)
|
||||||
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
PsychicEndpoint::PsychicEndpoint(PsychicHttpServer * server, http_method method, const char * uri)
|
PsychicEndpoint::PsychicEndpoint(PsychicHttpServer *server, http_method method, const char * uri) :
|
||||||
: _server(server)
|
_server(server),
|
||||||
, _uri(uri)
|
_uri(uri),
|
||||||
, _method(method)
|
_method(method),
|
||||||
, _handler(NULL) {
|
_handler(NULL)
|
||||||
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
PsychicEndpoint * PsychicEndpoint::setHandler(PsychicHandler * handler) {
|
PsychicEndpoint * PsychicEndpoint::setHandler(PsychicHandler *handler)
|
||||||
//clean up old / default handler
|
{
|
||||||
if (_handler != NULL)
|
//clean up old / default handler
|
||||||
delete _handler;
|
if (_handler != NULL)
|
||||||
|
delete _handler;
|
||||||
|
|
||||||
//get our new pointer
|
//get our new pointer
|
||||||
_handler = handler;
|
_handler = handler;
|
||||||
|
|
||||||
//keep a pointer to the server
|
//keep a pointer to the server
|
||||||
_handler->_server = _server;
|
_handler->_server = _server;
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
PsychicHandler * PsychicEndpoint::handler() {
|
PsychicHandler * PsychicEndpoint::handler()
|
||||||
return _handler;
|
{
|
||||||
|
return _handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
String PsychicEndpoint::uri() {
|
String PsychicEndpoint::uri() {
|
||||||
return _uri;
|
return _uri;
|
||||||
}
|
}
|
||||||
|
|
||||||
esp_err_t PsychicEndpoint::requestCallback(httpd_req_t * req) {
|
esp_err_t PsychicEndpoint::requestCallback(httpd_req_t *req)
|
||||||
#ifdef ENABLE_ASYNC
|
{
|
||||||
|
#ifdef ENABLE_ASYNC
|
||||||
if (is_on_async_worker_thread() == false) {
|
if (is_on_async_worker_thread() == false) {
|
||||||
if (submit_async_req(req, PsychicEndpoint::requestCallback) == ESP_OK) {
|
if (submit_async_req(req, PsychicEndpoint::requestCallback) == ESP_OK) {
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
} else {
|
} else {
|
||||||
httpd_resp_set_status(req, "503 Busy");
|
httpd_resp_set_status(req, "503 Busy");
|
||||||
httpd_resp_sendstr(req, "No workers available. Server busy.</div>");
|
httpd_resp_sendstr(req, "No workers available. Server busy.</div>");
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
PsychicEndpoint * self = (PsychicEndpoint *)req->user_ctx;
|
PsychicEndpoint *self = (PsychicEndpoint *)req->user_ctx;
|
||||||
PsychicHandler * handler = self->handler();
|
PsychicHandler *handler = self->handler();
|
||||||
PsychicRequest request(self->_server, req);
|
PsychicRequest request(self->_server, req);
|
||||||
|
|
||||||
//make sure we have a handler
|
//make sure we have a handler
|
||||||
if (handler != NULL) {
|
if (handler != NULL)
|
||||||
if (handler->filter(&request) && handler->canHandle(&request)) {
|
{
|
||||||
//check our credentials
|
if (handler->filter(&request) && handler->canHandle(&request))
|
||||||
if (handler->needsAuthentication(&request))
|
{
|
||||||
return handler->authenticate(&request);
|
//check our credentials
|
||||||
|
if (handler->needsAuthentication(&request))
|
||||||
|
return handler->authenticate(&request);
|
||||||
|
|
||||||
//pass it to our handler
|
//pass it to our handler
|
||||||
return handler->handleRequest(&request);
|
return handler->handleRequest(&request);
|
||||||
}
|
}
|
||||||
//pass it to our generic handlers
|
//pass it to our generic handlers
|
||||||
else
|
else
|
||||||
return PsychicHttpServer::notFoundHandler(req, HTTPD_500_INTERNAL_SERVER_ERROR);
|
return PsychicHttpServer::notFoundHandler(req, HTTPD_500_INTERNAL_SERVER_ERROR);
|
||||||
} else
|
}
|
||||||
return request.reply(500, "text/html", "No handler registered.");
|
else
|
||||||
|
return request.reply(500, "text/html", "No handler registered.");
|
||||||
}
|
}
|
||||||
|
|
||||||
PsychicEndpoint * PsychicEndpoint::setFilter(PsychicRequestFilterFunction fn) {
|
PsychicEndpoint* PsychicEndpoint::setFilter(PsychicRequestFilterFunction fn) {
|
||||||
_handler->setFilter(fn);
|
_handler->setFilter(fn);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
PsychicEndpoint *
|
PsychicEndpoint* PsychicEndpoint::setAuthentication(const char *username, const char *password, HTTPAuthMethod method, const char *realm, const char *authFailMsg) {
|
||||||
PsychicEndpoint::setAuthentication(const char * username, const char * password, HTTPAuthMethod method, const char * realm, const char * authFailMsg) {
|
_handler->setAuthentication(username, password, method, realm, authFailMsg);
|
||||||
_handler->setAuthentication(username, password, method, realm, authFailMsg);
|
return this;
|
||||||
return this;
|
|
||||||
};
|
};
|
||||||
@@ -2,35 +2,36 @@
|
|||||||
#define PsychicEndpoint_h
|
#define PsychicEndpoint_h
|
||||||
|
|
||||||
#include "PsychicCore.h"
|
#include "PsychicCore.h"
|
||||||
#include "PsychicHttpServer.h"
|
|
||||||
|
class PsychicHandler;
|
||||||
|
|
||||||
#ifdef ENABLE_ASYNC
|
#ifdef ENABLE_ASYNC
|
||||||
#include "async_worker.h"
|
#include "async_worker.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
class PsychicEndpoint {
|
class PsychicEndpoint
|
||||||
friend PsychicHttpServer;
|
{
|
||||||
|
friend PsychicHttpServer;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
PsychicHttpServer * _server;
|
PsychicHttpServer *_server;
|
||||||
String _uri;
|
String _uri;
|
||||||
http_method _method;
|
http_method _method;
|
||||||
PsychicHandler * _handler;
|
PsychicHandler *_handler;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
PsychicEndpoint();
|
PsychicEndpoint();
|
||||||
PsychicEndpoint(PsychicHttpServer * server, http_method method, const char * uri);
|
PsychicEndpoint(PsychicHttpServer *server, http_method method, const char * uri);
|
||||||
|
|
||||||
PsychicEndpoint * setHandler(PsychicHandler * handler);
|
PsychicEndpoint *setHandler(PsychicHandler *handler);
|
||||||
PsychicHandler * handler();
|
PsychicHandler *handler();
|
||||||
|
|
||||||
PsychicEndpoint * setFilter(PsychicRequestFilterFunction fn);
|
PsychicEndpoint* setFilter(PsychicRequestFilterFunction fn);
|
||||||
PsychicEndpoint *
|
PsychicEndpoint* setAuthentication(const char *username, const char *password, HTTPAuthMethod method = BASIC_AUTH, const char *realm = "", const char *authFailMsg = "");
|
||||||
setAuthentication(const char * username, const char * password, HTTPAuthMethod method = BASIC_AUTH, const char * realm = "", const char * authFailMsg = "");
|
|
||||||
|
|
||||||
String uri();
|
String uri();
|
||||||
|
|
||||||
static esp_err_t requestCallback(httpd_req_t * req);
|
static esp_err_t requestCallback(httpd_req_t *req);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // PsychicEndpoint_h
|
#endif // PsychicEndpoint_h
|
||||||
@@ -24,194 +24,204 @@
|
|||||||
// PsychicEventSource - Handler
|
// PsychicEventSource - Handler
|
||||||
/*****************************************/
|
/*****************************************/
|
||||||
|
|
||||||
PsychicEventSource::PsychicEventSource()
|
PsychicEventSource::PsychicEventSource() :
|
||||||
: PsychicHandler()
|
PsychicHandler(),
|
||||||
, _onOpen(NULL)
|
_onOpen(NULL),
|
||||||
, _onClose(NULL) {
|
_onClose(NULL)
|
||||||
}
|
{}
|
||||||
|
|
||||||
PsychicEventSource::~PsychicEventSource() {
|
PsychicEventSource::~PsychicEventSource() {
|
||||||
}
|
}
|
||||||
|
|
||||||
PsychicEventSourceClient * PsychicEventSource::getClient(int socket) {
|
PsychicEventSourceClient * PsychicEventSource::getClient(int socket)
|
||||||
PsychicClient * client = PsychicHandler::getClient(socket);
|
{
|
||||||
|
PsychicClient *client = PsychicHandler::getClient(socket);
|
||||||
|
|
||||||
if (client == NULL)
|
if (client == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
return (PsychicEventSourceClient *)client->_friend;
|
return (PsychicEventSourceClient *)client->_friend;
|
||||||
}
|
}
|
||||||
|
|
||||||
PsychicEventSourceClient * PsychicEventSource::getClient(PsychicClient * client) {
|
PsychicEventSourceClient * PsychicEventSource::getClient(PsychicClient *client) {
|
||||||
return getClient(client->socket());
|
return getClient(client->socket());
|
||||||
}
|
}
|
||||||
|
|
||||||
esp_err_t PsychicEventSource::handleRequest(PsychicRequest * request) {
|
esp_err_t PsychicEventSource::handleRequest(PsychicRequest *request)
|
||||||
//start our open ended HTTP response
|
{
|
||||||
PsychicEventSourceResponse response(request);
|
//start our open ended HTTP response
|
||||||
esp_err_t err = response.send();
|
PsychicEventSourceResponse response(request);
|
||||||
|
esp_err_t err = response.send();
|
||||||
|
|
||||||
//lookup our client
|
//lookup our client
|
||||||
PsychicClient * client = checkForNewClient(request->client());
|
PsychicClient *client = checkForNewClient(request->client());
|
||||||
if (client->isNew) {
|
if (client->isNew)
|
||||||
//did we get our last id?
|
{
|
||||||
if (request->hasHeader("Last-Event-ID")) {
|
//did we get our last id?
|
||||||
PsychicEventSourceClient * buddy = getClient(client);
|
if(request->hasHeader("Last-Event-ID"))
|
||||||
buddy->_lastId = atoi(request->header("Last-Event-ID").c_str());
|
{
|
||||||
}
|
PsychicEventSourceClient *buddy = getClient(client);
|
||||||
|
buddy->_lastId = atoi(request->header("Last-Event-ID").c_str());
|
||||||
//let our handler know.
|
|
||||||
openCallback(client);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return err;
|
//let our handler know.
|
||||||
|
openCallback(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
PsychicEventSource * PsychicEventSource::onOpen(PsychicEventSourceClientCallback fn) {
|
PsychicEventSource * PsychicEventSource::onOpen(PsychicEventSourceClientCallback fn) {
|
||||||
_onOpen = fn;
|
_onOpen = fn;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
PsychicEventSource * PsychicEventSource::onClose(PsychicEventSourceClientCallback fn) {
|
PsychicEventSource * PsychicEventSource::onClose(PsychicEventSourceClientCallback fn) {
|
||||||
_onClose = fn;
|
_onClose = fn;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PsychicEventSource::addClient(PsychicClient * client) {
|
void PsychicEventSource::addClient(PsychicClient *client) {
|
||||||
client->_friend = new PsychicEventSourceClient(client);
|
client->_friend = new PsychicEventSourceClient(client);
|
||||||
PsychicHandler::addClient(client);
|
PsychicHandler::addClient(client);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PsychicEventSource::removeClient(PsychicClient * client) {
|
void PsychicEventSource::removeClient(PsychicClient *client) {
|
||||||
PsychicHandler::removeClient(client);
|
PsychicHandler::removeClient(client);
|
||||||
delete (PsychicEventSourceClient *)client->_friend;
|
delete (PsychicEventSourceClient*)client->_friend;
|
||||||
client->_friend = NULL;
|
client->_friend = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PsychicEventSource::openCallback(PsychicClient * client) {
|
void PsychicEventSource::openCallback(PsychicClient *client) {
|
||||||
PsychicEventSourceClient * buddy = getClient(client);
|
PsychicEventSourceClient *buddy = getClient(client);
|
||||||
if (buddy == NULL) {
|
if (buddy == NULL)
|
||||||
TRACE();
|
{
|
||||||
return;
|
TRACE();
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (_onOpen != NULL)
|
if (_onOpen != NULL)
|
||||||
_onOpen(buddy);
|
_onOpen(buddy);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PsychicEventSource::closeCallback(PsychicClient * client) {
|
void PsychicEventSource::closeCallback(PsychicClient *client) {
|
||||||
PsychicEventSourceClient * buddy = getClient(client);
|
PsychicEventSourceClient *buddy = getClient(client);
|
||||||
if (buddy == NULL) {
|
if (buddy == NULL)
|
||||||
TRACE();
|
{
|
||||||
return;
|
TRACE();
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (_onClose != NULL)
|
if (_onClose != NULL)
|
||||||
_onClose(getClient(buddy));
|
_onClose(getClient(buddy));
|
||||||
}
|
}
|
||||||
|
|
||||||
void PsychicEventSource::send(const char * message, const char * event, uint32_t id, uint32_t reconnect) {
|
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) {
|
String ev = generateEventMessage(message, event, id, reconnect);
|
||||||
((PsychicEventSourceClient *)c->_friend)->sendEvent(ev.c_str());
|
for(PsychicClient *c : _clients) {
|
||||||
}
|
((PsychicEventSourceClient*)c->_friend)->sendEvent(ev.c_str());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*****************************************/
|
/*****************************************/
|
||||||
// PsychicEventSourceClient
|
// PsychicEventSourceClient
|
||||||
/*****************************************/
|
/*****************************************/
|
||||||
|
|
||||||
PsychicEventSourceClient::PsychicEventSourceClient(PsychicClient * client)
|
PsychicEventSourceClient::PsychicEventSourceClient(PsychicClient *client) :
|
||||||
: PsychicClient(client->server(), client->socket())
|
PsychicClient(client->server(), client->socket()),
|
||||||
, _lastId(0) {
|
_lastId(0)
|
||||||
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
PsychicEventSourceClient::~PsychicEventSourceClient() {
|
PsychicEventSourceClient::~PsychicEventSourceClient(){
|
||||||
}
|
}
|
||||||
|
|
||||||
void PsychicEventSourceClient::send(const char * message, const char * event, uint32_t id, uint32_t reconnect) {
|
void PsychicEventSourceClient::send(const char *message, const char *event, uint32_t id, uint32_t reconnect){
|
||||||
String ev = generateEventMessage(message, event, id, reconnect);
|
String ev = generateEventMessage(message, event, id, reconnect);
|
||||||
sendEvent(ev.c_str());
|
sendEvent(ev.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void PsychicEventSourceClient::sendEvent(const char * event) {
|
void PsychicEventSourceClient::sendEvent(const char *event) {
|
||||||
int result;
|
int result;
|
||||||
do {
|
do {
|
||||||
result = httpd_socket_send(this->server(), this->socket(), event, strlen(event), 0);
|
result = httpd_socket_send(this->server(), this->socket(), event, strlen(event), 0);
|
||||||
} while (result == HTTPD_SOCK_ERR_TIMEOUT);
|
} while (result == HTTPD_SOCK_ERR_TIMEOUT);
|
||||||
|
|
||||||
//if (result < 0)
|
//if (result < 0)
|
||||||
//error log here
|
//error log here
|
||||||
}
|
}
|
||||||
|
|
||||||
/*****************************************/
|
/*****************************************/
|
||||||
// PsychicEventSourceResponse
|
// PsychicEventSourceResponse
|
||||||
/*****************************************/
|
/*****************************************/
|
||||||
|
|
||||||
PsychicEventSourceResponse::PsychicEventSourceResponse(PsychicRequest * request)
|
PsychicEventSourceResponse::PsychicEventSourceResponse(PsychicRequest *request)
|
||||||
: PsychicResponse(request) {
|
: PsychicResponse(request)
|
||||||
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
esp_err_t PsychicEventSourceResponse::send() {
|
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
|
//build our main header
|
||||||
for (HTTPHeader header : DefaultHeaders::Instance().getHeaders())
|
String out = String();
|
||||||
out.concat(String(header.field) + ": " + String(header.value) + "\r\n");
|
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
|
//get our global headers out of the way first
|
||||||
out.concat("\r\n");
|
for (HTTPHeader header : DefaultHeaders::Instance().getHeaders())
|
||||||
|
out.concat(String(header.field) + ": " + String(header.value) + "\r\n");
|
||||||
|
|
||||||
int result;
|
//separator
|
||||||
do {
|
out.concat("\r\n");
|
||||||
result = httpd_send(_request->request(), out.c_str(), out.length());
|
|
||||||
} while (result == HTTPD_SOCK_ERR_TIMEOUT);
|
|
||||||
|
|
||||||
if (result < 0)
|
int result;
|
||||||
ESP_LOGE(PH_TAG, "EventSource send failed with %s", esp_err_to_name(result));
|
do {
|
||||||
|
result = httpd_send(_request->request(), out.c_str(), out.length());
|
||||||
|
} while (result == HTTPD_SOCK_ERR_TIMEOUT);
|
||||||
|
|
||||||
if (result > 0)
|
if (result < 0)
|
||||||
return ESP_OK;
|
ESP_LOGE(PH_TAG, "EventSource send failed with %s", esp_err_to_name(result));
|
||||||
else
|
|
||||||
return ESP_ERR_HTTPD_RESP_SEND;
|
if (result > 0)
|
||||||
|
return ESP_OK;
|
||||||
|
else
|
||||||
|
return ESP_ERR_HTTPD_RESP_SEND;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*****************************************/
|
/*****************************************/
|
||||||
// Event Message Generator
|
// Event Message Generator
|
||||||
/*****************************************/
|
/*****************************************/
|
||||||
|
|
||||||
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) {
|
||||||
String ev = "";
|
String ev = "";
|
||||||
|
|
||||||
if (reconnect) {
|
if(reconnect){
|
||||||
ev += "retry: ";
|
ev += "retry: ";
|
||||||
ev += String(reconnect);
|
ev += String(reconnect);
|
||||||
ev += "\r\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (id) {
|
|
||||||
ev += "id: ";
|
|
||||||
ev += String(id);
|
|
||||||
ev += "\r\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (event != NULL) {
|
|
||||||
ev += "event: ";
|
|
||||||
ev += String(event);
|
|
||||||
ev += "\r\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (message != NULL) {
|
|
||||||
ev += "data: ";
|
|
||||||
ev += String(message);
|
|
||||||
ev += "\r\n";
|
|
||||||
}
|
|
||||||
ev += "\r\n";
|
ev += "\r\n";
|
||||||
|
}
|
||||||
|
|
||||||
return ev;
|
if(id){
|
||||||
|
ev += "id: ";
|
||||||
|
ev += String(id);
|
||||||
|
ev += "\r\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
if(event != NULL){
|
||||||
|
ev += "event: ";
|
||||||
|
ev += String(event);
|
||||||
|
ev += "\r\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
if(message != NULL){
|
||||||
|
ev += "data: ";
|
||||||
|
ev += String(message);
|
||||||
|
ev += "\r\n";
|
||||||
|
}
|
||||||
|
ev += "\r\n";
|
||||||
|
|
||||||
|
return ev;
|
||||||
}
|
}
|
||||||
@@ -21,8 +21,8 @@
|
|||||||
#define PsychicEventSource_H_
|
#define PsychicEventSource_H_
|
||||||
|
|
||||||
#include "PsychicCore.h"
|
#include "PsychicCore.h"
|
||||||
#include "PsychicClient.h"
|
|
||||||
#include "PsychicHandler.h"
|
#include "PsychicHandler.h"
|
||||||
|
#include "PsychicClient.h"
|
||||||
#include "PsychicResponse.h"
|
#include "PsychicResponse.h"
|
||||||
|
|
||||||
class PsychicEventSource;
|
class PsychicEventSource;
|
||||||
@@ -30,23 +30,21 @@ class PsychicEventSourceResponse;
|
|||||||
class PsychicEventSourceClient;
|
class PsychicEventSourceClient;
|
||||||
class PsychicResponse;
|
class PsychicResponse;
|
||||||
|
|
||||||
typedef std::function<void(PsychicEventSourceClient * client)> PsychicEventSourceClientCallback;
|
typedef std::function<void(PsychicEventSourceClient *client)> PsychicEventSourceClientCallback;
|
||||||
|
|
||||||
class PsychicEventSourceClient : public PsychicClient {
|
class PsychicEventSourceClient : public PsychicClient {
|
||||||
friend PsychicEventSource;
|
friend PsychicEventSource;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
uint32_t _lastId;
|
uint32_t _lastId;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
PsychicEventSourceClient(PsychicClient * client);
|
PsychicEventSourceClient(PsychicClient *client);
|
||||||
~PsychicEventSourceClient();
|
~PsychicEventSourceClient();
|
||||||
|
|
||||||
uint32_t lastId() const {
|
uint32_t lastId() const { return _lastId; }
|
||||||
return _lastId;
|
void send(const char *message, const char *event=NULL, uint32_t id=0, uint32_t reconnect=0);
|
||||||
}
|
void sendEvent(const char *event);
|
||||||
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 {
|
class PsychicEventSource : public PsychicHandler {
|
||||||
@@ -59,26 +57,26 @@ class PsychicEventSource : public PsychicHandler {
|
|||||||
~PsychicEventSource();
|
~PsychicEventSource();
|
||||||
|
|
||||||
PsychicEventSourceClient * getClient(int socket) override;
|
PsychicEventSourceClient * getClient(int socket) override;
|
||||||
PsychicEventSourceClient * getClient(PsychicClient * client) override;
|
PsychicEventSourceClient * getClient(PsychicClient *client) override;
|
||||||
void addClient(PsychicClient * client) override;
|
void addClient(PsychicClient *client) override;
|
||||||
void removeClient(PsychicClient * client) override;
|
void removeClient(PsychicClient *client) override;
|
||||||
void openCallback(PsychicClient * client) override;
|
void openCallback(PsychicClient *client) override;
|
||||||
void closeCallback(PsychicClient * client) override;
|
void closeCallback(PsychicClient *client) override;
|
||||||
|
|
||||||
PsychicEventSource * onOpen(PsychicEventSourceClientCallback fn);
|
PsychicEventSource *onOpen(PsychicEventSourceClientCallback fn);
|
||||||
PsychicEventSource * onClose(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:
|
public:
|
||||||
PsychicEventSourceResponse(PsychicRequest * request);
|
PsychicEventSourceResponse(PsychicRequest *request);
|
||||||
virtual esp_err_t send() override;
|
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_ */
|
#endif /* PsychicEventSource_H_ */
|
||||||
@@ -3,167 +3,156 @@
|
|||||||
#include "PsychicRequest.h"
|
#include "PsychicRequest.h"
|
||||||
|
|
||||||
|
|
||||||
PsychicFileResponse::PsychicFileResponse(PsychicRequest * request, FS & fs, const String & path, const String & contentType, bool download)
|
PsychicFileResponse::PsychicFileResponse(PsychicRequest *request, FS &fs, const String& path, const String& contentType, bool download)
|
||||||
: PsychicResponse(request) {
|
: PsychicResponse(request) {
|
||||||
//_code = 200;
|
//_code = 200;
|
||||||
_path = path;
|
_path = path;
|
||||||
|
|
||||||
if (!download && !fs.exists(_path) && fs.exists(_path + ".gz")) {
|
if(!download && !fs.exists(_path) && fs.exists(_path+".gz")){
|
||||||
_path = _path + ".gz";
|
_path = _path+".gz";
|
||||||
addHeader("Content-Encoding", "gzip");
|
addHeader("Content-Encoding", "gzip");
|
||||||
_sendContentLength = true;
|
_sendContentLength = true;
|
||||||
_chunked = false;
|
_chunked = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
_content = fs.open(_path, "r");
|
_content = fs.open(_path, "r");
|
||||||
_contentLength = _content.size();
|
_contentLength = _content.size();
|
||||||
|
|
||||||
if (contentType == "")
|
if(contentType == "")
|
||||||
_setContentType(path);
|
_setContentType(path);
|
||||||
else
|
else
|
||||||
_contentType = contentType;
|
_contentType = contentType;
|
||||||
setContentType(_contentType.c_str());
|
setContentType(_contentType.c_str());
|
||||||
|
|
||||||
int filenameStart = path.lastIndexOf('/') + 1;
|
int filenameStart = path.lastIndexOf('/') + 1;
|
||||||
char buf[26 + path.length() - filenameStart];
|
char buf[26+path.length()-filenameStart];
|
||||||
char * filename = (char *)path.c_str() + filenameStart;
|
char* filename = (char*)path.c_str() + filenameStart;
|
||||||
|
|
||||||
if (download) {
|
if(download) {
|
||||||
// set filename and force download
|
// set filename and force download
|
||||||
snprintf(buf, sizeof(buf), "attachment; filename=\"%s\"", filename);
|
snprintf(buf, sizeof (buf), "attachment; filename=\"%s\"", filename);
|
||||||
} else {
|
} else {
|
||||||
// set filename and force rendering
|
// set filename and force rendering
|
||||||
snprintf(buf, sizeof(buf), "inline; filename=\"%s\"", filename);
|
snprintf(buf, sizeof (buf), "inline; filename=\"%s\"", filename);
|
||||||
}
|
}
|
||||||
addHeader("Content-Disposition", buf);
|
addHeader("Content-Disposition", buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
PsychicFileResponse::PsychicFileResponse(PsychicRequest * request, File content, const String & path, const String & contentType, bool download)
|
PsychicFileResponse::PsychicFileResponse(PsychicRequest *request, File content, const String& path, const String& contentType, bool download)
|
||||||
: PsychicResponse(request) {
|
: PsychicResponse(request) {
|
||||||
_path = path;
|
_path = path;
|
||||||
|
|
||||||
if (!download && String(content.name()).endsWith(".gz") && !path.endsWith(".gz")) {
|
if(!download && String(content.name()).endsWith(".gz") && !path.endsWith(".gz")){
|
||||||
addHeader("Content-Encoding", "gzip");
|
addHeader("Content-Encoding", "gzip");
|
||||||
_sendContentLength = true;
|
_sendContentLength = true;
|
||||||
_chunked = false;
|
_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;
|
this->sendHeaders();
|
||||||
_contentLength = _content.size();
|
|
||||||
setContentLength(_contentLength);
|
|
||||||
|
|
||||||
if (contentType == "")
|
size_t chunksize;
|
||||||
_setContentType(path);
|
do {
|
||||||
else
|
/* Read file in chunks into the scratch buffer */
|
||||||
_contentType = contentType;
|
chunksize = _content.readBytes(chunk, FILE_CHUNK_SIZE);
|
||||||
setContentType(_contentType.c_str());
|
if (chunksize > 0)
|
||||||
|
{
|
||||||
int filenameStart = path.lastIndexOf('/') + 1;
|
err = this->sendChunk((uint8_t *)chunk, chunksize);
|
||||||
char buf[26 + path.length() - filenameStart];
|
if (err != ESP_OK)
|
||||||
char * filename = (char *)path.c_str() + filenameStart;
|
break;
|
||||||
|
|
||||||
if (download) {
|
|
||||||
snprintf(buf, sizeof(buf), "attachment; filename=\"%s\"", filename);
|
|
||||||
} else {
|
|
||||||
snprintf(buf, sizeof(buf), "inline; filename=\"%s\"", filename);
|
|
||||||
}
|
|
||||||
addHeader("Content-Disposition", buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
PsychicFileResponse::~PsychicFileResponse() {
|
|
||||||
if (_content)
|
|
||||||
_content.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
void PsychicFileResponse::_setContentType(const String & path) {
|
|
||||||
if (path.endsWith(".html"))
|
|
||||||
_contentType = "text/html";
|
|
||||||
else if (path.endsWith(".htm"))
|
|
||||||
_contentType = "text/html";
|
|
||||||
else if (path.endsWith(".css"))
|
|
||||||
_contentType = "text/css";
|
|
||||||
else if (path.endsWith(".json"))
|
|
||||||
_contentType = "application/json";
|
|
||||||
else if (path.endsWith(".js"))
|
|
||||||
_contentType = "application/javascript";
|
|
||||||
else if (path.endsWith(".png"))
|
|
||||||
_contentType = "image/png";
|
|
||||||
else if (path.endsWith(".gif"))
|
|
||||||
_contentType = "image/gif";
|
|
||||||
else if (path.endsWith(".jpg"))
|
|
||||||
_contentType = "image/jpeg";
|
|
||||||
else if (path.endsWith(".ico"))
|
|
||||||
_contentType = "image/x-icon";
|
|
||||||
else if (path.endsWith(".svg"))
|
|
||||||
_contentType = "image/svg+xml";
|
|
||||||
else if (path.endsWith(".eot"))
|
|
||||||
_contentType = "font/eot";
|
|
||||||
else if (path.endsWith(".woff"))
|
|
||||||
_contentType = "font/woff";
|
|
||||||
else if (path.endsWith(".woff2"))
|
|
||||||
_contentType = "font/woff2";
|
|
||||||
else if (path.endsWith(".ttf"))
|
|
||||||
_contentType = "font/ttf";
|
|
||||||
else if (path.endsWith(".xml"))
|
|
||||||
_contentType = "text/xml";
|
|
||||||
else if (path.endsWith(".pdf"))
|
|
||||||
_contentType = "application/pdf";
|
|
||||||
else if (path.endsWith(".zip"))
|
|
||||||
_contentType = "application/zip";
|
|
||||||
else if (path.endsWith(".gz"))
|
|
||||||
_contentType = "application/x-gzip";
|
|
||||||
else
|
|
||||||
_contentType = "text/plain";
|
|
||||||
}
|
|
||||||
|
|
||||||
esp_err_t PsychicFileResponse::send() {
|
|
||||||
esp_err_t err = ESP_OK;
|
|
||||||
|
|
||||||
//just send small files directly
|
|
||||||
size_t size = getContentLength();
|
|
||||||
if (size < FILE_CHUNK_SIZE) {
|
|
||||||
uint8_t * buffer = (uint8_t *)malloc(size);
|
|
||||||
int readSize = _content.readBytes((char *)buffer, size);
|
|
||||||
|
|
||||||
this->setContent(buffer, size);
|
|
||||||
err = PsychicResponse::send();
|
|
||||||
|
|
||||||
free(buffer);
|
|
||||||
} else {
|
|
||||||
/* Retrieve the pointer to scratch buffer for temporary storage */
|
|
||||||
char * chunk = (char *)malloc(FILE_CHUNK_SIZE);
|
|
||||||
if (chunk == NULL) {
|
|
||||||
/* Respond with 500 Internal Server Error */
|
|
||||||
httpd_resp_send_err(this->_request->request(), HTTPD_500_INTERNAL_SERVER_ERROR, "Unable to allocate memory.");
|
|
||||||
return ESP_FAIL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this->sendHeaders();
|
/* Keep looping till the whole file is sent */
|
||||||
|
} while (chunksize != 0);
|
||||||
|
|
||||||
size_t chunksize;
|
//keep track of our memory
|
||||||
do {
|
free(chunk);
|
||||||
/* Read file in chunks into the scratch buffer */
|
|
||||||
chunksize = _content.readBytes(chunk, FILE_CHUNK_SIZE);
|
|
||||||
if (chunksize > 0) {
|
|
||||||
err = this->sendChunk((uint8_t *)chunk, chunksize);
|
|
||||||
if (err != ESP_OK)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Keep looping till the whole file is sent */
|
if (err == ESP_OK)
|
||||||
} while (chunksize != 0);
|
{
|
||||||
|
ESP_LOGI(PH_TAG, "File sending complete");
|
||||||
//keep track of our memory
|
this->finishChunking();
|
||||||
free(chunk);
|
|
||||||
|
|
||||||
if (err == ESP_OK) {
|
|
||||||
ESP_LOGI(PH_TAG, "File sending complete");
|
|
||||||
this->finishChunking();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Close file after sending complete */
|
|
||||||
_content.close();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return err;
|
/* Close file after sending complete */
|
||||||
|
_content.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,21 +6,20 @@
|
|||||||
|
|
||||||
class PsychicRequest;
|
class PsychicRequest;
|
||||||
|
|
||||||
class PsychicFileResponse : public PsychicResponse {
|
class PsychicFileResponse: public PsychicResponse
|
||||||
using File = fs::File;
|
{
|
||||||
using FS = fs::FS;
|
using File = fs::File;
|
||||||
|
using FS = fs::FS;
|
||||||
private:
|
private:
|
||||||
File _content;
|
File _content;
|
||||||
String _path;
|
String _path;
|
||||||
bool _sendContentLength;
|
bool _sendContentLength;
|
||||||
bool _chunked;
|
bool _chunked;
|
||||||
String _contentType;
|
String _contentType;
|
||||||
void _setContentType(const String & path);
|
void _setContentType(const String& path);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
PsychicFileResponse(PsychicRequest * request, FS & fs, 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(PsychicRequest *request, File content, const String& path, const String& contentType=String(), bool download=false);
|
||||||
~PsychicFileResponse();
|
~PsychicFileResponse();
|
||||||
esp_err_t send();
|
esp_err_t send();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,63 +5,57 @@
|
|||||||
#include "PsychicRequest.h"
|
#include "PsychicRequest.h"
|
||||||
|
|
||||||
class PsychicEndpoint;
|
class PsychicEndpoint;
|
||||||
|
class PsychicHttpServer;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* HANDLER :: Can be attached to any endpoint or as a generic request handler.
|
* HANDLER :: Can be attached to any endpoint or as a generic request handler.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class PsychicHandler {
|
class PsychicHandler {
|
||||||
friend PsychicEndpoint;
|
friend PsychicEndpoint;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
PsychicRequestFilterFunction _filter;
|
PsychicRequestFilterFunction _filter;
|
||||||
PsychicHttpServer * _server;
|
PsychicHttpServer *_server;
|
||||||
|
|
||||||
String _username;
|
String _username;
|
||||||
String _password;
|
String _password;
|
||||||
HTTPAuthMethod _method;
|
HTTPAuthMethod _method;
|
||||||
String _realm;
|
String _realm;
|
||||||
String _authFailMsg;
|
String _authFailMsg;
|
||||||
|
|
||||||
std::list<PsychicClient *> _clients;
|
std::list<PsychicClient*> _clients;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
PsychicHandler();
|
PsychicHandler();
|
||||||
~PsychicHandler();
|
~PsychicHandler();
|
||||||
|
|
||||||
PsychicHandler * setFilter(PsychicRequestFilterFunction fn);
|
PsychicHandler* setFilter(PsychicRequestFilterFunction fn);
|
||||||
bool filter(PsychicRequest * request);
|
bool filter(PsychicRequest *request);
|
||||||
|
|
||||||
PsychicHandler *
|
PsychicHandler* setAuthentication(const char *username, const char *password, HTTPAuthMethod method = BASIC_AUTH, const char *realm = "", const char *authFailMsg = "");
|
||||||
setAuthentication(const char * username, const char * password, HTTPAuthMethod method = BASIC_AUTH, const char * realm = "", const char * authFailMsg = "");
|
bool needsAuthentication(PsychicRequest *request);
|
||||||
bool needsAuthentication(PsychicRequest * request);
|
esp_err_t authenticate(PsychicRequest *request);
|
||||||
esp_err_t authenticate(PsychicRequest * request);
|
|
||||||
|
|
||||||
virtual bool isWebSocket() {
|
virtual bool isWebSocket() { return false; };
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
PsychicClient * checkForNewClient(PsychicClient * client);
|
PsychicClient * checkForNewClient(PsychicClient *client);
|
||||||
void checkForClosedClient(PsychicClient * client);
|
void checkForClosedClient(PsychicClient *client);
|
||||||
|
|
||||||
virtual void addClient(PsychicClient * client);
|
virtual void addClient(PsychicClient *client);
|
||||||
virtual void removeClient(PsychicClient * client);
|
virtual void removeClient(PsychicClient *client);
|
||||||
virtual PsychicClient * getClient(int socket);
|
virtual PsychicClient * getClient(int socket);
|
||||||
virtual PsychicClient * getClient(PsychicClient * client);
|
virtual PsychicClient * getClient(PsychicClient *client);
|
||||||
virtual void openCallback(PsychicClient * client){};
|
virtual void openCallback(PsychicClient *client) {};
|
||||||
virtual void closeCallback(PsychicClient * client){};
|
virtual void closeCallback(PsychicClient *client) {};
|
||||||
|
|
||||||
bool hasClient(PsychicClient * client);
|
bool hasClient(PsychicClient *client);
|
||||||
int count() {
|
int count() { return _clients.size(); };
|
||||||
return _clients.size();
|
const std::list<PsychicClient*>& getClientList();
|
||||||
};
|
|
||||||
const std::list<PsychicClient *> & getClientList();
|
|
||||||
|
|
||||||
//derived classes must implement these functions
|
//derived classes must implement these functions
|
||||||
virtual bool canHandle(PsychicRequest * request) {
|
virtual bool canHandle(PsychicRequest *request) { return true; };
|
||||||
return true;
|
virtual esp_err_t handleRequest(PsychicRequest *request) = 0;
|
||||||
};
|
|
||||||
virtual esp_err_t handleRequest(PsychicRequest * request) = 0;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
#ifndef PsychicHttp_h
|
#ifndef PsychicHttp_h
|
||||||
#define PsychicHttp_h
|
#define PsychicHttp_h
|
||||||
|
|
||||||
//#define ENABLE_ASYNC // This is something added in ESP-IDF 5.1.x where each request can be handled in its own thread
|
// #define ENABLE_ASYNC // This is something added in ESP-IDF 5.1.x where each request can be handled in its own thread
|
||||||
|
|
||||||
#include <http_status.h>
|
#include <http_status.h>
|
||||||
#include "PsychicHttpServer.h"
|
#include "PsychicHttpServer.h"
|
||||||
@@ -11,6 +11,7 @@
|
|||||||
#include "PsychicHandler.h"
|
#include "PsychicHandler.h"
|
||||||
#include "PsychicStaticFileHandler.h"
|
#include "PsychicStaticFileHandler.h"
|
||||||
#include "PsychicFileResponse.h"
|
#include "PsychicFileResponse.h"
|
||||||
|
#include "PsychicStreamResponse.h"
|
||||||
#include "PsychicUploadHandler.h"
|
#include "PsychicUploadHandler.h"
|
||||||
#include "PsychicWebSocket.h"
|
#include "PsychicWebSocket.h"
|
||||||
#include "PsychicEventSource.h"
|
#include "PsychicEventSource.h"
|
||||||
|
|||||||
@@ -4,28 +4,29 @@
|
|||||||
#include "PsychicWebHandler.h"
|
#include "PsychicWebHandler.h"
|
||||||
#include "PsychicStaticFileHandler.h"
|
#include "PsychicStaticFileHandler.h"
|
||||||
#include "PsychicWebSocket.h"
|
#include "PsychicWebSocket.h"
|
||||||
|
#include "PsychicJson.h"
|
||||||
#include "WiFi.h"
|
#include "WiFi.h"
|
||||||
#include "PsychicJson.h" // added by proddy
|
|
||||||
|
|
||||||
PsychicHttpServer::PsychicHttpServer()
|
PsychicHttpServer::PsychicHttpServer() :
|
||||||
: _onOpen(NULL)
|
_onOpen(NULL),
|
||||||
, _onClose(NULL) {
|
_onClose(NULL)
|
||||||
maxRequestBodySize = MAX_REQUEST_BODY_SIZE;
|
{
|
||||||
maxUploadSize = MAX_UPLOAD_SIZE;
|
maxRequestBodySize = MAX_REQUEST_BODY_SIZE;
|
||||||
|
maxUploadSize = MAX_UPLOAD_SIZE;
|
||||||
|
|
||||||
defaultEndpoint = new PsychicEndpoint(this, HTTP_GET, "");
|
defaultEndpoint = new PsychicEndpoint(this, HTTP_GET, "");
|
||||||
onNotFound(PsychicHttpServer::defaultNotFoundHandler);
|
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
|
#ifdef ENABLE_ASYNC
|
||||||
config = HTTPD_DEFAULT_CONFIG();
|
|
||||||
config.open_fn = PsychicHttpServer::openCallback;
|
|
||||||
config.close_fn = PsychicHttpServer::closeCallback;
|
|
||||||
config.uri_match_fn = httpd_uri_match_wildcard;
|
|
||||||
config.global_user_ctx = this;
|
|
||||||
config.global_user_ctx_free_fn = destroy;
|
|
||||||
config.max_uri_handlers = 20;
|
|
||||||
|
|
||||||
#ifdef ENABLE_ASYNC
|
|
||||||
// It is advisable that httpd_config_t->max_open_sockets > MAX_ASYNC_REQUESTS
|
// It is advisable that httpd_config_t->max_open_sockets > MAX_ASYNC_REQUESTS
|
||||||
// Why? This leaves at least one socket still available to handle
|
// Why? This leaves at least one socket still available to handle
|
||||||
// quick synchronous requests. Otherwise, all the sockets will
|
// quick synchronous requests. Otherwise, all the sockets will
|
||||||
@@ -33,297 +34,333 @@ PsychicHttpServer::PsychicHttpServer()
|
|||||||
// longer be responsive.
|
// longer be responsive.
|
||||||
config.max_open_sockets = ASYNC_WORKER_COUNT + 1;
|
config.max_open_sockets = ASYNC_WORKER_COUNT + 1;
|
||||||
config.lru_purge_enable = true;
|
config.lru_purge_enable = true;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
PsychicHttpServer::~PsychicHttpServer() {
|
PsychicHttpServer::~PsychicHttpServer()
|
||||||
for (auto * client : _clients)
|
{
|
||||||
delete (client);
|
for (auto *client : _clients)
|
||||||
_clients.clear();
|
delete(client);
|
||||||
|
_clients.clear();
|
||||||
|
|
||||||
for (auto * endpoint : _endpoints)
|
for (auto *endpoint : _endpoints)
|
||||||
delete (endpoint);
|
delete(endpoint);
|
||||||
_endpoints.clear();
|
_endpoints.clear();
|
||||||
|
|
||||||
for (auto * handler : _handlers)
|
for (auto *handler : _handlers)
|
||||||
delete (handler);
|
delete(handler);
|
||||||
_handlers.clear();
|
_handlers.clear();
|
||||||
|
|
||||||
delete defaultEndpoint;
|
delete defaultEndpoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PsychicHttpServer::destroy(void * ctx) {
|
void PsychicHttpServer::destroy(void *ctx)
|
||||||
PsychicHttpServer * temp = (PsychicHttpServer *)ctx;
|
{
|
||||||
delete temp;
|
PsychicHttpServer *temp = (PsychicHttpServer *)ctx;
|
||||||
|
delete temp;
|
||||||
}
|
}
|
||||||
|
|
||||||
esp_err_t PsychicHttpServer::listen(uint16_t port) {
|
esp_err_t PsychicHttpServer::listen(uint16_t port)
|
||||||
this->_use_ssl = false;
|
{
|
||||||
this->config.server_port = port;
|
this->_use_ssl = false;
|
||||||
|
this->config.server_port = port;
|
||||||
|
|
||||||
return this->_start();
|
return this->_start();
|
||||||
}
|
}
|
||||||
|
|
||||||
esp_err_t PsychicHttpServer::_start() {
|
esp_err_t PsychicHttpServer::_start()
|
||||||
esp_err_t ret;
|
{
|
||||||
|
esp_err_t ret;
|
||||||
|
|
||||||
#ifdef ENABLE_ASYNC
|
#ifdef ENABLE_ASYNC
|
||||||
// start workers
|
// start workers
|
||||||
start_async_req_workers();
|
start_async_req_workers();
|
||||||
#endif
|
#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));
|
|
||||||
|
|
||||||
|
//fire it up.
|
||||||
|
ret = _startServer();
|
||||||
|
if (ret != ESP_OK)
|
||||||
|
{
|
||||||
|
ESP_LOGE(PH_TAG, "Server start failed (%s)", esp_err_to_name(ret));
|
||||||
return 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() {
|
esp_err_t PsychicHttpServer::_startServer() {
|
||||||
return httpd_start(&this->server, &this->config);
|
return httpd_start(&this->server, &this->config);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PsychicHttpServer::stop() {
|
void PsychicHttpServer::stop()
|
||||||
httpd_stop(this->server);
|
{
|
||||||
|
httpd_stop(this->server);
|
||||||
}
|
}
|
||||||
|
|
||||||
PsychicHandler & PsychicHttpServer::addHandler(PsychicHandler * handler) {
|
PsychicHandler& PsychicHttpServer::addHandler(PsychicHandler* handler){
|
||||||
_handlers.push_back(handler);
|
_handlers.push_back(handler);
|
||||||
return *handler;
|
return *handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PsychicHttpServer::removeHandler(PsychicHandler * handler) {
|
void PsychicHttpServer::removeHandler(PsychicHandler *handler){
|
||||||
_handlers.remove(handler);
|
_handlers.remove(handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
PsychicEndpoint * PsychicHttpServer::on(const char * uri) {
|
PsychicEndpoint* PsychicHttpServer::on(const char* uri) {
|
||||||
return on(uri, HTTP_GET);
|
return on(uri, HTTP_GET);
|
||||||
}
|
}
|
||||||
|
|
||||||
PsychicEndpoint * PsychicHttpServer::on(const char * uri, PsychicHttpRequestCallback fn) {
|
PsychicEndpoint* PsychicHttpServer::on(const char* uri, http_method method)
|
||||||
return on(uri, HTTP_GET, fn);
|
{
|
||||||
|
PsychicWebHandler *handler = new PsychicWebHandler();
|
||||||
|
|
||||||
|
return on(uri, method, handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
PsychicEndpoint * PsychicHttpServer::on(const char * uri, http_method method, PsychicHttpRequestCallback fn) {
|
PsychicEndpoint* PsychicHttpServer::on(const char* uri, PsychicHandler *handler)
|
||||||
//these basic requests need a basic web handler
|
{
|
||||||
PsychicWebHandler * handler = new PsychicWebHandler();
|
return on(uri, HTTP_GET, handler);
|
||||||
handler->onRequest(fn);
|
|
||||||
|
|
||||||
return on(uri, method, handler);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// added by Proddy
|
PsychicEndpoint* PsychicHttpServer::on(const char* uri, http_method method, PsychicHandler *handler)
|
||||||
PsychicEndpoint * PsychicHttpServer::on(const char * uri, http_method method, PsychicJsonRequestCallback fn) {
|
{
|
||||||
PsychicJsonHandler * handler = new PsychicJsonHandler(fn);
|
//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) {
|
PsychicEndpoint* PsychicHttpServer::on(const char* uri, PsychicHttpRequestCallback fn)
|
||||||
PsychicWebHandler * handler = new PsychicWebHandler();
|
{
|
||||||
|
return on(uri, HTTP_GET, fn);
|
||||||
return on(uri, method, handler);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PsychicEndpoint * PsychicHttpServer::on(const char * uri, PsychicHandler * handler) {
|
PsychicEndpoint* PsychicHttpServer::on(const char* uri, http_method method, PsychicHttpRequestCallback fn)
|
||||||
return on(uri, HTTP_GET, handler);
|
{
|
||||||
|
//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) {
|
PsychicEndpoint* PsychicHttpServer::on(const char* uri, PsychicJsonRequestCallback fn)
|
||||||
//make our endpoint
|
{
|
||||||
PsychicEndpoint * endpoint = new PsychicEndpoint(this, method, uri);
|
return on(uri, HTTP_GET, fn);
|
||||||
|
|
||||||
//set our handler
|
|
||||||
endpoint->setHandler(handler);
|
|
||||||
|
|
||||||
// URI handler structure
|
|
||||||
httpd_uri_t my_uri{.uri = uri, .method = method, .handler = PsychicEndpoint::requestCallback, .user_ctx = endpoint, .is_websocket = handler->isWebSocket()};
|
|
||||||
|
|
||||||
// Register endpoint with ESP-IDF server
|
|
||||||
esp_err_t ret = httpd_register_uri_handler(this->server, &my_uri);
|
|
||||||
if (ret != ESP_OK) {
|
|
||||||
ESP_LOGE(PH_TAG, "Add endpoint %s failed (%s)", uri, esp_err_to_name(ret)); // modified by proddy
|
|
||||||
}
|
|
||||||
|
|
||||||
//save it for later
|
|
||||||
_endpoints.push_back(endpoint);
|
|
||||||
|
|
||||||
return endpoint;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PsychicHttpServer::onNotFound(PsychicHttpRequestCallback fn) {
|
PsychicEndpoint* PsychicHttpServer::on(const char* uri, http_method method, PsychicJsonRequestCallback fn)
|
||||||
PsychicWebHandler * handler = new PsychicWebHandler();
|
{
|
||||||
handler->onRequest(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) {
|
void PsychicHttpServer::onNotFound(PsychicHttpRequestCallback fn)
|
||||||
PsychicHttpServer * server = (PsychicHttpServer *)httpd_get_global_user_ctx(req->handle);
|
{
|
||||||
PsychicRequest request(server, req);
|
PsychicWebHandler *handler = new PsychicWebHandler();
|
||||||
|
handler->onRequest(fn);
|
||||||
|
|
||||||
//loop through our global handlers and see if anyone wants it
|
this->defaultEndpoint->setHandler(handler);
|
||||||
for (auto * handler : server->_handlers) {
|
}
|
||||||
//are we capable of handling this?
|
|
||||||
if (handler->filter(&request) && handler->canHandle(&request)) {
|
|
||||||
//check our credentials
|
|
||||||
if (handler->needsAuthentication(&request))
|
|
||||||
return handler->authenticate(&request);
|
|
||||||
else
|
|
||||||
return handler->handleRequest(&request);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//nothing found, give it to our defaultEndpoint
|
esp_err_t PsychicHttpServer::notFoundHandler(httpd_req_t *req, httpd_err_code_t err)
|
||||||
PsychicHandler * handler = server->defaultEndpoint->handler();
|
{
|
||||||
|
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))
|
if (handler->filter(&request) && handler->canHandle(&request))
|
||||||
|
{
|
||||||
|
//check our credentials
|
||||||
|
if (handler->needsAuthentication(&request))
|
||||||
|
return handler->authenticate(&request);
|
||||||
|
else
|
||||||
return handler->handleRequest(&request);
|
return handler->handleRequest(&request);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//not sure how we got this far.
|
//nothing found, give it to our defaultEndpoint
|
||||||
return ESP_ERR_HTTPD_INVALID_REQ;
|
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) {
|
esp_err_t PsychicHttpServer::defaultNotFoundHandler(PsychicRequest *request)
|
||||||
request->reply(404, "text/html", "That URI does not exist.");
|
{
|
||||||
|
request->reply(404, "text/html", "That URI does not exist.");
|
||||||
|
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PsychicHttpServer::onOpen(PsychicClientCallback handler) {
|
void PsychicHttpServer::onOpen(PsychicClientCallback handler) {
|
||||||
this->_onOpen = handler;
|
this->_onOpen = handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
esp_err_t PsychicHttpServer::openCallback(httpd_handle_t hd, int sockfd) {
|
esp_err_t PsychicHttpServer::openCallback(httpd_handle_t hd, int sockfd)
|
||||||
ESP_LOGI(PH_TAG, "New client connected %d", sockfd);
|
{
|
||||||
|
ESP_LOGI(PH_TAG, "New client connected %d", sockfd);
|
||||||
|
|
||||||
//get our global server reference
|
//get our global server reference
|
||||||
PsychicHttpServer * server = (PsychicHttpServer *)httpd_get_global_user_ctx(hd);
|
PsychicHttpServer *server = (PsychicHttpServer*)httpd_get_global_user_ctx(hd);
|
||||||
|
|
||||||
//lookup our client
|
//lookup our client
|
||||||
PsychicClient * client = server->getClient(sockfd);
|
PsychicClient *client = server->getClient(sockfd);
|
||||||
if (client == NULL) {
|
if (client == NULL)
|
||||||
client = new PsychicClient(hd, sockfd);
|
{
|
||||||
server->addClient(client);
|
client = new PsychicClient(hd, sockfd);
|
||||||
}
|
server->addClient(client);
|
||||||
|
}
|
||||||
|
|
||||||
//user callback
|
//user callback
|
||||||
if (server->_onOpen != NULL)
|
if (server->_onOpen != NULL)
|
||||||
server->_onOpen(client);
|
server->_onOpen(client);
|
||||||
|
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PsychicHttpServer::onClose(PsychicClientCallback handler) {
|
void PsychicHttpServer::onClose(PsychicClientCallback handler) {
|
||||||
this->_onClose = handler;
|
this->_onClose = handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PsychicHttpServer::closeCallback(httpd_handle_t hd, int sockfd) {
|
void PsychicHttpServer::closeCallback(httpd_handle_t hd, int sockfd)
|
||||||
ESP_LOGI(PH_TAG, "Client disconnected %d", sockfd);
|
{
|
||||||
|
ESP_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
|
//lookup our client
|
||||||
PsychicClient * client = server->getClient(sockfd);
|
PsychicClient *client = server->getClient(sockfd);
|
||||||
if (client != NULL) {
|
if (client != NULL)
|
||||||
//give our handlers a chance to handle a disconnect first
|
{
|
||||||
for (PsychicEndpoint * endpoint : server->_endpoints) {
|
//give our handlers a chance to handle a disconnect first
|
||||||
PsychicHandler * handler = endpoint->handler();
|
for (PsychicEndpoint * endpoint : server->_endpoints)
|
||||||
handler->checkForClosedClient(client);
|
{
|
||||||
}
|
PsychicHandler *handler = endpoint->handler();
|
||||||
|
handler->checkForClosedClient(client);
|
||||||
|
}
|
||||||
|
|
||||||
//do we have a callback attached?
|
//do we have a callback attached?
|
||||||
if (server->_onClose != NULL)
|
if (server->_onClose != NULL)
|
||||||
server->_onClose(client);
|
server->_onClose(client);
|
||||||
|
|
||||||
//remove it from our list
|
//remove it from our list
|
||||||
server->removeClient(client);
|
server->removeClient(client);
|
||||||
} else
|
}
|
||||||
ESP_LOGE(PH_TAG, "No client record %d", sockfd);
|
else
|
||||||
|
ESP_LOGE(PH_TAG, "No client record %d", sockfd);
|
||||||
|
|
||||||
//finally close it out.
|
//finally close it out.
|
||||||
close(sockfd);
|
close(sockfd);
|
||||||
}
|
}
|
||||||
|
|
||||||
PsychicStaticFileHandler * PsychicHttpServer::serveStatic(const char * uri, fs::FS & fs, const char * path, const char * cache_control) {
|
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* handler = new PsychicStaticFileHandler(uri, fs, path, cache_control);
|
||||||
|
this->addHandler(handler);
|
||||||
|
|
||||||
return handler;
|
return handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PsychicHttpServer::addClient(PsychicClient * client) {
|
void PsychicHttpServer::addClient(PsychicClient *client) {
|
||||||
_clients.push_back(client);
|
_clients.push_back(client);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PsychicHttpServer::removeClient(PsychicClient * client) {
|
void PsychicHttpServer::removeClient(PsychicClient *client) {
|
||||||
_clients.remove(client);
|
_clients.remove(client);
|
||||||
delete client;
|
delete client;
|
||||||
}
|
}
|
||||||
|
|
||||||
PsychicClient * PsychicHttpServer::getClient(int socket) {
|
PsychicClient * PsychicHttpServer::getClient(int socket) {
|
||||||
for (PsychicClient * client : _clients)
|
for (PsychicClient * client : _clients)
|
||||||
if (client->socket() == socket)
|
if (client->socket() == socket)
|
||||||
return client;
|
return client;
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
PsychicClient * PsychicHttpServer::getClient(httpd_req_t * req) {
|
PsychicClient * PsychicHttpServer::getClient(httpd_req_t *req) {
|
||||||
return getClient(httpd_req_to_sockfd(req));
|
return getClient(httpd_req_to_sockfd(req));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PsychicHttpServer::hasClient(int socket) {
|
bool PsychicHttpServer::hasClient(int socket) {
|
||||||
return getClient(socket) != NULL;
|
return getClient(socket) != NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::list<PsychicClient *> & PsychicHttpServer::getClientList() {
|
const std::list<PsychicClient*>& PsychicHttpServer::getClientList() {
|
||||||
return _clients;
|
return _clients;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ON_STA_FILTER(PsychicRequest * request) {
|
bool ON_STA_FILTER(PsychicRequest *request) {
|
||||||
return WiFi.localIP() == request->client()->localIP();
|
return WiFi.localIP() == request->client()->localIP();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ON_AP_FILTER(PsychicRequest * request) {
|
bool ON_AP_FILTER(PsychicRequest *request) {
|
||||||
return WiFi.softAPIP() == request->client()->localIP();
|
return WiFi.softAPIP() == request->client()->localIP();
|
||||||
}
|
}
|
||||||
|
|
||||||
String urlDecode(const char * encoded) {
|
String urlDecode(const char* encoded)
|
||||||
size_t length = strlen(encoded);
|
{
|
||||||
char * decoded = (char *)malloc(length + 1);
|
size_t length = strlen(encoded);
|
||||||
if (!decoded) {
|
char* decoded = (char*)malloc(length + 1);
|
||||||
return "";
|
if (!decoded) {
|
||||||
}
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
size_t i, j = 0;
|
size_t i, j = 0;
|
||||||
for (i = 0; i < length; ++i) {
|
for (i = 0; i < length; ++i) {
|
||||||
if (encoded[i] == '%' && isxdigit(encoded[i + 1]) && isxdigit(encoded[i + 2])) {
|
if (encoded[i] == '%' && isxdigit(encoded[i + 1]) && isxdigit(encoded[i + 2])) {
|
||||||
// Valid percent-encoded sequence
|
// Valid percent-encoded sequence
|
||||||
int hex;
|
int hex;
|
||||||
sscanf(encoded + i + 1, "%2x", &hex);
|
sscanf(encoded + i + 1, "%2x", &hex);
|
||||||
decoded[j++] = (char)hex;
|
decoded[j++] = (char)hex;
|
||||||
i += 2; // Skip the two hexadecimal characters
|
i += 2; // Skip the two hexadecimal characters
|
||||||
} else if (encoded[i] == '+') {
|
} else if (encoded[i] == '+') {
|
||||||
// Convert '+' to space
|
// Convert '+' to space
|
||||||
decoded[j++] = ' ';
|
decoded[j++] = ' ';
|
||||||
} else {
|
} else {
|
||||||
// Copy other characters as they are
|
// Copy other characters as they are
|
||||||
decoded[j++] = encoded[i];
|
decoded[j++] = encoded[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
decoded[j] = '\0'; // Null-terminate the decoded string
|
decoded[j] = '\0'; // Null-terminate the decoded string
|
||||||
|
|
||||||
String output(decoded);
|
String output(decoded);
|
||||||
free(decoded);
|
free(decoded);
|
||||||
|
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
@@ -4,27 +4,23 @@
|
|||||||
#include "PsychicCore.h"
|
#include "PsychicCore.h"
|
||||||
#include "PsychicClient.h"
|
#include "PsychicClient.h"
|
||||||
#include "PsychicHandler.h"
|
#include "PsychicHandler.h"
|
||||||
#include <ArduinoJson.h> // added by proddy
|
|
||||||
|
|
||||||
class PsychicEndpoint;
|
class PsychicEndpoint;
|
||||||
class PsychicHandler;
|
class PsychicHandler;
|
||||||
class PsychicStaticFileHandler;
|
class PsychicStaticFileHandler;
|
||||||
|
|
||||||
//callback definitions
|
class PsychicHttpServer
|
||||||
typedef std::function<esp_err_t(PsychicRequest * request)> PsychicHttpRequestCallback;
|
{
|
||||||
typedef std::function<esp_err_t(PsychicRequest * request, JsonVariant & json)> PsychicJsonRequestCallback; // added by proddy
|
|
||||||
|
|
||||||
class PsychicHttpServer {
|
|
||||||
protected:
|
protected:
|
||||||
bool _use_ssl = false;
|
bool _use_ssl = false;
|
||||||
std::list<PsychicEndpoint *> _endpoints;
|
std::list<PsychicEndpoint*> _endpoints;
|
||||||
std::list<PsychicHandler *> _handlers;
|
std::list<PsychicHandler*> _handlers;
|
||||||
std::list<PsychicClient *> _clients;
|
std::list<PsychicClient*> _clients;
|
||||||
|
|
||||||
PsychicClientCallback _onOpen;
|
PsychicClientCallback _onOpen;
|
||||||
PsychicClientCallback _onClose;
|
PsychicClientCallback _onClose;
|
||||||
|
|
||||||
esp_err_t _start();
|
esp_err_t _start();
|
||||||
virtual esp_err_t _startServer();
|
virtual esp_err_t _startServer();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@@ -39,48 +35,47 @@ class PsychicHttpServer {
|
|||||||
unsigned long maxUploadSize;
|
unsigned long maxUploadSize;
|
||||||
unsigned long maxRequestBodySize;
|
unsigned long maxRequestBodySize;
|
||||||
|
|
||||||
PsychicEndpoint * defaultEndpoint;
|
PsychicEndpoint *defaultEndpoint;
|
||||||
|
|
||||||
static void destroy(void * ctx);
|
static void destroy(void *ctx);
|
||||||
|
|
||||||
esp_err_t listen(uint16_t port);
|
esp_err_t listen(uint16_t port);
|
||||||
|
|
||||||
virtual void stop();
|
virtual void stop();
|
||||||
|
|
||||||
PsychicHandler & addHandler(PsychicHandler * handler);
|
PsychicHandler& addHandler(PsychicHandler* handler);
|
||||||
void removeHandler(PsychicHandler * handler);
|
void removeHandler(PsychicHandler* handler);
|
||||||
|
|
||||||
void addClient(PsychicClient * client);
|
void addClient(PsychicClient *client);
|
||||||
void removeClient(PsychicClient * client);
|
void removeClient(PsychicClient *client);
|
||||||
PsychicClient * getClient(int socket);
|
PsychicClient* getClient(int socket);
|
||||||
PsychicClient * getClient(httpd_req_t * req);
|
PsychicClient* getClient(httpd_req_t *req);
|
||||||
bool hasClient(int socket);
|
bool hasClient(int socket);
|
||||||
int count() {
|
int count() { return _clients.size(); };
|
||||||
return _clients.size();
|
const std::list<PsychicClient*>& getClientList();
|
||||||
};
|
|
||||||
const std::list<PsychicClient *> & getClientList();
|
|
||||||
|
|
||||||
PsychicEndpoint * on(const char * uri);
|
PsychicEndpoint* on(const char* uri);
|
||||||
PsychicEndpoint * on(const char * uri, http_method method);
|
PsychicEndpoint* on(const char* uri, http_method method);
|
||||||
PsychicEndpoint * on(const char * uri, PsychicHttpRequestCallback onRequest);
|
PsychicEndpoint* on(const char* uri, PsychicHandler *handler);
|
||||||
PsychicEndpoint * on(const char * uri, http_method method, PsychicHttpRequestCallback onRequest);
|
PsychicEndpoint* on(const char* uri, http_method method, PsychicHandler *handler);
|
||||||
PsychicEndpoint * on(const char * uri, PsychicHandler * handler);
|
PsychicEndpoint* on(const char* uri, PsychicHttpRequestCallback onRequest);
|
||||||
PsychicEndpoint * on(const char * uri, http_method method, PsychicHandler * handler);
|
PsychicEndpoint* on(const char* uri, http_method method, PsychicHttpRequestCallback onRequest);
|
||||||
PsychicEndpoint * on(const char * uri, http_method method, PsychicJsonRequestCallback onRequest); // added proddy
|
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 notFoundHandler(httpd_req_t *req, httpd_err_code_t err);
|
||||||
static esp_err_t defaultNotFoundHandler(PsychicRequest * request);
|
static esp_err_t defaultNotFoundHandler(PsychicRequest *request);
|
||||||
void onNotFound(PsychicHttpRequestCallback fn);
|
void onNotFound(PsychicHttpRequestCallback fn);
|
||||||
|
|
||||||
void onOpen(PsychicClientCallback handler);
|
void onOpen(PsychicClientCallback handler);
|
||||||
void onClose(PsychicClientCallback handler);
|
void onClose(PsychicClientCallback handler);
|
||||||
static esp_err_t openCallback(httpd_handle_t hd, int sockfd);
|
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_STA_FILTER(PsychicRequest *request);
|
||||||
bool ON_AP_FILTER(PsychicRequest * request);
|
bool ON_AP_FILTER(PsychicRequest *request);
|
||||||
|
|
||||||
#endif // PsychicHttpServer_h
|
#endif // PsychicHttpServer_h
|
||||||
@@ -1,48 +1,50 @@
|
|||||||
#include "PsychicHttpsServer.h"
|
#include "PsychicHttpsServer.h"
|
||||||
|
|
||||||
PsychicHttpsServer::PsychicHttpsServer()
|
PsychicHttpsServer::PsychicHttpsServer() : PsychicHttpServer()
|
||||||
: PsychicHttpServer() {
|
{
|
||||||
//for a SSL server
|
//for a SSL server
|
||||||
ssl_config = HTTPD_SSL_CONFIG_DEFAULT();
|
ssl_config = HTTPD_SSL_CONFIG_DEFAULT();
|
||||||
ssl_config.httpd.open_fn = PsychicHttpServer::openCallback;
|
ssl_config.httpd.open_fn = PsychicHttpServer::openCallback;
|
||||||
ssl_config.httpd.close_fn = PsychicHttpServer::closeCallback;
|
ssl_config.httpd.close_fn = PsychicHttpServer::closeCallback;
|
||||||
ssl_config.httpd.uri_match_fn = httpd_uri_match_wildcard;
|
ssl_config.httpd.uri_match_fn = httpd_uri_match_wildcard;
|
||||||
ssl_config.httpd.global_user_ctx = this;
|
ssl_config.httpd.global_user_ctx = this;
|
||||||
ssl_config.httpd.global_user_ctx_free_fn = destroy;
|
ssl_config.httpd.global_user_ctx_free_fn = destroy;
|
||||||
ssl_config.httpd.max_uri_handlers = 20;
|
ssl_config.httpd.max_uri_handlers = 20;
|
||||||
|
|
||||||
// each SSL connection takes about 45kb of heap
|
// each SSL connection takes about 45kb of heap
|
||||||
// a barebones sketch with PsychicHttp has ~150kb of heap available
|
// 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.
|
// 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.
|
// not to mention there is no heap left over for the program itself.
|
||||||
ssl_config.httpd.max_open_sockets = 2;
|
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) {
|
esp_err_t PsychicHttpsServer::_startServer()
|
||||||
this->_use_ssl = true;
|
{
|
||||||
|
if (this->_use_ssl)
|
||||||
this->ssl_config.port_secure = port;
|
return httpd_ssl_start(&this->server, &this->ssl_config);
|
||||||
this->ssl_config.cacert_pem = (uint8_t *)cert;
|
else
|
||||||
this->ssl_config.cacert_len = strlen(cert) + 1;
|
return httpd_start(&this->server, &this->config);
|
||||||
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() {
|
void PsychicHttpsServer::stop()
|
||||||
if (this->_use_ssl)
|
{
|
||||||
return httpd_ssl_start(&this->server, &this->ssl_config);
|
if (this->_use_ssl)
|
||||||
else
|
httpd_ssl_stop(this->server);
|
||||||
return httpd_start(&this->server, &this->config);
|
else
|
||||||
}
|
httpd_stop(this->server);
|
||||||
|
|
||||||
void PsychicHttpsServer::stop() {
|
|
||||||
if (this->_use_ssl)
|
|
||||||
httpd_ssl_stop(this->server);
|
|
||||||
else
|
|
||||||
httpd_stop(this->server);
|
|
||||||
}
|
}
|
||||||
@@ -6,12 +6,13 @@
|
|||||||
#include <esp_https_server.h>
|
#include <esp_https_server.h>
|
||||||
|
|
||||||
#if !CONFIG_HTTPD_WS_SUPPORT
|
#if !CONFIG_HTTPD_WS_SUPPORT
|
||||||
#error PsychicHttpsServer cannot be used unless HTTPD_WS_SUPPORT is enabled in esp-http-server component configuration
|
#error PsychicHttpsServer cannot be used unless HTTPD_WS_SUPPORT is enabled in esp-http-server component configuration
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define PSY_ENABLE_SSL //you can use this define in your code to enable/disable these features
|
#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:
|
protected:
|
||||||
bool _use_ssl = false;
|
bool _use_ssl = false;
|
||||||
|
|
||||||
@@ -22,10 +23,10 @@ class PsychicHttpsServer : public PsychicHttpServer {
|
|||||||
httpd_ssl_config_t ssl_config;
|
httpd_ssl_config_t ssl_config;
|
||||||
|
|
||||||
using PsychicHttpServer::listen; //keep the regular version
|
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 esp_err_t _startServer() override final;
|
||||||
virtual void stop() override final;
|
virtual void stop() override final;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // PsychicHttpsServer_h
|
#endif // PsychicHttpsServer_h
|
||||||
150
lib/PsychicHttp/src/PsychicJson.DELETEME
Normal file
150
lib/PsychicHttp/src/PsychicJson.DELETEME
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
#include "PsychicJson.h"
|
||||||
|
|
||||||
|
#ifdef ARDUINOJSON_5_COMPATIBILITY
|
||||||
|
PsychicJsonResponse::PsychicJsonResponse(PsychicRequest *request, bool isArray) : PsychicResponse(request)
|
||||||
|
{
|
||||||
|
setContentType(JSON_MIMETYPE);
|
||||||
|
if (isArray)
|
||||||
|
_root = _jsonBuffer.createArray();
|
||||||
|
else
|
||||||
|
_root = _jsonBuffer.createObject();
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
PsychicJsonResponse::PsychicJsonResponse(PsychicRequest *request, bool isArray, size_t maxJsonBufferSize) :
|
||||||
|
PsychicResponse(request),
|
||||||
|
_jsonBuffer(maxJsonBufferSize)
|
||||||
|
{
|
||||||
|
setContentType(JSON_MIMETYPE);
|
||||||
|
if (isArray)
|
||||||
|
_root = _jsonBuffer.createNestedArray();
|
||||||
|
else
|
||||||
|
_root = _jsonBuffer.createNestedObject();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
JsonVariant &PsychicJsonResponse::getRoot() { return _root; }
|
||||||
|
|
||||||
|
size_t PsychicJsonResponse::getLength()
|
||||||
|
{
|
||||||
|
#ifdef ARDUINOJSON_5_COMPATIBILITY
|
||||||
|
return _root.measureLength();
|
||||||
|
#else
|
||||||
|
return measureJson(_root);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t PsychicJsonResponse::send()
|
||||||
|
{
|
||||||
|
esp_err_t err = ESP_OK;
|
||||||
|
size_t length = getLength();
|
||||||
|
size_t buffer_size;
|
||||||
|
char *buffer;
|
||||||
|
|
||||||
|
DUMP(length);
|
||||||
|
|
||||||
|
//how big of a buffer do we want?
|
||||||
|
if (length < JSON_BUFFER_SIZE)
|
||||||
|
buffer_size = length+1;
|
||||||
|
else
|
||||||
|
buffer_size = JSON_BUFFER_SIZE;
|
||||||
|
|
||||||
|
DUMP(buffer_size);
|
||||||
|
|
||||||
|
buffer = (char *)malloc(buffer_size);
|
||||||
|
if (buffer == NULL) {
|
||||||
|
httpd_resp_send_err(this->_request->request(), HTTPD_500_INTERNAL_SERVER_ERROR, "Unable to allocate memory.");
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
//send it in one shot or no?
|
||||||
|
if (length < JSON_BUFFER_SIZE)
|
||||||
|
{
|
||||||
|
TRACE();
|
||||||
|
|
||||||
|
#ifdef ARDUINOJSON_5_COMPATIBILITY
|
||||||
|
_root.printTo(buffer, buffer_size);
|
||||||
|
#else
|
||||||
|
serializeJson(_root, buffer, buffer_size);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
this->setContent((uint8_t *)buffer, length);
|
||||||
|
this->setContentType(JSON_MIMETYPE);
|
||||||
|
|
||||||
|
err = PsychicResponse::send();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//helper class that acts as a stream to print chunked responses
|
||||||
|
ChunkPrinter dest(this, (uint8_t *)buffer, buffer_size);
|
||||||
|
|
||||||
|
//keep our headers
|
||||||
|
this->sendHeaders();
|
||||||
|
|
||||||
|
//these commands write to the ChunkPrinter which does the sending
|
||||||
|
#ifdef ARDUINOJSON_5_COMPATIBILITY
|
||||||
|
_root.printTo(dest);
|
||||||
|
#else
|
||||||
|
serializeJson(_root, dest);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//send the last bits
|
||||||
|
dest.flush();
|
||||||
|
|
||||||
|
//done with our chunked response too
|
||||||
|
err = this->finishChunking();
|
||||||
|
}
|
||||||
|
|
||||||
|
//let the buffer go
|
||||||
|
free(buffer);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef ARDUINOJSON_5_COMPATIBILITY
|
||||||
|
PsychicJsonHandler::PsychicJsonHandler() :
|
||||||
|
_onRequest(NULL)
|
||||||
|
{};
|
||||||
|
|
||||||
|
PsychicJsonHandler::PsychicJsonHandler(PsychicJsonRequestCallback onRequest) :
|
||||||
|
_onRequest(onRequest)
|
||||||
|
{}
|
||||||
|
#else
|
||||||
|
PsychicJsonHandler::PsychicJsonHandler(size_t maxJsonBufferSize) :
|
||||||
|
_onRequest(NULL),
|
||||||
|
_maxJsonBufferSize(maxJsonBufferSize)
|
||||||
|
{};
|
||||||
|
|
||||||
|
PsychicJsonHandler::PsychicJsonHandler(PsychicJsonRequestCallback onRequest, size_t maxJsonBufferSize) :
|
||||||
|
_onRequest(onRequest),
|
||||||
|
_maxJsonBufferSize(maxJsonBufferSize)
|
||||||
|
{}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void PsychicJsonHandler::onRequest(PsychicJsonRequestCallback fn) { _onRequest = fn; }
|
||||||
|
|
||||||
|
esp_err_t PsychicJsonHandler::handleRequest(PsychicRequest *request)
|
||||||
|
{
|
||||||
|
//process basic stuff
|
||||||
|
PsychicWebHandler::handleRequest(request);
|
||||||
|
|
||||||
|
if (_onRequest)
|
||||||
|
{
|
||||||
|
#ifdef ARDUINOJSON_5_COMPATIBILITY
|
||||||
|
DynamicJsonBuffer jsonBuffer;
|
||||||
|
JsonVariant json = jsonBuffer.parse();
|
||||||
|
if (!json.success())
|
||||||
|
return request->reply(400);
|
||||||
|
#else
|
||||||
|
DynamicJsonDocument jsonBuffer(this->_maxJsonBufferSize);
|
||||||
|
DeserializationError error = deserializeJson(jsonBuffer, request->body());
|
||||||
|
if (error)
|
||||||
|
return request->reply(400);
|
||||||
|
|
||||||
|
JsonVariant json = jsonBuffer.as<JsonVariant>();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return _onRequest(request, json);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return request->reply(500);
|
||||||
|
}
|
||||||
@@ -98,10 +98,12 @@ class PsychicJsonResponse : public PsychicResponse {
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
virtual esp_err_t send() override {
|
virtual esp_err_t send() override {
|
||||||
esp_err_t err = ESP_OK;
|
esp_err_t err = ESP_OK;
|
||||||
size_t length = getLength();
|
// size_t length = getLength();
|
||||||
size_t buffer_size;
|
size_t length = JSON_BUFFER_SIZE; // TODO force chunking
|
||||||
char * buffer;
|
|
||||||
|
size_t buffer_size;
|
||||||
|
char * buffer;
|
||||||
|
|
||||||
DUMP(length);
|
DUMP(length);
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#include "PsychicRequest.h"
|
#include "PsychicRequest.h"
|
||||||
#include "PsychicResponse.h"
|
#include "http_status.h"
|
||||||
#include <http_status.h>
|
#include "PsychicHttpServer.h"
|
||||||
|
|
||||||
|
|
||||||
PsychicRequest::PsychicRequest(PsychicHttpServer *server, httpd_req_t *req) :
|
PsychicRequest::PsychicRequest(PsychicHttpServer *server, httpd_req_t *req) :
|
||||||
_server(server),
|
_server(server),
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include "PsychicHttpServer.h"
|
#include "PsychicHttpServer.h"
|
||||||
#include "PsychicClient.h"
|
#include "PsychicClient.h"
|
||||||
#include "PsychicWebParameter.h"
|
#include "PsychicWebParameter.h"
|
||||||
|
#include "PsychicResponse.h"
|
||||||
|
|
||||||
typedef std::map<String, String> SessionData;
|
typedef std::map<String, String> SessionData;
|
||||||
|
|
||||||
|
|||||||
@@ -2,137 +2,154 @@
|
|||||||
#include "PsychicRequest.h"
|
#include "PsychicRequest.h"
|
||||||
#include <http_status.h>
|
#include <http_status.h>
|
||||||
|
|
||||||
PsychicResponse::PsychicResponse(PsychicRequest * request)
|
PsychicResponse::PsychicResponse(PsychicRequest *request) :
|
||||||
: _request(request)
|
_request(request),
|
||||||
, _code(200)
|
_code(200),
|
||||||
, _status("")
|
_status(""),
|
||||||
, _contentLength(0)
|
_contentLength(0),
|
||||||
, _body("") {
|
_body("")
|
||||||
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
PsychicResponse::~PsychicResponse() {
|
PsychicResponse::~PsychicResponse()
|
||||||
//clean up our header variables. we have to do this since httpd_resp_send doesn't store copies
|
{
|
||||||
for (HTTPHeader header : _headers) {
|
//clean up our header variables. we have to do this since httpd_resp_send doesn't store copies
|
||||||
free(header.field);
|
for (HTTPHeader header : _headers)
|
||||||
free(header.value);
|
{
|
||||||
}
|
free(header.field);
|
||||||
_headers.clear();
|
free(header.value);
|
||||||
|
}
|
||||||
|
_headers.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PsychicResponse::addHeader(const char * field, const char * value) {
|
void PsychicResponse::addHeader(const char *field, const char *value)
|
||||||
//these get freed during send()
|
{
|
||||||
HTTPHeader header;
|
//these get freed during send()
|
||||||
header.field = (char *)malloc(strlen(field) + 1);
|
HTTPHeader header;
|
||||||
header.value = (char *)malloc(strlen(value) + 1);
|
header.field =(char *)malloc(strlen(field)+1);
|
||||||
|
header.value = (char *)malloc(strlen(value)+1);
|
||||||
|
|
||||||
strlcpy(header.field, field, strlen(field) + 1);
|
strlcpy(header.field, field, strlen(field)+1);
|
||||||
strlcpy(header.value, value, strlen(value) + 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) {
|
void PsychicResponse::setCookie(const char *name, const char *value, unsigned long secondsFromNow, const char *extras)
|
||||||
time_t now = time(nullptr);
|
{
|
||||||
|
time_t now = time(nullptr);
|
||||||
|
|
||||||
String output;
|
String output;
|
||||||
output = urlEncode(name) + "=" + urlEncode(value);
|
output = urlEncode(name) + "=" + urlEncode(value);
|
||||||
|
|
||||||
//if current time isn't modern, default to using max age
|
//if current time isn't modern, default to using max age
|
||||||
if (now < 1700000000)
|
if (now < 1700000000)
|
||||||
output += "; Max-Age=" + String(secondsFromNow);
|
output += "; Max-Age=" + String(secondsFromNow);
|
||||||
//otherwise, set an expiration date
|
//otherwise, set an expiration date
|
||||||
else {
|
else
|
||||||
time_t expirationTimestamp = now + secondsFromNow;
|
{
|
||||||
|
time_t expirationTimestamp = now + secondsFromNow;
|
||||||
|
|
||||||
// Convert the expiration timestamp to a formatted string for the "expires" attribute
|
// Convert the expiration timestamp to a formatted string for the "expires" attribute
|
||||||
struct tm * tmInfo = gmtime(&expirationTimestamp);
|
struct tm* tmInfo = gmtime(&expirationTimestamp);
|
||||||
char expires[30];
|
char expires[30];
|
||||||
strftime(expires, sizeof(expires), "%a, %d %b %Y %H:%M:%S GMT", tmInfo);
|
strftime(expires, sizeof(expires), "%a, %d %b %Y %H:%M:%S GMT", tmInfo);
|
||||||
output += "; Expires=" + String(expires);
|
output += "; Expires=" + String(expires);
|
||||||
}
|
}
|
||||||
|
|
||||||
//did we get any extras?
|
//did we get any extras?
|
||||||
if (strlen(extras))
|
if (strlen(extras))
|
||||||
output += "; " + String(extras);
|
output += "; " + String(extras);
|
||||||
|
|
||||||
//okay, add it in.
|
//okay, add it in.
|
||||||
addHeader("Set-Cookie", output.c_str());
|
addHeader("Set-Cookie", output.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
// time_t now = time(nullptr);
|
// time_t now = time(nullptr);
|
||||||
// // Set the cookie with the "expires" attribute
|
// // Set the cookie with the "expires" attribute
|
||||||
|
|
||||||
void PsychicResponse::setCode(int code) {
|
void PsychicResponse::setCode(int code)
|
||||||
_code = code;
|
{
|
||||||
|
_code = code;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PsychicResponse::setContentType(const char * contentType) {
|
void PsychicResponse::setContentType(const char *contentType)
|
||||||
httpd_resp_set_type(_request->request(), contentType);
|
{
|
||||||
|
httpd_resp_set_type(_request->request(), contentType);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PsychicResponse::setContent(const char * content) {
|
void PsychicResponse::setContent(const char *content)
|
||||||
_body = content;
|
{
|
||||||
setContentLength(strlen(content));
|
_body = content;
|
||||||
|
setContentLength(strlen(content));
|
||||||
}
|
}
|
||||||
|
|
||||||
void PsychicResponse::setContent(const uint8_t * content, size_t len) {
|
void PsychicResponse::setContent(const uint8_t *content, size_t len)
|
||||||
_body = (char *)content;
|
{
|
||||||
setContentLength(len);
|
_body = (char *)content;
|
||||||
|
setContentLength(len);
|
||||||
}
|
}
|
||||||
|
|
||||||
const char * PsychicResponse::getContent() {
|
const char * PsychicResponse::getContent()
|
||||||
return _body;
|
{
|
||||||
|
return _body;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t PsychicResponse::getContentLength() {
|
size_t PsychicResponse::getContentLength()
|
||||||
return _contentLength;
|
{
|
||||||
|
return _contentLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
esp_err_t PsychicResponse::send() {
|
esp_err_t PsychicResponse::send()
|
||||||
//esp-idf makes you set the whole status.
|
{
|
||||||
sprintf(_status, "%u %s", _code, http_status_reason(_code));
|
//esp-idf makes you set the whole status.
|
||||||
httpd_resp_set_status(_request->request(), _status);
|
sprintf(_status, "%u %s", _code, http_status_reason(_code));
|
||||||
|
httpd_resp_set_status(_request->request(), _status);
|
||||||
|
|
||||||
//our headers too
|
//our headers too
|
||||||
this->sendHeaders();
|
this->sendHeaders();
|
||||||
|
|
||||||
//now send it off
|
//now send it off
|
||||||
esp_err_t err = httpd_resp_send(_request->request(), getContent(), getContentLength());
|
esp_err_t err = httpd_resp_send(_request->request(), getContent(), getContentLength());
|
||||||
|
|
||||||
//did something happen?
|
//did something happen?
|
||||||
if (err != ESP_OK)
|
if (err != ESP_OK)
|
||||||
ESP_LOGE(PH_TAG, "Send response failed (%s)", esp_err_to_name(err));
|
ESP_LOGE(PH_TAG, "Send response failed (%s)", esp_err_to_name(err));
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PsychicResponse::sendHeaders() {
|
void PsychicResponse::sendHeaders()
|
||||||
//get our global headers out of the way first
|
{
|
||||||
for (HTTPHeader header : DefaultHeaders::Instance().getHeaders())
|
//get our global headers out of the way first
|
||||||
httpd_resp_set_hdr(_request->request(), header.field, header.value);
|
for (HTTPHeader header : DefaultHeaders::Instance().getHeaders())
|
||||||
|
httpd_resp_set_hdr(_request->request(), header.field, header.value);
|
||||||
|
|
||||||
//now do our individual headers
|
//now do our individual headers
|
||||||
for (HTTPHeader header : _headers)
|
for (HTTPHeader header : _headers)
|
||||||
httpd_resp_set_hdr(this->_request->request(), header.field, header.value);
|
httpd_resp_set_hdr(this->_request->request(), header.field, header.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
esp_err_t PsychicResponse::sendChunk(uint8_t * chunk, size_t chunksize) {
|
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);
|
/* Send the buffer contents as HTTP response chunk */
|
||||||
if (err != ESP_OK) {
|
esp_err_t err = httpd_resp_send_chunk(this->_request->request(), (char *)chunk, chunksize);
|
||||||
ESP_LOGE(PH_TAG, "File sending failed (%s)", esp_err_to_name(err));
|
if (err != ESP_OK)
|
||||||
|
{
|
||||||
|
ESP_LOGE(PH_TAG, "File sending failed (%s)", esp_err_to_name(err));
|
||||||
|
|
||||||
/* Abort sending file */
|
/* Abort sending file */
|
||||||
httpd_resp_sendstr_chunk(this->_request->request(), NULL);
|
httpd_resp_sendstr_chunk(this->_request->request(), NULL);
|
||||||
|
|
||||||
/* Respond with 500 Internal Server Error */
|
/* Respond with 500 Internal Server Error */
|
||||||
httpd_resp_send_err(this->_request->request(), HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to send file");
|
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() {
|
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);
|
/* Respond with an empty chunk to signal HTTP response completion */
|
||||||
|
return httpd_resp_send_chunk(this->_request->request(), NULL, 0);
|
||||||
}
|
}
|
||||||
@@ -6,44 +6,41 @@
|
|||||||
|
|
||||||
class PsychicRequest;
|
class PsychicRequest;
|
||||||
|
|
||||||
class PsychicResponse {
|
class PsychicResponse
|
||||||
|
{
|
||||||
protected:
|
protected:
|
||||||
PsychicRequest * _request;
|
PsychicRequest *_request;
|
||||||
|
|
||||||
int _code;
|
int _code;
|
||||||
char _status[60];
|
char _status[60];
|
||||||
std::list<HTTPHeader> _headers;
|
std::list<HTTPHeader> _headers;
|
||||||
int64_t _contentLength;
|
int64_t _contentLength;
|
||||||
const char * _body;
|
const char * _body;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
PsychicResponse(PsychicRequest * request);
|
PsychicResponse(PsychicRequest *request);
|
||||||
virtual ~PsychicResponse();
|
virtual ~PsychicResponse();
|
||||||
|
|
||||||
void setCode(int code);
|
void setCode(int code);
|
||||||
|
|
||||||
void setContentType(const char * contentType);
|
void setContentType(const char *contentType);
|
||||||
void setContentLength(int64_t contentLength) {
|
void setContentLength(int64_t contentLength) { _contentLength = contentLength; }
|
||||||
_contentLength = contentLength;
|
int64_t getContentLength(int64_t contentLength) { return _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 char *content);
|
||||||
void setContent(const uint8_t * content, size_t len);
|
void setContent(const uint8_t *content, size_t len);
|
||||||
|
|
||||||
const char * getContent();
|
const char * getContent();
|
||||||
size_t getContentLength();
|
size_t getContentLength();
|
||||||
|
|
||||||
virtual esp_err_t send();
|
virtual esp_err_t send();
|
||||||
void sendHeaders();
|
void sendHeaders();
|
||||||
esp_err_t sendChunk(uint8_t * chunk, size_t chunksize);
|
esp_err_t sendChunk(uint8_t *chunk, size_t chunksize);
|
||||||
esp_err_t finishChunking();
|
esp_err_t finishChunking();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // PsychicResponse_h
|
#endif // PsychicResponse_h
|
||||||
@@ -4,192 +4,190 @@
|
|||||||
/* PsychicStaticFileHandler */
|
/* PsychicStaticFileHandler */
|
||||||
/*************************************/
|
/*************************************/
|
||||||
|
|
||||||
PsychicStaticFileHandler::PsychicStaticFileHandler(const char * uri, FS & fs, const char * path, const char * cache_control)
|
PsychicStaticFileHandler::PsychicStaticFileHandler(const char* uri, FS& fs, const char* path, const char* cache_control)
|
||||||
: _fs(fs)
|
: _fs(fs), _uri(uri), _path(path), _default_file("index.html"), _cache_control(cache_control), _last_modified("")
|
||||||
, _uri(uri)
|
{
|
||||||
, _path(path)
|
// Ensure leading '/'
|
||||||
, _default_file("index.html")
|
if (_uri.length() == 0 || _uri[0] != '/') _uri = "/" + _uri;
|
||||||
, _cache_control(cache_control)
|
if (_path.length() == 0 || _path[0] != '/') _path = "/" + _path;
|
||||||
, _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.
|
// 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.
|
// However - if it does not end with '/' we, can't assume a file, path can still be a directory.
|
||||||
_isDir = _path[_path.length() - 1] == '/';
|
_isDir = _path[_path.length()-1] == '/';
|
||||||
|
|
||||||
// Remove the trailing '/' so we can handle default file
|
// Remove the trailing '/' so we can handle default file
|
||||||
// Notice that root will be "" not "/"
|
// Notice that root will be "" not "/"
|
||||||
if (_uri[_uri.length() - 1] == '/')
|
if (_uri[_uri.length()-1] == '/') _uri = _uri.substring(0, _uri.length()-1);
|
||||||
_uri = _uri.substring(0, _uri.length() - 1);
|
if (_path[_path.length()-1] == '/') _path = _path.substring(0, _path.length()-1);
|
||||||
if (_path[_path.length() - 1] == '/')
|
|
||||||
_path = _path.substring(0, _path.length() - 1);
|
|
||||||
|
|
||||||
// Reset stats
|
// Reset stats
|
||||||
_gzipFirst = false;
|
_gzipFirst = false;
|
||||||
_gzipStats = 0xF8;
|
_gzipStats = 0xF8;
|
||||||
}
|
}
|
||||||
|
|
||||||
PsychicStaticFileHandler & PsychicStaticFileHandler::setIsDir(bool isDir) {
|
PsychicStaticFileHandler& PsychicStaticFileHandler::setIsDir(bool isDir){
|
||||||
_isDir = isDir;
|
_isDir = isDir;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
PsychicStaticFileHandler & PsychicStaticFileHandler::setDefaultFile(const char * filename) {
|
PsychicStaticFileHandler& PsychicStaticFileHandler::setDefaultFile(const char* filename){
|
||||||
_default_file = String(filename);
|
_default_file = String(filename);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
PsychicStaticFileHandler & PsychicStaticFileHandler::setCacheControl(const char * cache_control) {
|
PsychicStaticFileHandler& PsychicStaticFileHandler::setCacheControl(const char* cache_control){
|
||||||
_cache_control = String(cache_control);
|
_cache_control = String(cache_control);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
PsychicStaticFileHandler & PsychicStaticFileHandler::setLastModified(const char * last_modified) {
|
PsychicStaticFileHandler& PsychicStaticFileHandler::setLastModified(const char* last_modified){
|
||||||
_last_modified = String(last_modified);
|
_last_modified = String(last_modified);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
PsychicStaticFileHandler & PsychicStaticFileHandler::setLastModified(struct tm * last_modified) {
|
PsychicStaticFileHandler& PsychicStaticFileHandler::setLastModified(struct tm* last_modified){
|
||||||
char result[30];
|
char result[30];
|
||||||
strftime(result, 30, "%a, %d %b %Y %H:%M:%S %Z", last_modified);
|
strftime (result,30,"%a, %d %b %Y %H:%M:%S %Z", last_modified);
|
||||||
return setLastModified((const char *)result);
|
return setLastModified((const char *)result);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PsychicStaticFileHandler::canHandle(PsychicRequest * request) {
|
bool PsychicStaticFileHandler::canHandle(PsychicRequest *request)
|
||||||
if (request->method() != HTTP_GET || !request->uri().startsWith(_uri))
|
{
|
||||||
return false;
|
if(request->method() != HTTP_GET || !request->uri().startsWith(_uri) )
|
||||||
|
|
||||||
if (_getFile(request))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
if (_getFile(request))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PsychicStaticFileHandler::_getFile(PsychicRequest * request) {
|
bool PsychicStaticFileHandler::_getFile(PsychicRequest *request)
|
||||||
// Remove the found uri
|
{
|
||||||
String path = request->uri().substring(_uri.length());
|
// 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 '/'
|
// 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] == '/');
|
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
|
// Do we have a file or .gz file
|
||||||
if (!canSkipFileCheck && _fileExists(path))
|
if (!canSkipFileCheck && _fileExists(path))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
// Can't handle if not default file
|
// Can't handle if not default file
|
||||||
if (_default_file.length() == 0)
|
if (_default_file.length() == 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Try to add default file, ensure there is a trailing '/' ot the path.
|
// Try to add default file, ensure there is a trailing '/' ot the path.
|
||||||
if (path.length() == 0 || path[path.length() - 1] != '/')
|
if (path.length() == 0 || path[path.length()-1] != '/')
|
||||||
path += "/";
|
path += "/";
|
||||||
path += _default_file;
|
path += _default_file;
|
||||||
|
|
||||||
return _fileExists(path);
|
return _fileExists(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define FILE_IS_REAL(f) (f == true && !f.isDirectory())
|
#define FILE_IS_REAL(f) (f == true && !f.isDirectory())
|
||||||
|
|
||||||
bool PsychicStaticFileHandler::_fileExists(const String & path) {
|
bool PsychicStaticFileHandler::_fileExists(const String& path)
|
||||||
bool fileFound = false;
|
{
|
||||||
bool gzipFound = false;
|
bool fileFound = false;
|
||||||
|
bool gzipFound = false;
|
||||||
|
|
||||||
String gzip = path + ".gz";
|
String gzip = path + ".gz";
|
||||||
|
|
||||||
if (_gzipFirst) {
|
if (_gzipFirst) {
|
||||||
_file = _fs.open(gzip, "r");
|
_file = _fs.open(gzip, "r");
|
||||||
gzipFound = FILE_IS_REAL(_file);
|
gzipFound = FILE_IS_REAL(_file);
|
||||||
if (!gzipFound) {
|
if (!gzipFound){
|
||||||
_file = _fs.open(path, "r");
|
_file = _fs.open(path, "r");
|
||||||
fileFound = FILE_IS_REAL(_file);
|
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
bool found = fileFound || gzipFound;
|
_file = _fs.open(path, "r");
|
||||||
|
fileFound = FILE_IS_REAL(_file);
|
||||||
if (found) {
|
if (!fileFound){
|
||||||
_filename = path;
|
_file = _fs.open(gzip, "r");
|
||||||
|
gzipFound = FILE_IS_REAL(_file);
|
||||||
// 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;
|
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 PsychicStaticFileHandler::_countBits(const uint8_t value) const
|
||||||
uint8_t w = value;
|
{
|
||||||
uint8_t n;
|
uint8_t w = value;
|
||||||
for (n = 0; w != 0; n++)
|
uint8_t n;
|
||||||
w &= w - 1;
|
for (n=0; w!=0; n++) w&=w-1;
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
esp_err_t PsychicStaticFileHandler::handleRequest(PsychicRequest * request) {
|
esp_err_t PsychicStaticFileHandler::handleRequest(PsychicRequest *request)
|
||||||
if (_file == true) {
|
{
|
||||||
DUMP(_filename);
|
if (_file == true)
|
||||||
|
{
|
||||||
|
DUMP(_filename);
|
||||||
|
|
||||||
//is it not modified?
|
//is it not modified?
|
||||||
String etag = String(_file.size());
|
String etag = String(_file.size());
|
||||||
if (_last_modified.length() && _last_modified == request->header("If-Modified-Since")) {
|
if (_last_modified.length() && _last_modified == request->header("If-Modified-Since"))
|
||||||
DUMP("Last Modified Hit");
|
{
|
||||||
TRACE();
|
DUMP("Last Modified Hit");
|
||||||
_file.close();
|
TRACE();
|
||||||
request->reply(304); // Not modified
|
_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);
|
|
||||||
}
|
}
|
||||||
|
//does our Etag match?
|
||||||
|
else if (_cache_control.length() && request->hasHeader("If-None-Match") && request->header("If-None-Match").equals(etag))
|
||||||
|
{
|
||||||
|
DUMP("Etag Hit");
|
||||||
|
DUMP(etag);
|
||||||
|
DUMP(_cache_control);
|
||||||
|
|
||||||
return ESP_OK;
|
_file.close();
|
||||||
|
|
||||||
|
PsychicResponse response(request);
|
||||||
|
response.addHeader("Cache-Control", _cache_control.c_str());
|
||||||
|
response.addHeader("ETag", etag.c_str());
|
||||||
|
response.setCode(304);
|
||||||
|
response.send();
|
||||||
|
}
|
||||||
|
//nope, send them the full file.
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DUMP("No cache hit");
|
||||||
|
DUMP(_last_modified);
|
||||||
|
DUMP(_cache_control);
|
||||||
|
|
||||||
|
PsychicFileResponse response(request, _fs, _filename);
|
||||||
|
|
||||||
|
if (_last_modified.length())
|
||||||
|
response.addHeader("Last-Modified", _last_modified.c_str());
|
||||||
|
if (_cache_control.length()) {
|
||||||
|
response.addHeader("Cache-Control", _cache_control.c_str());
|
||||||
|
response.addHeader("ETag", etag.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.send();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return request->reply(404);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
@@ -8,36 +8,33 @@
|
|||||||
#include "PsychicFileResponse.h"
|
#include "PsychicFileResponse.h"
|
||||||
|
|
||||||
class PsychicStaticFileHandler : public PsychicWebHandler {
|
class PsychicStaticFileHandler : public PsychicWebHandler {
|
||||||
using File = fs::File;
|
using File = fs::File;
|
||||||
using FS = fs::FS;
|
using FS = fs::FS;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool _getFile(PsychicRequest * request);
|
bool _getFile(PsychicRequest *request);
|
||||||
bool _fileExists(const String & path);
|
bool _fileExists(const String& path);
|
||||||
uint8_t _countBits(const uint8_t value) const;
|
uint8_t _countBits(const uint8_t value) const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
FS _fs;
|
FS _fs;
|
||||||
File _file;
|
File _file;
|
||||||
String _filename;
|
String _filename;
|
||||||
String _uri;
|
String _uri;
|
||||||
String _path;
|
String _path;
|
||||||
String _default_file;
|
String _default_file;
|
||||||
String _cache_control;
|
String _cache_control;
|
||||||
String _last_modified;
|
String _last_modified;
|
||||||
bool _isDir;
|
bool _isDir;
|
||||||
bool _gzipFirst;
|
bool _gzipFirst;
|
||||||
uint8_t _gzipStats;
|
uint8_t _gzipStats;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
PsychicStaticFileHandler(const char * uri, FS & fs, const char * path, const char * cache_control);
|
PsychicStaticFileHandler(const char* uri, FS& fs, const char* path, const char* cache_control);
|
||||||
bool canHandle(PsychicRequest * request) override;
|
bool canHandle(PsychicRequest *request) override;
|
||||||
esp_err_t handleRequest(PsychicRequest * request) override;
|
esp_err_t handleRequest(PsychicRequest *request) override;
|
||||||
PsychicStaticFileHandler & setIsDir(bool isDir);
|
PsychicStaticFileHandler& setIsDir(bool isDir);
|
||||||
PsychicStaticFileHandler & setDefaultFile(const char * filename);
|
PsychicStaticFileHandler& setDefaultFile(const char* filename);
|
||||||
PsychicStaticFileHandler & setCacheControl(const char * cache_control);
|
PsychicStaticFileHandler& setCacheControl(const char* cache_control);
|
||||||
PsychicStaticFileHandler & setLastModified(const char * last_modified);
|
PsychicStaticFileHandler& setLastModified(const char* last_modified);
|
||||||
PsychicStaticFileHandler & setLastModified(struct tm * last_modified);
|
PsychicStaticFileHandler& setLastModified(struct tm* last_modified);
|
||||||
//PsychicStaticFileHandler& setTemplateProcessor(AwsTemplateProcessor newCallback) {_callback = newCallback; return *this;}
|
//PsychicStaticFileHandler& setTemplateProcessor(AwsTemplateProcessor newCallback) {_callback = newCallback; return *this;}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
89
lib/PsychicHttp/src/PsychicStreamResponse.cpp
Normal file
89
lib/PsychicHttp/src/PsychicStreamResponse.cpp
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
#include "PsychicStreamResponse.h"
|
||||||
|
#include "PsychicResponse.h"
|
||||||
|
#include "PsychicRequest.h"
|
||||||
|
|
||||||
|
PsychicStreamResponse::PsychicStreamResponse(PsychicRequest *request, const String& contentType)
|
||||||
|
: PsychicResponse(request), _buffer(NULL) {
|
||||||
|
|
||||||
|
setContentType(contentType.c_str());
|
||||||
|
addHeader("Content-Disposition", "inline");
|
||||||
|
}
|
||||||
|
|
||||||
|
PsychicStreamResponse::PsychicStreamResponse(PsychicRequest *request, const String& contentType, const String& name)
|
||||||
|
: PsychicResponse(request), _buffer(NULL) {
|
||||||
|
|
||||||
|
setContentType(contentType.c_str());
|
||||||
|
|
||||||
|
char buf[26+name.length()];
|
||||||
|
snprintf(buf, sizeof (buf), "attachment; filename=\"%s\"", name);
|
||||||
|
addHeader("Content-Disposition", buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
PsychicStreamResponse::~PsychicStreamResponse()
|
||||||
|
{
|
||||||
|
endSend();
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t PsychicStreamResponse::beginSend()
|
||||||
|
{
|
||||||
|
if(_buffer)
|
||||||
|
return ESP_OK;
|
||||||
|
|
||||||
|
//Buffer to hold ChunkPrinter and stream buffer. Using placement new will keep us at a single allocation.
|
||||||
|
_buffer = (uint8_t*)malloc(STREAM_CHUNK_SIZE + sizeof(ChunkPrinter));
|
||||||
|
|
||||||
|
if(!_buffer)
|
||||||
|
{
|
||||||
|
/* Respond with 500 Internal Server Error */
|
||||||
|
httpd_resp_send_err(_request->request(), HTTPD_500_INTERNAL_SERVER_ERROR, "Unable to allocate memory.");
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
_printer = new (_buffer) ChunkPrinter(this, _buffer + sizeof(ChunkPrinter), STREAM_CHUNK_SIZE);
|
||||||
|
|
||||||
|
sendHeaders();
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t PsychicStreamResponse::endSend()
|
||||||
|
{
|
||||||
|
esp_err_t err = ESP_OK;
|
||||||
|
|
||||||
|
if(!_buffer)
|
||||||
|
err = ESP_FAIL;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//flush & send remaining data.
|
||||||
|
_printer->flush();
|
||||||
|
err = finishChunking();
|
||||||
|
|
||||||
|
//Free memory
|
||||||
|
_printer->~ChunkPrinter();
|
||||||
|
free(_buffer);
|
||||||
|
_buffer = NULL;
|
||||||
|
}
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PsychicStreamResponse::flush()
|
||||||
|
{
|
||||||
|
if(_buffer)
|
||||||
|
_printer->flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t PsychicStreamResponse::write(uint8_t data)
|
||||||
|
{
|
||||||
|
return _buffer ? _printer->write(data) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t PsychicStreamResponse::copyFrom(Stream &stream)
|
||||||
|
{
|
||||||
|
size_t sentCount = 0;
|
||||||
|
|
||||||
|
if(_buffer)
|
||||||
|
{
|
||||||
|
while(stream.available())
|
||||||
|
sentCount += _printer->write(stream.read());
|
||||||
|
}
|
||||||
|
return sentCount;
|
||||||
|
}
|
||||||
33
lib/PsychicHttp/src/PsychicStreamResponse.h
Normal file
33
lib/PsychicHttp/src/PsychicStreamResponse.h
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
#ifndef PsychicStreamResponse_h
|
||||||
|
#define PsychicStreamResponse_h
|
||||||
|
|
||||||
|
#include "PsychicCore.h"
|
||||||
|
#include "PsychicResponse.h"
|
||||||
|
#include "ChunkPrinter.h"
|
||||||
|
|
||||||
|
class PsychicRequest;
|
||||||
|
|
||||||
|
class PsychicStreamResponse : public PsychicResponse, public Print
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
ChunkPrinter *_printer;
|
||||||
|
uint8_t *_buffer;
|
||||||
|
public:
|
||||||
|
|
||||||
|
PsychicStreamResponse(PsychicRequest *request, const String& contentType);
|
||||||
|
PsychicStreamResponse(PsychicRequest *request, const String& contentType, const String& name); //Download
|
||||||
|
|
||||||
|
~PsychicStreamResponse();
|
||||||
|
|
||||||
|
esp_err_t beginSend();
|
||||||
|
esp_err_t endSend();
|
||||||
|
|
||||||
|
virtual void flush() override;
|
||||||
|
|
||||||
|
size_t write(uint8_t data);
|
||||||
|
size_t copyFrom(Stream &stream);
|
||||||
|
|
||||||
|
using Print::write;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // PsychicStreamResponse_h
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
#include "PsychicWebParameter.h"
|
#include "PsychicWebParameter.h"
|
||||||
|
|
||||||
//callback definitions
|
//callback definitions
|
||||||
typedef std::function<esp_err_t(PsychicRequest * request, const String & filename, uint64_t index, uint8_t * data, size_t len, bool final)> PsychicUploadCallback;
|
typedef std::function<esp_err_t(PsychicRequest *request, const String& filename, uint64_t index, uint8_t *data, size_t len, bool final)> PsychicUploadCallback;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* HANDLER :: Can be attached to any endpoint or as a generic request handler.
|
* HANDLER :: Can be attached to any endpoint or as a generic request handler.
|
||||||
@@ -18,25 +18,25 @@ class PsychicUploadHandler : public PsychicWebHandler {
|
|||||||
protected:
|
protected:
|
||||||
PsychicUploadCallback _uploadCallback;
|
PsychicUploadCallback _uploadCallback;
|
||||||
|
|
||||||
PsychicRequest * _request;
|
PsychicRequest *_request;
|
||||||
|
|
||||||
String _temp;
|
String _temp;
|
||||||
size_t _parsedLength;
|
size_t _parsedLength;
|
||||||
uint8_t _multiParseState;
|
uint8_t _multiParseState;
|
||||||
String _boundary;
|
String _boundary;
|
||||||
uint8_t _boundaryPosition;
|
uint8_t _boundaryPosition;
|
||||||
size_t _itemStartIndex;
|
size_t _itemStartIndex;
|
||||||
size_t _itemSize;
|
size_t _itemSize;
|
||||||
String _itemName;
|
String _itemName;
|
||||||
String _itemFilename;
|
String _itemFilename;
|
||||||
String _itemType;
|
String _itemType;
|
||||||
String _itemValue;
|
String _itemValue;
|
||||||
uint8_t * _itemBuffer;
|
uint8_t *_itemBuffer;
|
||||||
size_t _itemBufferIndex;
|
size_t _itemBufferIndex;
|
||||||
bool _itemIsFile;
|
bool _itemIsFile;
|
||||||
|
|
||||||
esp_err_t _basicUploadHandler(PsychicRequest * request);
|
esp_err_t _basicUploadHandler(PsychicRequest *request);
|
||||||
esp_err_t _multipartUploadHandler(PsychicRequest * request);
|
esp_err_t _multipartUploadHandler(PsychicRequest *request);
|
||||||
|
|
||||||
void _handleUploadByte(uint8_t data, bool last);
|
void _handleUploadByte(uint8_t data, bool last);
|
||||||
void _parseMultipartPostByte(uint8_t data, bool last);
|
void _parseMultipartPostByte(uint8_t data, bool last);
|
||||||
@@ -45,24 +45,24 @@ class PsychicUploadHandler : public PsychicWebHandler {
|
|||||||
PsychicUploadHandler();
|
PsychicUploadHandler();
|
||||||
~PsychicUploadHandler();
|
~PsychicUploadHandler();
|
||||||
|
|
||||||
bool canHandle(PsychicRequest * request) override;
|
bool canHandle(PsychicRequest *request) override;
|
||||||
esp_err_t handleRequest(PsychicRequest * request) override;
|
esp_err_t handleRequest(PsychicRequest *request) override;
|
||||||
|
|
||||||
PsychicUploadHandler * onUpload(PsychicUploadCallback fn);
|
PsychicUploadHandler * onUpload(PsychicUploadCallback fn);
|
||||||
};
|
};
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
EXPECT_BOUNDARY,
|
EXPECT_BOUNDARY,
|
||||||
PARSE_HEADERS,
|
PARSE_HEADERS,
|
||||||
WAIT_FOR_RETURN1,
|
WAIT_FOR_RETURN1,
|
||||||
EXPECT_FEED1,
|
EXPECT_FEED1,
|
||||||
EXPECT_DASH1,
|
EXPECT_DASH1,
|
||||||
EXPECT_DASH2,
|
EXPECT_DASH2,
|
||||||
BOUNDARY_OR_DATA,
|
BOUNDARY_OR_DATA,
|
||||||
DASH3_OR_RETURN2,
|
DASH3_OR_RETURN2,
|
||||||
EXPECT_FEED2,
|
EXPECT_FEED2,
|
||||||
PARSING_FINISHED,
|
PARSING_FINISHED,
|
||||||
PARSE_ERROR
|
PARSE_ERROR
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // PsychicUploadHandler_h
|
#endif // PsychicUploadHandler_h
|
||||||
@@ -1,46 +1,74 @@
|
|||||||
#include "PsychicWebHandler.h"
|
#include "PsychicWebHandler.h"
|
||||||
|
|
||||||
PsychicWebHandler::PsychicWebHandler()
|
PsychicWebHandler::PsychicWebHandler() :
|
||||||
: PsychicHandler()
|
PsychicHandler(),
|
||||||
, _requestCallback(NULL) {
|
_requestCallback(NULL),
|
||||||
}
|
_onOpen(NULL),
|
||||||
PsychicWebHandler::~PsychicWebHandler() {
|
_onClose(NULL)
|
||||||
|
{}
|
||||||
|
PsychicWebHandler::~PsychicWebHandler() {}
|
||||||
|
|
||||||
|
bool PsychicWebHandler::canHandle(PsychicRequest *request) {
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PsychicWebHandler::canHandle(PsychicRequest * request) {
|
esp_err_t PsychicWebHandler::handleRequest(PsychicRequest *request)
|
||||||
return true;
|
{
|
||||||
}
|
//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 */
|
||||||
/* Request body cannot be larger than a limit */
|
if (request->contentLength() > request->server()->maxRequestBodySize)
|
||||||
if (request->contentLength() > request->server()->maxRequestBodySize) {
|
{
|
||||||
ESP_LOGE(PH_TAG, "Request body too large : %d bytes", request->contentLength());
|
ESP_LOGE(PH_TAG, "Request body too large : %d bytes", request->contentLength());
|
||||||
|
|
||||||
/* Respond with 400 Bad Request */
|
/* Respond with 400 Bad Request */
|
||||||
char error[60];
|
char error[60];
|
||||||
sprintf(error, "Request body must be less than %u bytes!", request->server()->maxRequestBodySize);
|
sprintf(error, "Request body must be less than %u bytes!", request->server()->maxRequestBodySize);
|
||||||
httpd_resp_send_err(request->request(), HTTPD_400_BAD_REQUEST, error);
|
httpd_resp_send_err(request->request(), HTTPD_400_BAD_REQUEST, error);
|
||||||
|
|
||||||
/* Return failure to close underlying connection else the incoming file content will keep the socket busy */
|
/* Return failure to close underlying connection else the incoming file content will keep the socket busy */
|
||||||
return ESP_FAIL;
|
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);
|
|
||||||
|
|
||||||
|
//get our body loaded up.
|
||||||
|
esp_err_t err = request->loadBody();
|
||||||
|
if (err != ESP_OK)
|
||||||
return err;
|
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) {
|
PsychicWebHandler * PsychicWebHandler::onRequest(PsychicHttpRequestCallback fn) {
|
||||||
_requestCallback = fn;
|
_requestCallback = fn;
|
||||||
return this;
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PsychicWebHandler::openCallback(PsychicClient *client) {
|
||||||
|
if (_onOpen != NULL)
|
||||||
|
_onOpen(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PsychicWebHandler::closeCallback(PsychicClient *client) {
|
||||||
|
if (_onClose != NULL)
|
||||||
|
_onClose(getClient(client));
|
||||||
|
}
|
||||||
|
|
||||||
|
PsychicWebHandler * PsychicWebHandler::onOpen(PsychicClientCallback fn) {
|
||||||
|
_onOpen = fn;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
PsychicWebHandler * PsychicWebHandler::onClose(PsychicClientCallback fn) {
|
||||||
|
_onClose = fn;
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
#ifndef PsychicWebHandler_h
|
#ifndef PsychicWebHandler_h
|
||||||
#define PsychicWebHandler_h
|
#define PsychicWebHandler_h
|
||||||
|
|
||||||
#include "PsychicCore.h"
|
// #include "PsychicCore.h"
|
||||||
#include "PsychicHttpServer.h"
|
// #include "PsychicHttpServer.h"
|
||||||
#include "PsychicRequest.h"
|
// #include "PsychicRequest.h"
|
||||||
#include "PsychicHandler.h"
|
#include "PsychicHandler.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -13,14 +13,22 @@
|
|||||||
class PsychicWebHandler : public PsychicHandler {
|
class PsychicWebHandler : public PsychicHandler {
|
||||||
protected:
|
protected:
|
||||||
PsychicHttpRequestCallback _requestCallback;
|
PsychicHttpRequestCallback _requestCallback;
|
||||||
|
PsychicClientCallback _onOpen;
|
||||||
|
PsychicClientCallback _onClose;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
PsychicWebHandler();
|
PsychicWebHandler();
|
||||||
~PsychicWebHandler();
|
~PsychicWebHandler();
|
||||||
|
|
||||||
virtual bool canHandle(PsychicRequest * request) override;
|
virtual bool canHandle(PsychicRequest *request) override;
|
||||||
virtual esp_err_t handleRequest(PsychicRequest * request) override;
|
virtual esp_err_t handleRequest(PsychicRequest *request) override;
|
||||||
PsychicWebHandler * onRequest(PsychicHttpRequestCallback fn);
|
PsychicWebHandler * onRequest(PsychicHttpRequestCallback fn);
|
||||||
|
|
||||||
|
virtual void openCallback(PsychicClient *client);
|
||||||
|
virtual void closeCallback(PsychicClient *client);
|
||||||
|
|
||||||
|
PsychicWebHandler *onOpen(PsychicClientCallback fn);
|
||||||
|
PsychicWebHandler *onClose(PsychicClientCallback fn);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -10,32 +10,16 @@ class PsychicWebParameter {
|
|||||||
String _name;
|
String _name;
|
||||||
String _value;
|
String _value;
|
||||||
size_t _size;
|
size_t _size;
|
||||||
bool _isForm;
|
bool _isForm;
|
||||||
bool _isFile;
|
bool _isFile;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
PsychicWebParameter(const String & name, const String & value, bool form = false, bool file = false, size_t size = 0)
|
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){}
|
||||||
: _name(name)
|
const String& name() const { return _name; }
|
||||||
, _value(value)
|
const String& value() const { return _value; }
|
||||||
, _size(size)
|
size_t size() const { return _size; }
|
||||||
, _isForm(form)
|
bool isPost() const { return _isForm; }
|
||||||
, _isFile(file) {
|
bool isFile() const { return _isFile; }
|
||||||
}
|
|
||||||
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
|
#endif //PsychicWebParameter_h
|
||||||
@@ -4,240 +4,257 @@
|
|||||||
/* PsychicWebSocketRequest */
|
/* PsychicWebSocketRequest */
|
||||||
/*************************************/
|
/*************************************/
|
||||||
|
|
||||||
PsychicWebSocketRequest::PsychicWebSocketRequest(PsychicRequest * req)
|
PsychicWebSocketRequest::PsychicWebSocketRequest(PsychicRequest *req) :
|
||||||
: PsychicRequest(req->server(), req->request())
|
PsychicRequest(req->server(), req->request()),
|
||||||
, _client(req->client()) {
|
_client(req->client())
|
||||||
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
PsychicWebSocketRequest::~PsychicWebSocketRequest() {
|
PsychicWebSocketRequest::~PsychicWebSocketRequest()
|
||||||
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
PsychicWebSocketClient * PsychicWebSocketRequest::client() {
|
PsychicWebSocketClient * PsychicWebSocketRequest::client() {
|
||||||
return &_client;
|
return &_client;
|
||||||
}
|
}
|
||||||
|
|
||||||
esp_err_t PsychicWebSocketRequest::reply(httpd_ws_frame_t * ws_pkt) {
|
esp_err_t PsychicWebSocketRequest::reply(httpd_ws_frame_t * ws_pkt)
|
||||||
return httpd_ws_send_frame(this->_req, ws_pkt);
|
{
|
||||||
|
return httpd_ws_send_frame(this->_req, ws_pkt);
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
esp_err_t PsychicWebSocketRequest::reply(const char *buf)
|
||||||
httpd_ws_frame_t ws_pkt;
|
{
|
||||||
memset(&ws_pkt, 0, sizeof(httpd_ws_frame_t));
|
return this->reply(HTTPD_WS_TYPE_TEXT, buf, strlen(buf));
|
||||||
|
|
||||||
ws_pkt.payload = (uint8_t *)data;
|
|
||||||
ws_pkt.len = len;
|
|
||||||
ws_pkt.type = op;
|
|
||||||
|
|
||||||
return this->reply(&ws_pkt);
|
|
||||||
}
|
|
||||||
|
|
||||||
esp_err_t PsychicWebSocketRequest::reply(const char * buf) {
|
|
||||||
return this->reply(HTTPD_WS_TYPE_TEXT, buf, strlen(buf));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*************************************/
|
/*************************************/
|
||||||
/* PsychicWebSocketClient */
|
/* PsychicWebSocketClient */
|
||||||
/*************************************/
|
/*************************************/
|
||||||
|
|
||||||
PsychicWebSocketClient::PsychicWebSocketClient(PsychicClient * client)
|
PsychicWebSocketClient::PsychicWebSocketClient(PsychicClient *client)
|
||||||
: PsychicClient(client->server(), client->socket()) {
|
: PsychicClient(client->server(), client->socket())
|
||||||
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
PsychicWebSocketClient::~PsychicWebSocketClient() {
|
PsychicWebSocketClient::~PsychicWebSocketClient() {
|
||||||
}
|
}
|
||||||
|
|
||||||
esp_err_t PsychicWebSocketClient::sendMessage(httpd_ws_frame_t * ws_pkt) {
|
esp_err_t PsychicWebSocketClient::sendMessage(httpd_ws_frame_t * ws_pkt)
|
||||||
return httpd_ws_send_frame_async(this->server(), this->socket(), ws_pkt);
|
{
|
||||||
|
return httpd_ws_send_frame_async(this->server(), this->socket(), ws_pkt);
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
esp_err_t PsychicWebSocketClient::sendMessage(const char *buf)
|
||||||
httpd_ws_frame_t ws_pkt;
|
{
|
||||||
memset(&ws_pkt, 0, sizeof(httpd_ws_frame_t));
|
return this->sendMessage(HTTPD_WS_TYPE_TEXT, buf, strlen(buf));
|
||||||
|
|
||||||
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) {
|
PsychicWebSocketHandler::PsychicWebSocketHandler() :
|
||||||
return this->sendMessage(HTTPD_WS_TYPE_TEXT, buf, strlen(buf));
|
PsychicHandler(),
|
||||||
}
|
_onOpen(NULL),
|
||||||
|
_onFrame(NULL),
|
||||||
PsychicWebSocketHandler::PsychicWebSocketHandler()
|
_onClose(NULL)
|
||||||
: PsychicHandler()
|
{
|
||||||
, _onOpen(NULL)
|
}
|
||||||
, _onFrame(NULL)
|
|
||||||
, _onClose(NULL) {
|
|
||||||
}
|
|
||||||
|
|
||||||
PsychicWebSocketHandler::~PsychicWebSocketHandler() {
|
PsychicWebSocketHandler::~PsychicWebSocketHandler() {
|
||||||
}
|
}
|
||||||
|
|
||||||
PsychicWebSocketClient * PsychicWebSocketHandler::getClient(int socket) {
|
PsychicWebSocketClient * PsychicWebSocketHandler::getClient(int socket)
|
||||||
PsychicClient * client = PsychicHandler::getClient(socket);
|
{
|
||||||
if (client == NULL)
|
PsychicClient *client = PsychicHandler::getClient(socket);
|
||||||
return NULL;
|
if (client == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
if (client->_friend == NULL) {
|
if (client->_friend == NULL)
|
||||||
DUMP(socket);
|
{
|
||||||
return NULL;
|
DUMP(socket);
|
||||||
}
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
return (PsychicWebSocketClient *)client->_friend;
|
return (PsychicWebSocketClient *)client->_friend;
|
||||||
}
|
}
|
||||||
|
|
||||||
PsychicWebSocketClient * PsychicWebSocketHandler::getClient(PsychicClient * client) {
|
PsychicWebSocketClient * PsychicWebSocketHandler::getClient(PsychicClient *client) {
|
||||||
return getClient(client->socket());
|
return getClient(client->socket());
|
||||||
}
|
}
|
||||||
|
|
||||||
void PsychicWebSocketHandler::addClient(PsychicClient * client) {
|
void PsychicWebSocketHandler::addClient(PsychicClient *client) {
|
||||||
client->_friend = new PsychicWebSocketClient(client);
|
client->_friend = new PsychicWebSocketClient(client);
|
||||||
PsychicHandler::addClient(client);
|
PsychicHandler::addClient(client);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PsychicWebSocketHandler::removeClient(PsychicClient * client) {
|
void PsychicWebSocketHandler::removeClient(PsychicClient *client) {
|
||||||
PsychicHandler::removeClient(client);
|
PsychicHandler::removeClient(client);
|
||||||
delete (PsychicWebSocketClient *)client->_friend;
|
delete (PsychicWebSocketClient*)client->_friend;
|
||||||
client->_friend = NULL;
|
client->_friend = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PsychicWebSocketHandler::openCallback(PsychicClient * client) {
|
void PsychicWebSocketHandler::openCallback(PsychicClient *client) {
|
||||||
PsychicWebSocketClient * buddy = getClient(client);
|
PsychicWebSocketClient *buddy = getClient(client);
|
||||||
if (buddy == NULL) {
|
if (buddy == NULL)
|
||||||
TRACE();
|
{
|
||||||
return;
|
TRACE();
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (_onOpen != NULL)
|
if (_onOpen != NULL)
|
||||||
_onOpen(getClient(buddy));
|
_onOpen(getClient(buddy));
|
||||||
}
|
}
|
||||||
|
|
||||||
void PsychicWebSocketHandler::closeCallback(PsychicClient * client) {
|
void PsychicWebSocketHandler::closeCallback(PsychicClient *client) {
|
||||||
PsychicWebSocketClient * buddy = getClient(client);
|
PsychicWebSocketClient *buddy = getClient(client);
|
||||||
if (buddy == NULL) {
|
if (buddy == NULL)
|
||||||
TRACE();
|
{
|
||||||
return;
|
TRACE();
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (_onClose != NULL)
|
if (_onClose != NULL)
|
||||||
_onClose(getClient(buddy));
|
_onClose(getClient(buddy));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PsychicWebSocketHandler::isWebSocket() {
|
bool PsychicWebSocketHandler::isWebSocket() { return true; }
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
esp_err_t PsychicWebSocketHandler::handleRequest(PsychicRequest * request) {
|
esp_err_t PsychicWebSocketHandler::handleRequest(PsychicRequest *request)
|
||||||
//lookup our client
|
{
|
||||||
PsychicClient * client = checkForNewClient(request->client());
|
//lookup our client
|
||||||
|
PsychicClient *client = checkForNewClient(request->client());
|
||||||
|
|
||||||
// beginning of the ws URI handler and our onConnect hook
|
// beginning of the ws URI handler and our onConnect hook
|
||||||
if (request->method() == HTTP_GET) {
|
if (request->method() == HTTP_GET)
|
||||||
if (client->isNew)
|
{
|
||||||
openCallback(client);
|
if (client->isNew)
|
||||||
|
openCallback(client);
|
||||||
|
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
//prep our request
|
//prep our request
|
||||||
PsychicWebSocketRequest wsRequest(request);
|
PsychicWebSocketRequest wsRequest(request);
|
||||||
|
|
||||||
//init our memory for storing the packet
|
//init our memory for storing the packet
|
||||||
httpd_ws_frame_t ws_pkt;
|
httpd_ws_frame_t ws_pkt;
|
||||||
memset(&ws_pkt, 0, sizeof(httpd_ws_frame_t));
|
memset(&ws_pkt, 0, sizeof(httpd_ws_frame_t));
|
||||||
ws_pkt.type = HTTPD_WS_TYPE_TEXT;
|
ws_pkt.type = HTTPD_WS_TYPE_TEXT;
|
||||||
uint8_t * buf = NULL;
|
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);
|
|
||||||
|
|
||||||
|
/* 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;
|
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) {
|
PsychicWebSocketHandler * PsychicWebSocketHandler::onOpen(PsychicWebSocketClientCallback fn) {
|
||||||
_onOpen = fn;
|
_onOpen = fn;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
PsychicWebSocketHandler * PsychicWebSocketHandler::onFrame(PsychicWebSocketFrameCallback fn) {
|
PsychicWebSocketHandler * PsychicWebSocketHandler::onFrame(PsychicWebSocketFrameCallback fn) {
|
||||||
_onFrame = fn;
|
_onFrame = fn;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
PsychicWebSocketHandler * PsychicWebSocketHandler::onClose(PsychicWebSocketClientCallback fn) {
|
PsychicWebSocketHandler * PsychicWebSocketHandler::onClose(PsychicWebSocketClientCallback fn) {
|
||||||
_onClose = fn;
|
_onClose = fn;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PsychicWebSocketHandler::sendAll(httpd_ws_frame_t * ws_pkt) {
|
void PsychicWebSocketHandler::sendAll(httpd_ws_frame_t * ws_pkt)
|
||||||
for (PsychicClient * client : _clients) {
|
{
|
||||||
ESP_LOGI(PH_TAG, "Active client (fd=%d) -> sending async message", client->socket());
|
for (PsychicClient *client : _clients)
|
||||||
|
{
|
||||||
|
ESP_LOGI(PH_TAG, "Active client (fd=%d) -> sending async message", client->socket());
|
||||||
|
|
||||||
if (client->_friend == NULL) {
|
if (client->_friend == NULL)
|
||||||
TRACE();
|
{
|
||||||
return;
|
TRACE();
|
||||||
}
|
return;
|
||||||
|
|
||||||
if (((PsychicWebSocketClient *)client->_friend)->sendMessage(ws_pkt) != ESP_OK)
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (((PsychicWebSocketClient*)client->_friend)->sendMessage(ws_pkt) != ESP_OK)
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PsychicWebSocketHandler::sendAll(httpd_ws_type_t op, const void * data, size_t len) {
|
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));
|
httpd_ws_frame_t ws_pkt;
|
||||||
|
memset(&ws_pkt, 0, sizeof(httpd_ws_frame_t));
|
||||||
|
|
||||||
ws_pkt.payload = (uint8_t *)data;
|
ws_pkt.payload = (uint8_t*)data;
|
||||||
ws_pkt.len = len;
|
ws_pkt.len = len;
|
||||||
ws_pkt.type = op;
|
ws_pkt.type = op;
|
||||||
|
|
||||||
this->sendAll(&ws_pkt);
|
this->sendAll(&ws_pkt);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PsychicWebSocketHandler::sendAll(const char * buf) {
|
void PsychicWebSocketHandler::sendAll(const char *buf)
|
||||||
this->sendAll(HTTPD_WS_TYPE_TEXT, buf, strlen(buf));
|
{
|
||||||
|
this->sendAll(HTTPD_WS_TYPE_TEXT, buf, strlen(buf));
|
||||||
}
|
}
|
||||||
@@ -8,38 +8,40 @@ class PsychicWebSocketRequest;
|
|||||||
class PsychicWebSocketClient;
|
class PsychicWebSocketClient;
|
||||||
|
|
||||||
//callback function definitions
|
//callback function definitions
|
||||||
typedef std::function<void(PsychicWebSocketClient * client)> PsychicWebSocketClientCallback;
|
typedef std::function<void(PsychicWebSocketClient *client)> PsychicWebSocketClientCallback;
|
||||||
typedef std::function<esp_err_t(PsychicWebSocketRequest * request, httpd_ws_frame * frame)> PsychicWebSocketFrameCallback;
|
typedef std::function<esp_err_t(PsychicWebSocketRequest *request, httpd_ws_frame *frame)> PsychicWebSocketFrameCallback;
|
||||||
|
|
||||||
class PsychicWebSocketClient : public PsychicClient {
|
class PsychicWebSocketClient : public PsychicClient
|
||||||
|
{
|
||||||
public:
|
public:
|
||||||
PsychicWebSocketClient(PsychicClient * client);
|
PsychicWebSocketClient(PsychicClient *client);
|
||||||
~PsychicWebSocketClient();
|
~PsychicWebSocketClient();
|
||||||
|
|
||||||
esp_err_t sendMessage(httpd_ws_frame_t * ws_pkt);
|
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(httpd_ws_type_t op, const void *data, size_t len);
|
||||||
esp_err_t sendMessage(const char * buf);
|
esp_err_t sendMessage(const char *buf);
|
||||||
};
|
};
|
||||||
|
|
||||||
class PsychicWebSocketRequest : public PsychicRequest {
|
class PsychicWebSocketRequest : public PsychicRequest
|
||||||
|
{
|
||||||
private:
|
private:
|
||||||
PsychicWebSocketClient _client;
|
PsychicWebSocketClient _client;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
PsychicWebSocketRequest(PsychicRequest * req);
|
PsychicWebSocketRequest(PsychicRequest *req);
|
||||||
virtual ~PsychicWebSocketRequest();
|
virtual ~PsychicWebSocketRequest();
|
||||||
|
|
||||||
PsychicWebSocketClient * client() override;
|
PsychicWebSocketClient * client() override;
|
||||||
|
|
||||||
esp_err_t reply(httpd_ws_frame_t * ws_pkt);
|
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(httpd_ws_type_t op, const void *data, size_t len);
|
||||||
esp_err_t reply(const char * buf);
|
esp_err_t reply(const char *buf);
|
||||||
};
|
};
|
||||||
|
|
||||||
class PsychicWebSocketHandler : public PsychicHandler {
|
class PsychicWebSocketHandler : public PsychicHandler {
|
||||||
protected:
|
protected:
|
||||||
PsychicWebSocketClientCallback _onOpen;
|
PsychicWebSocketClientCallback _onOpen;
|
||||||
PsychicWebSocketFrameCallback _onFrame;
|
PsychicWebSocketFrameCallback _onFrame;
|
||||||
PsychicWebSocketClientCallback _onClose;
|
PsychicWebSocketClientCallback _onClose;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@@ -47,22 +49,22 @@ class PsychicWebSocketHandler : public PsychicHandler {
|
|||||||
~PsychicWebSocketHandler();
|
~PsychicWebSocketHandler();
|
||||||
|
|
||||||
PsychicWebSocketClient * getClient(int socket) override;
|
PsychicWebSocketClient * getClient(int socket) override;
|
||||||
PsychicWebSocketClient * getClient(PsychicClient * client) override;
|
PsychicWebSocketClient * getClient(PsychicClient *client) override;
|
||||||
void addClient(PsychicClient * client) override;
|
void addClient(PsychicClient *client) override;
|
||||||
void removeClient(PsychicClient * client) override;
|
void removeClient(PsychicClient *client) override;
|
||||||
void openCallback(PsychicClient * client) override;
|
void openCallback(PsychicClient *client) override;
|
||||||
void closeCallback(PsychicClient * client) override;
|
void closeCallback(PsychicClient *client) override;
|
||||||
|
|
||||||
bool isWebSocket() override final;
|
bool isWebSocket() override final;
|
||||||
esp_err_t handleRequest(PsychicRequest * request) override;
|
esp_err_t handleRequest(PsychicRequest *request) override;
|
||||||
|
|
||||||
PsychicWebSocketHandler * onOpen(PsychicWebSocketClientCallback fn);
|
PsychicWebSocketHandler *onOpen(PsychicWebSocketClientCallback fn);
|
||||||
PsychicWebSocketHandler * onFrame(PsychicWebSocketFrameCallback fn);
|
PsychicWebSocketHandler *onFrame(PsychicWebSocketFrameCallback fn);
|
||||||
PsychicWebSocketHandler * onClose(PsychicWebSocketClientCallback fn);
|
PsychicWebSocketHandler *onClose(PsychicWebSocketClientCallback fn);
|
||||||
|
|
||||||
void sendAll(httpd_ws_frame_t * ws_pkt);
|
void sendAll(httpd_ws_frame_t * ws_pkt);
|
||||||
void sendAll(httpd_ws_type_t op, const void * data, size_t len);
|
void sendAll(httpd_ws_type_t op, const void *data, size_t len);
|
||||||
void sendAll(const char * buf);
|
void sendAll(const char *buf);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // PsychicWebSocket_h
|
#endif // PsychicWebSocket_h
|
||||||
@@ -1,30 +1,37 @@
|
|||||||
#include "http_status.h"
|
#include "http_status.h"
|
||||||
|
|
||||||
bool http_informational(int code) {
|
bool http_informational(int code)
|
||||||
|
{
|
||||||
return code >= 100 && code < 200;
|
return code >= 100 && code < 200;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool http_success(int code) {
|
bool http_success(int code)
|
||||||
|
{
|
||||||
return code >= 200 && code < 300;
|
return code >= 200 && code < 300;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool http_redirection(int code) {
|
bool http_redirection(int code)
|
||||||
|
{
|
||||||
return code >= 300 && code < 400;
|
return code >= 300 && code < 400;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool http_client_error(int code) {
|
bool http_client_error(int code)
|
||||||
|
{
|
||||||
return code >= 400 && code < 500;
|
return code >= 400 && code < 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool http_server_error(int code) {
|
bool http_server_error(int code)
|
||||||
|
{
|
||||||
return code >= 500 && code < 600;
|
return code >= 500 && code < 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool http_failure(int code) {
|
bool http_failure(int code)
|
||||||
|
{
|
||||||
return code >= 400 && code < 600;
|
return code >= 400 && code < 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char * http_status_group(int code) {
|
const char *http_status_group(int code)
|
||||||
|
{
|
||||||
if (http_informational(code))
|
if (http_informational(code))
|
||||||
return "Informational";
|
return "Informational";
|
||||||
|
|
||||||
@@ -43,8 +50,10 @@ const char * http_status_group(int code) {
|
|||||||
return "Unknown";
|
return "Unknown";
|
||||||
}
|
}
|
||||||
|
|
||||||
const char * http_status_reason(int code) {
|
const char *http_status_reason(int code)
|
||||||
switch (code) {
|
{
|
||||||
|
switch (code)
|
||||||
|
{
|
||||||
/*####### 1xx - Informational #######*/
|
/*####### 1xx - Informational #######*/
|
||||||
case 100:
|
case 100:
|
||||||
return "Continue";
|
return "Continue";
|
||||||
|
|||||||
@@ -3,13 +3,13 @@
|
|||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
bool http_informational(int code);
|
bool http_informational(int code);
|
||||||
bool http_success(int code);
|
bool http_success(int code);
|
||||||
bool http_redirection(int code);
|
bool http_redirection(int code);
|
||||||
bool http_client_error(int code);
|
bool http_client_error(int code);
|
||||||
bool http_server_error(int code);
|
bool http_server_error(int code);
|
||||||
bool http_failure(int code);
|
bool http_failure(int code);
|
||||||
const char * http_status_group(int code);
|
const char *http_status_group(int code);
|
||||||
const char * http_status_reason(int code);
|
const char *http_status_reason(int code);
|
||||||
|
|
||||||
#endif // MICRO_HTTP_STATUS_H
|
#endif // MICRO_HTTP_STATUS_H
|
||||||
@@ -49,6 +49,7 @@ void ESP8266React::registerURI() {
|
|||||||
_mqttStatus.registerURI();
|
_mqttStatus.registerURI();
|
||||||
|
|
||||||
_securitySettingsService.registerURI();
|
_securitySettingsService.registerURI();
|
||||||
|
|
||||||
_otaSettingsService.registerURI();
|
_otaSettingsService.registerURI();
|
||||||
|
|
||||||
_restartService.registerURI();
|
_restartService.registerURI();
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ class HttpEndpoint {
|
|||||||
|
|
||||||
// register the web server on() endpoints
|
// register the web server on() endpoints
|
||||||
void registerURI() {
|
void registerURI() {
|
||||||
|
// GET
|
||||||
_server->on(_servicePath,
|
_server->on(_servicePath,
|
||||||
HTTP_GET,
|
HTTP_GET,
|
||||||
_securityManager->wrapRequest(
|
_securityManager->wrapRequest(
|
||||||
@@ -57,6 +58,7 @@ class HttpEndpoint {
|
|||||||
},
|
},
|
||||||
_authenticationPredicate));
|
_authenticationPredicate));
|
||||||
|
|
||||||
|
// POST
|
||||||
_server->on(_servicePath,
|
_server->on(_servicePath,
|
||||||
HTTP_POST,
|
HTTP_POST,
|
||||||
_securityManager->wrapCallback(
|
_securityManager->wrapCallback(
|
||||||
|
|||||||
@@ -17,12 +17,14 @@ void NetworkSettingsService::begin() {
|
|||||||
// WiFi.mode(WIFI_OFF);
|
// 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.useStaticBuffers(true); // uses 40kb more heap and max alloc, so not recommended
|
||||||
WiFi.persistent(false);
|
WiFi.persistent(false);
|
||||||
WiFi.setAutoReconnect(false);
|
WiFi.setAutoReconnect(false);
|
||||||
|
|
||||||
WiFi.mode(WIFI_MODE_MAX);
|
// WiFi.mode(WIFI_MODE_MAX); // TODO commented out, not sure what use it has
|
||||||
WiFi.mode(WIFI_MODE_NULL);
|
// WiFi.mode(WIFI_MODE_NULL);
|
||||||
|
|
||||||
// WiFi.setScanMethod(WIFI_ALL_CHANNEL_SCAN); // default is FAST_SCAN, connect issues in 2.0.14
|
// 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
|
// WiFi.setSortMethod(WIFI_CONNECT_AP_BY_SIGNAL); // is default, no need to set
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
/*
|
||||||
#ifndef WebSocketTxRx_h
|
#ifndef WebSocketTxRx_h
|
||||||
#define WebSocketTxRx_h
|
#define WebSocketTxRx_h
|
||||||
|
|
||||||
@@ -107,13 +108,12 @@ class WebSocketTx : virtual public WebSocketConnector<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// Broadcasts the payload to the destination, if provided. Otherwise broadcasts to all clients except the origin, if
|
||||||
* Broadcasts the payload to the destination, if provided. Otherwise broadcasts to all clients except the origin, if
|
// specified.
|
||||||
* specified.
|
//
|
||||||
*
|
// Original implementation sent clients their own IDs so they could ignore updates they initiated. This approach
|
||||||
* 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.
|
||||||
* simplifies the client and the server implementation but may not be sufficent for all use-cases.
|
//
|
||||||
*/
|
|
||||||
void transmitData(AsyncWebSocketClient * client, const String & originId) {
|
void transmitData(AsyncWebSocketClient * client, const String & originId) {
|
||||||
DynamicJsonDocument jsonDocument = DynamicJsonDocument(WebSocketConnector<T>::_bufferSize);
|
DynamicJsonDocument jsonDocument = DynamicJsonDocument(WebSocketConnector<T>::_bufferSize);
|
||||||
JsonObject root = jsonDocument.to<JsonObject>();
|
JsonObject root = jsonDocument.to<JsonObject>();
|
||||||
@@ -214,3 +214,4 @@ class WebSocketTxRx : public WebSocketTx<T>, public WebSocketRx<T> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
*/
|
||||||
298
platformio.ini
298
platformio.ini
@@ -1,71 +1,60 @@
|
|||||||
; PlatformIO Project Configuration File for EMS-ESP
|
; PlatformIO Project Configuration File
|
||||||
; override any settings with your own local ones in pio_local.ini
|
;
|
||||||
|
; 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]
|
[platformio]
|
||||||
; default_envs = esp32_4M
|
|
||||||
default_envs = lolin_s3
|
default_envs = lolin_s3
|
||||||
; default_envs = esp32_16M
|
extra_configs =
|
||||||
; default_envs = standalone
|
factory_settings.ini
|
||||||
; default_envs = https
|
pio_local.ini
|
||||||
|
|
||||||
extra_configs =
|
|
||||||
factory_settings.ini
|
|
||||||
pio_local.ini
|
|
||||||
|
|
||||||
[common]
|
[common]
|
||||||
core_build_flags =
|
core_build_flags =
|
||||||
-D ARDUINO_ARCH_ESP32=1
|
-D ARDUINO_ARCH_ESP32=1
|
||||||
-D ESP32=1
|
-D ESP32=1
|
||||||
; -std=gnu++17
|
core_unbuild_flags =
|
||||||
|
my_build_flags =
|
||||||
; core_unbuild_flags = -std=gnu++11
|
build_flags =
|
||||||
core_unbuild_flags =
|
${common.core_build_flags}
|
||||||
|
${factory_settings.build_flags}
|
||||||
; my_build_flags is set in pio_local.ini
|
${common.my_build_flags}
|
||||||
my_build_flags =
|
-D ONEWIRE_CRC16=0
|
||||||
|
-D NO_GLOBAL_ARDUINOOTA
|
||||||
build_flags =
|
-D ARDUINOJSON_ENABLE_STD_STRING=1
|
||||||
${common.core_build_flags}
|
-D ARDUINOJSON_USE_DOUBLE=0
|
||||||
${factory_settings.build_flags}
|
-D ARDUINOTRACE_ENABLE=0
|
||||||
${common.my_build_flags}
|
-D CONFIG_ETH_ENABLED
|
||||||
-D ONEWIRE_CRC16=0
|
-D ARDUINOTRACE_ENABLE=0
|
||||||
-D NO_GLOBAL_ARDUINOOTA
|
unbuild_flags =
|
||||||
-D ARDUINOJSON_ENABLE_STD_STRING=1
|
${common.core_unbuild_flags}
|
||||||
-D ARDUINOJSON_USE_DOUBLE=0
|
|
||||||
-D ARDUINOTRACE_ENABLE=0
|
|
||||||
; -D CONFIG_UART_ISR_IN_IRAM
|
|
||||||
|
|
||||||
unbuild_flags =
|
|
||||||
${common.core_unbuild_flags}
|
|
||||||
|
|
||||||
[espressi32_base]
|
[espressi32_base]
|
||||||
platform = espressif32@6.5.0
|
platform = espressif32
|
||||||
framework = arduino
|
framework = arduino
|
||||||
board_build.filesystem = littlefs
|
board_build.filesystem = littlefs
|
||||||
build_flags = ${common.build_flags}
|
build_flags = ${common.build_flags}
|
||||||
build_unflags = ${common.unbuild_flags}
|
build_unflags = ${common.unbuild_flags}
|
||||||
extra_scripts =
|
extra_scripts =
|
||||||
pre:scripts/build_interface.py
|
pre:scripts/build_interface.py
|
||||||
scripts/rename_fw.py
|
scripts/rename_fw.py
|
||||||
|
|
||||||
[espressi32_base_tasmota]
|
[espressi32_base_tasmota]
|
||||||
; use Tasmota's libary which removes some libs (like mbedtsl) and increases available heap
|
platform = https://github.com/tasmota/platform-espressif32/releases/download/2023.10.03/platform-espressif32-2023.10.03.zip
|
||||||
; 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
|
|
||||||
framework = arduino
|
framework = arduino
|
||||||
board_build.filesystem = littlefs
|
board_build.filesystem = littlefs
|
||||||
build_flags =
|
build_flags =
|
||||||
${common.build_flags}
|
${common.build_flags}
|
||||||
-DTASMOTA_SDK
|
-DTASMOTA_SDK
|
||||||
build_unflags = ${common.unbuild_flags}
|
build_unflags = ${common.unbuild_flags}
|
||||||
extra_scripts =
|
extra_scripts =
|
||||||
pre:scripts/build_interface.py
|
pre:scripts/build_interface.py
|
||||||
scripts/rename_fw.py
|
scripts/rename_fw.py
|
||||||
|
|
||||||
[env]
|
[env]
|
||||||
monitor_speed = 115200
|
monitor_speed = 115200
|
||||||
@@ -73,16 +62,12 @@ monitor_filters = esp32_exception_decoder
|
|||||||
upload_speed = 921600
|
upload_speed = 921600
|
||||||
build_type = release
|
build_type = release
|
||||||
lib_ldf_mode = chain+
|
lib_ldf_mode = chain+
|
||||||
; board_build.flash_mode = qio
|
|
||||||
|
|
||||||
check_tool = cppcheck, clangtidy
|
check_tool = cppcheck, clangtidy
|
||||||
check_severity = high, medium
|
check_severity = high, medium
|
||||||
check_flags =
|
check_flags =
|
||||||
cppcheck: --std=c++11 -v
|
cppcheck: --std=c++11 -v
|
||||||
clangtidy: --checks=-*,clang-analyzer-*,performance-*
|
clangtidy: --checks=-*,clang-analyzer-*,performance-*
|
||||||
|
|
||||||
; build for GitHub Actions CI
|
|
||||||
; the Web interface is built seperately
|
|
||||||
[env:ci]
|
[env:ci]
|
||||||
extends = espressi32_base_tasmota
|
extends = espressi32_base_tasmota
|
||||||
extra_scripts = scripts/rename_fw.py
|
extra_scripts = scripts/rename_fw.py
|
||||||
@@ -98,19 +83,19 @@ board_build.f_cpu = 240000000L
|
|||||||
board_upload.flash_size = 16MB
|
board_upload.flash_size = 16MB
|
||||||
board_build.partitions = esp32_partition_16M.csv
|
board_build.partitions = esp32_partition_16M.csv
|
||||||
build_unflags = ${common.unbuild_flags}
|
build_unflags = ${common.unbuild_flags}
|
||||||
build_flags =
|
build_flags =
|
||||||
${espressi32_base.build_flags}
|
${espressi32_base.build_flags}
|
||||||
-O2
|
-O2
|
||||||
'-DEMSESP_DEFAULT_BOARD_PROFILE="S32S3"'
|
'-DEMSESP_DEFAULT_BOARD_PROFILE="S32S3"'
|
||||||
|
|
||||||
[env:esp32_4M]
|
[env:esp32_4M]
|
||||||
extends = espressi32_base_tasmota
|
extends = espressi32_base_tasmota
|
||||||
board = esp32dev
|
board = esp32dev
|
||||||
board_upload.flash_size = 4MB
|
board_upload.flash_size = 4MB
|
||||||
board_build.partitions = esp32_partition_4M.csv
|
board_build.partitions = esp32_partition_4M.csv
|
||||||
build_flags =
|
build_flags =
|
||||||
${espressi32_base_tasmota.build_flags}
|
${espressi32_base_tasmota.build_flags}
|
||||||
-Os
|
-Os
|
||||||
|
|
||||||
[env:esp32_4Mplus]
|
[env:esp32_4Mplus]
|
||||||
extends = espressi32_base_tasmota
|
extends = espressi32_base_tasmota
|
||||||
@@ -130,30 +115,28 @@ extends = espressi32_base_tasmota
|
|||||||
board = lolin_c3_mini
|
board = lolin_c3_mini
|
||||||
board_upload.flash_size = 4MB
|
board_upload.flash_size = 4MB
|
||||||
board_build.partitions = esp32_partition_4M.csv
|
board_build.partitions = esp32_partition_4M.csv
|
||||||
build_flags =
|
build_flags =
|
||||||
${espressi32_base_tasmota.build_flags}
|
${espressi32_base_tasmota.build_flags}
|
||||||
'-DEMSESP_DEFAULT_BOARD_PROFILE="C3MINI"'
|
'-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]
|
[env:lolin_c3_mini_v1]
|
||||||
extends = espressi32_base_tasmota
|
extends = espressi32_base_tasmota
|
||||||
board = lolin_c3_mini
|
board = lolin_c3_mini
|
||||||
board_upload.flash_size = 4MB
|
board_upload.flash_size = 4MB
|
||||||
board_build.partitions = esp32_partition_4M.csv
|
board_build.partitions = esp32_partition_4M.csv
|
||||||
build_flags =
|
build_flags =
|
||||||
${espressi32_base_tasmota.build_flags}
|
${espressi32_base_tasmota.build_flags}
|
||||||
-DBOARD_C3_MINI_V1
|
-DBOARD_C3_MINI_V1
|
||||||
'-DEMSESP_DEFAULT_BOARD_PROFILE="C3MINI"'
|
'-DEMSESP_DEFAULT_BOARD_PROFILE="C3MINI"'
|
||||||
|
|
||||||
[env:lolin_s2_mini]
|
[env:lolin_s2_mini]
|
||||||
extends = espressi32_base_tasmota
|
extends = espressi32_base_tasmota
|
||||||
board = lolin_s2_mini
|
board = lolin_s2_mini
|
||||||
board_upload.flash_size = 4MB
|
board_upload.flash_size = 4MB
|
||||||
board_build.partitions = esp32_partition_4M.csv
|
board_build.partitions = esp32_partition_4M.csv
|
||||||
build_flags =
|
build_flags =
|
||||||
${espressi32_base_tasmota.build_flags}
|
${espressi32_base_tasmota.build_flags}
|
||||||
'-DEMSESP_DEFAULT_BOARD_PROFILE="S2MINI"'
|
'-DEMSESP_DEFAULT_BOARD_PROFILE="S2MINI"'
|
||||||
|
|
||||||
[env:lolin_s3]
|
[env:lolin_s3]
|
||||||
extends = espressi32_base
|
extends = espressi32_base
|
||||||
@@ -163,91 +146,110 @@ board_upload.flash_size = 16MB
|
|||||||
board_build.partitions = esp32_partition_16M.csv
|
board_build.partitions = esp32_partition_16M.csv
|
||||||
board_upload.use_1200bps_touch = false
|
board_upload.use_1200bps_touch = false
|
||||||
board_upload.wait_for_upload_port = false
|
board_upload.wait_for_upload_port = false
|
||||||
build_flags =
|
build_flags =
|
||||||
${espressi32_base.build_flags}
|
${espressi32_base.build_flags}
|
||||||
-O2
|
-O2
|
||||||
'-DEMSESP_DEFAULT_BOARD_PROFILE="S32S3"'
|
'-DEMSESP_DEFAULT_BOARD_PROFILE="S32S3"'
|
||||||
|
|
||||||
[env:https]
|
[env:https]
|
||||||
; use Tasmota's libary which removes some libs (like mbedtsl) and increases available heap
|
platform = espressif32
|
||||||
; 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
|
|
||||||
framework = arduino
|
framework = arduino
|
||||||
board = esp32dev
|
board = esp32dev
|
||||||
board_build.filesystem = littlefs
|
board_build.filesystem = littlefs
|
||||||
board_build.f_cpu = 240000000L
|
board_build.f_cpu = 240000000L
|
||||||
board_upload.flash_size = 4MB
|
board_upload.flash_size = 4MB
|
||||||
board_build.partitions = esp32_partition_4M.csv
|
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.use_1200bps_touch = false
|
||||||
board_upload.wait_for_upload_port = true
|
board_upload.wait_for_upload_port = true
|
||||||
upload_port = /dev/ttyUSB0
|
upload_port = /dev/ttyUSB0
|
||||||
extra_scripts =
|
extra_scripts =
|
||||||
; pre:scripts/build_interface.py
|
pre:scripts/build_interface.py
|
||||||
scripts/rename_fw.py
|
scripts/rename_fw.py
|
||||||
build_unflags = ${common.unbuild_flags}
|
build_unflags = ${common.unbuild_flags}
|
||||||
build_flags =
|
build_flags =
|
||||||
${common.core_build_flags}
|
${common.core_build_flags}
|
||||||
${factory_settings.build_flags}
|
${factory_settings.build_flags}
|
||||||
${common.my_build_flags}
|
${common.my_build_flags}
|
||||||
-D ONEWIRE_CRC16=0
|
-D ONEWIRE_CRC16=0
|
||||||
-D NO_GLOBAL_ARDUINOOTA
|
-D NO_GLOBAL_ARDUINOOTA
|
||||||
-D ARDUINOJSON_ENABLE_STD_STRING=1
|
-D ARDUINOJSON_ENABLE_STD_STRING=1
|
||||||
-D ARDUINOJSON_USE_DOUBLE=0
|
-D ARDUINOJSON_USE_DOUBLE=0
|
||||||
; -D ARDUINOTRACE_ENABLE=1
|
-D ARDUINOTRACE_ENABLE=1
|
||||||
-D ARDUINOTRACE_ENABLE=0
|
-D TASMOTA_SDK
|
||||||
-D TASMOTA_SDK
|
-D CORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_WARN
|
||||||
; -D CORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_WARN
|
-D EMSESP_TEST
|
||||||
-D EMSESP_TEST
|
-D EMSESP_DEBUG
|
||||||
-D EMSESP_DEBUG
|
-D CONFIG_ETH_ENABLED
|
||||||
-D CONFIG_ETH_ENABLED
|
'-DEMSESP_DEFAULT_BOARD_PROFILE="Test"'
|
||||||
; -D BOARD_HAS_PSRAM
|
|
||||||
'-DEMSESP_DEFAULT_BOARD_PROFILE="Test"'
|
|
||||||
|
|
||||||
; to build and run: pio run -e standalone -t exec
|
|
||||||
[env:standalone]
|
[env:standalone]
|
||||||
platform = native
|
platform = native
|
||||||
build_flags =
|
build_flags =
|
||||||
-DARDUINOJSON_ENABLE_STD_STRING=1 -DARDUINOJSON_ENABLE_PROGMEM=1 -DARDUINOJSON_ENABLE_ARDUINO_STRING -DARDUINOJSON_USE_DOUBLE=0
|
-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_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\"
|
-DEMSESP_DEFAULT_LOCALE=\"en\" -DEMSESP_DEFAULT_TX_MODE=8 -DEMSESP_DEFAULT_VERSION=\"3.6.4-dev\" -DEMSESP_DEFAULT_BOARD_PROFILE=\"S32\"
|
||||||
-lpthread
|
-lpthread
|
||||||
-std=gnu++11 -Og -ggdb
|
-std=gnu++11 -Og -ggdb
|
||||||
build_src_flags =
|
build_src_flags =
|
||||||
; -Wall -Wextra -Werror -Wswitch-enum -Wno-unused-parameter -Wno-inconsistent-missing-override -Wno-unused-lambda-capture -Wno-sign-compare
|
-Wno-missing-braces
|
||||||
; -Wall -Wextra -Werror
|
-I./lib_standalone
|
||||||
-Wno-missing-braces
|
-I./lib/ArduinoJson/src
|
||||||
-I./lib_standalone
|
-I./lib/uuid-common/src
|
||||||
-I./lib/ArduinoJson/src
|
-I./lib/uuid-console/src
|
||||||
-I./lib/uuid-common/src
|
-I./lib/uuid-log/src
|
||||||
-I./lib/uuid-console/src
|
-I./lib/semver
|
||||||
-I./lib/uuid-log/src
|
-I./lib/PButton
|
||||||
-I./lib/semver
|
-I./lib/espMqttClient/src
|
||||||
-I./lib/PButton
|
-I./lib/espMqttClient/src/Transport
|
||||||
-I./lib/espMqttClient/src
|
build_src_filter =
|
||||||
-I./lib/espMqttClient/src/Transport
|
|
||||||
build_src_filter =
|
|
||||||
+<*>
|
+<*>
|
||||||
-<.git/>
|
-<.git/>
|
||||||
+<../lib_standalone>
|
+<../lib_standalone>
|
||||||
+<../lib/uuid-common>
|
+<../lib/uuid-common>
|
||||||
+<../lib/uuid-console>
|
+<../lib/uuid-console>
|
||||||
+<../lib/uuid-log>
|
+<../lib/uuid-log>
|
||||||
+<../lib/semver>
|
+<../lib/semver>
|
||||||
+<../lib/PButton>
|
+<../lib/PButton>
|
||||||
+<../lib/espMqttClient/src>
|
+<../lib/espMqttClient/src>
|
||||||
+<../lib/espMqttClient/src/Transport>
|
+<../lib/espMqttClient/src/Transport>
|
||||||
lib_compat_mode = off
|
lib_compat_mode = off
|
||||||
lib_ldf_mode = off
|
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\"
|
||||||
|
|||||||
@@ -1405,24 +1405,26 @@ EMSESP::EMSESP()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// add web server endpoint
|
// 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) {
|
PsychicHttpRequestCallback fn = [contentType, content, len](PsychicRequest * request) {
|
||||||
PsychicResponse response(request);
|
PsychicResponse response(request);
|
||||||
response.setCode(200);
|
response.setCode(200);
|
||||||
response.setContentType(contentType);
|
response.setContentType(contentType.c_str());
|
||||||
response.addHeader("Content-Encoding", "gzip");
|
|
||||||
// response.addHeader("Content-Encoding", "br"); // Brotli - only works over HTTPS
|
// 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);
|
response.setContent(content, len);
|
||||||
return response.send();
|
return response.send();
|
||||||
};
|
};
|
||||||
|
|
||||||
PsychicWebHandler * handler = new PsychicWebHandler();
|
PsychicWebHandler * handler = new PsychicWebHandler();
|
||||||
handler->onRequest(fn);
|
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
|
// Set default end-point for all non matching requests
|
||||||
// this is easier than using webServer.onNotFound()
|
// 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);
|
webServer.defaultEndpoint->setHandler(handler);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -1435,14 +1437,43 @@ void EMSESP::setupWeb() {
|
|||||||
// esp8266React services has 13
|
// esp8266React services has 13
|
||||||
// custom projects has around 23
|
// custom projects has around 23
|
||||||
webServer.config.max_uri_handlers = 80;
|
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 = 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
|
// TODO add support for https
|
||||||
webServer.listen(80); // start the web server
|
webServer.listen(80); // start the web server
|
||||||
|
|
||||||
DefaultHeaders::Instance().addHeader("Server", "EMS-ESP");
|
|
||||||
|
|
||||||
#ifndef EMSESP_STANDALONE
|
#ifndef EMSESP_STANDALONE
|
||||||
WWWData::registerRoutes(handler); // add webServer.on() endpoints from the generated web code
|
WWWData::registerRoutes(handler); // add webServer.on() endpoints from the generated web code
|
||||||
@@ -1562,6 +1593,9 @@ void EMSESP::start() {
|
|||||||
analogsensor_.start(); // Analog external sensors
|
analogsensor_.start(); // Analog external sensors
|
||||||
webLogService.start(); // apply settings to weblog service
|
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
|
// Load our library of known devices into stack mem. Names are stored in Flash memory
|
||||||
device_library_ = {
|
device_library_ = {
|
||||||
#include "device_library.h"
|
#include "device_library.h"
|
||||||
|
|||||||
@@ -241,7 +241,8 @@ class EMSESP {
|
|||||||
static WebSchedulerService webSchedulerService;
|
static WebSchedulerService webSchedulerService;
|
||||||
static WebCustomEntityService webCustomEntityService;
|
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:
|
private:
|
||||||
static std::string device_tostring(const uint8_t device_id);
|
static std::string device_tostring(const uint8_t device_id);
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
#define EMSESP_APP_VERSION "3.6.5-https.1"
|
#define EMSESP_APP_VERSION "3.6.5-https.2"
|
||||||
|
|||||||
@@ -23,27 +23,19 @@ using namespace std::placeholders;
|
|||||||
namespace emsesp {
|
namespace emsesp {
|
||||||
|
|
||||||
WebLogService::WebLogService(PsychicHttpServer * server, SecurityManager * securityManager)
|
WebLogService::WebLogService(PsychicHttpServer * server, SecurityManager * securityManager)
|
||||||
// TODO fix event source
|
: _server(server)
|
||||||
: // : _events(EVENT_SOURCE_LOG_PATH)
|
|
||||||
_server(server)
|
|
||||||
, _securityManager(securityManager) {
|
, _securityManager(securityManager) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void WebLogService::registerURI() {
|
void WebLogService::registerURI() {
|
||||||
// TODO fix event source
|
// eventsource
|
||||||
/*
|
_server->on(EVENT_SOURCE_LOG_PATH, &_events);
|
||||||
_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_);
|
|
||||||
*/
|
|
||||||
|
|
||||||
// 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) {
|
_server->on(LOG_SETTINGS_PATH, HTTP_POST, [this](PsychicRequest * request, JsonVariant & json) {
|
||||||
auto && body = json.as<JsonObject>();
|
auto && body = json.as<JsonObject>();
|
||||||
|
|
||||||
@@ -59,9 +51,7 @@ void WebLogService::registerURI() {
|
|||||||
return request->reply(200); // OK
|
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) {
|
_server->on(LOG_SETTINGS_PATH, HTTP_GET, [this](PsychicRequest * request) {
|
||||||
PsychicJsonResponse response = PsychicJsonResponse(request, false, EMSESP_JSON_SIZE_SMALL);
|
PsychicJsonResponse response = PsychicJsonResponse(request, false, EMSESP_JSON_SIZE_SMALL);
|
||||||
JsonObject root = response.getRoot();
|
JsonObject root = response.getRoot();
|
||||||
@@ -72,6 +62,7 @@ void WebLogService::registerURI() {
|
|||||||
return response.send();
|
return response.send();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// /rest/fetchLog
|
||||||
// for bring back the whole log - is a command, hence a POST
|
// 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
|
// send the complete log buffer to the API, not filtering on log level
|
||||||
// done by resetting the pointer
|
// done by resetting the pointer
|
||||||
@@ -79,9 +70,6 @@ void WebLogService::registerURI() {
|
|||||||
log_message_id_tail_ = 0;
|
log_message_id_tail_ = 0;
|
||||||
return request->reply(200);
|
return request->reply(200);
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO this can be removed when ported over
|
|
||||||
// server->addHandler(&events_);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// start the log service with INFO level
|
// 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
|
// send to web eventsource
|
||||||
void WebLogService::transmit(const QueuedLogMessage & message) {
|
void WebLogService::transmit(const QueuedLogMessage & message) {
|
||||||
DynamicJsonDocument jsonDocument(EMSESP_JSON_SIZE_LARGE);
|
DynamicJsonDocument jsonDocument(EMSESP_JSON_SIZE_LARGE);
|
||||||
if (jsonDocument.capacity() == 0) {
|
// if (jsonDocument.capacity() == 0) {
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
JsonObject logEvent = jsonDocument.to<JsonObject>();
|
JsonObject logEvent = jsonDocument.to<JsonObject>();
|
||||||
char time_string[25];
|
char time_string[25];
|
||||||
|
|
||||||
@@ -244,47 +232,13 @@ void WebLogService::transmit(const QueuedLogMessage & message) {
|
|||||||
logEvent["n"] = message.content_->name;
|
logEvent["n"] = message.content_->name;
|
||||||
logEvent["m"] = message.content_->text;
|
logEvent["m"] = message.content_->text;
|
||||||
|
|
||||||
size_t len = measureJson(jsonDocument);
|
size_t len = measureJson(jsonDocument) + 1;
|
||||||
char * buffer = new char[len + 1];
|
char * buffer = (char *)malloc(len);
|
||||||
if (buffer) {
|
if (buffer != NULL) {
|
||||||
serializeJson(jsonDocument, buffer, len + 1);
|
serializeJson(jsonDocument, buffer, len);
|
||||||
_events.send(buffer, "message", message.id_);
|
_events.send(buffer, "message", message.id_);
|
||||||
}
|
}
|
||||||
delete[] buffer;
|
free(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
// send the complete log buffer to the API, not filtering on log level
|
} // namespace emsesp
|
||||||
// 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<JsonObject>();
|
|
||||||
|
|
||||||
// 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
|
|
||||||
|
|||||||
@@ -50,8 +50,7 @@ class WebLogService : public uuid::log::Handler {
|
|||||||
private:
|
private:
|
||||||
SecurityManager * _securityManager;
|
SecurityManager * _securityManager;
|
||||||
PsychicHttpServer * _server;
|
PsychicHttpServer * _server;
|
||||||
|
PsychicEventSource _events;
|
||||||
PsychicEventSource _events;
|
|
||||||
|
|
||||||
class QueuedLogMessage {
|
class QueuedLogMessage {
|
||||||
public:
|
public:
|
||||||
|
|||||||
Reference in New Issue
Block a user