/* Asynchronous WebServer library for Espressif MCUs Copyright (c) 2016 Hristo Gochkov. All rights reserved. This file is part of the esp8266 core for Arduino environment. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _ESPAsyncWebServer_H_ #define _ESPAsyncWebServer_H_ #include "Arduino.h" #include #include #include #include "FS.h" #include // added by proddy for EMS-ESP #include "StringArray.h" #ifdef ESP32 #include #include #elif defined(ESP8266) #include #include #else #error Platform not supported #endif #define ASYNCWEBSERVER_VERSION "2.6.1" #define ASYNCWEBSERVER_VERSION_MAJOR 2 #define ASYNCWEBSERVER_VERSION_MINOR 6 #define ASYNCWEBSERVER_VERSION_REVISION 1 #define ASYNCWEBSERVER_FORK_mathieucarbou #ifdef ASYNCWEBSERVER_REGEX #define ASYNCWEBSERVER_REGEX_ATTRIBUTE #else #define ASYNCWEBSERVER_REGEX_ATTRIBUTE __attribute__((warning("ASYNCWEBSERVER_REGEX not defined"))) #endif class AsyncWebServer; class AsyncWebServerRequest; class AsyncWebServerResponse; class AsyncWebHeader; class AsyncWebParameter; class AsyncWebRewrite; class AsyncWebHandler; class AsyncStaticWebHandler; class AsyncCallbackWebHandler; 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, } 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; }; }; // namespace fs #else #include "FileOpenMode.h" #endif //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 std::function ArDisconnectHandler; /* * PARAMETER :: Chainable object to hold GET/POST and FILE parameters * */ class AsyncWebParameter { private: String _name; String _value; size_t _size; 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; } }; /* * HEADER :: Chainable object to hold the headers * */ class AsyncWebHeader { private: String _name; String _value; public: 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 & 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"); } }; /* * REQUEST :: Each incoming Client is wrapped inside a Request and both live together until disconnect * */ 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; class AsyncWebServerRequest { 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; uint8_t _parseState; 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; 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; 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 _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 _handleUploadStart(); void _handleUploadByte(uint8_t data, bool last); void _handleUploadEnd(); public: File _tempFile; void * _tempObject; 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; } bool isExpectedRequestedConnType(RequestedConnectionType erct1, RequestedConnectionType erct2 = RCT_NOT_USED, RequestedConnectionType erct3 = RCT_NOT_USED); void onDisconnect(ArDisconnectHandler fn); //hash is the string representation of: // base64(user:pass) for basic or // user:realm:md5(user:realm:pass) for digest bool authenticate(const char * hash); 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); // added by proddy for EMS-ESP 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; bool ON_STA_FILTER(AsyncWebServerRequest * request); bool ON_AP_FILTER(AsyncWebServerRequest * request); /* * REWRITE :: One instance can be handle any Request (done by the Server) * */ class AsyncWebRewrite { protected: 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); } }; /* * HANDLER :: One instance can be attached to any Request (done by the Server) * */ class AsyncWebHandler { protected: ArRequestFilterFunction _filter; String _username; String _password; public: AsyncWebHandler() : _username("") , _password("") { } AsyncWebHandler & setFilter(ArRequestFilterFunction fn) { _filter = fn; return *this; } AsyncWebHandler & setAuthentication(const String & username, const String & password) { _username = username; _password = password; 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; class AsyncWebServerResponse { protected: 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); 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 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); }; /* * SERVER :: One instance * */ typedef std::function ArRequestHandlerFunction; typedef std::function ArUploadHandlerFunction; typedef std::function ArBodyHandlerFunction; typedef std::function ArJsonRequestHandlerFunction; // added by proddy for EMS-ESP class AsyncWebServer { protected: AsyncServer _server; LinkedList _rewrites; LinkedList _handlers; AsyncCallbackWebHandler * _catchAllHandler; public: AsyncWebServer(uint16_t port); ~AsyncWebServer(); void begin(); 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); #endif AsyncWebRewrite & addRewrite(AsyncWebRewrite * rewrite); bool removeRewrite(AsyncWebRewrite * rewrite); AsyncWebRewrite & rewrite(const char * from, const char * to); 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); void on(const char * uri, ArJsonRequestHandlerFunction onRequest); // added by proddy for EMS-ESP 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 reset(); //remove all writers and handlers, with onNotFound/onFileUpload/onRequestBody void _handleDisconnect(AsyncWebServerRequest * request); void _attachHandler(AsyncWebServerRequest * request); void _rewriteRequest(AsyncWebServerRequest * request); }; class DefaultHeaders { using headers_t = std::list; headers_t _headers; public: DefaultHeaders() = default; using ConstIterator = headers_t::const_iterator; 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(); } DefaultHeaders(DefaultHeaders const &) = delete; DefaultHeaders & operator=(DefaultHeaders const &) = delete; static DefaultHeaders & Instance() { static DefaultHeaders instance; return instance; } }; #include "WebResponseImpl.h" #include "WebHandlerImpl.h" #include "AsyncWebSocket.h" #include "AsyncEventSource.h" #endif /* _AsyncWebServer_H_ */