diff --git a/.gitignore b/.gitignore index 127a92df7..b9391d904 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,5 @@ # vscode -.vscode/c_cpp_properties.json -.vscode/extensions.json -.vscode/launch.json -.vscode/settings.json +.vscode/* # c++ compiling .clang_complete @@ -60,3 +57,4 @@ bw-output/ # testing emsesp + diff --git a/.vscode/extensions.json b/.vscode/extensions.json index a35d0dc12..080e70d08 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -2,8 +2,6 @@ // See http://go.microsoft.com/fwlink/?LinkId=827846 // for the documentation about the extensions.json format "recommendations": [ - "dbaeumer.vscode-eslint", - "esbenp.prettier-vscode", "platformio.platformio-ide" ], "unwantedRecommendations": [ diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 425cd1cdf..000000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,88 +0,0 @@ -{ - "search.exclude": { - "**/.yarn": true, - "**/.pnp.*": true - }, - "editor.codeActionsOnSave": { - "source.fixAll": "explicit" - }, - "eslint.nodePath": "interface/.yarn/sdks", - "eslint.workingDirectories": ["interface"], - "prettier.prettierPath": "", - "typescript.enablePromptUseWorkspaceTsdk": true, - "files.associations": { - "*.tsx": "typescriptreact", - "*.tcc": "cpp", - "optional": "cpp", - "istream": "cpp", - "ostream": "cpp", - "ratio": "cpp", - "system_error": "cpp", - "array": "cpp", - "functional": "cpp", - "regex": "cpp", - "tuple": "cpp", - "type_traits": "cpp", - "utility": "cpp", - "string": "cpp", - "string_view": "cpp", - "atomic": "cpp", - "bitset": "cpp", - "cctype": "cpp", - "chrono": "cpp", - "clocale": "cpp", - "cmath": "cpp", - "condition_variable": "cpp", - "cstdarg": "cpp", - "cstddef": "cpp", - "cstdint": "cpp", - "cstdio": "cpp", - "cstdlib": "cpp", - "cstring": "cpp", - "ctime": "cpp", - "cwchar": "cpp", - "cwctype": "cpp", - "deque": "cpp", - "list": "cpp", - "unordered_map": "cpp", - "unordered_set": "cpp", - "vector": "cpp", - "exception": "cpp", - "algorithm": "cpp", - "iterator": "cpp", - "map": "cpp", - "memory": "cpp", - "memory_resource": "cpp", - "numeric": "cpp", - "random": "cpp", - "set": "cpp", - "fstream": "cpp", - "initializer_list": "cpp", - "iomanip": "cpp", - "iosfwd": "cpp", - "iostream": "cpp", - "limits": "cpp", - "mutex": "cpp", - "new": "cpp", - "sstream": "cpp", - "stdexcept": "cpp", - "streambuf": "cpp", - "thread": "cpp", - "cinttypes": "cpp", - "typeinfo": "cpp" - }, - "todo-tree.filtering.excludeGlobs": [ - "**/vendor/**", - "**/node_modules/**", - "**/dist/**", - "**/bower_components/**", - "**/build/**", - "**/.vscode/**", - "**/.github/**", - "**/_output/**", - "**/*.min.*", - "**/*.map", - "**/ArduinoJson/**" - ], - "cSpell.enableFiletypes": ["!cpp"] -} diff --git a/.vscode/tasks.json b/.vscode/tasks.json deleted file mode 100644 index edcb3abc1..000000000 --- a/.vscode/tasks.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - // See https://go.microsoft.com/fwlink/?LinkId=733558 - // for the documentation about the tasks.json format - "version": "2.0.0", - "tasks": [ - { - "type": "shell", - "label": "build standalone emsesp", - "command": "make", - "args": [], - "problemMatcher": ["$gcc"], - "group": { - "kind": "build", - "isDefault": true - } - } - ] -} diff --git a/lib/ESPAsyncWebServer/src/AsyncJson.h b/lib/ESPAsyncWebServer/src/AsyncJson.h index 207200519..c1c604d54 100644 --- a/lib/ESPAsyncWebServer/src/AsyncJson.h +++ b/lib/ESPAsyncWebServer/src/AsyncJson.h @@ -63,7 +63,7 @@ class AsyncJsonResponse : public AsyncAbstractResponse { ~AsyncJsonResponse() { } - JsonVariant & getRoot() { + JsonVariant getRoot() { return _root; } bool _sourceValid() const { @@ -108,7 +108,7 @@ class PrettyAsyncJsonResponse : public AsyncJsonResponse { } }; -typedef std::function ArJsonRequestHandlerFunction; +typedef std::function ArJsonRequestHandlerFunction; class AsyncCallbackJsonWebHandler : public AsyncWebHandler { private: @@ -142,13 +142,15 @@ class AsyncCallbackJsonWebHandler : public AsyncWebHandler { if (!_onRequest) return false; - if (!(_method & request->method())) + WebRequestMethodComposite request_method = request->method(); + + if (!(_method & request_method)) return false; if (_uri.length() && (_uri != request->url() && !request->url().startsWith(_uri + "/"))) return false; - if (!request->contentType().equalsIgnoreCase(JSON_MIMETYPE)) + if (request_method != HTTP_GET && !request->contentType().equalsIgnoreCase(JSON_MIMETYPE)) return false; request->addInterestingHeader("ANY"); @@ -156,18 +158,23 @@ class AsyncCallbackJsonWebHandler : public AsyncWebHandler { } virtual void handleRequest(AsyncWebServerRequest * request) override final { + if ((_username != "" && _password != "") && !request->authenticate(_username.c_str(), _password.c_str())) + return request->requestAuthentication(); if (_onRequest) { - JsonVariant json; // empty variant - if (request->_tempObject != NULL) { + if (request->method() == HTTP_GET) { + JsonVariant json; + _onRequest(request, json); + return; + } else if (request->_tempObject != NULL) { JsonDocument jsonBuffer; DeserializationError error = deserializeJson(jsonBuffer, (uint8_t *)(request->_tempObject)); if (!error) { - json = jsonBuffer.as(); + JsonVariant json = jsonBuffer.as(); _onRequest(request, json); return; } } - _onRequest(request, json); + request->send(_contentLength > _maxContentLength ? 413 : 400); } else { request->send(500); } diff --git a/lib/ESPAsyncWebServer/src/ESPAsyncWebServer.h b/lib/ESPAsyncWebServer/src/ESPAsyncWebServer.h index 8955c39f2..c17af6700 100644 --- a/lib/ESPAsyncWebServer/src/ESPAsyncWebServer.h +++ b/lib/ESPAsyncWebServer/src/ESPAsyncWebServer.h @@ -28,6 +28,8 @@ #include #include "FS.h" +#include // added by proddy + #include "StringArray.h" #ifdef ESP32 @@ -65,26 +67,26 @@ class AsyncResponseStream; #ifndef WEBSERVER_H typedef enum { - HTTP_GET = 0b00000001, - HTTP_POST = 0b00000010, - HTTP_DELETE = 0b00000100, - HTTP_PUT = 0b00001000, - HTTP_PATCH = 0b00010000, - HTTP_HEAD = 0b00100000, - HTTP_OPTIONS = 0b01000000, - HTTP_ANY = 0b01111111, + HTTP_GET = 0b00000001, + HTTP_POST = 0b00000010, + HTTP_DELETE = 0b00000100, + HTTP_PUT = 0b00001000, + HTTP_PATCH = 0b00010000, + HTTP_HEAD = 0b00100000, + HTTP_OPTIONS = 0b01000000, + HTTP_ANY = 0b01111111, } WebRequestMethod; #endif #ifndef HAVE_FS_FILE_OPEN_MODE namespace fs { - class FileOpenMode { - public: - static const char *read; - static const char *write; - static const char *append; - }; +class FileOpenMode { + public: + static const char * read; + static const char * write; + static const char * append; }; +}; // namespace fs #else #include "FileOpenMode.h" #endif @@ -92,7 +94,7 @@ namespace fs { //if this value is returned when asked for data, packet will not be sent and you will be asked for data again #define RESPONSE_TRY_AGAIN 0xFFFFFFFF -typedef uint8_t WebRequestMethodComposite; +typedef uint8_t WebRequestMethodComposite; typedef std::function ArDisconnectHandler; /* @@ -104,17 +106,32 @@ class AsyncWebParameter { String _name; String _value; size_t _size; - bool _isForm; - bool _isFile; + bool _isForm; + bool _isFile; public: - - AsyncWebParameter(const String& name, const String& value, bool form=false, bool file=false, size_t size=0): _name(name), _value(value), _size(size), _isForm(form), _isFile(file){} - const String& name() const { return _name; } - const String& value() const { return _value; } - size_t size() const { return _size; } - bool isPost() const { return _isForm; } - bool isFile() const { return _isFile; } + AsyncWebParameter(const String & name, const String & value, bool form = false, bool file = false, size_t size = 0) + : _name(name) + , _value(value) + , _size(size) + , _isForm(form) + , _isFile(file) { + } + const String & name() const { + return _name; + } + const String & value() const { + return _value; + } + size_t size() const { + return _size; + } + bool isPost() const { + return _isForm; + } + bool isFile() const { + return _isFile; + } }; /* @@ -127,23 +144,36 @@ class AsyncWebHeader { String _value; public: - AsyncWebHeader() = default; + AsyncWebHeader() = default; AsyncWebHeader(const AsyncWebHeader &) = default; - AsyncWebHeader(const String& name, const String& value): _name(name), _value(value){} - AsyncWebHeader(const String& data): _name(), _value(){ - if(!data) return; - int index = data.indexOf(':'); - if (index < 0) return; - _name = data.substring(0, index); - _value = data.substring(index + 2); + AsyncWebHeader(const String & name, const String & value) + : _name(name) + , _value(value) { + } + AsyncWebHeader(const String & data) + : _name() + , _value() { + if (!data) + return; + int index = data.indexOf(':'); + if (index < 0) + return; + _name = data.substring(0, index); + _value = data.substring(index + 2); } - AsyncWebHeader &operator=(const AsyncWebHeader &) = default; + AsyncWebHeader & operator=(const AsyncWebHeader &) = default; - const String& name() const { return _name; } - const String& value() const { return _value; } - String toString() const { return _name + F(": ") + _value + F("\r\n"); } + const String & name() const { + return _name; + } + const String & value() const { + return _value; + } + String toString() const { + return _name + F(": ") + _value + F("\r\n"); + } }; /* @@ -152,98 +182,117 @@ class AsyncWebHeader { typedef enum { RCT_NOT_USED = -1, RCT_DEFAULT = 0, RCT_HTTP, RCT_WS, RCT_EVENT, RCT_MAX } RequestedConnectionType; -typedef std::function AwsResponseFiller; -typedef std::function AwsTemplateProcessor; +typedef std::function AwsResponseFiller; +typedef std::function AwsTemplateProcessor; class AsyncWebServerRequest { - using File = fs::File; - using FS = fs::FS; - friend class AsyncWebServer; - friend class AsyncCallbackWebHandler; + using File = fs::File; + using FS = fs::FS; + friend class AsyncWebServer; + friend class AsyncCallbackWebHandler; + private: - AsyncClient* _client; - AsyncWebServer* _server; - AsyncWebHandler* _handler; - AsyncWebServerResponse* _response; - std::vector _interestingHeaders; - ArDisconnectHandler _onDisconnectfn; - - String _temp; + AsyncClient * _client; + AsyncWebServer * _server; + AsyncWebHandler * _handler; + AsyncWebServerResponse * _response; + std::vector _interestingHeaders; + ArDisconnectHandler _onDisconnectfn; + + String _temp; uint8_t _parseState; - uint8_t _version; + uint8_t _version; WebRequestMethodComposite _method; - String _url; - String _host; - String _contentType; - String _boundary; - String _authorization; - RequestedConnectionType _reqconntype; - void _removeNotInterestingHeaders(); - bool _isDigest; - bool _isMultipart; - bool _isPlainPost; - bool _expectingContinue; - size_t _contentLength; - size_t _parsedLength; - - std::list _headers; + String _url; + String _host; + String _contentType; + String _boundary; + String _authorization; + RequestedConnectionType _reqconntype; + void _removeNotInterestingHeaders(); + bool _isDigest; + bool _isMultipart; + bool _isPlainPost; + bool _expectingContinue; + size_t _contentLength; + size_t _parsedLength; + + std::list _headers; LinkedList _params; - std::vector _pathParams; - - uint8_t _multiParseState; - uint8_t _boundaryPosition; - size_t _itemStartIndex; - size_t _itemSize; - String _itemName; - String _itemFilename; - String _itemType; - String _itemValue; - uint8_t *_itemBuffer; - size_t _itemBufferIndex; - bool _itemIsFile; + std::vector _pathParams; + + uint8_t _multiParseState; + uint8_t _boundaryPosition; + size_t _itemStartIndex; + size_t _itemSize; + String _itemName; + String _itemFilename; + String _itemType; + String _itemValue; + uint8_t * _itemBuffer; + size_t _itemBufferIndex; + bool _itemIsFile; void _onPoll(); void _onAck(size_t len, uint32_t time); void _onError(int8_t error); void _onTimeout(uint32_t time); void _onDisconnect(); - void _onData(void *buf, size_t len); + void _onData(void * buf, size_t len); - void _addParam(AsyncWebParameter*); - void _addPathParam(const char *param); + void _addParam(AsyncWebParameter *); + void _addPathParam(const char * param); bool _parseReqHead(); bool _parseReqHeader(); void _parseLine(); void _parsePlainPostChar(uint8_t data); void _parseMultipartPostByte(uint8_t data, bool last); - void _addGetParams(const String& params); + void _addGetParams(const String & params); void _handleUploadStart(); void _handleUploadByte(uint8_t data, bool last); void _handleUploadEnd(); public: - File _tempFile; - void *_tempObject; + File _tempFile; + void * _tempObject; - AsyncWebServerRequest(AsyncWebServer*, AsyncClient*); + AsyncWebServerRequest(AsyncWebServer *, AsyncClient *); ~AsyncWebServerRequest(); - AsyncClient* client(){ return _client; } - uint8_t version() const { return _version; } - WebRequestMethodComposite method() const { return _method; } - const String& url() const { return _url; } - const String& host() const { return _host; } - const String& contentType() const { return _contentType; } - size_t contentLength() const { return _contentLength; } - bool multipart() const { return _isMultipart; } - const __FlashStringHelper *methodToString() const; - const __FlashStringHelper *requestedConnTypeToString() const; - RequestedConnectionType requestedConnType() const { return _reqconntype; } + AsyncClient * client() { + return _client; + } + uint8_t version() const { + return _version; + } + WebRequestMethodComposite method() const { + return _method; + } + const String & url() const { + return _url; + } + const String & host() const { + return _host; + } + const String & contentType() const { + return _contentType; + } + size_t contentLength() const { + return _contentLength; + } + bool multipart() const { + return _isMultipart; + } + const __FlashStringHelper * methodToString() const; + const __FlashStringHelper * requestedConnTypeToString() const; + RequestedConnectionType requestedConnType() const { + return _reqconntype; + } bool isExpectedRequestedConnType(RequestedConnectionType erct1, RequestedConnectionType erct2 = RCT_NOT_USED, RequestedConnectionType erct3 = RCT_NOT_USED); - void onDisconnect (ArDisconnectHandler fn); + void onDisconnect(ArDisconnectHandler fn); //hash is the string representation of: // base64(user:pass) for basic or @@ -252,76 +301,85 @@ class AsyncWebServerRequest { bool authenticate(const char * username, const char * password, const char * realm = NULL, bool passwordIsHash = false); void requestAuthentication(const char * realm = NULL, bool isDigest = true); - void setHandler(AsyncWebHandler *handler){ _handler = handler; } - void addInterestingHeader(const String& name); - - void redirect(const String& url); - - void send(AsyncWebServerResponse *response); - void send(int code, const String& contentType=String(), const String& content=String()); - void send(FS &fs, const String& path, const String& contentType=String(), bool download=false, AwsTemplateProcessor callback=nullptr); - void send(File content, const String& path, const String& contentType=String(), bool download=false, AwsTemplateProcessor callback=nullptr); - void send(Stream &stream, const String& contentType, size_t len, AwsTemplateProcessor callback=nullptr); - void send(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback=nullptr); - void sendChunked(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback=nullptr); - void send_P(int code, const String& contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback=nullptr); - void send_P(int code, const String& contentType, PGM_P content, AwsTemplateProcessor callback=nullptr); - - AsyncWebServerResponse *beginResponse(int code, const String& contentType=String(), const String& content=String()); - AsyncWebServerResponse *beginResponse(FS &fs, const String& path, const String& contentType=String(), bool download=false, AwsTemplateProcessor callback=nullptr); - AsyncWebServerResponse *beginResponse(File content, const String& path, const String& contentType=String(), bool download=false, AwsTemplateProcessor callback=nullptr); - AsyncWebServerResponse *beginResponse(Stream &stream, const String& contentType, size_t len, AwsTemplateProcessor callback=nullptr); - AsyncWebServerResponse *beginResponse(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback=nullptr); - AsyncWebServerResponse *beginChunkedResponse(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback=nullptr); - AsyncResponseStream *beginResponseStream(const String& contentType, size_t bufferSize=1460); - AsyncWebServerResponse *beginResponse_P(int code, const String& contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback=nullptr); - AsyncWebServerResponse *beginResponse_P(int code, const String& contentType, PGM_P content, AwsTemplateProcessor callback=nullptr); - - size_t headers() const; // get header count - bool hasHeader(const String& name) const; // check if header exists - bool hasHeader(const __FlashStringHelper * data) const; // check if header exists - - AsyncWebHeader* getHeader(const String& name); - const AsyncWebHeader* getHeader(const String& name) const; - AsyncWebHeader* getHeader(const __FlashStringHelper * data); - const AsyncWebHeader* getHeader(const __FlashStringHelper * data) const; - AsyncWebHeader* getHeader(size_t num); - const AsyncWebHeader* getHeader(size_t num) const; - - size_t params() const; // get arguments count - bool hasParam(const String& name, bool post=false, bool file=false) const; - bool hasParam(const __FlashStringHelper * data, bool post=false, bool file=false) const; - - AsyncWebParameter* getParam(const String& name, bool post=false, bool file=false) const; - AsyncWebParameter* getParam(const __FlashStringHelper * data, bool post, bool file) const; - AsyncWebParameter* getParam(size_t num) const; - - size_t args() const { return params(); } // get arguments count - const String& arg(const String& name) const; // get request argument value by name - const String& arg(const __FlashStringHelper * data) const; // get request argument value by F(name) - const String& arg(size_t i) const; // get request argument value by number - const String& argName(size_t i) const; // get request argument name by number - bool hasArg(const char* name) const; // check if argument exists - bool hasArg(const __FlashStringHelper * data) const; // check if F(argument) exists - - const String& ASYNCWEBSERVER_REGEX_ATTRIBUTE pathArg(size_t i) const; - - const String& header(const char* name) const;// get request header value by name - const String& header(const __FlashStringHelper * data) const;// get request header value by F(name) - const String& header(size_t i) const; // get request header value by number - const String& headerName(size_t i) const; // get request header name by number - String urlDecode(const String& text) const; + void setHandler(AsyncWebHandler * handler) { + _handler = handler; + } + void addInterestingHeader(const String & name); + + void redirect(const String & url); + + void send(AsyncWebServerResponse * response); + void send(int code, const String & contentType = String(), const String & content = String()); + void send(FS & fs, const String & path, const String & contentType = String(), bool download = false, AwsTemplateProcessor callback = nullptr); + void send(File content, const String & path, const String & contentType = String(), bool download = false, AwsTemplateProcessor callback = nullptr); + void send(Stream & stream, const String & contentType, size_t len, AwsTemplateProcessor callback = nullptr); + void send(const String & contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr); + void sendChunked(const String & contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr); + void send_P(int code, const String & contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback = nullptr); + void send_P(int code, const String & contentType, PGM_P content, AwsTemplateProcessor callback = nullptr); + + AsyncWebServerResponse * beginResponse(int code, const String & contentType = String(), const String & content = String()); + AsyncWebServerResponse * + beginResponse(FS & fs, const String & path, const String & contentType = String(), bool download = false, AwsTemplateProcessor callback = nullptr); + AsyncWebServerResponse * + beginResponse(File content, const String & path, const String & contentType = String(), bool download = false, AwsTemplateProcessor callback = nullptr); + AsyncWebServerResponse * beginResponse(Stream & stream, const String & contentType, size_t len, AwsTemplateProcessor callback = nullptr); + AsyncWebServerResponse * beginResponse(const String & contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr); + AsyncWebServerResponse * beginChunkedResponse(const String & contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr); + AsyncResponseStream * beginResponseStream(const String & contentType, size_t bufferSize = 1460); + AsyncWebServerResponse * beginResponse_P(int code, const String & contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback = nullptr); + AsyncWebServerResponse * beginResponse_P(int code, const String & contentType, PGM_P content, AwsTemplateProcessor callback = nullptr); + + // added by proddy + AsyncWebServerResponse * beginResponse(const String & contentType, const uint8_t * content, size_t len); + + size_t headers() const; // get header count + bool hasHeader(const String & name) const; // check if header exists + bool hasHeader(const __FlashStringHelper * data) const; // check if header exists + + AsyncWebHeader * getHeader(const String & name); + const AsyncWebHeader * getHeader(const String & name) const; + AsyncWebHeader * getHeader(const __FlashStringHelper * data); + const AsyncWebHeader * getHeader(const __FlashStringHelper * data) const; + AsyncWebHeader * getHeader(size_t num); + const AsyncWebHeader * getHeader(size_t num) const; + + size_t params() const; // get arguments count + bool hasParam(const String & name, bool post = false, bool file = false) const; + bool hasParam(const __FlashStringHelper * data, bool post = false, bool file = false) const; + + AsyncWebParameter * getParam(const String & name, bool post = false, bool file = false) const; + AsyncWebParameter * getParam(const __FlashStringHelper * data, bool post, bool file) const; + AsyncWebParameter * getParam(size_t num) const; + + size_t args() const { + return params(); + } // get arguments count + const String & arg(const String & name) const; // get request argument value by name + const String & arg(const __FlashStringHelper * data) const; // get request argument value by F(name) + const String & arg(size_t i) const; // get request argument value by number + const String & argName(size_t i) const; // get request argument name by number + bool hasArg(const char * name) const; // check if argument exists + bool hasArg(const __FlashStringHelper * data) const; // check if F(argument) exists + + const String & ASYNCWEBSERVER_REGEX_ATTRIBUTE pathArg(size_t i) const; + + const String & header(const char * name) const; // get request header value by name + const String & header(const __FlashStringHelper * data) const; // get request header value by F(name) + const String & header(size_t i) const; // get request header value by number + const String & headerName(size_t i) const; // get request header name by number + String urlDecode(const String & text) const; }; /* * FILTER :: Callback to filter AsyncWebRewrite and AsyncWebHandler (done by the Server) * */ -typedef std::function ArRequestFilterFunction; +typedef std::function ArRequestFilterFunction; -bool ON_STA_FILTER(AsyncWebServerRequest *request); +bool ON_STA_FILTER(AsyncWebServerRequest * request); -bool ON_AP_FILTER(AsyncWebServerRequest *request); +bool ON_AP_FILTER(AsyncWebServerRequest * request); /* * REWRITE :: One instance can be handle any Request (done by the Server) @@ -329,25 +387,44 @@ bool ON_AP_FILTER(AsyncWebServerRequest *request); class AsyncWebRewrite { protected: - String _from; - String _toUrl; - String _params; + String _from; + String _toUrl; + String _params; ArRequestFilterFunction _filter; + public: - AsyncWebRewrite(const char* from, const char* to): _from(from), _toUrl(to), _params(String()), _filter(NULL){ - int index = _toUrl.indexOf('?'); - if (index > 0) { - _params = _toUrl.substring(index +1); - _toUrl = _toUrl.substring(0, index); - } - } - virtual ~AsyncWebRewrite(){} - AsyncWebRewrite& setFilter(ArRequestFilterFunction fn) { _filter = fn; return *this; } - bool filter(AsyncWebServerRequest *request) const { return _filter == NULL || _filter(request); } - const String& from(void) const { return _from; } - const String& toUrl(void) const { return _toUrl; } - const String& params(void) const { return _params; } - virtual bool match(AsyncWebServerRequest *request) { return from() == request->url() && filter(request); } + AsyncWebRewrite(const char * from, const char * to) + : _from(from) + , _toUrl(to) + , _params(String()) + , _filter(NULL) { + int index = _toUrl.indexOf('?'); + if (index > 0) { + _params = _toUrl.substring(index + 1); + _toUrl = _toUrl.substring(0, index); + } + } + virtual ~AsyncWebRewrite() { + } + AsyncWebRewrite & setFilter(ArRequestFilterFunction fn) { + _filter = fn; + return *this; + } + bool filter(AsyncWebServerRequest * request) const { + return _filter == NULL || _filter(request); + } + const String & from(void) const { + return _from; + } + const String & toUrl(void) const { + return _toUrl; + } + const String & params(void) const { + return _params; + } + virtual bool match(AsyncWebServerRequest * request) { + return from() == request->url() && filter(request); + } }; /* @@ -357,78 +434,107 @@ class AsyncWebRewrite { class AsyncWebHandler { protected: ArRequestFilterFunction _filter; - String _username; - String _password; + String _username; + String _password; + public: - AsyncWebHandler():_username(""), _password(""){} - AsyncWebHandler& setFilter(ArRequestFilterFunction fn) { _filter = fn; return *this; } - AsyncWebHandler& setAuthentication(const char *username, const char *password){ _username = String(username);_password = String(password); return *this; }; - bool filter(AsyncWebServerRequest *request){ return _filter == NULL || _filter(request); } - virtual ~AsyncWebHandler(){} - virtual bool canHandle(AsyncWebServerRequest *request __attribute__((unused))){ - return false; - } - virtual void handleRequest(AsyncWebServerRequest *request __attribute__((unused))){} - virtual void handleUpload(AsyncWebServerRequest *request __attribute__((unused)), const String& filename __attribute__((unused)), size_t index __attribute__((unused)), uint8_t *data __attribute__((unused)), size_t len __attribute__((unused)), bool final __attribute__((unused))){} - virtual void handleBody(AsyncWebServerRequest *request __attribute__((unused)), uint8_t *data __attribute__((unused)), size_t len __attribute__((unused)), size_t index __attribute__((unused)), size_t total __attribute__((unused))){} - virtual bool isRequestHandlerTrivial(){return true;} + AsyncWebHandler() + : _username("") + , _password("") { + } + AsyncWebHandler & setFilter(ArRequestFilterFunction fn) { + _filter = fn; + return *this; + } + AsyncWebHandler & setAuthentication(const char * username, const char * password) { + _username = String(username); + _password = String(password); + return *this; + }; + bool filter(AsyncWebServerRequest * request) { + return _filter == NULL || _filter(request); + } + virtual ~AsyncWebHandler() { + } + virtual bool canHandle(AsyncWebServerRequest * request __attribute__((unused))) { + return false; + } + virtual void handleRequest(AsyncWebServerRequest * request __attribute__((unused))) { + } + virtual void handleUpload(AsyncWebServerRequest * request __attribute__((unused)), + const String & filename __attribute__((unused)), + size_t index __attribute__((unused)), + uint8_t * data __attribute__((unused)), + size_t len __attribute__((unused)), + bool final __attribute__((unused))) { + } + virtual void handleBody(AsyncWebServerRequest * request __attribute__((unused)), + uint8_t * data __attribute__((unused)), + size_t len __attribute__((unused)), + size_t index __attribute__((unused)), + size_t total __attribute__((unused))) { + } + virtual bool isRequestHandlerTrivial() { + return true; + } }; /* * RESPONSE :: One instance is created for each Request (attached by the Handler) * */ -typedef enum { - RESPONSE_SETUP, RESPONSE_HEADERS, RESPONSE_CONTENT, RESPONSE_WAIT_ACK, RESPONSE_END, RESPONSE_FAILED -} WebResponseState; +typedef enum { RESPONSE_SETUP, RESPONSE_HEADERS, RESPONSE_CONTENT, RESPONSE_WAIT_ACK, RESPONSE_END, RESPONSE_FAILED } WebResponseState; class AsyncWebServerResponse { protected: - int _code; + int _code; std::list _headers; - String _contentType; - size_t _contentLength; - bool _sendContentLength; - bool _chunked; - size_t _headLength; - size_t _sentLength; - size_t _ackedLength; - size_t _writtenLength; - WebResponseState _state; - const char* _responseCodeToString(int code); -public: - static const __FlashStringHelper *responseCodeToString(int code); + String _contentType; + size_t _contentLength; + bool _sendContentLength; + bool _chunked; + size_t _headLength; + size_t _sentLength; + size_t _ackedLength; + size_t _writtenLength; + WebResponseState _state; + const char * _responseCodeToString(int code); + + public: + static const __FlashStringHelper * responseCodeToString(int code); public: AsyncWebServerResponse(); virtual ~AsyncWebServerResponse(); - virtual void setCode(int code); - virtual void setContentLength(size_t len); - virtual void setContentType(const String& type); - virtual void addHeader(const String& name, const String& value); + virtual void setCode(int code); + virtual void setContentLength(size_t len); + virtual void setContentType(const String & type); + virtual void addHeader(const String & name, const String & value); virtual String _assembleHead(uint8_t version); - virtual bool _started() const; - virtual bool _finished() const; - virtual bool _failed() const; - virtual bool _sourceValid() const; - virtual void _respond(AsyncWebServerRequest *request); - virtual size_t _ack(AsyncWebServerRequest *request, size_t len, uint32_t time); + virtual bool _started() const; + virtual bool _finished() const; + virtual bool _failed() const; + virtual bool _sourceValid() const; + virtual void _respond(AsyncWebServerRequest * request); + virtual size_t _ack(AsyncWebServerRequest * request, size_t len, uint32_t time); }; /* * SERVER :: One instance * */ -typedef std::function ArRequestHandlerFunction; -typedef std::function ArUploadHandlerFunction; -typedef std::function ArBodyHandlerFunction; +typedef std::function ArRequestHandlerFunction; +typedef std::function ArUploadHandlerFunction; +typedef std::function ArBodyHandlerFunction; + +typedef std::function ArJsonRequestHandlerFunction; // added by proddy class AsyncWebServer { protected: - AsyncServer _server; - LinkedList _rewrites; - LinkedList _handlers; - AsyncCallbackWebHandler* _catchAllHandler; + AsyncServer _server; + LinkedList _rewrites; + LinkedList _handlers; + AsyncCallbackWebHandler * _catchAllHandler; public: AsyncWebServer(uint16_t port); @@ -438,58 +544,65 @@ class AsyncWebServer { void end(); #if ASYNC_TCP_SSL_ENABLED - void onSslFileRequest(AcSSlFileHandler cb, void* arg); - void beginSecure(const char *cert, const char *private_key_file, const char *password); + void onSslFileRequest(AcSSlFileHandler cb, void * arg); + void beginSecure(const char * cert, const char * private_key_file, const char * password); #endif - AsyncWebRewrite& addRewrite(AsyncWebRewrite* rewrite); - bool removeRewrite(AsyncWebRewrite* rewrite); - AsyncWebRewrite& rewrite(const char* from, const char* to); + AsyncWebRewrite & addRewrite(AsyncWebRewrite * rewrite); + bool removeRewrite(AsyncWebRewrite * rewrite); + AsyncWebRewrite & rewrite(const char * from, const char * to); + + AsyncWebHandler & addHandler(AsyncWebHandler * handler); + bool removeHandler(AsyncWebHandler * handler); - AsyncWebHandler& addHandler(AsyncWebHandler* handler); - bool removeHandler(AsyncWebHandler* handler); + AsyncCallbackWebHandler & on(const char * uri, ArRequestHandlerFunction onRequest); + AsyncCallbackWebHandler & on(const char * uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest); + AsyncCallbackWebHandler & on(const char * uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload); + AsyncCallbackWebHandler & + on(const char * uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload, ArBodyHandlerFunction onBody); - AsyncCallbackWebHandler& on(const char* uri, ArRequestHandlerFunction onRequest); - AsyncCallbackWebHandler& on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest); - AsyncCallbackWebHandler& on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload); - AsyncCallbackWebHandler& on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload, ArBodyHandlerFunction onBody); + void on(const char * uri, ArJsonRequestHandlerFunction onRequest); // added by proddy - AsyncStaticWebHandler& serveStatic(const char* uri, fs::FS& fs, const char* path, const char* cache_control = NULL); + AsyncStaticWebHandler & serveStatic(const char * uri, fs::FS & fs, const char * path, const char * cache_control = NULL); void onNotFound(ArRequestHandlerFunction fn); //called when handler is not assigned void onFileUpload(ArUploadHandlerFunction fn); //handle file uploads - void onRequestBody(ArBodyHandlerFunction fn); //handle posts with plain body content (JSON often transmitted this way as a request) + void onRequestBody(ArBodyHandlerFunction fn); //handle posts with plain body content (JSON often transmitted this way as a request) void reset(); //remove all writers and handlers, with onNotFound/onFileUpload/onRequestBody - void _handleDisconnect(AsyncWebServerRequest *request); - void _attachHandler(AsyncWebServerRequest *request); - void _rewriteRequest(AsyncWebServerRequest *request); + void _handleDisconnect(AsyncWebServerRequest * request); + void _attachHandler(AsyncWebServerRequest * request); + void _rewriteRequest(AsyncWebServerRequest * request); }; class DefaultHeaders { - using headers_t = std::list; - headers_t _headers; + using headers_t = std::list; + headers_t _headers; -public: - DefaultHeaders() = default; + public: + DefaultHeaders() = default; - using ConstIterator = headers_t::const_iterator; + using ConstIterator = headers_t::const_iterator; - void addHeader(const String& name, const String& value){ - _headers.emplace_back(name, value); - } + void addHeader(const String & name, const String & value) { + _headers.emplace_back(name, value); + } - ConstIterator begin() const { return _headers.begin(); } - ConstIterator end() const { return _headers.end(); } + ConstIterator begin() const { + return _headers.begin(); + } + ConstIterator end() const { + return _headers.end(); + } - DefaultHeaders(DefaultHeaders const &) = delete; - DefaultHeaders &operator=(DefaultHeaders const &) = delete; + DefaultHeaders(DefaultHeaders const &) = delete; + DefaultHeaders & operator=(DefaultHeaders const &) = delete; - static DefaultHeaders &Instance() { - static DefaultHeaders instance; - return instance; - } + static DefaultHeaders & Instance() { + static DefaultHeaders instance; + return instance; + } }; #include "WebResponseImpl.h" diff --git a/lib/ESPAsyncWebServer/src/WebRequest.cpp b/lib/ESPAsyncWebServer/src/WebRequest.cpp index 31613dfda..bfc74579d 100644 --- a/lib/ESPAsyncWebServer/src/WebRequest.cpp +++ b/lib/ESPAsyncWebServer/src/WebRequest.cpp @@ -30,964 +30,1059 @@ enum { PARSE_REQ_START, PARSE_REQ_HEADERS, PARSE_REQ_BODY, PARSE_REQ_END, PARSE_REQ_FAIL }; -AsyncWebServerRequest::AsyncWebServerRequest(AsyncWebServer* s, AsyncClient* c) - : _client(c) - , _server(s) - , _handler(NULL) - , _response(NULL) - , _temp() - , _parseState(0) - , _version(0) - , _method(HTTP_ANY) - , _url() - , _host() - , _contentType() - , _boundary() - , _authorization() - , _reqconntype(RCT_HTTP) - , _isDigest(false) - , _isMultipart(false) - , _isPlainPost(false) - , _expectingContinue(false) - , _contentLength(0) - , _parsedLength(0) - , _params(LinkedList([](AsyncWebParameter *p){ delete p; })) - , _multiParseState(0) - , _boundaryPosition(0) - , _itemStartIndex(0) - , _itemSize(0) - , _itemName() - , _itemFilename() - , _itemType() - , _itemValue() - , _itemBuffer(0) - , _itemBufferIndex(0) - , _itemIsFile(false) - , _tempObject(NULL) -{ - c->onError([](void *r, AsyncClient* c, int8_t error){ (void)c; AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onError(error); }, this); - c->onAck([](void *r, AsyncClient* c, size_t len, uint32_t time){ (void)c; AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onAck(len, time); }, this); - c->onDisconnect([](void *r, AsyncClient* c){ AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onDisconnect(); delete c; }, this); - c->onTimeout([](void *r, AsyncClient* c, uint32_t time){ (void)c; AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onTimeout(time); }, this); - c->onData([](void *r, AsyncClient* c, void *buf, size_t len){ (void)c; AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onData(buf, len); }, this); - c->onPoll([](void *r, AsyncClient* c){ (void)c; AsyncWebServerRequest *req = ( AsyncWebServerRequest*)r; req->_onPoll(); }, this); -} - -AsyncWebServerRequest::~AsyncWebServerRequest(){ - _headers.clear(); - - _params.free(); - _pathParams.clear(); - - _interestingHeaders.clear(); - - if(_response != NULL){ - delete _response; - } - - if(_tempObject != NULL){ - free(_tempObject); - } - - if(_tempFile){ - _tempFile.close(); - } - - if(_itemBuffer){ - free(_itemBuffer); - } - -} - -void AsyncWebServerRequest::_onData(void *buf, size_t len){ - size_t i = 0; - while (true) { - - if(_parseState < PARSE_REQ_BODY){ - // Find new line in buf - char *str = (char*)buf; - for (i = 0; i < len; i++) { - if (str[i] == '\n') { - break; - } - } - if (i == len) { // No new line, just add the buffer in _temp - char ch = str[len-1]; - str[len-1] = 0; - _temp.reserve(_temp.length()+len); - _temp.concat(str); - _temp.concat(ch); - } else { // Found new line - extract it and parse - str[i] = 0; // Terminate the string at the end of the line. - _temp.concat(str); - _temp.trim(); - _parseLine(); - if (++i < len) { - // Still have more buffer to process - buf = str+i; - len-= i; - continue; - } - } - } else if(_parseState == PARSE_REQ_BODY){ - // A handler should be already attached at this point in _parseLine function. - // If handler does nothing (_onRequest is NULL), we don't need to really parse the body. - const bool needParse = _handler && !_handler->isRequestHandlerTrivial(); - if(_isMultipart){ - if(needParse){ - size_t i; - for(i=0; iisRequestHandlerTrivial(); + if (_isMultipart) { + if (needParse) { + size_t i; + for (i = 0; i < len; i++) { + _parseMultipartPostByte(((uint8_t *)buf)[i], i == len - 1); + _parsedLength++; + } + } else + _parsedLength += len; + } else { + if (_parsedLength == 0) { + if (_contentType.startsWith(F("application/x-www-form-urlencoded"))) { + _isPlainPost = true; + } else if (_contentType == F("text/plain") && __is_param_char(((char *)buf)[0])) { + size_t i = 0; + while (i < len && __is_param_char(((char *)buf)[i++])) + ; + if (i < len && ((char *)buf)[i - 1] == '=') { + _isPlainPost = true; + } + } + } + if (!_isPlainPost) { + //check if authenticated before calling the body + if (_handler) + _handler->handleBody(this, (uint8_t *)buf, len, _parsedLength, _contentLength); + _parsedLength += len; + } else if (needParse) { + size_t i; + for (i = 0; i < len; i++) { + _parsedLength++; + _parsePlainPostChar(((uint8_t *)buf)[i]); + } + } else { + _parsedLength += len; + } + } + if (_parsedLength == _contentLength) { + _parseState = PARSE_REQ_END; + //check if authenticated before calling handleRequest and request auth instead + if (_handler) + _handler->handleRequest(this); + else + send(501); + } } - } - if(!_isPlainPost) { - //check if authenticated before calling the body - if(_handler) _handler->handleBody(this, (uint8_t*)buf, len, _parsedLength, _contentLength); - _parsedLength += len; - } else if(needParse) { - size_t i; - for(i=0; iname(); + + if (std::none_of(std::begin(_interestingHeaders), std::end(_interestingHeaders), [&name](const String & str) { return str.equalsIgnoreCase(name); })) + iter = _headers.erase(iter); + else + iter++; + } +} + +void AsyncWebServerRequest::_onPoll() { + //os_printf("p\n"); + if (_response != NULL && _client != NULL && _client->canSend()) { + if (!_response->_finished()) { + _response->_ack(this, 0, 0); + } else { + AsyncWebServerResponse * r = _response; + _response = NULL; + delete r; + + _client->close(); } - } else { - _parsedLength += len; - } - } - if(_parsedLength == _contentLength){ - _parseState = PARSE_REQ_END; - //check if authenticated before calling handleRequest and request auth instead - if(_handler) _handler->handleRequest(this); - else send(501); - } - } - break; - } -} - -void AsyncWebServerRequest::_removeNotInterestingHeaders(){ - if (std::any_of(std::begin(_interestingHeaders), std::end(_interestingHeaders), - [](const String &str){ return str.equalsIgnoreCase(F("ANY")); })) - return; // nothing to do - - for(auto iter = std::begin(_headers); iter != std::end(_headers); ) - { - const auto name = iter->name(); - - if (std::none_of(std::begin(_interestingHeaders), std::end(_interestingHeaders), - [&name](const String &str){ return str.equalsIgnoreCase(name); })) - iter = _headers.erase(iter); - else - iter++; - } -} - -void AsyncWebServerRequest::_onPoll(){ - //os_printf("p\n"); - if(_response != NULL && _client != NULL && _client->canSend()){ - if(!_response->_finished()){ - _response->_ack(this, 0, 0); - } else { - AsyncWebServerResponse* r = _response; - _response = NULL; - delete r; + } +} - _client->close(); +void AsyncWebServerRequest::_onAck(size_t len, uint32_t time) { + //os_printf("a:%u:%u\n", len, time); + if (_response != NULL) { + if (!_response->_finished()) { + _response->_ack(this, len, time); + } else if (_response->_finished()) { + AsyncWebServerResponse * r = _response; + _response = NULL; + delete r; + + _client->close(); + } } - } } -void AsyncWebServerRequest::_onAck(size_t len, uint32_t time){ - //os_printf("a:%u:%u\n", len, time); - if(_response != NULL){ - if(!_response->_finished()){ - _response->_ack(this, len, time); - } else if(_response->_finished()){ - AsyncWebServerResponse* r = _response; - _response = NULL; - delete r; +void AsyncWebServerRequest::_onError(int8_t error) { + (void)error; +} + +void AsyncWebServerRequest::_onTimeout(uint32_t time) { + (void)time; + //os_printf("TIMEOUT: %u, state: %s\n", time, _client->stateToString()); + _client->close(); +} + +void AsyncWebServerRequest::onDisconnect(ArDisconnectHandler fn) { + _onDisconnectfn = fn; +} - _client->close(); +void AsyncWebServerRequest::_onDisconnect() { + //os_printf("d\n"); + if (_onDisconnectfn) { + _onDisconnectfn(); } - } + _server->_handleDisconnect(this); } -void AsyncWebServerRequest::_onError(int8_t error){ - (void)error; +void AsyncWebServerRequest::_addParam(AsyncWebParameter * p) { + _params.add(p); } -void AsyncWebServerRequest::_onTimeout(uint32_t time){ - (void)time; - //os_printf("TIMEOUT: %u, state: %s\n", time, _client->stateToString()); - _client->close(); +void AsyncWebServerRequest::_addPathParam(const char * p) { + _pathParams.emplace_back(p); } -void AsyncWebServerRequest::onDisconnect (ArDisconnectHandler fn){ - _onDisconnectfn=fn; -} - -void AsyncWebServerRequest::_onDisconnect(){ - //os_printf("d\n"); - if(_onDisconnectfn) { - _onDisconnectfn(); - } - _server->_handleDisconnect(this); -} - -void AsyncWebServerRequest::_addParam(AsyncWebParameter *p){ - _params.add(p); -} - -void AsyncWebServerRequest::_addPathParam(const char *p){ - _pathParams.emplace_back(p); -} - -void AsyncWebServerRequest::_addGetParams(const String& params){ - size_t start = 0; - while (start < params.length()){ - int end = params.indexOf('&', start); - if (end < 0) end = params.length(); - int equal = params.indexOf('=', start); - if (equal < 0 || equal > end) equal = end; - String name = params.substring(start, equal); - String value = equal + 1 < end ? params.substring(equal + 1, end) : String(); - _addParam(new AsyncWebParameter(urlDecode(name), urlDecode(value))); - start = end + 1; - } -} - -bool AsyncWebServerRequest::_parseReqHead(){ - // Split the head into method, url and version - int index = _temp.indexOf(' '); - String m = _temp.substring(0, index); - index = _temp.indexOf(' ', index+1); - String u = _temp.substring(m.length()+1, index); - _temp = _temp.substring(index+1); - - if(m == F("GET")){ - _method = HTTP_GET; - } else if(m == F("POST")){ - _method = HTTP_POST; - } else if(m == F("DELETE")){ - _method = HTTP_DELETE; - } else if(m == F("PUT")){ - _method = HTTP_PUT; - } else if(m == F("PATCH")){ - _method = HTTP_PATCH; - } else if(m == F("HEAD")){ - _method = HTTP_HEAD; - } else if(m == F("OPTIONS")){ - _method = HTTP_OPTIONS; - } - - String g; - index = u.indexOf('?'); - if(index > 0){ - g = u.substring(index +1); - u = u.substring(0, index); - } - _url = urlDecode(u); - _addGetParams(g); - - if(!_temp.startsWith(F("HTTP/1.0"))) - _version = 1; - - _temp = String(); - return true; -} - -bool strContains(const String &src, const String &find, bool mindcase = true) { - int pos=0, i=0; - const int slen = src.length(); - const int flen = find.length(); - - if (slen < flen) return false; - while (pos <= (slen - flen)) { - for (i=0; i < flen; i++) { - if (mindcase) { - if (src[pos+i] != find[i]) i = flen + 1; // no match - } - else if (tolower(src[pos+i]) != tolower(find[i])) { - i = flen + 1; // no match - } - } - if (i == flen) return true; - pos++; - } - return false; -} - -bool AsyncWebServerRequest::_parseReqHeader(){ - int index = _temp.indexOf(':'); - if(index){ - String name = _temp.substring(0, index); - String value = _temp.substring(index + 2); - if(name.equalsIgnoreCase("Host")){ - _host = value; - } else if(name.equalsIgnoreCase(F("Content-Type"))){ - _contentType = value.substring(0, value.indexOf(';')); - if (value.startsWith(F("multipart/"))){ - _boundary = value.substring(value.indexOf('=')+1); - _boundary.replace(String('"'), String()); - _isMultipart = true; - } - } else if(name.equalsIgnoreCase(F("Content-Length"))){ - _contentLength = atoi(value.c_str()); - } else if(name.equalsIgnoreCase(F("Expect")) && value == F("100-continue")){ - _expectingContinue = true; - } else if(name.equalsIgnoreCase(F("Authorization"))){ - if(value.length() > 5 && value.substring(0,5).equalsIgnoreCase(F("Basic"))){ - _authorization = value.substring(6); - } else if(value.length() > 6 && value.substring(0,6).equalsIgnoreCase(F("Digest"))){ - _isDigest = true; - _authorization = value.substring(7); - } - } else { - if(name.equalsIgnoreCase(F("Upgrade")) && value.equalsIgnoreCase(F("websocket"))){ - // WebSocket request can be uniquely identified by header: [Upgrade: websocket] - _reqconntype = RCT_WS; - } else { - if(name.equalsIgnoreCase(F("Accept")) && strContains(value, F("text/event-stream"), false)){ - // WebEvent request can be uniquely identified by header: [Accept: text/event-stream] - _reqconntype = RCT_EVENT; +void AsyncWebServerRequest::_addGetParams(const String & params) { + size_t start = 0; + while (start < params.length()) { + int end = params.indexOf('&', start); + if (end < 0) + end = params.length(); + int equal = params.indexOf('=', start); + if (equal < 0 || equal > end) + equal = end; + String name = params.substring(start, equal); + String value = equal + 1 < end ? params.substring(equal + 1, end) : String(); + _addParam(new AsyncWebParameter(urlDecode(name), urlDecode(value))); + start = end + 1; + } +} + +bool AsyncWebServerRequest::_parseReqHead() { + // Split the head into method, url and version + int index = _temp.indexOf(' '); + String m = _temp.substring(0, index); + index = _temp.indexOf(' ', index + 1); + String u = _temp.substring(m.length() + 1, index); + _temp = _temp.substring(index + 1); + + if (m == F("GET")) { + _method = HTTP_GET; + } else if (m == F("POST")) { + _method = HTTP_POST; + } else if (m == F("DELETE")) { + _method = HTTP_DELETE; + } else if (m == F("PUT")) { + _method = HTTP_PUT; + } else if (m == F("PATCH")) { + _method = HTTP_PATCH; + } else if (m == F("HEAD")) { + _method = HTTP_HEAD; + } else if (m == F("OPTIONS")) { + _method = HTTP_OPTIONS; + } + + String g; + index = u.indexOf('?'); + if (index > 0) { + g = u.substring(index + 1); + u = u.substring(0, index); + } + _url = urlDecode(u); + _addGetParams(g); + + if (!_temp.startsWith(F("HTTP/1.0"))) + _version = 1; + + _temp = String(); + return true; +} + +bool strContains(const String & src, const String & find, bool mindcase = true) { + int pos = 0, i = 0; + const int slen = src.length(); + const int flen = find.length(); + + if (slen < flen) + return false; + while (pos <= (slen - flen)) { + for (i = 0; i < flen; i++) { + if (mindcase) { + if (src[pos + i] != find[i]) + i = flen + 1; // no match + } else if (tolower(src[pos + i]) != tolower(find[i])) { + i = flen + 1; // no match + } } - } + if (i == flen) + return true; + pos++; } - _headers.emplace_back(name, value); - } - _temp = String(); - return true; + return false; } -void AsyncWebServerRequest::_parsePlainPostChar(uint8_t data){ - if(data && (char)data != '&') - _temp += (char)data; - if(!data || (char)data == '&' || _parsedLength == _contentLength){ - String name = F("body"); - String value = _temp; - if(!_temp.startsWith(String('{')) && !_temp.startsWith(String('[')) && _temp.indexOf('=') > 0){ - name = _temp.substring(0, _temp.indexOf('=')); - value = _temp.substring(_temp.indexOf('=') + 1); +bool AsyncWebServerRequest::_parseReqHeader() { + int index = _temp.indexOf(':'); + if (index) { + String name = _temp.substring(0, index); + String value = _temp.substring(index + 2); + if (name.equalsIgnoreCase("Host")) { + _host = value; + } else if (name.equalsIgnoreCase(F("Content-Type"))) { + _contentType = value.substring(0, value.indexOf(';')); + if (value.startsWith(F("multipart/"))) { + _boundary = value.substring(value.indexOf('=') + 1); + _boundary.replace(String('"'), String()); + _isMultipart = true; + } + } else if (name.equalsIgnoreCase(F("Content-Length"))) { + _contentLength = atoi(value.c_str()); + } else if (name.equalsIgnoreCase(F("Expect")) && value == F("100-continue")) { + _expectingContinue = true; + } else if (name.equalsIgnoreCase(F("Authorization"))) { + if (value.length() > 5 && value.substring(0, 5).equalsIgnoreCase(F("Basic"))) { + _authorization = value.substring(6); + } else if (value.length() > 6 && value.substring(0, 6).equalsIgnoreCase(F("Digest"))) { + _isDigest = true; + _authorization = value.substring(7); + } + } else { + if (name.equalsIgnoreCase(F("Upgrade")) && value.equalsIgnoreCase(F("websocket"))) { + // WebSocket request can be uniquely identified by header: [Upgrade: websocket] + _reqconntype = RCT_WS; + } else { + if (name.equalsIgnoreCase(F("Accept")) && strContains(value, F("text/event-stream"), false)) { + // WebEvent request can be uniquely identified by header: [Accept: text/event-stream] + _reqconntype = RCT_EVENT; + } + } + } + _headers.emplace_back(name, value); } - _addParam(new AsyncWebParameter(urlDecode(name), urlDecode(value), true)); _temp = String(); - } + return true; +} + +void AsyncWebServerRequest::_parsePlainPostChar(uint8_t data) { + if (data && (char)data != '&') + _temp += (char)data; + if (!data || (char)data == '&' || _parsedLength == _contentLength) { + String name = F("body"); + String value = _temp; + if (!_temp.startsWith(String('{')) && !_temp.startsWith(String('[')) && _temp.indexOf('=') > 0) { + name = _temp.substring(0, _temp.indexOf('=')); + value = _temp.substring(_temp.indexOf('=') + 1); + } + _addParam(new AsyncWebParameter(urlDecode(name), urlDecode(value), true)); + _temp = String(); + } } -void AsyncWebServerRequest::_handleUploadByte(uint8_t data, bool last){ - _itemBuffer[_itemBufferIndex++] = data; +void AsyncWebServerRequest::_handleUploadByte(uint8_t data, bool last) { + _itemBuffer[_itemBufferIndex++] = data; - if(last || _itemBufferIndex == 1460){ - //check if authenticated before calling the upload - if(_handler) - _handler->handleUpload(this, _itemFilename, _itemSize - _itemBufferIndex, _itemBuffer, _itemBufferIndex, false); - _itemBufferIndex = 0; - } + if (last || _itemBufferIndex == 1460) { + //check if authenticated before calling the upload + if (_handler) + _handler->handleUpload(this, _itemFilename, _itemSize - _itemBufferIndex, _itemBuffer, _itemBufferIndex, false); + _itemBufferIndex = 0; + } } enum { - EXPECT_BOUNDARY, - PARSE_HEADERS, - WAIT_FOR_RETURN1, - EXPECT_FEED1, - EXPECT_DASH1, - EXPECT_DASH2, - BOUNDARY_OR_DATA, - DASH3_OR_RETURN2, - EXPECT_FEED2, - PARSING_FINISHED, - PARSE_ERROR + EXPECT_BOUNDARY, + PARSE_HEADERS, + WAIT_FOR_RETURN1, + EXPECT_FEED1, + EXPECT_DASH1, + EXPECT_DASH2, + BOUNDARY_OR_DATA, + DASH3_OR_RETURN2, + EXPECT_FEED2, + PARSING_FINISHED, + PARSE_ERROR }; -void AsyncWebServerRequest::_parseMultipartPostByte(uint8_t data, bool last){ -#define itemWriteByte(b) do { _itemSize++; if(_itemIsFile) _handleUploadByte(b, last); else _itemValue+=(char)(b); } while(0) +void AsyncWebServerRequest::_parseMultipartPostByte(uint8_t data, bool last) { +#define itemWriteByte(b) \ + do { \ + _itemSize++; \ + if (_itemIsFile) \ + _handleUploadByte(b, last); \ + else \ + _itemValue += (char)(b); \ + } while (0) + + if (!_parsedLength) { + _multiParseState = EXPECT_BOUNDARY; + _temp = String(); + _itemName = String(); + _itemFilename = String(); + _itemType = String(); + } - if(!_parsedLength){ - _multiParseState = EXPECT_BOUNDARY; - _temp = String(); - _itemName = String(); - _itemFilename = String(); - _itemType = String(); - } - - if(_multiParseState == WAIT_FOR_RETURN1){ - if(data != '\r'){ - itemWriteByte(data); - } else { - _multiParseState = EXPECT_FEED1; - } - } else if(_multiParseState == EXPECT_BOUNDARY){ - if(_parsedLength < 2 && data != '-'){ - _multiParseState = PARSE_ERROR; - return; - } else if(_parsedLength - 2 < _boundary.length() && _boundary.c_str()[_parsedLength - 2] != data){ - _multiParseState = PARSE_ERROR; - return; - } else if(_parsedLength - 2 == _boundary.length() && data != '\r'){ - _multiParseState = PARSE_ERROR; - return; - } else if(_parsedLength - 3 == _boundary.length()){ - if(data != '\n'){ - _multiParseState = PARSE_ERROR; - return; - } - _multiParseState = PARSE_HEADERS; - _itemIsFile = false; - } - } else if(_multiParseState == PARSE_HEADERS){ - if((char)data != '\r' && (char)data != '\n') - _temp += (char)data; - if((char)data == '\n'){ - if(_temp.length()){ - if(_temp.length() > 12 && _temp.substring(0, 12).equalsIgnoreCase(F("Content-Type"))){ - _itemType = _temp.substring(14); - _itemIsFile = true; - } else if(_temp.length() > 19 && _temp.substring(0, 19).equalsIgnoreCase(F("Content-Disposition"))){ - _temp = _temp.substring(_temp.indexOf(';') + 2); - while(_temp.indexOf(';') > 0){ - String name = _temp.substring(0, _temp.indexOf('=')); - String nameVal = _temp.substring(_temp.indexOf('=') + 2, _temp.indexOf(';') - 1); - if(name == F("name")){ - _itemName = nameVal; - } else if(name == F("filename")){ - _itemFilename = nameVal; - _itemIsFile = true; - } - _temp = _temp.substring(_temp.indexOf(';') + 2); - } - String name = _temp.substring(0, _temp.indexOf('=')); - String nameVal = _temp.substring(_temp.indexOf('=') + 2, _temp.length() - 1); - if(name == F("name")){ - _itemName = nameVal; - } else if(name == F("filename")){ - _itemFilename = nameVal; - _itemIsFile = true; - } + if (_multiParseState == WAIT_FOR_RETURN1) { + if (data != '\r') { + itemWriteByte(data); + } else { + _multiParseState = EXPECT_FEED1; } - _temp = String(); - } else { - _multiParseState = WAIT_FOR_RETURN1; - //value starts from here - _itemSize = 0; - _itemStartIndex = _parsedLength; - _itemValue = String(); - if(_itemIsFile){ - if(_itemBuffer) - free(_itemBuffer); - _itemBuffer = (uint8_t*)malloc(1460); - if(_itemBuffer == NULL){ + } else if (_multiParseState == EXPECT_BOUNDARY) { + if (_parsedLength < 2 && data != '-') { _multiParseState = PARSE_ERROR; return; - } - _itemBufferIndex = 0; + } else if (_parsedLength - 2 < _boundary.length() && _boundary.c_str()[_parsedLength - 2] != data) { + _multiParseState = PARSE_ERROR; + return; + } else if (_parsedLength - 2 == _boundary.length() && data != '\r') { + _multiParseState = PARSE_ERROR; + return; + } else if (_parsedLength - 3 == _boundary.length()) { + if (data != '\n') { + _multiParseState = PARSE_ERROR; + return; + } + _multiParseState = PARSE_HEADERS; + _itemIsFile = false; } - } - } - } else if(_multiParseState == EXPECT_FEED1){ - if(data != '\n'){ - _multiParseState = WAIT_FOR_RETURN1; - itemWriteByte('\r'); _parseMultipartPostByte(data, last); - } else { - _multiParseState = EXPECT_DASH1; - } - } else if(_multiParseState == EXPECT_DASH1){ - if(data != '-'){ - _multiParseState = WAIT_FOR_RETURN1; - itemWriteByte('\r'); itemWriteByte('\n'); _parseMultipartPostByte(data, last); - } else { - _multiParseState = EXPECT_DASH2; - } - } else if(_multiParseState == EXPECT_DASH2){ - if(data != '-'){ - _multiParseState = WAIT_FOR_RETURN1; - itemWriteByte('\r'); itemWriteByte('\n'); itemWriteByte('-'); _parseMultipartPostByte(data, last); - } else { - _multiParseState = BOUNDARY_OR_DATA; - _boundaryPosition = 0; - } - } else if(_multiParseState == BOUNDARY_OR_DATA){ - if(_boundaryPosition < _boundary.length() && _boundary.c_str()[_boundaryPosition] != data){ - _multiParseState = WAIT_FOR_RETURN1; - itemWriteByte('\r'); itemWriteByte('\n'); itemWriteByte('-'); itemWriteByte('-'); - uint8_t i; - for(i=0; i<_boundaryPosition; i++) - itemWriteByte(_boundary.c_str()[i]); - _parseMultipartPostByte(data, last); - } else if(_boundaryPosition == _boundary.length() - 1){ - _multiParseState = DASH3_OR_RETURN2; - if(!_itemIsFile){ - _addParam(new AsyncWebParameter(_itemName, _itemValue, true)); - } else { - if(_itemSize){ - //check if authenticated before calling the upload - if(_handler) _handler->handleUpload(this, _itemFilename, _itemSize - _itemBufferIndex, _itemBuffer, _itemBufferIndex, true); - _itemBufferIndex = 0; - _addParam(new AsyncWebParameter(_itemName, _itemFilename, true, true, _itemSize)); + } else if (_multiParseState == PARSE_HEADERS) { + if ((char)data != '\r' && (char)data != '\n') + _temp += (char)data; + if ((char)data == '\n') { + if (_temp.length()) { + if (_temp.length() > 12 && _temp.substring(0, 12).equalsIgnoreCase(F("Content-Type"))) { + _itemType = _temp.substring(14); + _itemIsFile = true; + } else if (_temp.length() > 19 && _temp.substring(0, 19).equalsIgnoreCase(F("Content-Disposition"))) { + _temp = _temp.substring(_temp.indexOf(';') + 2); + while (_temp.indexOf(';') > 0) { + String name = _temp.substring(0, _temp.indexOf('=')); + String nameVal = _temp.substring(_temp.indexOf('=') + 2, _temp.indexOf(';') - 1); + if (name == F("name")) { + _itemName = nameVal; + } else if (name == F("filename")) { + _itemFilename = nameVal; + _itemIsFile = true; + } + _temp = _temp.substring(_temp.indexOf(';') + 2); + } + String name = _temp.substring(0, _temp.indexOf('=')); + String nameVal = _temp.substring(_temp.indexOf('=') + 2, _temp.length() - 1); + if (name == F("name")) { + _itemName = nameVal; + } else if (name == F("filename")) { + _itemFilename = nameVal; + _itemIsFile = true; + } + } + _temp = String(); + } else { + _multiParseState = WAIT_FOR_RETURN1; + //value starts from here + _itemSize = 0; + _itemStartIndex = _parsedLength; + _itemValue = String(); + if (_itemIsFile) { + if (_itemBuffer) + free(_itemBuffer); + _itemBuffer = (uint8_t *)malloc(1460); + if (_itemBuffer == NULL) { + _multiParseState = PARSE_ERROR; + return; + } + _itemBufferIndex = 0; + } + } } - free(_itemBuffer); - _itemBuffer = NULL; - } + } else if (_multiParseState == EXPECT_FEED1) { + if (data != '\n') { + _multiParseState = WAIT_FOR_RETURN1; + itemWriteByte('\r'); + _parseMultipartPostByte(data, last); + } else { + _multiParseState = EXPECT_DASH1; + } + } else if (_multiParseState == EXPECT_DASH1) { + if (data != '-') { + _multiParseState = WAIT_FOR_RETURN1; + itemWriteByte('\r'); + itemWriteByte('\n'); + _parseMultipartPostByte(data, last); + } else { + _multiParseState = EXPECT_DASH2; + } + } else if (_multiParseState == EXPECT_DASH2) { + if (data != '-') { + _multiParseState = WAIT_FOR_RETURN1; + itemWriteByte('\r'); + itemWriteByte('\n'); + itemWriteByte('-'); + _parseMultipartPostByte(data, last); + } else { + _multiParseState = BOUNDARY_OR_DATA; + _boundaryPosition = 0; + } + } else if (_multiParseState == BOUNDARY_OR_DATA) { + if (_boundaryPosition < _boundary.length() && _boundary.c_str()[_boundaryPosition] != data) { + _multiParseState = WAIT_FOR_RETURN1; + itemWriteByte('\r'); + itemWriteByte('\n'); + itemWriteByte('-'); + itemWriteByte('-'); + uint8_t i; + for (i = 0; i < _boundaryPosition; i++) + itemWriteByte(_boundary.c_str()[i]); + _parseMultipartPostByte(data, last); + } else if (_boundaryPosition == _boundary.length() - 1) { + _multiParseState = DASH3_OR_RETURN2; + if (!_itemIsFile) { + _addParam(new AsyncWebParameter(_itemName, _itemValue, true)); + } else { + if (_itemSize) { + //check if authenticated before calling the upload + if (_handler) + _handler->handleUpload(this, _itemFilename, _itemSize - _itemBufferIndex, _itemBuffer, _itemBufferIndex, true); + _itemBufferIndex = 0; + _addParam(new AsyncWebParameter(_itemName, _itemFilename, true, true, _itemSize)); + } + free(_itemBuffer); + _itemBuffer = NULL; + } - } else { - _boundaryPosition++; + } else { + _boundaryPosition++; + } + } else if (_multiParseState == DASH3_OR_RETURN2) { + if (data == '-' && (_contentLength - _parsedLength - 4) != 0) { + //os_printf("ERROR: The parser got to the end of the POST but is expecting %u bytes more!\nDrop an issue so we can have more info on the matter!\n", _contentLength - _parsedLength - 4); + _contentLength = _parsedLength + 4; //lets close the request gracefully + } + if (data == '\r') { + _multiParseState = EXPECT_FEED2; + } else if (data == '-' && _contentLength == (_parsedLength + 4)) { + _multiParseState = PARSING_FINISHED; + } else { + _multiParseState = WAIT_FOR_RETURN1; + itemWriteByte('\r'); + itemWriteByte('\n'); + itemWriteByte('-'); + itemWriteByte('-'); + uint8_t i; + for (i = 0; i < _boundary.length(); i++) + itemWriteByte(_boundary.c_str()[i]); + _parseMultipartPostByte(data, last); + } + } else if (_multiParseState == EXPECT_FEED2) { + if (data == '\n') { + _multiParseState = PARSE_HEADERS; + _itemIsFile = false; + } else { + _multiParseState = WAIT_FOR_RETURN1; + itemWriteByte('\r'); + itemWriteByte('\n'); + itemWriteByte('-'); + itemWriteByte('-'); + uint8_t i; + for (i = 0; i < _boundary.length(); i++) + itemWriteByte(_boundary.c_str()[i]); + itemWriteByte('\r'); + _parseMultipartPostByte(data, last); + } } - } else if(_multiParseState == DASH3_OR_RETURN2){ - if(data == '-' && (_contentLength - _parsedLength - 4) != 0){ - //os_printf("ERROR: The parser got to the end of the POST but is expecting %u bytes more!\nDrop an issue so we can have more info on the matter!\n", _contentLength - _parsedLength - 4); - _contentLength = _parsedLength + 4;//lets close the request gracefully +} + +void AsyncWebServerRequest::_parseLine() { + if (_parseState == PARSE_REQ_START) { + if (!_temp.length()) { + _parseState = PARSE_REQ_FAIL; + _client->close(); + } else { + _parseReqHead(); + _parseState = PARSE_REQ_HEADERS; + } + return; } - if(data == '\r'){ - _multiParseState = EXPECT_FEED2; - } else if(data == '-' && _contentLength == (_parsedLength + 4)){ - _multiParseState = PARSING_FINISHED; - } else { - _multiParseState = WAIT_FOR_RETURN1; - itemWriteByte('\r'); itemWriteByte('\n'); itemWriteByte('-'); itemWriteByte('-'); - uint8_t i; for(i=0; i<_boundary.length(); i++) itemWriteByte(_boundary.c_str()[i]); - _parseMultipartPostByte(data, last); - } - } else if(_multiParseState == EXPECT_FEED2){ - if(data == '\n'){ - _multiParseState = PARSE_HEADERS; - _itemIsFile = false; - } else { - _multiParseState = WAIT_FOR_RETURN1; - itemWriteByte('\r'); itemWriteByte('\n'); itemWriteByte('-'); itemWriteByte('-'); - uint8_t i; for(i=0; i<_boundary.length(); i++) itemWriteByte(_boundary.c_str()[i]); - itemWriteByte('\r'); _parseMultipartPostByte(data, last); + + if (_parseState == PARSE_REQ_HEADERS) { + if (!_temp.length()) { + //end of headers + _server->_rewriteRequest(this); + _server->_attachHandler(this); + _removeNotInterestingHeaders(); + if (_expectingContinue) { + String response = F("HTTP/1.1 100 Continue\r\n\r\n"); + _client->write(response.c_str(), response.length()); + } + //check handler for authentication + if (_contentLength) { + _parseState = PARSE_REQ_BODY; + } else { + _parseState = PARSE_REQ_END; + if (_handler) + _handler->handleRequest(this); + else + send(501); + } + } else + _parseReqHeader(); } - } } -void AsyncWebServerRequest::_parseLine(){ - if(_parseState == PARSE_REQ_START){ - if(!_temp.length()){ - _parseState = PARSE_REQ_FAIL; - _client->close(); - } else { - _parseReqHead(); - _parseState = PARSE_REQ_HEADERS; - } - return; - } - - if(_parseState == PARSE_REQ_HEADERS){ - if(!_temp.length()){ - //end of headers - _server->_rewriteRequest(this); - _server->_attachHandler(this); - _removeNotInterestingHeaders(); - if(_expectingContinue){ - String response = F("HTTP/1.1 100 Continue\r\n\r\n"); - _client->write(response.c_str(), response.length()); - } - //check handler for authentication - if(_contentLength){ - _parseState = PARSE_REQ_BODY; - } else { - _parseState = PARSE_REQ_END; - if(_handler) _handler->handleRequest(this); - else send(501); - } - } else _parseReqHeader(); - } -} - -size_t AsyncWebServerRequest::headers() const{ - return _headers.size(); -} - -bool AsyncWebServerRequest::hasHeader(const String& name) const { - for(const auto& h: _headers){ - if(h.name().equalsIgnoreCase(name)){ - return true; - } - } - return false; +size_t AsyncWebServerRequest::headers() const { + return _headers.size(); +} + +bool AsyncWebServerRequest::hasHeader(const String & name) const { + for (const auto & h : _headers) { + if (h.name().equalsIgnoreCase(name)) { + return true; + } + } + return false; } bool AsyncWebServerRequest::hasHeader(const __FlashStringHelper * data) const { - return hasHeader(String(data)); + return hasHeader(String(data)); } -AsyncWebHeader* AsyncWebServerRequest::getHeader(const String& name) { - auto iter = std::find_if(std::begin(_headers), std::end(_headers), - [&name](const AsyncWebHeader &header){ return header.name().equalsIgnoreCase(name); }); +AsyncWebHeader * AsyncWebServerRequest::getHeader(const String & name) { + auto iter = std::find_if(std::begin(_headers), std::end(_headers), [&name](const AsyncWebHeader & header) { return header.name().equalsIgnoreCase(name); }); - if (iter == std::end(_headers)) + if (iter == std::end(_headers)) return nullptr; - return &(*iter); + return &(*iter); } -const AsyncWebHeader* AsyncWebServerRequest::getHeader(const String& name) const { - auto iter = std::find_if(std::begin(_headers), std::end(_headers), - [&name](const AsyncWebHeader &header){ return header.name().equalsIgnoreCase(name); }); +const AsyncWebHeader * AsyncWebServerRequest::getHeader(const String & name) const { + auto iter = std::find_if(std::begin(_headers), std::end(_headers), [&name](const AsyncWebHeader & header) { return header.name().equalsIgnoreCase(name); }); - if (iter == std::end(_headers)) + if (iter == std::end(_headers)) return nullptr; - return &(*iter); + return &(*iter); } -AsyncWebHeader* AsyncWebServerRequest::getHeader(const __FlashStringHelper * data) { - PGM_P p = reinterpret_cast(data); - size_t n = strlen_P(p); - char * name = (char*) malloc(n+1); - if (name) { - strcpy_P(name, p); - AsyncWebHeader* result = getHeader( String(name)); - free(name); - return result; - } else { - return nullptr; - } -} - -const AsyncWebHeader* AsyncWebServerRequest::getHeader(const __FlashStringHelper * data) const { - PGM_P p = reinterpret_cast(data); - size_t n = strlen_P(p); - char * name = (char*) malloc(n+1); - if (name) { - strcpy_P(name, p); - const AsyncWebHeader* result = getHeader( String(name)); - free(name); - return result; - } else { - return nullptr; - } +AsyncWebHeader * AsyncWebServerRequest::getHeader(const __FlashStringHelper * data) { + PGM_P p = reinterpret_cast(data); + size_t n = strlen_P(p); + char * name = (char *)malloc(n + 1); + if (name) { + strcpy_P(name, p); + AsyncWebHeader * result = getHeader(String(name)); + free(name); + return result; + } else { + return nullptr; + } +} + +const AsyncWebHeader * AsyncWebServerRequest::getHeader(const __FlashStringHelper * data) const { + PGM_P p = reinterpret_cast(data); + size_t n = strlen_P(p); + char * name = (char *)malloc(n + 1); + if (name) { + strcpy_P(name, p); + const AsyncWebHeader * result = getHeader(String(name)); + free(name); + return result; + } else { + return nullptr; + } } -AsyncWebHeader* AsyncWebServerRequest::getHeader(size_t num) { - if (num >= _headers.size()) - return nullptr; - return &(*std::next(std::begin(_headers), num)); +AsyncWebHeader * AsyncWebServerRequest::getHeader(size_t num) { + if (num >= _headers.size()) + return nullptr; + return &(*std::next(std::begin(_headers), num)); } -const AsyncWebHeader* AsyncWebServerRequest::getHeader(size_t num) const { - if (num >= _headers.size()) - return nullptr; - return &(*std::next(std::begin(_headers), num)); +const AsyncWebHeader * AsyncWebServerRequest::getHeader(size_t num) const { + if (num >= _headers.size()) + return nullptr; + return &(*std::next(std::begin(_headers), num)); } size_t AsyncWebServerRequest::params() const { - return _params.length(); + return _params.length(); } -bool AsyncWebServerRequest::hasParam(const String& name, bool post, bool file) const { - for(const auto& p: _params){ - if(p->name() == name && p->isPost() == post && p->isFile() == file){ - return true; +bool AsyncWebServerRequest::hasParam(const String & name, bool post, bool file) const { + for (const auto & p : _params) { + if (p->name() == name && p->isPost() == post && p->isFile() == file) { + return true; + } } - } - return false; + return false; } bool AsyncWebServerRequest::hasParam(const __FlashStringHelper * data, bool post, bool file) const { - return hasParam(String(data).c_str(), post, file); + return hasParam(String(data).c_str(), post, file); } -AsyncWebParameter* AsyncWebServerRequest::getParam(const String& name, bool post, bool file) const { - for(const auto& p: _params){ - if(p->name() == name && p->isPost() == post && p->isFile() == file){ - return p; +AsyncWebParameter * AsyncWebServerRequest::getParam(const String & name, bool post, bool file) const { + for (const auto & p : _params) { + if (p->name() == name && p->isPost() == post && p->isFile() == file) { + return p; + } } - } - return nullptr; + return nullptr; } -AsyncWebParameter* AsyncWebServerRequest::getParam(const __FlashStringHelper * data, bool post, bool file) const { - return getParam(String(data).c_str(), post, file); +AsyncWebParameter * AsyncWebServerRequest::getParam(const __FlashStringHelper * data, bool post, bool file) const { + return getParam(String(data).c_str(), post, file); } -AsyncWebParameter* AsyncWebServerRequest::getParam(size_t num) const { - auto param = _params.nth(num); - return param ? *param : nullptr; +AsyncWebParameter * AsyncWebServerRequest::getParam(size_t num) const { + auto param = _params.nth(num); + return param ? *param : nullptr; } -void AsyncWebServerRequest::addInterestingHeader(const String& name){ - if(std::none_of(std::begin(_interestingHeaders), std::end(_interestingHeaders), - [&name](const String &str){ return str.equalsIgnoreCase(name); })) - _interestingHeaders.push_back(name); +void AsyncWebServerRequest::addInterestingHeader(const String & name) { + if (std::none_of(std::begin(_interestingHeaders), std::end(_interestingHeaders), [&name](const String & str) { return str.equalsIgnoreCase(name); })) + _interestingHeaders.push_back(name); } -void AsyncWebServerRequest::send(AsyncWebServerResponse *response){ - _response = response; - if(_response == NULL){ - _client->close(true); - _onDisconnect(); - return; - } - if(!_response->_sourceValid()){ - delete response; - _response = NULL; - send(500); - } - else { - _client->setRxTimeout(0); - _response->_respond(this); - } +void AsyncWebServerRequest::send(AsyncWebServerResponse * response) { + _response = response; + if (_response == NULL) { + _client->close(true); + _onDisconnect(); + return; + } + if (!_response->_sourceValid()) { + delete response; + _response = NULL; + send(500); + } else { + _client->setRxTimeout(0); + _response->_respond(this); + } } -AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(int code, const String& contentType, const String& content){ - return new AsyncBasicResponse(code, contentType, content); +AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(int code, const String & contentType, const String & content) { + return new AsyncBasicResponse(code, contentType, content); } -AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(FS &fs, const String& path, const String& contentType, bool download, AwsTemplateProcessor callback){ - if(fs.exists(path) || (!download && fs.exists(path+F(".gz")))) - return new AsyncFileResponse(fs, path, contentType, download, callback); - return NULL; +AsyncWebServerResponse * +AsyncWebServerRequest::beginResponse(FS & fs, const String & path, const String & contentType, bool download, AwsTemplateProcessor callback) { + if (fs.exists(path) || (!download && fs.exists(path + F(".gz")))) + return new AsyncFileResponse(fs, path, contentType, download, callback); + return NULL; } -AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(File content, const String& path, const String& contentType, bool download, AwsTemplateProcessor callback){ - if(content == true) - return new AsyncFileResponse(content, path, contentType, download, callback); - return NULL; +AsyncWebServerResponse * +AsyncWebServerRequest::beginResponse(File content, const String & path, const String & contentType, bool download, AwsTemplateProcessor callback) { + if (content == true) + return new AsyncFileResponse(content, path, contentType, download, callback); + return NULL; } -AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(Stream &stream, const String& contentType, size_t len, AwsTemplateProcessor callback){ - return new AsyncStreamResponse(stream, contentType, len, callback); +AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(Stream & stream, const String & contentType, size_t len, AwsTemplateProcessor callback) { + return new AsyncStreamResponse(stream, contentType, len, callback); } -AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback){ - return new AsyncCallbackResponse(contentType, len, callback, templateCallback); +AsyncWebServerResponse * +AsyncWebServerRequest::beginResponse(const String & contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback) { + return new AsyncCallbackResponse(contentType, len, callback, templateCallback); } -AsyncWebServerResponse * AsyncWebServerRequest::beginChunkedResponse(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback){ - if(_version) - return new AsyncChunkedResponse(contentType, callback, templateCallback); - return new AsyncCallbackResponse(contentType, 0, callback, templateCallback); +AsyncWebServerResponse * +AsyncWebServerRequest::beginChunkedResponse(const String & contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback) { + if (_version) + return new AsyncChunkedResponse(contentType, callback, templateCallback); + return new AsyncCallbackResponse(contentType, 0, callback, templateCallback); } -AsyncResponseStream * AsyncWebServerRequest::beginResponseStream(const String& contentType, size_t bufferSize){ - return new AsyncResponseStream(contentType, bufferSize); +AsyncResponseStream * AsyncWebServerRequest::beginResponseStream(const String & contentType, size_t bufferSize) { + return new AsyncResponseStream(contentType, bufferSize); } -AsyncWebServerResponse * AsyncWebServerRequest::beginResponse_P(int code, const String& contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback){ - return new AsyncProgmemResponse(code, contentType, content, len, callback); +AsyncWebServerResponse * +AsyncWebServerRequest::beginResponse_P(int code, const String & contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback) { + return new AsyncProgmemResponse(code, contentType, content, len, callback); } -AsyncWebServerResponse * AsyncWebServerRequest::beginResponse_P(int code, const String& contentType, PGM_P content, AwsTemplateProcessor callback){ - return beginResponse_P(code, contentType, (const uint8_t *)content, strlen_P(content), callback); +AsyncWebServerResponse * AsyncWebServerRequest::beginResponse_P(int code, const String & contentType, PGM_P content, AwsTemplateProcessor callback) { + return beginResponse_P(code, contentType, (const uint8_t *)content, strlen_P(content), callback); } -void AsyncWebServerRequest::send(int code, const String& contentType, const String& content){ - send(beginResponse(code, contentType, content)); +// added by proddy +AsyncWebServerResponse * +AsyncWebServerRequest::beginResponse(const String & contentType, const uint8_t * content, size_t len) { + return new AsyncResponse(contentType, content, len); } -void AsyncWebServerRequest::send(FS &fs, const String& path, const String& contentType, bool download, AwsTemplateProcessor callback){ - if(fs.exists(path) || (!download && fs.exists(path+F(".gz")))){ - send(beginResponse(fs, path, contentType, download, callback)); - } else send(404); +void AsyncWebServerRequest::send(int code, const String & contentType, const String & content) { + send(beginResponse(code, contentType, content)); } -void AsyncWebServerRequest::send(File content, const String& path, const String& contentType, bool download, AwsTemplateProcessor callback){ - if(content == true){ - send(beginResponse(content, path, contentType, download, callback)); - } else send(404); +void AsyncWebServerRequest::send(FS & fs, const String & path, const String & contentType, bool download, AwsTemplateProcessor callback) { + if (fs.exists(path) || (!download && fs.exists(path + F(".gz")))) { + send(beginResponse(fs, path, contentType, download, callback)); + } else + send(404); } -void AsyncWebServerRequest::send(Stream &stream, const String& contentType, size_t len, AwsTemplateProcessor callback){ - send(beginResponse(stream, contentType, len, callback)); +void AsyncWebServerRequest::send(File content, const String & path, const String & contentType, bool download, AwsTemplateProcessor callback) { + if (content == true) { + send(beginResponse(content, path, contentType, download, callback)); + } else + send(404); } -void AsyncWebServerRequest::send(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback){ - send(beginResponse(contentType, len, callback, templateCallback)); +void AsyncWebServerRequest::send(Stream & stream, const String & contentType, size_t len, AwsTemplateProcessor callback) { + send(beginResponse(stream, contentType, len, callback)); } -void AsyncWebServerRequest::sendChunked(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback){ - send(beginChunkedResponse(contentType, callback, templateCallback)); +void AsyncWebServerRequest::send(const String & contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback) { + send(beginResponse(contentType, len, callback, templateCallback)); } -void AsyncWebServerRequest::send_P(int code, const String& contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback){ - send(beginResponse_P(code, contentType, content, len, callback)); +void AsyncWebServerRequest::sendChunked(const String & contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback) { + send(beginChunkedResponse(contentType, callback, templateCallback)); } -void AsyncWebServerRequest::send_P(int code, const String& contentType, PGM_P content, AwsTemplateProcessor callback){ - send(beginResponse_P(code, contentType, content, callback)); +void AsyncWebServerRequest::send_P(int code, const String & contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback) { + send(beginResponse_P(code, contentType, content, len, callback)); } -void AsyncWebServerRequest::redirect(const String& url){ - AsyncWebServerResponse * response = beginResponse(302); - response->addHeader(F("Location"), url); - send(response); +void AsyncWebServerRequest::send_P(int code, const String & contentType, PGM_P content, AwsTemplateProcessor callback) { + send(beginResponse_P(code, contentType, content, callback)); } -bool AsyncWebServerRequest::authenticate(const char * username, const char * password, const char * realm, bool passwordIsHash){ - if(_authorization.length()){ - if(_isDigest) - return checkDigestAuthentication(_authorization.c_str(), methodToString(), username, password, realm, passwordIsHash, NULL, NULL, NULL); - else if(!passwordIsHash) - return checkBasicAuthentication(_authorization.c_str(), username, password); - else - return _authorization.equals(password); - } - return false; +void AsyncWebServerRequest::redirect(const String & url) { + AsyncWebServerResponse * response = beginResponse(302); + response->addHeader(F("Location"), url); + send(response); } -bool AsyncWebServerRequest::authenticate(const char * hash){ - if(!_authorization.length() || hash == NULL) +bool AsyncWebServerRequest::authenticate(const char * username, const char * password, const char * realm, bool passwordIsHash) { + if (_authorization.length()) { + if (_isDigest) + return checkDigestAuthentication(_authorization.c_str(), methodToString(), username, password, realm, passwordIsHash, NULL, NULL, NULL); + else if (!passwordIsHash) + return checkBasicAuthentication(_authorization.c_str(), username, password); + else + return _authorization.equals(password); + } return false; - - if(_isDigest){ - String hStr = String(hash); - int separator = hStr.indexOf(':'); - if(separator <= 0) - return false; - String username = hStr.substring(0, separator); - hStr = hStr.substring(separator + 1); - separator = hStr.indexOf(':'); - if(separator <= 0) - return false; - String realm = hStr.substring(0, separator); - hStr = hStr.substring(separator + 1); - return checkDigestAuthentication(_authorization.c_str(), methodToString(), username.c_str(), hStr.c_str(), realm.c_str(), true, NULL, NULL, NULL); - } - - return (_authorization.equals(hash)); -} - -void AsyncWebServerRequest::requestAuthentication(const char * realm, bool isDigest){ - AsyncWebServerResponse * r = beginResponse(401); - if(!isDigest && realm == NULL){ - r->addHeader(F("WWW-Authenticate"), F("Basic realm=\"Login Required\"")); - } else if(!isDigest){ - String header = F("Basic realm=\""); - header.concat(realm); - header += '"'; - r->addHeader(F("WWW-Authenticate"), header); - } else { - String header = F("Digest "); - header.concat(requestDigestAuthentication(realm)); - r->addHeader(F("WWW-Authenticate"), header); - } - send(r); -} - -bool AsyncWebServerRequest::hasArg(const char* name) const { - for(const auto& arg: _params){ - if(arg->name() == name){ - return true; - } - } - return false; } -bool AsyncWebServerRequest::hasArg(const __FlashStringHelper * data) const { - return hasArg(String(data).c_str()); -} +bool AsyncWebServerRequest::authenticate(const char * hash) { + if (!_authorization.length() || hash == NULL) + return false; + + if (_isDigest) { + String hStr = String(hash); + int separator = hStr.indexOf(':'); + if (separator <= 0) + return false; + String username = hStr.substring(0, separator); + hStr = hStr.substring(separator + 1); + separator = hStr.indexOf(':'); + if (separator <= 0) + return false; + String realm = hStr.substring(0, separator); + hStr = hStr.substring(separator + 1); + return checkDigestAuthentication(_authorization.c_str(), methodToString(), username.c_str(), hStr.c_str(), realm.c_str(), true, NULL, NULL, NULL); + } + return (_authorization.equals(hash)); +} -const String& AsyncWebServerRequest::arg(const String& name) const { - for(const auto& arg: _params){ - if(arg->name() == name){ - return arg->value(); +void AsyncWebServerRequest::requestAuthentication(const char * realm, bool isDigest) { + AsyncWebServerResponse * r = beginResponse(401); + if (!isDigest && realm == NULL) { + r->addHeader(F("WWW-Authenticate"), F("Basic realm=\"Login Required\"")); + } else if (!isDigest) { + String header = F("Basic realm=\""); + header.concat(realm); + header += '"'; + r->addHeader(F("WWW-Authenticate"), header); + } else { + String header = F("Digest "); + header.concat(requestDigestAuthentication(realm)); + r->addHeader(F("WWW-Authenticate"), header); } - } - return emptyString; + send(r); } -const String& AsyncWebServerRequest::arg(const __FlashStringHelper * data) const { - return arg(String(data).c_str()); +bool AsyncWebServerRequest::hasArg(const char * name) const { + for (const auto & arg : _params) { + if (arg->name() == name) { + return true; + } + } + return false; } -const String& AsyncWebServerRequest::arg(size_t i) const { - return getParam(i)->value(); +bool AsyncWebServerRequest::hasArg(const __FlashStringHelper * data) const { + return hasArg(String(data).c_str()); } -const String& AsyncWebServerRequest::argName(size_t i) const { - return getParam(i)->name(); -} -const String& AsyncWebServerRequest::pathArg(size_t i) const { - return i < _pathParams.size() ? _pathParams[i] : emptyString; +const String & AsyncWebServerRequest::arg(const String & name) const { + for (const auto & arg : _params) { + if (arg->name() == name) { + return arg->value(); + } + } + return emptyString; } -const String& AsyncWebServerRequest::header(const char* name) const { - const AsyncWebHeader* h = getHeader(String(name)); - return h ? h->value() : emptyString; +const String & AsyncWebServerRequest::arg(const __FlashStringHelper * data) const { + return arg(String(data).c_str()); } -const String& AsyncWebServerRequest::header(const __FlashStringHelper * data) const { - return header(String(data).c_str()); -}; - +const String & AsyncWebServerRequest::arg(size_t i) const { + return getParam(i)->value(); +} -const String& AsyncWebServerRequest::header(size_t i) const { - const AsyncWebHeader* h = getHeader(i); - return h ? h->value() : emptyString; +const String & AsyncWebServerRequest::argName(size_t i) const { + return getParam(i)->name(); } -const String& AsyncWebServerRequest::headerName(size_t i) const { - const AsyncWebHeader* h = getHeader(i); - return h ? h->name() : emptyString; +const String & AsyncWebServerRequest::pathArg(size_t i) const { + return i < _pathParams.size() ? _pathParams[i] : emptyString; } -String AsyncWebServerRequest::urlDecode(const String& text) const { - char temp[] = "0x00"; - unsigned int len = text.length(); - unsigned int i = 0; - String decoded = String(); - decoded.reserve(len); // Allocate the string internal buffer - never longer from source text - while (i < len){ - char decodedChar; - char encodedChar = text.charAt(i++); - if ((encodedChar == '%') && (i + 1 < len)){ - temp[2] = text.charAt(i++); - temp[3] = text.charAt(i++); - decodedChar = strtol(temp, NULL, 16); - } else if (encodedChar == '+') { - decodedChar = ' '; - } else { - decodedChar = encodedChar; // normal ascii char - } - decoded.concat(decodedChar); - } - return decoded; +const String & AsyncWebServerRequest::header(const char * name) const { + const AsyncWebHeader * h = getHeader(String(name)); + return h ? h->value() : emptyString; } +const String & AsyncWebServerRequest::header(const __FlashStringHelper * data) const { + return header(String(data).c_str()); +}; -const __FlashStringHelper *AsyncWebServerRequest::methodToString() const { - if(_method == HTTP_ANY) return F("ANY"); - else if(_method & HTTP_GET) return F("GET"); - else if(_method & HTTP_POST) return F("POST"); - else if(_method & HTTP_DELETE) return F("DELETE"); - else if(_method & HTTP_PUT) return F("PUT"); - else if(_method & HTTP_PATCH) return F("PATCH"); - else if(_method & HTTP_HEAD) return F("HEAD"); - else if(_method & HTTP_OPTIONS) return F("OPTIONS"); - return F("UNKNOWN"); -} -const __FlashStringHelper *AsyncWebServerRequest::requestedConnTypeToString() const { - switch (_reqconntype) { - case RCT_NOT_USED: return F("RCT_NOT_USED"); - case RCT_DEFAULT: return F("RCT_DEFAULT"); - case RCT_HTTP: return F("RCT_HTTP"); - case RCT_WS: return F("RCT_WS"); - case RCT_EVENT: return F("RCT_EVENT"); - default: return F("ERROR"); - } +const String & AsyncWebServerRequest::header(size_t i) const { + const AsyncWebHeader * h = getHeader(i); + return h ? h->value() : emptyString; +} + +const String & AsyncWebServerRequest::headerName(size_t i) const { + const AsyncWebHeader * h = getHeader(i); + return h ? h->name() : emptyString; +} + +String AsyncWebServerRequest::urlDecode(const String & text) const { + char temp[] = "0x00"; + unsigned int len = text.length(); + unsigned int i = 0; + String decoded = String(); + decoded.reserve(len); // Allocate the string internal buffer - never longer from source text + while (i < len) { + char decodedChar; + char encodedChar = text.charAt(i++); + if ((encodedChar == '%') && (i + 1 < len)) { + temp[2] = text.charAt(i++); + temp[3] = text.charAt(i++); + decodedChar = strtol(temp, NULL, 16); + } else if (encodedChar == '+') { + decodedChar = ' '; + } else { + decodedChar = encodedChar; // normal ascii char + } + decoded.concat(decodedChar); + } + return decoded; +} + + +const __FlashStringHelper * AsyncWebServerRequest::methodToString() const { + if (_method == HTTP_ANY) + return F("ANY"); + else if (_method & HTTP_GET) + return F("GET"); + else if (_method & HTTP_POST) + return F("POST"); + else if (_method & HTTP_DELETE) + return F("DELETE"); + else if (_method & HTTP_PUT) + return F("PUT"); + else if (_method & HTTP_PATCH) + return F("PATCH"); + else if (_method & HTTP_HEAD) + return F("HEAD"); + else if (_method & HTTP_OPTIONS) + return F("OPTIONS"); + return F("UNKNOWN"); +} + +const __FlashStringHelper * AsyncWebServerRequest::requestedConnTypeToString() const { + switch (_reqconntype) { + case RCT_NOT_USED: + return F("RCT_NOT_USED"); + case RCT_DEFAULT: + return F("RCT_DEFAULT"); + case RCT_HTTP: + return F("RCT_HTTP"); + case RCT_WS: + return F("RCT_WS"); + case RCT_EVENT: + return F("RCT_EVENT"); + default: + return F("ERROR"); + } } bool AsyncWebServerRequest::isExpectedRequestedConnType(RequestedConnectionType erct1, RequestedConnectionType erct2, RequestedConnectionType erct3) { bool res = false; - if ((erct1 != RCT_NOT_USED) && (erct1 == _reqconntype)) res = true; - if ((erct2 != RCT_NOT_USED) && (erct2 == _reqconntype)) res = true; - if ((erct3 != RCT_NOT_USED) && (erct3 == _reqconntype)) res = true; + if ((erct1 != RCT_NOT_USED) && (erct1 == _reqconntype)) + res = true; + if ((erct2 != RCT_NOT_USED) && (erct2 == _reqconntype)) + res = true; + if ((erct3 != RCT_NOT_USED) && (erct3 == _reqconntype)) + res = true; return res; } diff --git a/lib/ESPAsyncWebServer/src/WebResponseImpl.h b/lib/ESPAsyncWebServer/src/WebResponseImpl.h index 4a472254b..6285c0c36 100644 --- a/lib/ESPAsyncWebServer/src/WebResponseImpl.h +++ b/lib/ESPAsyncWebServer/src/WebResponseImpl.h @@ -31,34 +31,43 @@ // It is possible to restore these defines, but one can use _min and _max instead. Or std::min, std::max. -class AsyncBasicResponse: public AsyncWebServerResponse { +class AsyncBasicResponse : public AsyncWebServerResponse { private: String _content; + public: - AsyncBasicResponse(int code, const String& contentType=String(), const String& content=String()); - void _respond(AsyncWebServerRequest *request); - size_t _ack(AsyncWebServerRequest *request, size_t len, uint32_t time); - bool _sourceValid() const { return true; } + AsyncBasicResponse(int code, const String & contentType = String(), const String & content = String()); + void _respond(AsyncWebServerRequest * request); + size_t _ack(AsyncWebServerRequest * request, size_t len, uint32_t time); + bool _sourceValid() const { + return true; + } }; -class AsyncAbstractResponse: public AsyncWebServerResponse { +class AsyncAbstractResponse : public AsyncWebServerResponse { private: String _head; - // Data is inserted into cache at begin(). - // This is inefficient with vector, but if we use some other container, + // Data is inserted into cache at begin(). + // This is inefficient with vector, but if we use some other container, // we won't be able to access it as contiguous array of bytes when reading from it, // so by gaining performance in one place, we'll lose it in another. std::vector _cache; - size_t _readDataFromCacheOrContent(uint8_t* data, const size_t len); - size_t _fillBufferAndProcessTemplates(uint8_t* buf, size_t maxLen); + size_t _readDataFromCacheOrContent(uint8_t * data, const size_t len); + size_t _fillBufferAndProcessTemplates(uint8_t * buf, size_t maxLen); + protected: AwsTemplateProcessor _callback; + public: - AsyncAbstractResponse(AwsTemplateProcessor callback=nullptr); - void _respond(AsyncWebServerRequest *request); - size_t _ack(AsyncWebServerRequest *request, size_t len, uint32_t time); - bool _sourceValid() const { return false; } - virtual size_t _fillBuffer(uint8_t *buf __attribute__((unused)), size_t maxLen __attribute__((unused))) { return 0; } + AsyncAbstractResponse(AwsTemplateProcessor callback = nullptr); + void _respond(AsyncWebServerRequest * request); + size_t _ack(AsyncWebServerRequest * request, size_t len, uint32_t time); + bool _sourceValid() const { + return false; + } + virtual size_t _fillBuffer(uint8_t * buf __attribute__((unused)), size_t maxLen __attribute__((unused))) { + return 0; + } }; #ifndef TEMPLATE_PLACEHOLDER @@ -66,72 +75,105 @@ class AsyncAbstractResponse: public AsyncWebServerResponse { #endif #define TEMPLATE_PARAM_NAME_LENGTH 32 -class AsyncFileResponse: public AsyncAbstractResponse { - using File = fs::File; - using FS = fs::FS; +class AsyncFileResponse : public AsyncAbstractResponse { + using File = fs::File; + using FS = fs::FS; + private: - File _content; + File _content; String _path; - void _setContentType(const String& path); + void _setContentType(const String & path); + public: - AsyncFileResponse(FS &fs, const String& path, const String& contentType=String(), bool download=false, AwsTemplateProcessor callback=nullptr); - AsyncFileResponse(File content, const String& path, const String& contentType=String(), bool download=false, AwsTemplateProcessor callback=nullptr); + AsyncFileResponse(FS & fs, const String & path, const String & contentType = String(), bool download = false, AwsTemplateProcessor callback = nullptr); + AsyncFileResponse(File content, const String & path, const String & contentType = String(), bool download = false, AwsTemplateProcessor callback = nullptr); ~AsyncFileResponse(); - bool _sourceValid() const { return !!(_content); } - virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override; + bool _sourceValid() const { + return !!(_content); + } + virtual size_t _fillBuffer(uint8_t * buf, size_t maxLen) override; }; -class AsyncStreamResponse: public AsyncAbstractResponse { +class AsyncStreamResponse : public AsyncAbstractResponse { private: - Stream *_content; + Stream * _content; + public: - AsyncStreamResponse(Stream &stream, const String& contentType, size_t len, AwsTemplateProcessor callback=nullptr); - bool _sourceValid() const { return !!(_content); } - virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override; + AsyncStreamResponse(Stream & stream, const String & contentType, size_t len, AwsTemplateProcessor callback = nullptr); + bool _sourceValid() const { + return !!(_content); + } + virtual size_t _fillBuffer(uint8_t * buf, size_t maxLen) override; }; -class AsyncCallbackResponse: public AsyncAbstractResponse { +class AsyncCallbackResponse : public AsyncAbstractResponse { private: AwsResponseFiller _content; - size_t _filledLength; + size_t _filledLength; + public: - AsyncCallbackResponse(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback=nullptr); - bool _sourceValid() const { return !!(_content); } - virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override; + AsyncCallbackResponse(const String & contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr); + bool _sourceValid() const { + return !!(_content); + } + virtual size_t _fillBuffer(uint8_t * buf, size_t maxLen) override; }; -class AsyncChunkedResponse: public AsyncAbstractResponse { +class AsyncChunkedResponse : public AsyncAbstractResponse { private: AwsResponseFiller _content; - size_t _filledLength; + size_t _filledLength; + public: - AsyncChunkedResponse(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback=nullptr); - bool _sourceValid() const { return !!(_content); } - virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override; + AsyncChunkedResponse(const String & contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr); + bool _sourceValid() const { + return !!(_content); + } + virtual size_t _fillBuffer(uint8_t * buf, size_t maxLen) override; }; -class AsyncProgmemResponse: public AsyncAbstractResponse { +class AsyncProgmemResponse : public AsyncAbstractResponse { private: const uint8_t * _content; - size_t _readLength; + size_t _readLength; + + public: + AsyncProgmemResponse(int code, const String & contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback = nullptr); + bool _sourceValid() const { + return true; + } + virtual size_t _fillBuffer(uint8_t * buf, size_t maxLen) override; +}; + +// added by proddy +class AsyncResponse : public AsyncAbstractResponse { + private: + const uint8_t * _content; + size_t _readLength; + public: - AsyncProgmemResponse(int code, const String& contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback=nullptr); - bool _sourceValid() const { return true; } - virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override; + AsyncResponse(const String & contentType, const uint8_t * content, size_t len); + bool _sourceValid() const { + return true; + } + virtual size_t _fillBuffer(uint8_t * buf, size_t maxLen) override; }; class cbuf; -class AsyncResponseStream: public AsyncAbstractResponse, public Print { +class AsyncResponseStream : public AsyncAbstractResponse, public Print { private: std::unique_ptr _content; + public: - AsyncResponseStream(const String& contentType, size_t bufferSize); + AsyncResponseStream(const String & contentType, size_t bufferSize); ~AsyncResponseStream(); - bool _sourceValid() const { return (_state < RESPONSE_END); } - virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override; - size_t write(const uint8_t *data, size_t len); - size_t write(uint8_t data); + bool _sourceValid() const { + return (_state < RESPONSE_END); + } + virtual size_t _fillBuffer(uint8_t * buf, size_t maxLen) override; + size_t write(const uint8_t * data, size_t len); + size_t write(uint8_t data); using Print::write; }; diff --git a/lib/ESPAsyncWebServer/src/WebResponses.cpp b/lib/ESPAsyncWebServer/src/WebResponses.cpp index 22a549f65..07d0ef98c 100644 --- a/lib/ESPAsyncWebServer/src/WebResponses.cpp +++ b/lib/ESPAsyncWebServer/src/WebResponses.cpp @@ -23,221 +23,276 @@ #include "cbuf.h" // Since ESP8266 does not link memchr by default, here's its implementation. -void* memchr(void* ptr, int ch, size_t count) -{ - unsigned char* p = static_cast(ptr); - while(count--) - if(*p++ == static_cast(ch)) - return --p; - return nullptr; +void * memchr(void * ptr, int ch, size_t count) { + unsigned char * p = static_cast(ptr); + while (count--) + if (*p++ == static_cast(ch)) + return --p; + return nullptr; } /* * Abstract Response * */ -const char* AsyncWebServerResponse::_responseCodeToString(int code) { - return reinterpret_cast(responseCodeToString(code)); -} - -const __FlashStringHelper *AsyncWebServerResponse::responseCodeToString(int code) { - switch (code) { - case 100: return F("Continue"); - case 101: return F("Switching Protocols"); - case 200: return F("OK"); - case 201: return F("Created"); - case 202: return F("Accepted"); - case 203: return F("Non-Authoritative Information"); - case 204: return F("No Content"); - case 205: return F("Reset Content"); - case 206: return F("Partial Content"); - case 300: return F("Multiple Choices"); - case 301: return F("Moved Permanently"); - case 302: return F("Found"); - case 303: return F("See Other"); - case 304: return F("Not Modified"); - case 305: return F("Use Proxy"); - case 307: return F("Temporary Redirect"); - case 400: return F("Bad Request"); - case 401: return F("Unauthorized"); - case 402: return F("Payment Required"); - case 403: return F("Forbidden"); - case 404: return F("Not Found"); - case 405: return F("Method Not Allowed"); - case 406: return F("Not Acceptable"); - case 407: return F("Proxy Authentication Required"); - case 408: return F("Request Time-out"); - case 409: return F("Conflict"); - case 410: return F("Gone"); - case 411: return F("Length Required"); - case 412: return F("Precondition Failed"); - case 413: return F("Request Entity Too Large"); - case 414: return F("Request-URI Too Large"); - case 415: return F("Unsupported Media Type"); - case 416: return F("Requested range not satisfiable"); - case 417: return F("Expectation Failed"); - case 500: return F("Internal Server Error"); - case 501: return F("Not Implemented"); - case 502: return F("Bad Gateway"); - case 503: return F("Service Unavailable"); - case 504: return F("Gateway Time-out"); - case 505: return F("HTTP Version not supported"); - default: return F(""); - } +const char * AsyncWebServerResponse::_responseCodeToString(int code) { + return reinterpret_cast(responseCodeToString(code)); +} + +const __FlashStringHelper * AsyncWebServerResponse::responseCodeToString(int code) { + switch (code) { + case 100: + return F("Continue"); + case 101: + return F("Switching Protocols"); + case 200: + return F("OK"); + case 201: + return F("Created"); + case 202: + return F("Accepted"); + case 203: + return F("Non-Authoritative Information"); + case 204: + return F("No Content"); + case 205: + return F("Reset Content"); + case 206: + return F("Partial Content"); + case 300: + return F("Multiple Choices"); + case 301: + return F("Moved Permanently"); + case 302: + return F("Found"); + case 303: + return F("See Other"); + case 304: + return F("Not Modified"); + case 305: + return F("Use Proxy"); + case 307: + return F("Temporary Redirect"); + case 400: + return F("Bad Request"); + case 401: + return F("Unauthorized"); + case 402: + return F("Payment Required"); + case 403: + return F("Forbidden"); + case 404: + return F("Not Found"); + case 405: + return F("Method Not Allowed"); + case 406: + return F("Not Acceptable"); + case 407: + return F("Proxy Authentication Required"); + case 408: + return F("Request Time-out"); + case 409: + return F("Conflict"); + case 410: + return F("Gone"); + case 411: + return F("Length Required"); + case 412: + return F("Precondition Failed"); + case 413: + return F("Request Entity Too Large"); + case 414: + return F("Request-URI Too Large"); + case 415: + return F("Unsupported Media Type"); + case 416: + return F("Requested range not satisfiable"); + case 417: + return F("Expectation Failed"); + case 500: + return F("Internal Server Error"); + case 501: + return F("Not Implemented"); + case 502: + return F("Bad Gateway"); + case 503: + return F("Service Unavailable"); + case 504: + return F("Gateway Time-out"); + case 505: + return F("HTTP Version not supported"); + default: + return F(""); + } } AsyncWebServerResponse::AsyncWebServerResponse() - : _code(0) - , _contentType() - , _contentLength(0) - , _sendContentLength(true) - , _chunked(false) - , _headLength(0) - , _sentLength(0) - , _ackedLength(0) - , _writtenLength(0) - , _state(RESPONSE_SETUP) -{ - for(const auto &header: DefaultHeaders::Instance()) { - _headers.emplace_back(header); - } + : _code(0) + , _contentType() + , _contentLength(0) + , _sendContentLength(true) + , _chunked(false) + , _headLength(0) + , _sentLength(0) + , _ackedLength(0) + , _writtenLength(0) + , _state(RESPONSE_SETUP) { + for (const auto & header : DefaultHeaders::Instance()) { + _headers.emplace_back(header); + } } AsyncWebServerResponse::~AsyncWebServerResponse() = default; -void AsyncWebServerResponse::setCode(int code){ - if(_state == RESPONSE_SETUP) - _code = code; +void AsyncWebServerResponse::setCode(int code) { + if (_state == RESPONSE_SETUP) + _code = code; } -void AsyncWebServerResponse::setContentLength(size_t len){ - if(_state == RESPONSE_SETUP) - _contentLength = len; +void AsyncWebServerResponse::setContentLength(size_t len) { + if (_state == RESPONSE_SETUP) + _contentLength = len; } -void AsyncWebServerResponse::setContentType(const String& type){ - if(_state == RESPONSE_SETUP) - _contentType = type; +void AsyncWebServerResponse::setContentType(const String & type) { + if (_state == RESPONSE_SETUP) + _contentType = type; } -void AsyncWebServerResponse::addHeader(const String& name, const String& value){ - _headers.emplace_back(name, value); +void AsyncWebServerResponse::addHeader(const String & name, const String & value) { + _headers.emplace_back(name, value); } -String AsyncWebServerResponse::_assembleHead(uint8_t version){ - if(version){ - addHeader(F("Accept-Ranges"), F("none")); - if(_chunked) - addHeader(F("Transfer-Encoding"), F("chunked")); - } - String out = String(); - int bufSize = 300; - char buf[bufSize]; - - snprintf_P(buf, bufSize, PSTR("HTTP/1.%d %d %s\r\n"), version, _code, _responseCodeToString(_code)); - out.concat(buf); +String AsyncWebServerResponse::_assembleHead(uint8_t version) { + if (version) { + addHeader(F("Accept-Ranges"), F("none")); + if (_chunked) + addHeader(F("Transfer-Encoding"), F("chunked")); + } + String out = String(); + int bufSize = 300; + char buf[bufSize]; - if(_sendContentLength) { - snprintf_P(buf, bufSize, PSTR("Content-Length: %d\r\n"), _contentLength); - out.concat(buf); - } - if(_contentType.length()) { - snprintf_P(buf, bufSize, PSTR("Content-Type: %s\r\n"), _contentType.c_str()); + snprintf_P(buf, bufSize, PSTR("HTTP/1.%d %d %s\r\n"), version, _code, _responseCodeToString(_code)); out.concat(buf); - } - for(const auto& header: _headers){ - snprintf_P(buf, bufSize, PSTR("%s: %s\r\n"), header.name().c_str(), header.value().c_str()); - out.concat(buf); - } - _headers.clear(); + if (_sendContentLength) { + snprintf_P(buf, bufSize, PSTR("Content-Length: %d\r\n"), _contentLength); + out.concat(buf); + } + if (_contentType.length()) { + snprintf_P(buf, bufSize, PSTR("Content-Type: %s\r\n"), _contentType.c_str()); + out.concat(buf); + } - out.concat(F("\r\n")); - _headLength = out.length(); - return out; + for (const auto & header : _headers) { + snprintf_P(buf, bufSize, PSTR("%s: %s\r\n"), header.name().c_str(), header.value().c_str()); + out.concat(buf); + } + _headers.clear(); + + out.concat(F("\r\n")); + _headLength = out.length(); + return out; } -bool AsyncWebServerResponse::_started() const { return _state > RESPONSE_SETUP; } -bool AsyncWebServerResponse::_finished() const { return _state > RESPONSE_WAIT_ACK; } -bool AsyncWebServerResponse::_failed() const { return _state == RESPONSE_FAILED; } -bool AsyncWebServerResponse::_sourceValid() const { return false; } -void AsyncWebServerResponse::_respond(AsyncWebServerRequest *request){ _state = RESPONSE_END; request->client()->close(); } -size_t AsyncWebServerResponse::_ack(AsyncWebServerRequest *request, size_t len, uint32_t time){ (void)request; (void)len; (void)time; return 0; } +bool AsyncWebServerResponse::_started() const { + return _state > RESPONSE_SETUP; +} +bool AsyncWebServerResponse::_finished() const { + return _state > RESPONSE_WAIT_ACK; +} +bool AsyncWebServerResponse::_failed() const { + return _state == RESPONSE_FAILED; +} +bool AsyncWebServerResponse::_sourceValid() const { + return false; +} +void AsyncWebServerResponse::_respond(AsyncWebServerRequest * request) { + _state = RESPONSE_END; + request->client()->close(); +} +size_t AsyncWebServerResponse::_ack(AsyncWebServerRequest * request, size_t len, uint32_t time) { + (void)request; + (void)len; + (void)time; + return 0; +} /* * String/Code Response * */ -AsyncBasicResponse::AsyncBasicResponse(int code, const String& contentType, const String& content){ - _code = code; - _content = content; - _contentType = contentType; - if(_content.length()){ - _contentLength = _content.length(); - if(!_contentType.length()) - _contentType = F("text/plain"); - } - addHeader(F("Connection"), F("close")); -} - -void AsyncBasicResponse::_respond(AsyncWebServerRequest *request){ - _state = RESPONSE_HEADERS; - String out = _assembleHead(request->version()); - size_t outLen = out.length(); - size_t space = request->client()->space(); - if(!_contentLength && space >= outLen){ - _writtenLength += request->client()->write(out.c_str(), outLen); - _state = RESPONSE_WAIT_ACK; - } else if(_contentLength && space >= outLen + _contentLength){ - out += _content; - outLen += _contentLength; - _writtenLength += request->client()->write(out.c_str(), outLen); - _state = RESPONSE_WAIT_ACK; - } else if(space && space < outLen){ - String partial = out.substring(0, space); - _content = out.substring(space) + _content; - _contentLength += outLen - space; - _writtenLength += request->client()->write(partial.c_str(), partial.length()); - _state = RESPONSE_CONTENT; - } else if(space > outLen && space < (outLen + _contentLength)){ - size_t shift = space - outLen; - outLen += shift; - _sentLength += shift; - out += _content.substring(0, shift); - _content = _content.substring(shift); - _writtenLength += request->client()->write(out.c_str(), outLen); - _state = RESPONSE_CONTENT; - } else { - _content = out + _content; - _contentLength += outLen; - _state = RESPONSE_CONTENT; - } -} - -size_t AsyncBasicResponse::_ack(AsyncWebServerRequest *request, size_t len, uint32_t time){ - (void)time; - _ackedLength += len; - if(_state == RESPONSE_CONTENT){ - size_t available = _contentLength - _sentLength; - size_t space = request->client()->space(); - //we can fit in this packet - if(space > available){ - _writtenLength += request->client()->write(_content.c_str(), available); - _content = String(); - _state = RESPONSE_WAIT_ACK; - return available; +AsyncBasicResponse::AsyncBasicResponse(int code, const String & contentType, const String & content) { + _code = code; + _content = content; + _contentType = contentType; + if (_content.length()) { + _contentLength = _content.length(); + if (!_contentType.length()) + _contentType = F("text/plain"); } - //send some data, the rest on ack - String out = _content.substring(0, space); - _content = _content.substring(space); - _sentLength += space; - _writtenLength += request->client()->write(out.c_str(), space); - return space; - } else if(_state == RESPONSE_WAIT_ACK){ - if(_ackedLength >= _writtenLength){ - _state = RESPONSE_END; + addHeader(F("Connection"), F("close")); +} + +void AsyncBasicResponse::_respond(AsyncWebServerRequest * request) { + _state = RESPONSE_HEADERS; + String out = _assembleHead(request->version()); + size_t outLen = out.length(); + size_t space = request->client()->space(); + if (!_contentLength && space >= outLen) { + _writtenLength += request->client()->write(out.c_str(), outLen); + _state = RESPONSE_WAIT_ACK; + } else if (_contentLength && space >= outLen + _contentLength) { + out += _content; + outLen += _contentLength; + _writtenLength += request->client()->write(out.c_str(), outLen); + _state = RESPONSE_WAIT_ACK; + } else if (space && space < outLen) { + String partial = out.substring(0, space); + _content = out.substring(space) + _content; + _contentLength += outLen - space; + _writtenLength += request->client()->write(partial.c_str(), partial.length()); + _state = RESPONSE_CONTENT; + } else if (space > outLen && space < (outLen + _contentLength)) { + size_t shift = space - outLen; + outLen += shift; + _sentLength += shift; + out += _content.substring(0, shift); + _content = _content.substring(shift); + _writtenLength += request->client()->write(out.c_str(), outLen); + _state = RESPONSE_CONTENT; + } else { + _content = out + _content; + _contentLength += outLen; + _state = RESPONSE_CONTENT; + } +} + +size_t AsyncBasicResponse::_ack(AsyncWebServerRequest * request, size_t len, uint32_t time) { + (void)time; + _ackedLength += len; + if (_state == RESPONSE_CONTENT) { + size_t available = _contentLength - _sentLength; + size_t space = request->client()->space(); + //we can fit in this packet + if (space > available) { + _writtenLength += request->client()->write(_content.c_str(), available); + _content = String(); + _state = RESPONSE_WAIT_ACK; + return available; + } + //send some data, the rest on ack + String out = _content.substring(0, space); + _content = _content.substring(space); + _sentLength += space; + _writtenLength += request->client()->write(out.c_str(), space); + return space; + } else if (_state == RESPONSE_WAIT_ACK) { + if (_ackedLength >= _writtenLength) { + _state = RESPONSE_END; + } } - } - return 0; + return 0; } @@ -245,232 +300,231 @@ size_t AsyncBasicResponse::_ack(AsyncWebServerRequest *request, size_t len, uint * Abstract Response * */ -AsyncAbstractResponse::AsyncAbstractResponse(AwsTemplateProcessor callback): _callback(callback) -{ - // In case of template processing, we're unable to determine real response size - if(callback) { - _contentLength = 0; - _sendContentLength = false; - _chunked = true; - } +AsyncAbstractResponse::AsyncAbstractResponse(AwsTemplateProcessor callback) + : _callback(callback) { + // In case of template processing, we're unable to determine real response size + if (callback) { + _contentLength = 0; + _sendContentLength = false; + _chunked = true; + } } -void AsyncAbstractResponse::_respond(AsyncWebServerRequest *request){ - addHeader(F("Connection"), F("close")); - _head = _assembleHead(request->version()); - _state = RESPONSE_HEADERS; - _ack(request, 0, 0); +void AsyncAbstractResponse::_respond(AsyncWebServerRequest * request) { + addHeader(F("Connection"), F("close")); + _head = _assembleHead(request->version()); + _state = RESPONSE_HEADERS; + _ack(request, 0, 0); } -size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest *request, size_t len, uint32_t time){ - (void)time; - if(!_sourceValid()){ - _state = RESPONSE_FAILED; - request->client()->close(); - return 0; - } - _ackedLength += len; - size_t space = request->client()->space(); - - size_t headLen = _head.length(); - if(_state == RESPONSE_HEADERS){ - if(space >= headLen){ - _state = RESPONSE_CONTENT; - space -= headLen; - } else { - String out = _head.substring(0, space); - _head = _head.substring(space); - _writtenLength += request->client()->write(out.c_str(), out.length()); - return out.length(); - } - } - - if(_state == RESPONSE_CONTENT){ - size_t outLen; - if(_chunked){ - if(space <= 8){ +size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest * request, size_t len, uint32_t time) { + (void)time; + if (!_sourceValid()) { + _state = RESPONSE_FAILED; + request->client()->close(); return 0; - } - outLen = space; - } else if(!_sendContentLength){ - outLen = space; - } else { - outLen = ((_contentLength - _sentLength) > space)?space:(_contentLength - _sentLength); } + _ackedLength += len; + size_t space = request->client()->space(); - uint8_t *buf = (uint8_t *)malloc(outLen+headLen); - if (!buf) { - // os_printf("_ack malloc %d failed\n", outLen+headLen); - return 0; + size_t headLen = _head.length(); + if (_state == RESPONSE_HEADERS) { + if (space >= headLen) { + _state = RESPONSE_CONTENT; + space -= headLen; + } else { + String out = _head.substring(0, space); + _head = _head.substring(space); + _writtenLength += request->client()->write(out.c_str(), out.length()); + return out.length(); + } } - if(headLen){ - memcpy(buf, _head.c_str(), _head.length()); - } + if (_state == RESPONSE_CONTENT) { + size_t outLen; + if (_chunked) { + if (space <= 8) { + return 0; + } + outLen = space; + } else if (!_sendContentLength) { + outLen = space; + } else { + outLen = ((_contentLength - _sentLength) > space) ? space : (_contentLength - _sentLength); + } - size_t readLen = 0; - - if(_chunked){ - // HTTP 1.1 allows leading zeros in chunk length. Or spaces may be added. - // See RFC2616 sections 2, 3.6.1. - readLen = _fillBufferAndProcessTemplates(buf+headLen+6, outLen - 8); - if(readLen == RESPONSE_TRY_AGAIN){ - free(buf); - return 0; - } - outLen = sprintf_P((char*)buf+headLen, PSTR("%x"), readLen) + headLen; - while(outLen < headLen + 4) buf[outLen++] = ' '; - buf[outLen++] = '\r'; - buf[outLen++] = '\n'; - outLen += readLen; - buf[outLen++] = '\r'; - buf[outLen++] = '\n'; - } else { - readLen = _fillBufferAndProcessTemplates(buf+headLen, outLen); - if(readLen == RESPONSE_TRY_AGAIN){ - free(buf); - return 0; - } - outLen = readLen + headLen; - } + uint8_t * buf = (uint8_t *)malloc(outLen + headLen); + if (!buf) { + // os_printf("_ack malloc %d failed\n", outLen+headLen); + return 0; + } - if(headLen){ - _head = String(); - } + if (headLen) { + memcpy(buf, _head.c_str(), _head.length()); + } - if(outLen){ - _writtenLength += request->client()->write((const char*)buf, outLen); - } + size_t readLen = 0; + + if (_chunked) { + // HTTP 1.1 allows leading zeros in chunk length. Or spaces may be added. + // See RFC2616 sections 2, 3.6.1. + readLen = _fillBufferAndProcessTemplates(buf + headLen + 6, outLen - 8); + if (readLen == RESPONSE_TRY_AGAIN) { + free(buf); + return 0; + } + outLen = sprintf_P((char *)buf + headLen, PSTR("%x"), readLen) + headLen; + while (outLen < headLen + 4) + buf[outLen++] = ' '; + buf[outLen++] = '\r'; + buf[outLen++] = '\n'; + outLen += readLen; + buf[outLen++] = '\r'; + buf[outLen++] = '\n'; + } else { + readLen = _fillBufferAndProcessTemplates(buf + headLen, outLen); + if (readLen == RESPONSE_TRY_AGAIN) { + free(buf); + return 0; + } + outLen = readLen + headLen; + } - if(_chunked){ - _sentLength += readLen; - } else { - _sentLength += outLen - headLen; - } + if (headLen) { + _head = String(); + } - free(buf); + if (outLen) { + _writtenLength += request->client()->write((const char *)buf, outLen); + } - if((_chunked && readLen == 0) || (!_sendContentLength && outLen == 0) || (!_chunked && _sentLength == _contentLength)){ - _state = RESPONSE_WAIT_ACK; - } - return outLen; + if (_chunked) { + _sentLength += readLen; + } else { + _sentLength += outLen - headLen; + } + + free(buf); + + if ((_chunked && readLen == 0) || (!_sendContentLength && outLen == 0) || (!_chunked && _sentLength == _contentLength)) { + _state = RESPONSE_WAIT_ACK; + } + return outLen; - } else if(_state == RESPONSE_WAIT_ACK){ - if(!_sendContentLength || _ackedLength >= _writtenLength){ - _state = RESPONSE_END; - if(!_chunked && !_sendContentLength) - request->client()->close(true); + } else if (_state == RESPONSE_WAIT_ACK) { + if (!_sendContentLength || _ackedLength >= _writtenLength) { + _state = RESPONSE_END; + if (!_chunked && !_sendContentLength) + request->client()->close(true); + } } - } - return 0; + return 0; } -size_t AsyncAbstractResponse::_readDataFromCacheOrContent(uint8_t* data, const size_t len) -{ +size_t AsyncAbstractResponse::_readDataFromCacheOrContent(uint8_t * data, const size_t len) { // If we have something in cache, copy it to buffer const size_t readFromCache = std::min(len, _cache.size()); - if(readFromCache) { - memcpy(data, _cache.data(), readFromCache); - _cache.erase(_cache.begin(), _cache.begin() + readFromCache); + if (readFromCache) { + memcpy(data, _cache.data(), readFromCache); + _cache.erase(_cache.begin(), _cache.begin() + readFromCache); } // If we need to read more... - const size_t needFromFile = len - readFromCache; + const size_t needFromFile = len - readFromCache; const size_t readFromContent = _fillBuffer(data + readFromCache, needFromFile); return readFromCache + readFromContent; } -size_t AsyncAbstractResponse::_fillBufferAndProcessTemplates(uint8_t* data, size_t len) -{ - if(!_callback) - return _fillBuffer(data, len); - - const size_t originalLen = len; - len = _readDataFromCacheOrContent(data, len); - // Now we've read 'len' bytes, either from cache or from file - // Search for template placeholders - uint8_t* pTemplateStart = data; - while((pTemplateStart < &data[len]) && (pTemplateStart = (uint8_t*)memchr(pTemplateStart, TEMPLATE_PLACEHOLDER, &data[len - 1] - pTemplateStart + 1))) { // data[0] ... data[len - 1] - uint8_t* pTemplateEnd = (pTemplateStart < &data[len - 1]) ? (uint8_t*)memchr(pTemplateStart + 1, TEMPLATE_PLACEHOLDER, &data[len - 1] - pTemplateStart) : nullptr; - // temporary buffer to hold parameter name - uint8_t buf[TEMPLATE_PARAM_NAME_LENGTH + 1]; - String paramName; - // If closing placeholder is found: - if(pTemplateEnd) { - // prepare argument to callback - const size_t paramNameLength = std::min((size_t)sizeof(buf) - 1, (size_t)(pTemplateEnd - pTemplateStart - 1)); - if(paramNameLength) { - memcpy(buf, pTemplateStart + 1, paramNameLength); - buf[paramNameLength] = 0; - paramName = String(reinterpret_cast(buf)); - } else { // double percent sign encountered, this is single percent sign escaped. - // remove the 2nd percent sign - memmove(pTemplateEnd, pTemplateEnd + 1, &data[len] - pTemplateEnd - 1); - len += _readDataFromCacheOrContent(&data[len - 1], 1) - 1; - ++pTemplateStart; - } - } else if(&data[len - 1] - pTemplateStart + 1 < TEMPLATE_PARAM_NAME_LENGTH + 2) { // closing placeholder not found, check if it's in the remaining file data - memcpy(buf, pTemplateStart + 1, &data[len - 1] - pTemplateStart); - const size_t readFromCacheOrContent = _readDataFromCacheOrContent(buf + (&data[len - 1] - pTemplateStart), TEMPLATE_PARAM_NAME_LENGTH + 2 - (&data[len - 1] - pTemplateStart + 1)); - if(readFromCacheOrContent) { - pTemplateEnd = (uint8_t*)memchr(buf + (&data[len - 1] - pTemplateStart), TEMPLATE_PLACEHOLDER, readFromCacheOrContent); - if(pTemplateEnd) { - // prepare argument to callback - *pTemplateEnd = 0; - paramName = String(reinterpret_cast(buf)); - // Copy remaining read-ahead data into cache - _cache.insert(_cache.begin(), pTemplateEnd + 1, buf + (&data[len - 1] - pTemplateStart) + readFromCacheOrContent); - pTemplateEnd = &data[len - 1]; +size_t AsyncAbstractResponse::_fillBufferAndProcessTemplates(uint8_t * data, size_t len) { + if (!_callback) + return _fillBuffer(data, len); + + const size_t originalLen = len; + len = _readDataFromCacheOrContent(data, len); + // Now we've read 'len' bytes, either from cache or from file + // Search for template placeholders + uint8_t * pTemplateStart = data; + while ((pTemplateStart < &data[len]) + && (pTemplateStart = (uint8_t *)memchr(pTemplateStart, TEMPLATE_PLACEHOLDER, &data[len - 1] - pTemplateStart + 1))) { // data[0] ... data[len - 1] + uint8_t * pTemplateEnd = + (pTemplateStart < &data[len - 1]) ? (uint8_t *)memchr(pTemplateStart + 1, TEMPLATE_PLACEHOLDER, &data[len - 1] - pTemplateStart) : nullptr; + // temporary buffer to hold parameter name + uint8_t buf[TEMPLATE_PARAM_NAME_LENGTH + 1]; + String paramName; + // If closing placeholder is found: + if (pTemplateEnd) { + // prepare argument to callback + const size_t paramNameLength = std::min((size_t)sizeof(buf) - 1, (size_t)(pTemplateEnd - pTemplateStart - 1)); + if (paramNameLength) { + memcpy(buf, pTemplateStart + 1, paramNameLength); + buf[paramNameLength] = 0; + paramName = String(reinterpret_cast(buf)); + } else { // double percent sign encountered, this is single percent sign escaped. + // remove the 2nd percent sign + memmove(pTemplateEnd, pTemplateEnd + 1, &data[len] - pTemplateEnd - 1); + len += _readDataFromCacheOrContent(&data[len - 1], 1) - 1; + ++pTemplateStart; + } + } else if (&data[len - 1] - pTemplateStart + 1 < TEMPLATE_PARAM_NAME_LENGTH + 2) { // closing placeholder not found, check if it's in the remaining file data + memcpy(buf, pTemplateStart + 1, &data[len - 1] - pTemplateStart); + const size_t readFromCacheOrContent = + _readDataFromCacheOrContent(buf + (&data[len - 1] - pTemplateStart), TEMPLATE_PARAM_NAME_LENGTH + 2 - (&data[len - 1] - pTemplateStart + 1)); + if (readFromCacheOrContent) { + pTemplateEnd = (uint8_t *)memchr(buf + (&data[len - 1] - pTemplateStart), TEMPLATE_PLACEHOLDER, readFromCacheOrContent); + if (pTemplateEnd) { + // prepare argument to callback + *pTemplateEnd = 0; + paramName = String(reinterpret_cast(buf)); + // Copy remaining read-ahead data into cache + _cache.insert(_cache.begin(), pTemplateEnd + 1, buf + (&data[len - 1] - pTemplateStart) + readFromCacheOrContent); + pTemplateEnd = &data[len - 1]; + } else // closing placeholder not found in file data, store found percent symbol as is and advance to the next position + { + // but first, store read file data in cache + _cache.insert(_cache.begin(), buf + (&data[len - 1] - pTemplateStart), buf + (&data[len - 1] - pTemplateStart) + readFromCacheOrContent); + ++pTemplateStart; + } + } else // closing placeholder not found in content data, store found percent symbol as is and advance to the next position + ++pTemplateStart; + } else // closing placeholder not found in content data, store found percent symbol as is and advance to the next position + ++pTemplateStart; + if (paramName.length()) { + // call callback and replace with result. + // Everything in range [pTemplateStart, pTemplateEnd] can be safely replaced with parameter value. + // Data after pTemplateEnd may need to be moved. + // The first byte of data after placeholder is located at pTemplateEnd + 1. + // It should be located at pTemplateStart + numBytesCopied (to begin right after inserted parameter value). + const String paramValue(_callback(paramName)); + const char * pvstr = paramValue.c_str(); + const unsigned int pvlen = paramValue.length(); + const size_t numBytesCopied = std::min(pvlen, static_cast(&data[originalLen - 1] - pTemplateStart + 1)); + // make room for param value + // 1. move extra data to cache if parameter value is longer than placeholder AND if there is no room to store + if ((pTemplateEnd + 1 < pTemplateStart + numBytesCopied) && (originalLen - (pTemplateStart + numBytesCopied - pTemplateEnd - 1) < len)) { + _cache.insert(_cache.begin(), &data[originalLen - (pTemplateStart + numBytesCopied - pTemplateEnd - 1)], &data[len]); + //2. parameter value is longer than placeholder text, push the data after placeholder which not saved into cache further to the end + memmove(pTemplateStart + numBytesCopied, pTemplateEnd + 1, &data[originalLen] - pTemplateStart - numBytesCopied); + len = originalLen; // fix issue with truncated data, not sure if it has any side effects + } else if (pTemplateEnd + 1 != pTemplateStart + numBytesCopied) + //2. Either parameter value is shorter than placeholder text OR there is enough free space in buffer to fit. + // Move the entire data after the placeholder + memmove(pTemplateStart + numBytesCopied, pTemplateEnd + 1, &data[len] - pTemplateEnd - 1); + // 3. replace placeholder with actual value + memcpy(pTemplateStart, pvstr, numBytesCopied); + // If result is longer than buffer, copy the remainder into cache (this could happen only if placeholder text itself did not fit entirely in buffer) + if (numBytesCopied < pvlen) { + _cache.insert(_cache.begin(), pvstr + numBytesCopied, pvstr + pvlen); + } else if (pTemplateStart + numBytesCopied < pTemplateEnd + 1) { // result is copied fully; if result is shorter than placeholder text... + // there is some free room, fill it from cache + const size_t roomFreed = pTemplateEnd + 1 - pTemplateStart - numBytesCopied; + const size_t totalFreeRoom = originalLen - len + roomFreed; + len += _readDataFromCacheOrContent(&data[len - roomFreed], totalFreeRoom) - roomFreed; + } else { // result is copied fully; it is longer than placeholder text + const size_t roomTaken = pTemplateStart + numBytesCopied - pTemplateEnd - 1; + len = std::min(len + roomTaken, originalLen); + } } - else // closing placeholder not found in file data, store found percent symbol as is and advance to the next position - { - // but first, store read file data in cache - _cache.insert(_cache.begin(), buf + (&data[len - 1] - pTemplateStart), buf + (&data[len - 1] - pTemplateStart) + readFromCacheOrContent); - ++pTemplateStart; - } - } - else // closing placeholder not found in content data, store found percent symbol as is and advance to the next position - ++pTemplateStart; - } - else // closing placeholder not found in content data, store found percent symbol as is and advance to the next position - ++pTemplateStart; - if(paramName.length()) { - // call callback and replace with result. - // Everything in range [pTemplateStart, pTemplateEnd] can be safely replaced with parameter value. - // Data after pTemplateEnd may need to be moved. - // The first byte of data after placeholder is located at pTemplateEnd + 1. - // It should be located at pTemplateStart + numBytesCopied (to begin right after inserted parameter value). - const String paramValue(_callback(paramName)); - const char* pvstr = paramValue.c_str(); - const unsigned int pvlen = paramValue.length(); - const size_t numBytesCopied = std::min(pvlen, static_cast(&data[originalLen - 1] - pTemplateStart + 1)); - // make room for param value - // 1. move extra data to cache if parameter value is longer than placeholder AND if there is no room to store - if((pTemplateEnd + 1 < pTemplateStart + numBytesCopied) && (originalLen - (pTemplateStart + numBytesCopied - pTemplateEnd - 1) < len)) { - _cache.insert(_cache.begin(), &data[originalLen - (pTemplateStart + numBytesCopied - pTemplateEnd - 1)], &data[len]); - //2. parameter value is longer than placeholder text, push the data after placeholder which not saved into cache further to the end - memmove(pTemplateStart + numBytesCopied, pTemplateEnd + 1, &data[originalLen] - pTemplateStart - numBytesCopied); - len = originalLen; // fix issue with truncated data, not sure if it has any side effects - } else if(pTemplateEnd + 1 != pTemplateStart + numBytesCopied) - //2. Either parameter value is shorter than placeholder text OR there is enough free space in buffer to fit. - // Move the entire data after the placeholder - memmove(pTemplateStart + numBytesCopied, pTemplateEnd + 1, &data[len] - pTemplateEnd - 1); - // 3. replace placeholder with actual value - memcpy(pTemplateStart, pvstr, numBytesCopied); - // If result is longer than buffer, copy the remainder into cache (this could happen only if placeholder text itself did not fit entirely in buffer) - if(numBytesCopied < pvlen) { - _cache.insert(_cache.begin(), pvstr + numBytesCopied, pvstr + pvlen); - } else if(pTemplateStart + numBytesCopied < pTemplateEnd + 1) { // result is copied fully; if result is shorter than placeholder text... - // there is some free room, fill it from cache - const size_t roomFreed = pTemplateEnd + 1 - pTemplateStart - numBytesCopied; - const size_t totalFreeRoom = originalLen - len + roomFreed; - len += _readDataFromCacheOrContent(&data[len - roomFreed], totalFreeRoom) - roomFreed; - } else { // result is copied fully; it is longer than placeholder text - const size_t roomTaken = pTemplateStart + numBytesCopied - pTemplateEnd - 1; - len = std::min(len + roomTaken, originalLen); - } - } - } // while(pTemplateStart) - return len; + } // while(pTemplateStart) + return len; } @@ -478,227 +532,264 @@ size_t AsyncAbstractResponse::_fillBufferAndProcessTemplates(uint8_t* data, size * File Response * */ -AsyncFileResponse::~AsyncFileResponse(){ - if(_content) - _content.close(); +AsyncFileResponse::~AsyncFileResponse() { + if (_content) + _content.close(); } -void AsyncFileResponse::_setContentType(const String& path){ +void AsyncFileResponse::_setContentType(const String & path) { #if HAVE_EXTERN_GET_CONTENT_TYPE_FUNCTION - extern const __FlashStringHelper *getContentType(const String &path); - _contentType = getContentType(path); + extern const __FlashStringHelper * getContentType(const String & path); + _contentType = getContentType(path); #else - if (path.endsWith(F(".html"))) _contentType = F("text/html"); - else if (path.endsWith(F(".htm"))) _contentType = F("text/html"); - else if (path.endsWith(F(".css"))) _contentType = F("text/css"); - else if (path.endsWith(F(".json"))) _contentType = F("application/json"); - else if (path.endsWith(F(".js"))) _contentType = F("application/javascript"); - else if (path.endsWith(F(".png"))) _contentType = F("image/png"); - else if (path.endsWith(F(".gif"))) _contentType = F("image/gif"); - else if (path.endsWith(F(".jpg"))) _contentType = F("image/jpeg"); - else if (path.endsWith(F(".ico"))) _contentType = F("image/x-icon"); - else if (path.endsWith(F(".svg"))) _contentType = F("image/svg+xml"); - else if (path.endsWith(F(".eot"))) _contentType = F("font/eot"); - else if (path.endsWith(F(".woff"))) _contentType = F("font/woff"); - else if (path.endsWith(F(".woff2"))) _contentType = F("font/woff2"); - else if (path.endsWith(F(".ttf"))) _contentType = F("font/ttf"); - else if (path.endsWith(F(".xml"))) _contentType = F("text/xml"); - else if (path.endsWith(F(".pdf"))) _contentType = F("application/pdf"); - else if (path.endsWith(F(".zip"))) _contentType = F("application/zip"); - else if(path.endsWith(F(".gz"))) _contentType = F("application/x-gzip"); - else _contentType = F("text/plain"); + if (path.endsWith(F(".html"))) + _contentType = F("text/html"); + else if (path.endsWith(F(".htm"))) + _contentType = F("text/html"); + else if (path.endsWith(F(".css"))) + _contentType = F("text/css"); + else if (path.endsWith(F(".json"))) + _contentType = F("application/json"); + else if (path.endsWith(F(".js"))) + _contentType = F("application/javascript"); + else if (path.endsWith(F(".png"))) + _contentType = F("image/png"); + else if (path.endsWith(F(".gif"))) + _contentType = F("image/gif"); + else if (path.endsWith(F(".jpg"))) + _contentType = F("image/jpeg"); + else if (path.endsWith(F(".ico"))) + _contentType = F("image/x-icon"); + else if (path.endsWith(F(".svg"))) + _contentType = F("image/svg+xml"); + else if (path.endsWith(F(".eot"))) + _contentType = F("font/eot"); + else if (path.endsWith(F(".woff"))) + _contentType = F("font/woff"); + else if (path.endsWith(F(".woff2"))) + _contentType = F("font/woff2"); + else if (path.endsWith(F(".ttf"))) + _contentType = F("font/ttf"); + else if (path.endsWith(F(".xml"))) + _contentType = F("text/xml"); + else if (path.endsWith(F(".pdf"))) + _contentType = F("application/pdf"); + else if (path.endsWith(F(".zip"))) + _contentType = F("application/zip"); + else if (path.endsWith(F(".gz"))) + _contentType = F("application/x-gzip"); + else + _contentType = F("text/plain"); #endif } -AsyncFileResponse::AsyncFileResponse(FS &fs, const String& path, const String& contentType, bool download, AwsTemplateProcessor callback): AsyncAbstractResponse(callback){ - _code = 200; - _path = path; +AsyncFileResponse::AsyncFileResponse(FS & fs, const String & path, const String & contentType, bool download, AwsTemplateProcessor callback) + : AsyncAbstractResponse(callback) { + _code = 200; + _path = path; - if(!download && !fs.exists(_path) && fs.exists(_path + F(".gz"))){ - _path = _path + F(".gz"); - addHeader(F("Content-Encoding"), F("gzip")); - _callback = nullptr; // Unable to process zipped templates - _sendContentLength = true; - _chunked = false; - } + if (!download && !fs.exists(_path) && fs.exists(_path + F(".gz"))) { + _path = _path + F(".gz"); + addHeader(F("Content-Encoding"), F("gzip")); + _callback = nullptr; // Unable to process zipped templates + _sendContentLength = true; + _chunked = false; + } - _content = fs.open(_path, fs::FileOpenMode::read); - _contentLength = _content.size(); + _content = fs.open(_path, fs::FileOpenMode::read); + _contentLength = _content.size(); - if(contentType.length() == 0) - _setContentType(path); - else - _contentType = contentType; + if (contentType.length() == 0) + _setContentType(path); + else + _contentType = contentType; - int filenameStart = path.lastIndexOf('/') + 1; - char buf[26+path.length()-filenameStart]; - char* filename = (char*)path.c_str() + filenameStart; - - if(download) { - // set filename and force download - snprintf_P(buf, sizeof (buf), PSTR("attachment; filename=\"%s\""), filename); - } else { - // set filename and force rendering - snprintf_P(buf, sizeof (buf), PSTR("inline; filename=\"%s\""), filename); - } - addHeader(F("Content-Disposition"), buf); -} - -AsyncFileResponse::AsyncFileResponse(File content, const String& path, const String& contentType, bool download, AwsTemplateProcessor callback): AsyncAbstractResponse(callback){ - _code = 200; - _path = path; - - if(!download && String(content.name()).endsWith(F(".gz")) && !path.endsWith(F(".gz"))){ - addHeader(F("Content-Encoding"), F("gzip")); - _callback = nullptr; // Unable to process gzipped templates - _sendContentLength = true; - _chunked = false; - } - - _content = content; - _contentLength = _content.size(); - - if(contentType.length() == 0) - _setContentType(path); - else - _contentType = contentType; + int filenameStart = path.lastIndexOf('/') + 1; + char buf[26 + path.length() - filenameStart]; + char * filename = (char *)path.c_str() + filenameStart; + + if (download) { + // set filename and force download + snprintf_P(buf, sizeof(buf), PSTR("attachment; filename=\"%s\""), filename); + } else { + // set filename and force rendering + snprintf_P(buf, sizeof(buf), PSTR("inline; filename=\"%s\""), filename); + } + addHeader(F("Content-Disposition"), buf); +} + +AsyncFileResponse::AsyncFileResponse(File content, const String & path, const String & contentType, bool download, AwsTemplateProcessor callback) + : AsyncAbstractResponse(callback) { + _code = 200; + _path = path; - int filenameStart = path.lastIndexOf('/') + 1; - char buf[26+path.length()-filenameStart]; - char* filename = (char*)path.c_str() + filenameStart; + if (!download && String(content.name()).endsWith(F(".gz")) && !path.endsWith(F(".gz"))) { + addHeader(F("Content-Encoding"), F("gzip")); + _callback = nullptr; // Unable to process gzipped templates + _sendContentLength = true; + _chunked = false; + } + + _content = content; + _contentLength = _content.size(); + + if (contentType.length() == 0) + _setContentType(path); + else + _contentType = contentType; - if(download) { - snprintf_P(buf, sizeof (buf), PSTR("attachment; filename=\"%s\""), filename); - } else { - snprintf_P(buf, sizeof (buf), PSTR("inline; filename=\"%s\""), filename); - } - addHeader(F("Content-Disposition"), buf); + int filenameStart = path.lastIndexOf('/') + 1; + char buf[26 + path.length() - filenameStart]; + char * filename = (char *)path.c_str() + filenameStart; + + if (download) { + snprintf_P(buf, sizeof(buf), PSTR("attachment; filename=\"%s\""), filename); + } else { + snprintf_P(buf, sizeof(buf), PSTR("inline; filename=\"%s\""), filename); + } + addHeader(F("Content-Disposition"), buf); } -size_t AsyncFileResponse::_fillBuffer(uint8_t *data, size_t len){ - return _content.read(data, len); +size_t AsyncFileResponse::_fillBuffer(uint8_t * data, size_t len) { + return _content.read(data, len); } /* * Stream Response * */ -AsyncStreamResponse::AsyncStreamResponse(Stream &stream, const String& contentType, size_t len, AwsTemplateProcessor callback): AsyncAbstractResponse(callback) { - _code = 200; - _content = &stream; - _contentLength = len; - _contentType = contentType; +AsyncStreamResponse::AsyncStreamResponse(Stream & stream, const String & contentType, size_t len, AwsTemplateProcessor callback) + : AsyncAbstractResponse(callback) { + _code = 200; + _content = &stream; + _contentLength = len; + _contentType = contentType; } -size_t AsyncStreamResponse::_fillBuffer(uint8_t *data, size_t len){ - size_t available = _content->available(); - size_t outLen = (available > len)?len:available; - size_t i; - for(i=0;iread(); - return outLen; +size_t AsyncStreamResponse::_fillBuffer(uint8_t * data, size_t len) { + size_t available = _content->available(); + size_t outLen = (available > len) ? len : available; + size_t i; + for (i = 0; i < outLen; i++) + data[i] = _content->read(); + return outLen; } /* * Callback Response * */ -AsyncCallbackResponse::AsyncCallbackResponse(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback): AsyncAbstractResponse(templateCallback) { - _code = 200; - _content = callback; - _contentLength = len; - if(!len) - _sendContentLength = false; - _contentType = contentType; - _filledLength = 0; +AsyncCallbackResponse::AsyncCallbackResponse(const String & contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback) + : AsyncAbstractResponse(templateCallback) { + _code = 200; + _content = callback; + _contentLength = len; + if (!len) + _sendContentLength = false; + _contentType = contentType; + _filledLength = 0; } -size_t AsyncCallbackResponse::_fillBuffer(uint8_t *data, size_t len){ - size_t ret = _content(data, len, _filledLength); - if(ret != RESPONSE_TRY_AGAIN){ - _filledLength += ret; - } - return ret; +size_t AsyncCallbackResponse::_fillBuffer(uint8_t * data, size_t len) { + size_t ret = _content(data, len, _filledLength); + if (ret != RESPONSE_TRY_AGAIN) { + _filledLength += ret; + } + return ret; } /* * Chunked Response * */ -AsyncChunkedResponse::AsyncChunkedResponse(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor processorCallback): AsyncAbstractResponse(processorCallback) { - _code = 200; - _content = callback; - _contentLength = 0; - _contentType = contentType; - _sendContentLength = false; - _chunked = true; - _filledLength = 0; +AsyncChunkedResponse::AsyncChunkedResponse(const String & contentType, AwsResponseFiller callback, AwsTemplateProcessor processorCallback) + : AsyncAbstractResponse(processorCallback) { + _code = 200; + _content = callback; + _contentLength = 0; + _contentType = contentType; + _sendContentLength = false; + _chunked = true; + _filledLength = 0; } -size_t AsyncChunkedResponse::_fillBuffer(uint8_t *data, size_t len){ - size_t ret = _content(data, len, _filledLength); - if(ret != RESPONSE_TRY_AGAIN){ - _filledLength += ret; - } - return ret; +size_t AsyncChunkedResponse::_fillBuffer(uint8_t * data, size_t len) { + size_t ret = _content(data, len, _filledLength); + if (ret != RESPONSE_TRY_AGAIN) { + _filledLength += ret; + } + return ret; } /* * Progmem Response * */ -AsyncProgmemResponse::AsyncProgmemResponse(int code, const String& contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback): AsyncAbstractResponse(callback) { - _code = code; - _content = content; - _contentType = contentType; - _contentLength = len; - _readLength = 0; +AsyncProgmemResponse::AsyncProgmemResponse(int code, const String & contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback) + : AsyncAbstractResponse(callback) { + _code = code; + _content = content; + _contentType = contentType; + _contentLength = len; + _readLength = 0; } -size_t AsyncProgmemResponse::_fillBuffer(uint8_t *data, size_t len){ - size_t left = _contentLength - _readLength; - if (left > len) { - memcpy_P(data, _content + _readLength, len); - _readLength += len; - return len; - } - memcpy_P(data, _content + _readLength, left); - _readLength += left; - return left; +size_t AsyncProgmemResponse::_fillBuffer(uint8_t * data, size_t len) { + size_t left = _contentLength - _readLength; + if (left > len) { + memcpy_P(data, _content + _readLength, len); + _readLength += len; + return len; + } + memcpy_P(data, _content + _readLength, left); + _readLength += left; + return left; } +// added by proddy +AsyncResponse::AsyncResponse(const String & contentType, const uint8_t * content, size_t len) + : AsyncAbstractResponse(nullptr) { + _code = 200; + _content = content; + _contentType = contentType; + _contentLength = len; + _readLength = len; +} +size_t AsyncResponse::_fillBuffer(uint8_t * data, size_t len) { + memcpy(data, _content, len); + return len; +} /* * Response Stream (You can print/write/printf to it, up to the contentLen bytes) * */ -AsyncResponseStream::AsyncResponseStream(const String& contentType, size_t bufferSize) -{ - _code = 200; - _contentLength = 0; - _contentType = contentType; - _content = std::unique_ptr(new cbuf(bufferSize)); //std::make_unique(bufferSize); +AsyncResponseStream::AsyncResponseStream(const String & contentType, size_t bufferSize) { + _code = 200; + _contentLength = 0; + _contentType = contentType; + _content = std::unique_ptr(new cbuf(bufferSize)); //std::make_unique(bufferSize); } AsyncResponseStream::~AsyncResponseStream() = default; -size_t AsyncResponseStream::_fillBuffer(uint8_t *buf, size_t maxLen){ - return _content->read((char*)buf, maxLen); +size_t AsyncResponseStream::_fillBuffer(uint8_t * buf, size_t maxLen) { + return _content->read((char *)buf, maxLen); } -size_t AsyncResponseStream::write(const uint8_t *data, size_t len){ - if(_started()) - return 0; +size_t AsyncResponseStream::write(const uint8_t * data, size_t len) { + if (_started()) + return 0; - if(len > _content->room()){ - size_t needed = len - _content->room(); - _content->resizeAdd(needed); - } - size_t written = _content->write((const char*)data, len); - _contentLength += written; - return written; + if (len > _content->room()) { + size_t needed = len - _content->room(); + _content->resizeAdd(needed); + } + size_t written = _content->write((const char *)data, len); + _contentLength += written; + return written; } -size_t AsyncResponseStream::write(uint8_t data){ - return write(&data, 1); +size_t AsyncResponseStream::write(uint8_t data) { + return write(&data, 1); } diff --git a/lib/ESPAsyncWebServer/src/WebServer.cpp b/lib/ESPAsyncWebServer/src/WebServer.cpp index 62e85b23a..47545b4e2 100644 --- a/lib/ESPAsyncWebServer/src/WebServer.cpp +++ b/lib/ESPAsyncWebServer/src/WebServer.cpp @@ -20,179 +20,192 @@ */ #include "ESPAsyncWebServer.h" #include "WebHandlerImpl.h" +#include "AsyncJson.h" -bool ON_STA_FILTER(AsyncWebServerRequest *request) { - return WiFi.localIP() == request->client()->localIP(); +bool ON_STA_FILTER(AsyncWebServerRequest * request) { + return WiFi.localIP() == request->client()->localIP(); } -bool ON_AP_FILTER(AsyncWebServerRequest *request) { - return WiFi.localIP() != request->client()->localIP(); +bool ON_AP_FILTER(AsyncWebServerRequest * request) { + return WiFi.localIP() != request->client()->localIP(); } #ifndef HAVE_FS_FILE_OPEN_MODE -const char *fs::FileOpenMode::read = "r"; -const char *fs::FileOpenMode::write = "w"; -const char *fs::FileOpenMode::append = "a"; +const char * fs::FileOpenMode::read = "r"; +const char * fs::FileOpenMode::write = "w"; +const char * fs::FileOpenMode::append = "a"; #endif AsyncWebServer::AsyncWebServer(uint16_t port) - : _server(port) - , _rewrites(LinkedList([](AsyncWebRewrite* r){ delete r; })) - , _handlers(LinkedList([](AsyncWebHandler* h){ delete h; })) -{ - _catchAllHandler = new AsyncCallbackWebHandler(); - if(_catchAllHandler == NULL) - return; - _server.onClient([](void *s, AsyncClient* c){ - if(c == NULL) - return; - c->setRxTimeout(3); - AsyncWebServerRequest *r = new AsyncWebServerRequest((AsyncWebServer*)s, c); - if(r == NULL){ - c->close(true); - c->free(); - delete c; - } - }, this); + : _server(port) + , _rewrites(LinkedList([](AsyncWebRewrite * r) { delete r; })) + , _handlers(LinkedList([](AsyncWebHandler * h) { delete h; })) { + _catchAllHandler = new AsyncCallbackWebHandler(); + if (_catchAllHandler == NULL) + return; + _server.onClient( + [](void * s, AsyncClient * c) { + if (c == NULL) + return; + c->setRxTimeout(3); + AsyncWebServerRequest * r = new AsyncWebServerRequest((AsyncWebServer *)s, c); + if (r == NULL) { + c->close(true); + c->free(); + delete c; + } + }, + this); } -AsyncWebServer::~AsyncWebServer(){ - reset(); - end(); - if(_catchAllHandler) delete _catchAllHandler; +AsyncWebServer::~AsyncWebServer() { + reset(); + end(); + if (_catchAllHandler) + delete _catchAllHandler; } -AsyncWebRewrite& AsyncWebServer::addRewrite(AsyncWebRewrite* rewrite){ - _rewrites.add(rewrite); - return *rewrite; +AsyncWebRewrite & AsyncWebServer::addRewrite(AsyncWebRewrite * rewrite) { + _rewrites.add(rewrite); + return *rewrite; } -bool AsyncWebServer::removeRewrite(AsyncWebRewrite *rewrite){ - return _rewrites.remove(rewrite); +bool AsyncWebServer::removeRewrite(AsyncWebRewrite * rewrite) { + return _rewrites.remove(rewrite); } -AsyncWebRewrite& AsyncWebServer::rewrite(const char* from, const char* to){ - return addRewrite(new AsyncWebRewrite(from, to)); +AsyncWebRewrite & AsyncWebServer::rewrite(const char * from, const char * to) { + return addRewrite(new AsyncWebRewrite(from, to)); } -AsyncWebHandler& AsyncWebServer::addHandler(AsyncWebHandler* handler){ - _handlers.add(handler); - return *handler; +AsyncWebHandler & AsyncWebServer::addHandler(AsyncWebHandler * handler) { + _handlers.add(handler); + return *handler; } -bool AsyncWebServer::removeHandler(AsyncWebHandler *handler){ - return _handlers.remove(handler); +bool AsyncWebServer::removeHandler(AsyncWebHandler * handler) { + return _handlers.remove(handler); } -void AsyncWebServer::begin(){ - _server.setNoDelay(true); - _server.begin(); +void AsyncWebServer::begin() { + _server.setNoDelay(true); + _server.begin(); } -void AsyncWebServer::end(){ - _server.end(); +void AsyncWebServer::end() { + _server.end(); } #if ASYNC_TCP_SSL_ENABLED -void AsyncWebServer::onSslFileRequest(AcSSlFileHandler cb, void* arg){ - _server.onSslFileRequest(cb, arg); +void AsyncWebServer::onSslFileRequest(AcSSlFileHandler cb, void * arg) { + _server.onSslFileRequest(cb, arg); } -void AsyncWebServer::beginSecure(const char *cert, const char *key, const char *password){ - _server.beginSecure(cert, key, password); +void AsyncWebServer::beginSecure(const char * cert, const char * key, const char * password) { + _server.beginSecure(cert, key, password); } #endif -void AsyncWebServer::_handleDisconnect(AsyncWebServerRequest *request){ - delete request; +void AsyncWebServer::_handleDisconnect(AsyncWebServerRequest * request) { + delete request; } -void AsyncWebServer::_rewriteRequest(AsyncWebServerRequest *request){ - for(const auto& r: _rewrites){ - if (r->match(request)){ - request->_url = r->toUrl(); - request->_addGetParams(r->params()); +void AsyncWebServer::_rewriteRequest(AsyncWebServerRequest * request) { + for (const auto & r : _rewrites) { + if (r->match(request)) { + request->_url = r->toUrl(); + request->_addGetParams(r->params()); + } } - } } -void AsyncWebServer::_attachHandler(AsyncWebServerRequest *request){ - for(const auto& h: _handlers){ - if (h->filter(request) && h->canHandle(request)){ - request->setHandler(h); - return; +void AsyncWebServer::_attachHandler(AsyncWebServerRequest * request) { + for (const auto & h : _handlers) { + if (h->filter(request) && h->canHandle(request)) { + request->setHandler(h); + return; + } } - } - request->addInterestingHeader(F("ANY")); - request->setHandler(_catchAllHandler); + request->addInterestingHeader(F("ANY")); + request->setHandler(_catchAllHandler); } -AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload, ArBodyHandlerFunction onBody){ - AsyncCallbackWebHandler* handler = new AsyncCallbackWebHandler(); - handler->setUri(uri); - handler->setMethod(method); - handler->onRequest(onRequest); - handler->onUpload(onUpload); - handler->onBody(onBody); - addHandler(handler); - return *handler; +AsyncCallbackWebHandler & AsyncWebServer::on(const char * uri, + WebRequestMethodComposite method, + ArRequestHandlerFunction onRequest, + ArUploadHandlerFunction onUpload, + ArBodyHandlerFunction onBody) { + AsyncCallbackWebHandler * handler = new AsyncCallbackWebHandler(); + handler->setUri(uri); + handler->setMethod(method); + handler->onRequest(onRequest); + handler->onUpload(onUpload); + handler->onBody(onBody); + addHandler(handler); + return *handler; } -AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload){ - AsyncCallbackWebHandler* handler = new AsyncCallbackWebHandler(); - handler->setUri(uri); - handler->setMethod(method); - handler->onRequest(onRequest); - handler->onUpload(onUpload); - addHandler(handler); - return *handler; +AsyncCallbackWebHandler & +AsyncWebServer::on(const char * uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload) { + AsyncCallbackWebHandler * handler = new AsyncCallbackWebHandler(); + handler->setUri(uri); + handler->setMethod(method); + handler->onRequest(onRequest); + handler->onUpload(onUpload); + addHandler(handler); + return *handler; } -AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest){ - AsyncCallbackWebHandler* handler = new AsyncCallbackWebHandler(); - handler->setUri(uri); - handler->setMethod(method); - handler->onRequest(onRequest); - addHandler(handler); - return *handler; +AsyncCallbackWebHandler & AsyncWebServer::on(const char * uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest) { + AsyncCallbackWebHandler * handler = new AsyncCallbackWebHandler(); + handler->setUri(uri); + handler->setMethod(method); + handler->onRequest(onRequest); + addHandler(handler); + return *handler; } -AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, ArRequestHandlerFunction onRequest){ - AsyncCallbackWebHandler* handler = new AsyncCallbackWebHandler(); - handler->setUri(uri); - handler->onRequest(onRequest); - addHandler(handler); - return *handler; +AsyncCallbackWebHandler & AsyncWebServer::on(const char * uri, ArRequestHandlerFunction onRequest) { + AsyncCallbackWebHandler * handler = new AsyncCallbackWebHandler(); + handler->setUri(uri); + handler->onRequest(onRequest); + addHandler(handler); + return *handler; } -AsyncStaticWebHandler& AsyncWebServer::serveStatic(const char* uri, fs::FS& fs, const char* path, const char* cache_control){ - AsyncStaticWebHandler* handler = new AsyncStaticWebHandler(uri, fs, path, cache_control); - addHandler(handler); - return *handler; +// added by proddy +void AsyncWebServer::on(const char * uri, ArJsonRequestHandlerFunction onRequest) { + auto * handler = new AsyncCallbackJsonWebHandler(uri, onRequest); + addHandler(handler); } -void AsyncWebServer::onNotFound(ArRequestHandlerFunction fn){ - _catchAllHandler->onRequest(fn); +AsyncStaticWebHandler & AsyncWebServer::serveStatic(const char * uri, fs::FS & fs, const char * path, const char * cache_control) { + AsyncStaticWebHandler * handler = new AsyncStaticWebHandler(uri, fs, path, cache_control); + addHandler(handler); + return *handler; } -void AsyncWebServer::onFileUpload(ArUploadHandlerFunction fn){ - _catchAllHandler->onUpload(fn); +void AsyncWebServer::onNotFound(ArRequestHandlerFunction fn) { + _catchAllHandler->onRequest(fn); } -void AsyncWebServer::onRequestBody(ArBodyHandlerFunction fn){ - _catchAllHandler->onBody(fn); +void AsyncWebServer::onFileUpload(ArUploadHandlerFunction fn) { + _catchAllHandler->onUpload(fn); } -void AsyncWebServer::reset(){ - _rewrites.free(); - _handlers.free(); - - if (_catchAllHandler != NULL){ - _catchAllHandler->onRequest(NULL); - _catchAllHandler->onUpload(NULL); - _catchAllHandler->onBody(NULL); - } +void AsyncWebServer::onRequestBody(ArBodyHandlerFunction fn) { + _catchAllHandler->onBody(fn); } +void AsyncWebServer::reset() { + _rewrites.free(); + _handlers.free(); + + if (_catchAllHandler != NULL) { + _catchAllHandler->onRequest(NULL); + _catchAllHandler->onUpload(NULL); + _catchAllHandler->onBody(NULL); + } +} diff --git a/lib/framework/AuthenticationService.cpp b/lib/framework/AuthenticationService.cpp index 241e912c9..ca31cbb77 100644 --- a/lib/framework/AuthenticationService.cpp +++ b/lib/framework/AuthenticationService.cpp @@ -1,12 +1,9 @@ #include "AuthenticationService.h" AuthenticationService::AuthenticationService(AsyncWebServer * server, SecurityManager * securityManager) - : _securityManager(securityManager) - , _signInHandler(SIGN_IN_PATH, [this](AsyncWebServerRequest * request, JsonVariant json) { signIn(request, json); }) { + : _securityManager(securityManager) { server->on(VERIFY_AUTHORIZATION_PATH, HTTP_GET, [this](AsyncWebServerRequest * request) { verifyAuthorization(request); }); - _signInHandler.setMethod(HTTP_POST); - _signInHandler.setMaxContentLength(MAX_AUTHENTICATION_SIZE); - server->addHandler(&_signInHandler); + server->on(SIGN_IN_PATH, [this](AsyncWebServerRequest * request, JsonVariant json) { signIn(request, json); }); } /** diff --git a/lib/framework/AuthenticationService.h b/lib/framework/AuthenticationService.h index b4b7eac23..894b87143 100644 --- a/lib/framework/AuthenticationService.h +++ b/lib/framework/AuthenticationService.h @@ -9,17 +9,13 @@ #define VERIFY_AUTHORIZATION_PATH "/rest/verifyAuthorization" #define SIGN_IN_PATH "/rest/signIn" -#define MAX_AUTHENTICATION_SIZE 256 - class AuthenticationService { public: AuthenticationService(AsyncWebServer * server, SecurityManager * securityManager); private: - SecurityManager * _securityManager; - AsyncCallbackJsonWebHandler _signInHandler; + SecurityManager * _securityManager; - // endpoint functions void signIn(AsyncWebServerRequest * request, JsonVariant json); void verifyAuthorization(AsyncWebServerRequest * request); }; diff --git a/lib/framework/ESP8266React.cpp b/lib/framework/ESP8266React.cpp index d81fe7b83..8ed68db24 100644 --- a/lib/framework/ESP8266React.cpp +++ b/lib/framework/ESP8266React.cpp @@ -36,7 +36,8 @@ ESP8266React::ESP8266React(AsyncWebServer * server, FS * fs) return request->send(304); } - AsyncWebServerResponse * response = request->beginResponse_P(200, contentType, content, len); + AsyncWebServerResponse * response = request->beginResponse(contentType, content, len); + response->addHeader("Content-Encoding", "gzip"); // response->addHeader("Content-Encoding", "br"); // only works over HTTPS // response->addHeader("Cache-Control", "public, immutable, max-age=31536000"); diff --git a/lib/framework/HttpEndpoint.h b/lib/framework/HttpEndpoint.h index db33394b0..636638da3 100644 --- a/lib/framework/HttpEndpoint.h +++ b/lib/framework/HttpEndpoint.h @@ -17,8 +17,6 @@ class HttpEndpoint { JsonStateUpdater _stateUpdater; StatefulService * _statefulService; - AsyncCallbackJsonWebHandler * handler; - public: HttpEndpoint(JsonStateReader stateReader, JsonStateUpdater stateUpdater, @@ -30,12 +28,10 @@ class HttpEndpoint { : _stateReader(stateReader) , _stateUpdater(stateUpdater) , _statefulService(statefulService) { - // Create hander for both GET and POST endpoints - handler = new AsyncCallbackJsonWebHandler(servicePath, - securityManager->wrapCallback([this](AsyncWebServerRequest * request, - JsonVariant json) { handleRequest(request, json); }, - authenticationPredicate)); - server->addHandler(handler); + // Create handler for both GET and POST endpoints + server->on(servicePath.c_str(), + securityManager->wrapCallback([this](AsyncWebServerRequest * request, JsonVariant json) { handleRequest(request, json); }, + authenticationPredicate)); } protected: diff --git a/lib/framework/NTPSettingsService.cpp b/lib/framework/NTPSettingsService.cpp index 6c7e75e2b..3475f7253 100644 --- a/lib/framework/NTPSettingsService.cpp +++ b/lib/framework/NTPSettingsService.cpp @@ -5,13 +5,10 @@ NTPSettingsService::NTPSettingsService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager) : _httpEndpoint(NTPSettings::read, NTPSettings::update, this, server, NTP_SETTINGS_SERVICE_PATH, securityManager) , _fsPersistence(NTPSettings::read, NTPSettings::update, this, fs, NTP_SETTINGS_FILE) - , _timeHandler(TIME_PATH, - securityManager->wrapCallback([this](AsyncWebServerRequest * request, JsonVariant json) { configureTime(request, json); }, - AuthenticationPredicates::IS_ADMIN)) , _connected(false) { - _timeHandler.setMethod(HTTP_POST); - _timeHandler.setMaxContentLength(MAX_TIME_SIZE); - server->addHandler(&_timeHandler); + server->on(TIME_PATH, + securityManager->wrapCallback([this](AsyncWebServerRequest * request, JsonVariant json) { configureTime(request, json); }, + AuthenticationPredicates::IS_ADMIN)); WiFi.onEvent([this](WiFiEvent_t event, WiFiEventInfo_t info) { WiFiEvent(event); }); addUpdateHandler([this] { configureNTP(); }, false); diff --git a/lib/framework/NTPSettingsService.h b/lib/framework/NTPSettingsService.h index 9e1c503ac..38f4ed5e2 100644 --- a/lib/framework/NTPSettingsService.h +++ b/lib/framework/NTPSettingsService.h @@ -24,9 +24,8 @@ #endif #define NTP_SETTINGS_FILE "/config/ntpSettings.json" -#define NTP_SETTINGS_SERVICE_PATH "/rest/ntpSettings" -#define MAX_TIME_SIZE 256 +#define NTP_SETTINGS_SERVICE_PATH "/rest/ntpSettings" #define TIME_PATH "/rest/time" class NTPSettings { @@ -48,10 +47,9 @@ class NTPSettingsService : public StatefulService { static void ntp_received(struct timeval * tv); private: - HttpEndpoint _httpEndpoint; - FSPersistence _fsPersistence; - AsyncCallbackJsonWebHandler _timeHandler; - bool _connected; + HttpEndpoint _httpEndpoint; + FSPersistence _fsPersistence; + bool _connected; void WiFiEvent(WiFiEvent_t event); void configureNTP(); diff --git a/lib_standalone/ESPAsyncWebServer.h b/lib_standalone/ESPAsyncWebServer.h index 54d431706..504365b59 100644 --- a/lib_standalone/ESPAsyncWebServer.h +++ b/lib_standalone/ESPAsyncWebServer.h @@ -213,6 +213,7 @@ class AsyncWebServerResponse { typedef std::function ArRequestHandlerFunction; typedef std::function ArUploadHandlerFunction; typedef std::function ArBodyHandlerFunction; +typedef std::function ArJsonRequestHandlerFunction; // added by proddy class AsyncWebServer { protected: @@ -232,6 +233,7 @@ class AsyncWebServer { } void on(const char * uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest){}; + void on(const char * uri, ArJsonRequestHandlerFunction onRequest){}; // added by proddy }; diff --git a/scripts/api_test.http b/scripts/api_test.http index 07d6f49c2..5af6d5b9d 100755 --- a/scripts/api_test.http +++ b/scripts/api_test.http @@ -4,7 +4,7 @@ # The response will be shown in the right panel @host = http://ems-esp.local -@host_dev = http://ems-esp2.local +@host_dev = http://10.10.10.20 @token = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiYWRtaW4iOnRydWV9.2bHpWya2C7Q12WjNUBD6_7N3RCD7CMl-EGhyQVzFdDg @@ -95,3 +95,14 @@ Authorization: Bearer {{token}} ### GET {{host_dev}}/api/system/commands +### +POST {{host_dev}}/api/thermostat/seltemp +Content-Type: application/json +Authorization: Bearer {{token}} + +{ + "value" : 21.0 +} + +### +GET {{host_dev}}/api/thermostat/seltemp diff --git a/src/analogsensor.cpp b/src/analogsensor.cpp index 135733597..3620fca44 100644 --- a/src/analogsensor.cpp +++ b/src/analogsensor.cpp @@ -651,7 +651,7 @@ bool AnalogSensor::get_value_info(JsonObject output, const char * cmd, const int // this is for a specific sensor // make a copy of the string command for parsing, and lowercase it char sensor_name[COMMAND_MAX_LENGTH] = {'\0'}; - char * attribute_s = nullptr; + char * attribute_s = nullptr; strlcpy(sensor_name, Helpers::toLower(cmd).c_str(), sizeof(sensor_name)); // check specific attribute to fetch instead of the complete record diff --git a/src/temperaturesensor.cpp b/src/temperaturesensor.cpp index 125cc42c3..d09f91394 100644 --- a/src/temperaturesensor.cpp +++ b/src/temperaturesensor.cpp @@ -378,7 +378,7 @@ bool TemperatureSensor::get_value_info(JsonObject output, const char * cmd, cons // this is for a specific sensor // make a copy of the string command for parsing, and lowercase it char sensor_name[COMMAND_MAX_LENGTH] = {'\0'}; - char * attribute_s = nullptr; + char * attribute_s = nullptr; strlcpy(sensor_name, Helpers::toLower(cmd).c_str(), sizeof(sensor_name)); // check for a specific attribute to fetch instead of the complete record diff --git a/src/version.h b/src/version.h index c71553b2b..245a6b5a4 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define EMSESP_APP_VERSION "3.6.5-dev.16" +#define EMSESP_APP_VERSION "3.6.5-dev.17" diff --git a/src/web/WebAPIService.cpp b/src/web/WebAPIService.cpp index be53e9f4c..8d41bb2cd 100644 --- a/src/web/WebAPIService.cpp +++ b/src/web/WebAPIService.cpp @@ -24,12 +24,11 @@ uint32_t WebAPIService::api_count_ = 0; uint16_t WebAPIService::api_fails_ = 0; WebAPIService::WebAPIService(AsyncWebServer * server, SecurityManager * securityManager) - : _securityManager(securityManager) - , _apiHandler(EMSESP_API_SERVICE_PATH, [this](AsyncWebServerRequest * request, JsonVariant json) { webAPIService_post(request, json); }) { // for POSTs - server->on(EMSESP_API_SERVICE_PATH, HTTP_GET, [this](AsyncWebServerRequest * request) { webAPIService_get(request); }); // for GETs - server->addHandler(&_apiHandler); + : _securityManager(securityManager) { + // API + server->on(EMSESP_API_SERVICE_PATH, [this](AsyncWebServerRequest * request, JsonVariant json) { webAPIService(request, json); }); - // for settings + // settings server->on(GET_SETTINGS_PATH, HTTP_GET, securityManager->wrapRequest([this](AsyncWebServerRequest * request) { getSettings(request); }, AuthenticationPredicates::IS_ADMIN)); @@ -47,31 +46,23 @@ WebAPIService::WebAPIService(AsyncWebServer * server, SecurityManager * security securityManager->wrapRequest([this](AsyncWebServerRequest * request) { getEntities(request); }, AuthenticationPredicates::IS_ADMIN)); } -// HTTP GET -// GET /{device} -// GET /{device}/{entity} -void WebAPIService::webAPIService_get(AsyncWebServerRequest * request) { - // has no body JSON so create dummy as empty input object - JsonDocument input_doc; - JsonObject input = input_doc.to(); - parse(request, input); -} - -// For HTTP POSTS with an optional JSON body -// HTTP_POST | HTTP_PUT | HTTP_PATCH -// POST /{device}[/{hc|id}][/{name}] -void WebAPIService::webAPIService_post(AsyncWebServerRequest * request, JsonVariant json) { +// POST|GET /{device} +// POST|GET /{device}/{entity} +void WebAPIService::webAPIService(AsyncWebServerRequest * request, JsonVariant json) { + JsonObject input; // if no body then treat it as a secure GET - if (!json.is()) { - webAPIService_get(request); - return; + if ((request->method() == HTTP_GET) || (!json.is())) { + // HTTP GET + JsonDocument input_doc; // has no body JSON so create dummy as empty input object + input = input_doc.to(); + } else { + // HTTP_POST | HTTP_PUT | HTTP_PATCH + input = json.as(); // extract values from the json. these will be used as default values } - - // extract values from the json. these will be used as default values - auto && input = json.as(); parse(request, input); } + // parse the URL looking for query or path parameters // reporting back any errors void WebAPIService::parse(AsyncWebServerRequest * request, JsonObject input) { @@ -84,7 +75,7 @@ void WebAPIService::parse(AsyncWebServerRequest * request, JsonObject input) { // check for query parameters first, the old style from v2 // api?device={device}&cmd={name}&data={value}&id={hc} - if (request->url() == "/api") { + if (request->url() == EMSESP_API_SERVICE_PATH) { // get the device if (request->hasParam(F_(device))) { input["device"] = request->getParam(F_(device))->value().c_str(); diff --git a/src/web/WebAPIService.h b/src/web/WebAPIService.h index aa8dd28cb..b7907ef10 100644 --- a/src/web/WebAPIService.h +++ b/src/web/WebAPIService.h @@ -20,6 +20,7 @@ #define WebAPIService_h #define EMSESP_API_SERVICE_PATH "/api" + #define GET_SETTINGS_PATH "/rest/getSettings" #define GET_CUSTOMIZATIONS_PATH "/rest/getCustomizations" #define GET_SCHEDULE_PATH "/rest/getSchedule" @@ -31,8 +32,7 @@ class WebAPIService { public: WebAPIService(AsyncWebServer * server, SecurityManager * securityManager); - void webAPIService_post(AsyncWebServerRequest * request, JsonVariant json); // for POSTs - void webAPIService_get(AsyncWebServerRequest * request); // for GETs + void webAPIService(AsyncWebServerRequest * request, JsonVariant json); static uint32_t api_count() { return api_count_; @@ -43,14 +43,12 @@ class WebAPIService { } private: - SecurityManager * _securityManager; - AsyncCallbackJsonWebHandler _apiHandler; // for POSTs + SecurityManager * _securityManager; static uint32_t api_count_; static uint16_t api_fails_; void parse(AsyncWebServerRequest * request, JsonObject input); - void getSettings(AsyncWebServerRequest * request); void getCustomizations(AsyncWebServerRequest * request); void getSchedule(AsyncWebServerRequest * request); diff --git a/src/web/WebCustomizationService.cpp b/src/web/WebCustomizationService.cpp index 1181190e4..77883d117 100644 --- a/src/web/WebCustomizationService.cpp +++ b/src/web/WebCustomizationService.cpp @@ -23,10 +23,7 @@ namespace emsesp { bool WebCustomization::_start = true; WebCustomizationService::WebCustomizationService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager) - : _fsPersistence(WebCustomization::read, WebCustomization::update, this, fs, EMSESP_CUSTOMIZATION_FILE) - , _masked_entities_handler(CUSTOMIZATION_ENTITIES_PATH, - securityManager->wrapCallback([this](AsyncWebServerRequest * request, JsonVariant json) { customization_entities(request, json); }, - AuthenticationPredicates::IS_AUTHENTICATED)) { + : _fsPersistence(WebCustomization::read, WebCustomization::update, this, fs, EMSESP_CUSTOMIZATION_FILE) { server->on(DEVICE_ENTITIES_PATH, HTTP_GET, securityManager->wrapRequest([this](AsyncWebServerRequest * request) { device_entities(request); }, AuthenticationPredicates::IS_AUTHENTICATED)); @@ -39,9 +36,9 @@ WebCustomizationService::WebCustomizationService(AsyncWebServer * server, FS * f HTTP_POST, securityManager->wrapRequest([this](AsyncWebServerRequest * request) { reset_customization(request); }, AuthenticationPredicates::IS_ADMIN)); - _masked_entities_handler.setMethod(HTTP_POST); - _masked_entities_handler.setMaxContentLength(2048); - server->addHandler(&_masked_entities_handler); + server->on(CUSTOMIZATION_ENTITIES_PATH, + securityManager->wrapCallback([this](AsyncWebServerRequest * request, JsonVariant json) { customization_entities(request, json); }, + AuthenticationPredicates::IS_AUTHENTICATED)); } // this creates the customization file, saving it to the FS diff --git a/src/web/WebCustomizationService.h b/src/web/WebCustomizationService.h index 41ceea90c..80b805a9d 100644 --- a/src/web/WebCustomizationService.h +++ b/src/web/WebCustomizationService.h @@ -101,8 +101,6 @@ class WebCustomizationService : public StatefulService { // POST void customization_entities(AsyncWebServerRequest * request, JsonVariant json); void reset_customization(AsyncWebServerRequest * request); // command - - AsyncCallbackJsonWebHandler _masked_entities_handler; }; } // namespace emsesp diff --git a/src/web/WebDataService.cpp b/src/web/WebDataService.cpp index 2fb7f624b..4295668b9 100644 --- a/src/web/WebDataService.cpp +++ b/src/web/WebDataService.cpp @@ -21,16 +21,18 @@ namespace emsesp { WebDataService::WebDataService(AsyncWebServer * server, SecurityManager * securityManager) - : _write_value_handler(WRITE_DEVICE_VALUE_SERVICE_PATH, - securityManager->wrapCallback([this](AsyncWebServerRequest * request, JsonVariant json) { write_device_value(request, json); }, - AuthenticationPredicates::IS_ADMIN)) - , _write_temperature_handler(WRITE_TEMPERATURE_SENSOR_SERVICE_PATH, - securityManager->wrapCallback([this](AsyncWebServerRequest * request, - JsonVariant json) { write_temperature_sensor(request, json); }, - AuthenticationPredicates::IS_ADMIN)) - , _write_analog_handler(WRITE_ANALOG_SENSOR_SERVICE_PATH, - securityManager->wrapCallback([this](AsyncWebServerRequest * request, JsonVariant json) { write_analog_sensor(request, json); }, - AuthenticationPredicates::IS_ADMIN)) { + +{ + // write endpoints + server->on(WRITE_DEVICE_VALUE_SERVICE_PATH, + securityManager->wrapCallback([this](AsyncWebServerRequest * request, JsonVariant json) { write_device_value(request, json); }, + AuthenticationPredicates::IS_ADMIN)); + server->on(WRITE_TEMPERATURE_SENSOR_SERVICE_PATH, + securityManager->wrapCallback([this](AsyncWebServerRequest * request, JsonVariant json) { write_temperature_sensor(request, json); }, + AuthenticationPredicates::IS_ADMIN)); + server->on(WRITE_ANALOG_SENSOR_SERVICE_PATH, + securityManager->wrapCallback([this](AsyncWebServerRequest * request, JsonVariant json) { write_analog_sensor(request, json); }, + AuthenticationPredicates::IS_ADMIN)); // GET's server->on(DEVICE_DATA_SERVICE_PATH, HTTP_GET, @@ -49,19 +51,6 @@ WebDataService::WebDataService(AsyncWebServer * server, SecurityManager * securi server->on(SCAN_DEVICES_SERVICE_PATH, HTTP_POST, securityManager->wrapRequest([this](AsyncWebServerRequest * request) { scan_devices(request); }, AuthenticationPredicates::IS_ADMIN)); - - - _write_value_handler.setMethod(HTTP_POST); - _write_value_handler.setMaxContentLength(256); - server->addHandler(&_write_value_handler); - - _write_temperature_handler.setMethod(HTTP_POST); - _write_temperature_handler.setMaxContentLength(256); - server->addHandler(&_write_temperature_handler); - - _write_analog_handler.setMethod(HTTP_POST); - _write_analog_handler.setMaxContentLength(256); - server->addHandler(&_write_analog_handler); } // scan devices service diff --git a/src/web/WebDataService.h b/src/web/WebDataService.h index ada443d1f..f85fbe358 100644 --- a/src/web/WebDataService.h +++ b/src/web/WebDataService.h @@ -51,8 +51,6 @@ class WebDataService { void write_temperature_sensor(AsyncWebServerRequest * request, JsonVariant json); void write_analog_sensor(AsyncWebServerRequest * request, JsonVariant json); void scan_devices(AsyncWebServerRequest * request); // command - - AsyncCallbackJsonWebHandler _write_value_handler, _write_temperature_handler, _write_analog_handler; }; } // namespace emsesp diff --git a/src/web/WebLogService.cpp b/src/web/WebLogService.cpp index f4132b79d..066e55765 100644 --- a/src/web/WebLogService.cpp +++ b/src/web/WebLogService.cpp @@ -21,17 +21,16 @@ namespace emsesp { WebLogService::WebLogService(AsyncWebServer * server, SecurityManager * securityManager) - : events_(EVENT_SOURCE_LOG_PATH) - , setValues_(LOG_SETTINGS_PATH, [this](AsyncWebServerRequest * request, JsonVariant json) { setValues(request, json); }) { - events_.setFilter(securityManager->filterRequest(AuthenticationPredicates::IS_ADMIN)); - + : events_(EVENT_SOURCE_LOG_PATH) { + // set settings + server->on(LOG_SETTINGS_PATH, [this](AsyncWebServerRequest * request, JsonVariant json) { setValues(request, json); }); // get settings server->on(LOG_SETTINGS_PATH, HTTP_GET, [this](AsyncWebServerRequest * request) { getValues(request); }); // for bring back the whole log - is a command, hence a POST server->on(FETCH_LOG_PATH, HTTP_POST, [this](AsyncWebServerRequest * request) { fetchLog(request); }); - server->addHandler(&setValues_); + events_.setFilter(securityManager->filterRequest(AuthenticationPredicates::IS_ADMIN)); server->addHandler(&events_); } diff --git a/src/web/WebLogService.h b/src/web/WebLogService.h index a7be311fb..4b2ce0896 100644 --- a/src/web/WebLogService.h +++ b/src/web/WebLogService.h @@ -66,8 +66,6 @@ class WebLogService : public uuid::log::Handler { void setValues(AsyncWebServerRequest * request, JsonVariant json); - AsyncCallbackJsonWebHandler setValues_; // for POSTs - uint64_t last_transmit_ = 0; // Last transmit time size_t maximum_log_messages_ = MAX_LOG_MESSAGES; // Maximum number of log messages to buffer before they are output size_t limit_log_messages_ = 1; // dynamic limit