/* WebServer.h - Dead simple web-server. Supports only one simultaneous client, knows how to handle GET and POST. Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Modified 8 May 2015 by Hristo Gochkov (proper post and file upload handling) */ #ifndef WEBSERVER_H #define WEBSERVER_H #include #include #include "FS.h" #include "Network.h" #include "HTTP_Method.h" #include "Uri.h" enum HTTPUploadStatus { UPLOAD_FILE_START, UPLOAD_FILE_WRITE, UPLOAD_FILE_END, UPLOAD_FILE_ABORTED }; enum HTTPRawStatus { RAW_START, RAW_WRITE, RAW_END, RAW_ABORTED }; enum HTTPClientStatus { HC_NONE, HC_WAIT_READ, HC_WAIT_CLOSE }; enum HTTPAuthMethod { BASIC_AUTH, DIGEST_AUTH, OTHER_AUTH }; #define HTTP_DOWNLOAD_UNIT_SIZE 1436 #ifndef HTTP_UPLOAD_BUFLEN #define HTTP_UPLOAD_BUFLEN 1436 #endif #ifndef HTTP_RAW_BUFLEN #define HTTP_RAW_BUFLEN 1436 #endif #define HTTP_MAX_DATA_WAIT 5000 //ms to wait for the client to send the request #define HTTP_MAX_POST_WAIT 5000 //ms to wait for POST data to arrive #define HTTP_MAX_SEND_WAIT 5000 //ms to wait for data chunk to be ACKed #define HTTP_MAX_CLOSE_WAIT 5000 //ms to wait for the client to close the connection #define HTTP_MAX_BASIC_AUTH_LEN 256 // maximum length of a basic Auth base64 encoded username:password string #define CONTENT_LENGTH_UNKNOWN ((size_t) - 1) #define CONTENT_LENGTH_NOT_SET ((size_t) - 2) class WebServer; typedef struct { HTTPUploadStatus status; String filename; String name; String type; size_t totalSize; // file size size_t currentSize; // size of data currently in buf uint8_t buf[HTTP_UPLOAD_BUFLEN]; } HTTPUpload; typedef struct { HTTPRawStatus status; size_t totalSize; // content size size_t currentSize; // size of data currently in buf uint8_t buf[HTTP_RAW_BUFLEN]; void *data; // additional data } HTTPRaw; #include "middleware/Middleware.h" #include "detail/RequestHandler.h" namespace fs { class FS; } class WebServer { public: WebServer(IPAddress addr, int port = 80); WebServer(int port = 80); virtual ~WebServer(); virtual void begin(); virtual void begin(uint16_t port); virtual void handleClient(); virtual void close(); void stop(); const String AuthTypeDigest = F("Digest"); const String AuthTypeBasic = F("Basic"); /* Callbackhandler for authentication. The extra parameters depend on the * HTTPAuthMethod mode: * * BASIC_AUTH enteredUsernameOrReq contains the username entered by the user * param[0] password entered (in the clear) * param[1] authentication realm. * * To return - the password the user entered password is compared to. Or Null on fail. * * DIGEST_AUTH enteredUsernameOrReq contains the username entered by the user * param[0] autenticaiton realm * param[1] authentication URI * * To return - the password of which the digest will be based on for comparison. Or NULL * to fail. * * OTHER_AUTH enteredUsernameOrReq rest of the auth line. * params empty array * * To return - NULL to fail; or any string. */ typedef std::function THandlerFunctionAuthCheck; bool authenticate(THandlerFunctionAuthCheck fn); bool authenticate(const char *username, const char *password); bool authenticateBasicSHA1(const char *_username, const char *_sha1AsBase64orHex); void requestAuthentication(HTTPAuthMethod mode = BASIC_AUTH, const char *realm = NULL, const String &authFailMsg = String("")); typedef std::function THandlerFunction; typedef std::function FilterFunction; RequestHandler &on(const Uri &uri, THandlerFunction fn); RequestHandler &on(const Uri &uri, HTTPMethod method, THandlerFunction fn); RequestHandler &on(const Uri &uri, HTTPMethod method, THandlerFunction fn, THandlerFunction ufn); //ufn handles file uploads bool removeRoute(const char *uri); bool removeRoute(const char *uri, HTTPMethod method); bool removeRoute(const String &uri); bool removeRoute(const String &uri, HTTPMethod method); void addHandler(RequestHandler *handler); bool removeHandler(RequestHandler *handler); void serveStatic(const char *uri, fs::FS &fs, const char *path, const char *cache_header = NULL); void onNotFound(THandlerFunction fn); //called when handler is not assigned void onFileUpload(THandlerFunction ufn); //handle file uploads WebServer &addMiddleware(Middleware *middleware); WebServer &addMiddleware(Middleware::Function fn); WebServer &removeMiddleware(Middleware *middleware); String uri() const { return _currentUri; } HTTPMethod method() const { return _currentMethod; } virtual NetworkClient &client() { return _currentClient; } HTTPUpload &upload() { return *_currentUpload; } HTTPRaw &raw() { return *_currentRaw; } String pathArg(unsigned int i) const; // get request path argument by number String arg(const String &name) const; // get request argument value by name String arg(int i) const; // get request argument value by number String argName(int i) const; // get request argument name by number int args() const; // get arguments count bool hasArg(const String &name) const; // check if argument exists void collectHeaders(const char *headerKeys[], const size_t headerKeysCount); // set the request headers to collect void collectAllHeaders(); // collect all request headers String header(const String &name) const; // get request header value by name String header(int i) const; // get request header value by number String headerName(int i) const; // get request header name by number int headers() const; // get header count bool hasHeader(const String &name) const; // check if header exists int clientContentLength() const; // return "content-length" of incoming HTTP header from "_currentClient" const String version() const; // get the HTTP version string String hostHeader() const; // get request host header if available or empty String if not int responseCode() const; // get the HTTP response code set int responseHeaders() const; // get the HTTP response headers count const String &responseHeader(String name) const; // get the HTTP response header value by name const String &responseHeader(int i) const; // get the HTTP response header value by number const String &responseHeaderName(int i) const; // get the HTTP response header name by number bool hasResponseHeader(const String &name) const; // check if response header exists // send response to the client // code - HTTP response code, can be 200 or 404 // content_type - HTTP content type, like "text/plain" or "image/png" // content - actual content body void send(int code, const char *content_type = NULL, const String &content = String("")); void send(int code, char *content_type, const String &content); void send(int code, const String &content_type, const String &content); void send(int code, const char *content_type, const char *content); void send_P(int code, PGM_P content_type, PGM_P content); void send_P(int code, PGM_P content_type, PGM_P content, size_t contentLength); void enableDelay(boolean value); void enableCORS(boolean value = true); void enableCrossOrigin(boolean value = true); typedef std::function ETagFunction; void enableETag(bool enable, ETagFunction fn = nullptr); void setContentLength(const size_t contentLength); void sendHeader(const String &name, const String &value, bool first = false); void sendContent(const String &content); void sendContent(const char *content, size_t contentLength); void sendContent_P(PGM_P content); void sendContent_P(PGM_P content, size_t size); static String urlDecode(const String &text); template size_t streamFile(T &file, const String &contentType, const int code = 200) { _streamFileCore(file.size(), file.name(), contentType, code); return _currentClient.write(file); } bool _eTagEnabled = false; ETagFunction _eTagFunction = nullptr; static String responseCodeToString(int code); protected: virtual size_t _currentClientWrite(const char *b, size_t l) { return _currentClient.write(b, l); } virtual size_t _currentClientWrite_P(PGM_P b, size_t l) { return _currentClient.write_P(b, l); } void _addRequestHandler(RequestHandler *handler); bool _removeRequestHandler(RequestHandler *handler); bool _handleRequest(); void _finalizeResponse(); bool _parseRequest(NetworkClient &client); void _parseArguments(const String &data); bool _parseForm(NetworkClient &client, const String &boundary, uint32_t len); bool _parseFormUploadAborted(); void _uploadWriteByte(uint8_t b); int _uploadReadByte(NetworkClient &client); void _prepareHeader(String &response, int code, const char *content_type, size_t contentLength); bool _collectHeader(const char *headerName, const char *headerValue); void _streamFileCore(const size_t fileSize, const String &fileName, const String &contentType, const int code = 200); String _getRandomHexString(); // for extracting Auth parameters String _extractParam(String &authReq, const String ¶m, const char delimit = '"'); void _clearResponseHeaders(); void _clearRequestHeaders(); struct RequestArgument { String key; String value; RequestArgument *next; }; boolean _corsEnabled = false; NetworkServer _server; NetworkClient _currentClient; HTTPMethod _currentMethod = HTTP_ANY; String _currentUri; uint8_t _currentVersion = 0; HTTPClientStatus _currentStatus = HC_NONE; unsigned long _statusChange = 0; boolean _nullDelay = true; RequestHandler *_currentHandler = nullptr; RequestHandler *_firstHandler = nullptr; RequestHandler *_lastHandler = nullptr; THandlerFunction _notFoundHandler = nullptr; THandlerFunction _fileUploadHandler = nullptr; int _currentArgCount = 0; RequestArgument *_currentArgs = nullptr; int _postArgsLen = 0; RequestArgument *_postArgs = nullptr; std::unique_ptr _currentUpload; std::unique_ptr _currentRaw; int _headerKeysCount = 0; RequestArgument *_currentHeaders = nullptr; size_t _contentLength = 0; int _clientContentLength = 0; // "Content-Length" from header of incoming POST or GET request RequestArgument *_responseHeaders = nullptr; String _hostHeader; bool _chunked = false; String _snonce; // Store noance and opaque for future comparison String _sopaque; String _srealm; // Store the Auth realm between Calls int _responseHeaderCount = 0; int _responseCode = 0; bool _collectAllHeaders = false; MiddlewareChain *_chain = nullptr; }; #endif //ESP8266WEBSERVER_H