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