|
| 1 | +/* |
| 2 | + WebServer.h - Dead simple web-server. |
| 3 | + Supports only one simultaneous client, knows how to handle GET and POST. |
| 4 | +
|
| 5 | + Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. |
| 6 | +
|
| 7 | + This library is free software; you can redistribute it and/or |
| 8 | + modify it under the terms of the GNU Lesser General Public |
| 9 | + License as published by the Free Software Foundation; either |
| 10 | + version 2.1 of the License, or (at your option) any later version. |
| 11 | +
|
| 12 | + This library is distributed in the hope that it will be useful, |
| 13 | + but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 14 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 15 | + Lesser General Public License for more details. |
| 16 | +
|
| 17 | + You should have received a copy of the GNU Lesser General Public |
| 18 | + License along with this library; if not, write to the Free Software |
| 19 | + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
| 20 | + Modified 8 May 2015 by Hristo Gochkov (proper post and file upload handling) |
| 21 | +*/ |
| 22 | + |
| 23 | + |
| 24 | +#ifndef WEBSERVER_H |
| 25 | +#define WEBSERVER_H |
| 26 | + |
| 27 | +#include <functional> |
| 28 | +#include <WiFi.h> |
| 29 | +#include <Update.h> |
| 30 | +#include <WiFiUdp.h> |
| 31 | + |
| 32 | +enum HTTPMethod { HTTP_ANY, HTTP_GET, HTTP_POST, HTTP_PUT, HTTP_PATCH, HTTP_DELETE, HTTP_OPTIONS }; |
| 33 | +enum HTTPUploadStatus { UPLOAD_FILE_START, UPLOAD_FILE_WRITE, UPLOAD_FILE_END, |
| 34 | + UPLOAD_FILE_ABORTED }; |
| 35 | +enum HTTPClientStatus { HC_NONE, HC_WAIT_READ, HC_WAIT_CLOSE }; |
| 36 | + |
| 37 | +#define HTTP_DOWNLOAD_UNIT_SIZE 1460 |
| 38 | +#define HTTP_UPLOAD_BUFLEN 2048 |
| 39 | +#define HTTP_MAX_DATA_WAIT 1000 //ms to wait for the client to send the request |
| 40 | +#define HTTP_MAX_POST_WAIT 1000 //ms to wait for POST data to arrive |
| 41 | +#define HTTP_MAX_SEND_WAIT 5000 //ms to wait for data chunk to be ACKed |
| 42 | +#define HTTP_MAX_CLOSE_WAIT 2000 //ms to wait for the client to close the connection |
| 43 | + |
| 44 | +#define CONTENT_LENGTH_UNKNOWN ((size_t) -1) |
| 45 | +#define CONTENT_LENGTH_NOT_SET ((size_t) -2) |
| 46 | + |
| 47 | +class WebServer; |
| 48 | + |
| 49 | +typedef struct { |
| 50 | + HTTPUploadStatus status; |
| 51 | + String filename; |
| 52 | + String name; |
| 53 | + String type; |
| 54 | + size_t totalSize; // file size |
| 55 | + size_t currentSize; // size of data currently in buf |
| 56 | + uint8_t buf[HTTP_UPLOAD_BUFLEN]; |
| 57 | +} HTTPUpload; |
| 58 | + |
| 59 | +#include "detail/RequestHandler.h" |
| 60 | + |
| 61 | +namespace fs { |
| 62 | +class FS; |
| 63 | +} |
| 64 | + |
| 65 | +class WebServer |
| 66 | +{ |
| 67 | +public: |
| 68 | + WebServer(IPAddress addr, int port = 80); |
| 69 | + WebServer(int port = 80); |
| 70 | + ~WebServer(); |
| 71 | + |
| 72 | + void begin(); |
| 73 | + void handleClient(); |
| 74 | + |
| 75 | + void close(); |
| 76 | + void stop(); |
| 77 | + |
| 78 | + bool authenticate(const char * username, const char * password); |
| 79 | + void requestAuthentication(); |
| 80 | + |
| 81 | + typedef std::function<void(void)> THandlerFunction; |
| 82 | + void on(const String &uri, THandlerFunction handler); |
| 83 | + void on(const String &uri, HTTPMethod method, THandlerFunction fn); |
| 84 | + void on(const String &uri, HTTPMethod method, THandlerFunction fn, THandlerFunction ufn); |
| 85 | + void addHandler(RequestHandler* handler); |
| 86 | + void serveStatic(const char* uri, fs::FS& fs, const char* path, const char* cache_header = NULL ); |
| 87 | + void onNotFound(THandlerFunction fn); //called when handler is not assigned |
| 88 | + void onFileUpload(THandlerFunction fn); //handle file uploads |
| 89 | + |
| 90 | + String uri() { return _currentUri; } |
| 91 | + HTTPMethod method() { return _currentMethod; } |
| 92 | + WiFiClient client() { return _currentClient; } |
| 93 | + HTTPUpload& upload() { return _currentUpload; } |
| 94 | + |
| 95 | + String arg(String name); // get request argument value by name |
| 96 | + String arg(int i); // get request argument value by number |
| 97 | + String argName(int i); // get request argument name by number |
| 98 | + int args(); // get arguments count |
| 99 | + bool hasArg(String name); // check if argument exists |
| 100 | + void collectHeaders(const char* headerKeys[], const size_t headerKeysCount); // set the request headers to collect |
| 101 | + String header(String name); // get request header value by name |
| 102 | + String header(int i); // get request header value by number |
| 103 | + String headerName(int i); // get request header name by number |
| 104 | + int headers(); // get header count |
| 105 | + bool hasHeader(String name); // check if header exists |
| 106 | + |
| 107 | + String hostHeader(); // get request host header if available or empty String if not |
| 108 | + |
| 109 | + // send response to the client |
| 110 | + // code - HTTP response code, can be 200 or 404 |
| 111 | + // content_type - HTTP content type, like "text/plain" or "image/png" |
| 112 | + // content - actual content body |
| 113 | + void send(int code, const char* content_type = NULL, const String& content = String("")); |
| 114 | + void send(int code, char* content_type, const String& content); |
| 115 | + void send(int code, const String& content_type, const String& content); |
| 116 | + void send_P(int code, PGM_P content_type, PGM_P content); |
| 117 | + void send_P(int code, PGM_P content_type, PGM_P content, size_t contentLength); |
| 118 | + |
| 119 | + void setContentLength(size_t contentLength); |
| 120 | + void sendHeader(const String& name, const String& value, bool first = false); |
| 121 | + void sendContent(const String& content); |
| 122 | + void sendContent_P(PGM_P content); |
| 123 | + void sendContent_P(PGM_P content, size_t size); |
| 124 | + |
| 125 | + static String urlDecode(const String& text); |
| 126 | + |
| 127 | +template<typename T> size_t streamFile(T &file, const String& contentType){ |
| 128 | + setContentLength(file.size()); |
| 129 | + if (String(file.name()).endsWith(".gz") && |
| 130 | + contentType != "application/x-gzip" && |
| 131 | + contentType != "application/octet-stream"){ |
| 132 | + sendHeader("Content-Encoding", "gzip"); |
| 133 | + } |
| 134 | + send(200, contentType, ""); |
| 135 | + return _currentClient.write(file); |
| 136 | +} |
| 137 | + |
| 138 | +protected: |
| 139 | + void _addRequestHandler(RequestHandler* handler); |
| 140 | + void _handleRequest(); |
| 141 | + bool _parseRequest(WiFiClient& client); |
| 142 | + void _parseArguments(String data); |
| 143 | + static String _responseCodeToString(int code); |
| 144 | + bool _parseForm(WiFiClient& client, String boundary, uint32_t len); |
| 145 | + bool _parseFormUploadAborted(); |
| 146 | + void _uploadWriteByte(uint8_t b); |
| 147 | + uint8_t _uploadReadByte(WiFiClient& client); |
| 148 | + void _prepareHeader(String& response, int code, const char* content_type, size_t contentLength); |
| 149 | + bool _collectHeader(const char* headerName, const char* headerValue); |
| 150 | + |
| 151 | + struct RequestArgument { |
| 152 | + String key; |
| 153 | + String value; |
| 154 | + }; |
| 155 | + |
| 156 | + WiFiServer _server; |
| 157 | + |
| 158 | + WiFiClient _currentClient; |
| 159 | + HTTPMethod _currentMethod; |
| 160 | + String _currentUri; |
| 161 | + uint8_t _currentVersion; |
| 162 | + HTTPClientStatus _currentStatus; |
| 163 | + unsigned long _statusChange; |
| 164 | + |
| 165 | + RequestHandler* _currentHandler; |
| 166 | + RequestHandler* _firstHandler; |
| 167 | + RequestHandler* _lastHandler; |
| 168 | + THandlerFunction _notFoundHandler; |
| 169 | + THandlerFunction _fileUploadHandler; |
| 170 | + |
| 171 | + int _currentArgCount; |
| 172 | + RequestArgument* _currentArgs; |
| 173 | + HTTPUpload _currentUpload; |
| 174 | + |
| 175 | + int _headerKeysCount; |
| 176 | + RequestArgument* _currentHeaders; |
| 177 | + size_t _contentLength; |
| 178 | + String _responseHeaders; |
| 179 | + |
| 180 | + String _hostHeader; |
| 181 | + bool _chunked; |
| 182 | + |
| 183 | +}; |
| 184 | + |
| 185 | + |
| 186 | +#endif //WebServer_H |
0 commit comments