ESP_IOT v2.5
IOT ESP Coding
WebServer.cpp
Go to the documentation of this file.
1//! \link WebServer
2/*
3 WebServer.cpp - Dead simple web-server.
4 Supports only one simultaneous client, knows how to handle GET and POST.
5
6 Copyright (c) 2014 Ivan Grokhotkov. All rights reserved.
7
8 This library is free software; you can redistribute it and/or
9 modify it under the terms of the GNU Lesser General Public
10 License as published by the Free Software Foundation; either
11 version 2.1 of the License, or (at your option) any later version.
12
13 This library is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public
19 License along with this library; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21 Modified 8 May 2015 by Hristo Gochkov (proper post and file upload handling)
22*/
23
24
25#include "../../Defines.h"
26
27#include <Arduino.h>
28#include <libb64/cencode.h>
29#include "WiFiServer.h"
30#include "WiFiClient.h"
31#include "WebServer.h"
32#include "FS.h"
33#include "RequestHandlersImpl.h"
34
35//#define DEBUG_ESP_HTTP_SERVER
36#ifdef DEBUG_ESP_PORT
37#define DEBUG_OUTPUT DEBUG_ESP_PORT
38#else
39#define DEBUG_OUTPUT Serial
40#endif
41
42const char * AUTHORIZATION_HEADER = "Authorization";
43
44WebServer::WebServer(IPAddress addr, int port)
45: _server(addr, port)
46, _currentMethod(HTTP_ANY)
47, _currentVersion(0)
48, _currentStatus(HC_NONE)
49, _statusChange(0)
50, _currentHandler(0)
51, _firstHandler(0)
52, _lastHandler(0)
53, _currentArgCount(0)
54, _currentArgs(0)
55, _headerKeysCount(0)
56, _currentHeaders(0)
58, _chunked(false)
59{
60}
61
63: _server(port)
64, _currentMethod(HTTP_ANY)
65, _currentVersion(0)
66, _currentStatus(HC_NONE)
67, _statusChange(0)
68, _currentHandler(0)
69, _firstHandler(0)
70, _lastHandler(0)
71, _currentArgCount(0)
72, _currentArgs(0)
73, _headerKeysCount(0)
74, _currentHeaders(0)
76, _chunked(false)
77{
78}
79
82 delete[]_currentHeaders;
85 while (handler) {
86 RequestHandler* next = handler->next();
87 delete handler;
88 handler = next;
89 }
90 close();
91}
92
95 _server.begin();
97 collectHeaders(0, 0);
98}
99
100bool WebServer::authenticate(const char * username, const char * password){
102 String authReq = header(AUTHORIZATION_HEADER);
103 if(authReq.startsWith("Basic")){
104 authReq = authReq.substring(6);
105 authReq.trim();
106 char toencodeLen = strlen(username)+strlen(password)+1;
107 char *toencode = new char[toencodeLen + 1];
108 if(toencode == NULL){
109 authReq = String();
110 return false;
111 }
112 char *encoded = new char[base64_encode_expected_len(toencodeLen)+1];
113 if(encoded == NULL){
114 authReq = String();
115 delete[] toencode;
116 return false;
117 }
118 sprintf(toencode, "%s:%s", username, password);
119 if(base64_encode_chars(toencode, toencodeLen, encoded) > 0 && authReq.equals(encoded)){
120 authReq = String();
121 delete[] toencode;
122 delete[] encoded;
123 return true;
124 }
125 delete[] toencode;
126 delete[] encoded;
127 }
128 authReq = String();
129 }
130 return false;
131}
132
134 sendHeader("WWW-Authenticate", "Basic realm=\"Login Required\"");
135 send(401);
136}
137
138void WebServer::on(const String &uri, WebServer::THandlerFunction handler) {
139 on(uri, HTTP_ANY, handler);
140}
141
142void WebServer::on(const String &uri, HTTPMethod method, WebServer::THandlerFunction fn) {
144}
145
148}
149
151 _addRequestHandler(handler);
152}
153
155 if (!_lastHandler) {
156 _firstHandler = handler;
157 _lastHandler = handler;
158 }
159 else {
160 _lastHandler->next(handler);
161 _lastHandler = handler;
162 }
163}
164
165void WebServer::serveStatic(const char* uri, FS& fs, const char* path, const char* cache_header) {
166 _addRequestHandler(new StaticRequestHandler(fs, path, uri, cache_header));
167}
168
170 if (_currentStatus == HC_NONE) {
171 WiFiClient client = _server.available();
172 if (!client) {
173 return;
174 }
175
176#ifdef DEBUG_ESP_HTTP_SERVER
177 SerialDebug.println("New client");
178#endif
179
182 _statusChange = millis();
183 }
184
185 if (!_currentClient.connected()) {
186 _currentClient = WiFiClient();
188 return;
189 }
190
191 // Wait for data from client to become available
193 if (!_currentClient.available()) {
194 if (millis() - _statusChange > HTTP_MAX_DATA_WAIT) {
195 _currentClient = WiFiClient();
197 }
198 yield();
199 return;
200 }
201
203 _currentClient = WiFiClient();
205 return;
206 }
210
211 if (!_currentClient.connected()) {
212 _currentClient = WiFiClient();
214 return;
215 } else {
217 _statusChange = millis();
218 return;
219 }
220 }
221
223 if (millis() - _statusChange > HTTP_MAX_CLOSE_WAIT) {
224 _currentClient = WiFiClient();
226 } else {
227 yield();
228 return;
229 }
230 }
231}
232
234 _server.end();
235}
236
238 close();
239}
240
241void WebServer::sendHeader(const String& name, const String& value, bool first) {
242 String headerLine = name;
243 headerLine += ": ";
244 headerLine += value;
245 headerLine += "\r\n";
246
247 if (first) {
248 _responseHeaders = headerLine + _responseHeaders;
249 }
250 else {
251 _responseHeaders += headerLine;
252 }
253}
254
255void WebServer::setContentLength(size_t contentLength) {
256 _contentLength = contentLength;
257}
258
259void WebServer::_prepareHeader(String& response, int code, const char* content_type, size_t contentLength) {
260 response = "HTTP/1."+String(_currentVersion)+" ";
261 response += String(code);
262 response += " ";
263 response += _responseCodeToString(code);
264 response += "\r\n";
265
266 if (!content_type)
267 content_type = "text/html";
268
269 sendHeader("Content-Type", content_type, true);
271 sendHeader("Content-Length", String(contentLength));
273 sendHeader("Content-Length", String(_contentLength));
274 } else if(_contentLength == CONTENT_LENGTH_UNKNOWN && _currentVersion){ //HTTP/1.1 or above client
275 //let's do chunked
276 _chunked = true;
277 sendHeader("Accept-Ranges","none");
278 sendHeader("Transfer-Encoding","chunked");
279 }
280 sendHeader("Connection", "close");
281
282 response += _responseHeaders;
283 response += "\r\n";
284 _responseHeaders = String();
285}
286
287void WebServer::send(int code, const char* content_type, const String& content) {
288 String header;
289 // Can we asume the following?
290 //if(code == 200 && content.length() == 0 && _contentLength == CONTENT_LENGTH_NOT_SET)
291 // _contentLength = CONTENT_LENGTH_UNKNOWN;
292 _prepareHeader(header, code, content_type, content.length());
293 _currentClient.write(header.c_str(), header.length());
294 if(content.length())
295 sendContent(content);
296}
297
298void WebServer::send_P(int code, PGM_P content_type, PGM_P content) {
299 size_t contentLength = 0;
300
301 if (content != NULL) {
302 contentLength = strlen_P(content);
303 }
304
305 String header;
306 char type[64];
307 memccpy_P((void*)type, (PGM_VOID_P)content_type, 0, sizeof(type));
308 _prepareHeader(header, code, (const char* )type, contentLength);
309 _currentClient.write(header.c_str(), header.length());
310 sendContent_P(content);
311}
312
313void WebServer::send_P(int code, PGM_P content_type, PGM_P content, size_t contentLength) {
314 String header;
315 char type[64];
316 memccpy_P((void*)type, (PGM_VOID_P)content_type, 0, sizeof(type));
317 _prepareHeader(header, code, (const char* )type, contentLength);
319 sendContent_P(content, contentLength);
320}
321
322void WebServer::send(int code, char* content_type, const String& content) {
323 send(code, (const char*)content_type, content);
324}
325
326void WebServer::send(int code, const String& content_type, const String& content) {
327 send(code, (const char*)content_type.c_str(), content);
328}
329
330void WebServer::sendContent(const String& content) {
331 const char * footer = "\r\n";
332 size_t len = content.length();
333 if(_chunked) {
334 char * chunkSize = (char *)malloc(11);
335 if(chunkSize){
336 sprintf(chunkSize, "%x%s", len, footer);
337 _currentClient.write(chunkSize, strlen(chunkSize));
338 free(chunkSize);
339 }
340 }
341 _currentClient.write(content.c_str(), len);
342 if(_chunked){
343 _currentClient.write(footer, 2);
344 }
345}
346
347void WebServer::sendContent_P(PGM_P content) {
348 sendContent_P(content, strlen_P(content));
349}
350
351void WebServer::sendContent_P(PGM_P content, size_t size) {
352 const char * footer = "\r\n";
353 if(_chunked) {
354 char * chunkSize = (char *)malloc(11);
355 if(chunkSize){
356 sprintf(chunkSize, "%x%s", size, footer);
357 _currentClient.write(chunkSize, strlen(chunkSize));
358 free(chunkSize);
359 }
360 }
361 _currentClient.write(content, size);
362 if(_chunked){
363 _currentClient.write(footer, 2);
364 }
365}
366
367
368String WebServer::arg(String name) {
369 for (int i = 0; i < _currentArgCount; ++i) {
370 if ( _currentArgs[i].key == name )
371 return _currentArgs[i].value;
372 }
373 return String();
374}
375
376String WebServer::arg(int i) {
377 if (i < _currentArgCount)
378 return _currentArgs[i].value;
379 return String();
380}
381
382String WebServer::argName(int i) {
383 if (i < _currentArgCount)
384 return _currentArgs[i].key;
385 return String();
386}
387
389 return _currentArgCount;
390}
391
392bool WebServer::hasArg(String name) {
393 for (int i = 0; i < _currentArgCount; ++i) {
394 if (_currentArgs[i].key == name)
395 return true;
396 }
397 return false;
398}
399
400
401String WebServer::header(String name) {
402 for (int i = 0; i < _headerKeysCount; ++i) {
403 if (_currentHeaders[i].key.equalsIgnoreCase(name))
404 return _currentHeaders[i].value;
405 }
406 return String();
407}
408
409void WebServer::collectHeaders(const char* headerKeys[], const size_t headerKeysCount) {
410 _headerKeysCount = headerKeysCount + 1;
411 if (_currentHeaders)
412 delete[]_currentHeaders;
415 for (int i = 1; i < _headerKeysCount; i++){
416 _currentHeaders[i].key = headerKeys[i-1];
417 }
418}
419
420String WebServer::header(int i) {
421 if (i < _headerKeysCount)
422 return _currentHeaders[i].value;
423 return String();
424}
425
427 if (i < _headerKeysCount)
428 return _currentHeaders[i].key;
429 return String();
430}
431
433 return _headerKeysCount;
434}
435
436bool WebServer::hasHeader(String name) {
437 for (int i = 0; i < _headerKeysCount; ++i) {
438 if ((_currentHeaders[i].key.equalsIgnoreCase(name)) && (_currentHeaders[i].value.length() > 0))
439 return true;
440 }
441 return false;
442}
443
445 return _hostHeader;
446}
447
450}
451
453 _notFoundHandler = fn;
454}
455
457 bool handled = false;
458 if (!_currentHandler){
459#ifdef DEBUG_ESP_HTTP_SERVER
460 SerialDebug.println("request handler not found");
461#endif
462 }
463 else {
465#ifdef DEBUG_ESP_HTTP_SERVER
466 if (!handled) {
467 SerialDebug.println("request handler failed to handle request");
468 }
469#endif
470 }
471
472 if (!handled) {
473 if(_notFoundHandler) {
475 }
476 else {
477 send(404, "text/plain", String("Not found: ") + _currentUri);
478 }
479 }
480
481 _currentUri = String();
482}
483
485 switch (code) {
486 case 100: return F("Continue");
487 case 101: return F("Switching Protocols");
488 case 200: return F("OK");
489 case 201: return F("Created");
490 case 202: return F("Accepted");
491 case 203: return F("Non-Authoritative Information");
492 case 204: return F("No Content");
493 case 205: return F("Reset Content");
494 case 206: return F("Partial Content");
495 case 300: return F("Multiple Choices");
496 case 301: return F("Moved Permanently");
497 case 302: return F("Found");
498 case 303: return F("See Other");
499 case 304: return F("Not Modified");
500 case 305: return F("Use Proxy");
501 case 307: return F("Temporary Redirect");
502 case 400: return F("Bad Request");
503 case 401: return F("Unauthorized");
504 case 402: return F("Payment Required");
505 case 403: return F("Forbidden");
506 case 404: return F("Not Found");
507 case 405: return F("Method Not Allowed");
508 case 406: return F("Not Acceptable");
509 case 407: return F("Proxy Authentication Required");
510 case 408: return F("Request Time-out");
511 case 409: return F("Conflict");
512 case 410: return F("Gone");
513 case 411: return F("Length Required");
514 case 412: return F("Precondition Failed");
515 case 413: return F("Request Entity Too Large");
516 case 414: return F("Request-URI Too Large");
517 case 415: return F("Unsupported Media Type");
518 case 416: return F("Requested range not satisfiable");
519 case 417: return F("Expectation Failed");
520 case 500: return F("Internal Server Error");
521 case 501: return F("Not Implemented");
522 case 502: return F("Bad Gateway");
523 case 503: return F("Service Unavailable");
524 case 504: return F("Gateway Time-out");
525 case 505: return F("HTTP Version not supported");
526 default: return "";
527 }
528}
long _contentLength
const char * AUTHORIZATION_HEADER
Definition: WebServer.cpp:42
#define CONTENT_LENGTH_NOT_SET
Definition: WebServer.h:46
#define HTTP_MAX_DATA_WAIT
Definition: WebServer.h:40
HTTPMethod
Definition: WebServer.h:33
@ HTTP_ANY
Definition: WebServer.h:33
#define HTTP_MAX_SEND_WAIT
Definition: WebServer.h:42
@ HC_WAIT_CLOSE
Definition: WebServer.h:36
@ HC_NONE
Definition: WebServer.h:36
@ HC_WAIT_READ
Definition: WebServer.h:36
#define HTTP_MAX_CLOSE_WAIT
Definition: WebServer.h:43
#define CONTENT_LENGTH_UNKNOWN
Definition: WebServer.h:45
RequestHandler * next()
virtual bool handle(WebServer &server, HTTPMethod requestMethod, String requestUri)
String headerName(int i)
Definition: WebServer.cpp:426
int args()
Definition: WebServer.cpp:388
WiFiClient client()
Definition: WebServer.h:93
bool authenticate(const char *username, const char *password)
Definition: WebServer.cpp:100
bool hasHeader(String name)
Definition: WebServer.cpp:436
int headers()
Definition: WebServer.cpp:432
void _prepareHeader(String &response, int code, const char *content_type, size_t contentLength)
Definition: WebServer.cpp:259
RequestHandler * _currentHandler
Definition: WebServer.h:166
size_t _contentLength
Definition: WebServer.h:178
void on(const String &uri, THandlerFunction handler)
Definition: WebServer.cpp:138
void begin()
Definition: WebServer.cpp:93
void _handleRequest()
Definition: WebServer.cpp:456
RequestArgument * _currentArgs
Definition: WebServer.h:173
static String _responseCodeToString(int code)
Definition: WebServer.cpp:484
void sendContent(const String &content)
Definition: WebServer.cpp:330
void requestAuthentication()
Definition: WebServer.cpp:133
String hostHeader()
Definition: WebServer.cpp:444
HTTPMethod _currentMethod
Definition: WebServer.h:160
HTTPClientStatus _currentStatus
Definition: WebServer.h:163
String _currentUri
Definition: WebServer.h:161
String _responseHeaders
Definition: WebServer.h:179
WiFiClient _currentClient
Definition: WebServer.h:159
String argName(int i)
Definition: WebServer.cpp:382
void _addRequestHandler(RequestHandler *handler)
Definition: WebServer.cpp:154
void addHandler(RequestHandler *handler)
Definition: WebServer.cpp:150
void handleClient()
Definition: WebServer.cpp:169
WiFiServer _server
Definition: WebServer.h:157
RequestHandler * _firstHandler
Definition: WebServer.h:167
void collectHeaders(const char *headerKeys[], const size_t headerKeysCount)
Definition: WebServer.cpp:409
void send(int code, const char *content_type=NULL, const String &content=String(""))
Definition: WebServer.cpp:287
unsigned long _statusChange
Definition: WebServer.h:164
bool _chunked
Definition: WebServer.h:182
String header(String name)
Definition: WebServer.cpp:401
void sendHeader(const String &name, const String &value, bool first=false)
Definition: WebServer.cpp:241
void onNotFound(THandlerFunction fn)
Definition: WebServer.cpp:452
WebServer(IPAddress addr, int port=80)
Definition: WebServer.cpp:44
String arg(String name)
Definition: WebServer.cpp:368
bool hasArg(String name)
Definition: WebServer.cpp:392
uint8_t _currentVersion
Definition: WebServer.h:162
int _headerKeysCount
Definition: WebServer.h:176
bool _parseRequest(WiFiClient &client)
Definition: Parsing.cpp:67
void stop()
Definition: WebServer.cpp:237
void setContentLength(size_t contentLength)
Definition: WebServer.cpp:255
THandlerFunction _fileUploadHandler
Definition: WebServer.h:170
THandlerFunction _notFoundHandler
Definition: WebServer.h:169
RequestHandler * _lastHandler
Definition: WebServer.h:168
RequestArgument * _currentHeaders
Definition: WebServer.h:177
void close()
Definition: WebServer.cpp:233
int _currentArgCount
Definition: WebServer.h:172
void onFileUpload(THandlerFunction fn)
Definition: WebServer.cpp:448
void send_P(int code, PGM_P content_type, PGM_P content)
Definition: WebServer.cpp:298
void sendContent_P(PGM_P content)
Definition: WebServer.cpp:347
void serveStatic(const char *uri, fs::FS &fs, const char *path, const char *cache_header=NULL)
Definition: WebServer.cpp:165
String _hostHeader
Definition: WebServer.h:181
HTTPMethod method()
Definition: WebServer.h:92
std::function< void(void)> THandlerFunction
Definition: WebServer.h:82
String uri()
Definition: WebServer.h:91
Definition: WebServer.h:62