25#include "../../Defines.h"
26#ifdef USE_LIB_WEBSERVER
29#include <libb64/cencode.h>
30#include "WiFiServer.h"
31#include "WiFiClient.h"
38#define DEBUG_OUTPUT DEBUG_ESP_PORT
40#define DEBUG_OUTPUT Serial
43const char * AUTHORIZATION_HEADER =
"Authorization";
45WebServer::WebServer(IPAddress addr,
int port)
47, _currentMethod(HTTP_ANY)
49, _currentStatus(HC_NONE)
63WebServer::WebServer(
int port)
65, _currentMethod(HTTP_ANY)
67, _currentStatus(HC_NONE)
81WebServer::~WebServer() {
83 delete[]_currentHeaders;
85 RequestHandler* handler = _firstHandler;
87 RequestHandler* next = handler->next();
94void WebServer::begin() {
95 _currentStatus = HC_NONE;
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);
107 char toencodeLen = strlen(username)+strlen(password)+1;
108 char *toencode =
new char[toencodeLen + 1];
109 if(toencode == NULL){
113 char *encoded =
new char[base64_encode_expected_len(toencodeLen)+1];
119 sprintf(toencode,
"%s:%s", username, password);
120 if(base64_encode_chars(toencode, toencodeLen, encoded) > 0 && authReq.equals(encoded)){
134void WebServer::requestAuthentication(){
135 sendHeader(
"WWW-Authenticate",
"Basic realm=\"Login Required\"");
139void WebServer::on(
const String &uri, WebServer::THandlerFunction handler) {
140 on(uri, HTTP_ANY, handler);
143void WebServer::on(
const String &uri, HTTPMethod method, WebServer::THandlerFunction fn) {
144 on(uri, method, fn, _fileUploadHandler);
147void WebServer::on(
const String &uri, HTTPMethod method, WebServer::THandlerFunction fn, WebServer::THandlerFunction ufn) {
148 _addRequestHandler(
new FunctionRequestHandler(fn, ufn, uri, method));
151void WebServer::addHandler(RequestHandler* handler) {
152 _addRequestHandler(handler);
155void WebServer::_addRequestHandler(RequestHandler* handler) {
157 _firstHandler = handler;
158 _lastHandler = handler;
161 _lastHandler->next(handler);
162 _lastHandler = handler;
166void WebServer::serveStatic(
const char* uri, FS& fs,
const char* path,
const char* cache_header) {
167 _addRequestHandler(
new StaticRequestHandler(fs, path, uri, cache_header));
170void WebServer::handleClient() {
171 if (_currentStatus == HC_NONE) {
172 WiFiClient client = _server.available();
177#ifdef DEBUG_ESP_HTTP_SERVER
178 SerialDebug.println(
"New client");
181 _currentClient = client;
182 _currentStatus = HC_WAIT_READ;
186 if (!_currentClient.connected()) {
187 _currentClient = WiFiClient();
188 _currentStatus = HC_NONE;
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;
203 if (!_parseRequest(_currentClient)) {
204 _currentClient = WiFiClient();
205 _currentStatus = HC_NONE;
208 _currentClient.setTimeout(HTTP_MAX_SEND_WAIT);
212 if (!_currentClient.connected()) {
213 _currentClient = WiFiClient();
214 _currentStatus = HC_NONE;
217 _currentStatus = HC_WAIT_CLOSE;
223 if (_currentStatus == HC_WAIT_CLOSE) {
224 if (
millis() - _statusChange > HTTP_MAX_CLOSE_WAIT) {
225 _currentClient = WiFiClient();
226 _currentStatus = HC_NONE;
234void WebServer::close() {
238void WebServer::stop() {
242void WebServer::sendHeader(
const String& name,
const String& value,
bool first) {
243 String headerLine = name;
246 headerLine +=
"\r\n";
249 _responseHeaders = headerLine + _responseHeaders;
252 _responseHeaders += headerLine;
256void WebServer::setContentLength(
size_t contentLength) {
260void WebServer::_prepareHeader(String& response,
int code,
const char* content_type,
size_t contentLength) {
261 response =
"HTTP/1."+String(_currentVersion)+
" ";
262 response += String(code);
264 response += _responseCodeToString(code);
268 content_type =
"text/html";
270 sendHeader(
"Content-Type", content_type,
true);
272 sendHeader(
"Content-Length", String(contentLength));
275 }
else if(
_contentLength == CONTENT_LENGTH_UNKNOWN && _currentVersion){
278 sendHeader(
"Accept-Ranges",
"none");
279 sendHeader(
"Transfer-Encoding",
"chunked");
281 sendHeader(
"Connection",
"close");
283 response += _responseHeaders;
285 _responseHeaders = String();
288void WebServer::send(
int code,
const char* content_type,
const String& content) {
293 _prepareHeader(header, code, content_type, content.length());
294 _currentClient.write(header.c_str(), header.length());
296 sendContent(content);
299void WebServer::send_P(
int code, PGM_P content_type, PGM_P content) {
300 size_t contentLength = 0;
302 if (content != NULL) {
303 contentLength = strlen_P(content);
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);
314void WebServer::send_P(
int code, PGM_P content_type, PGM_P content,
size_t contentLength) {
317 memccpy_P((
void*)type, (PGM_VOID_P)content_type, 0,
sizeof(type));
318 _prepareHeader(header, code, (
const char* )type, contentLength);
320 sendContent_P(content, contentLength);
323void WebServer::send(
int code,
char* content_type,
const String& content) {
324 send(code, (
const char*)content_type, content);
327void WebServer::send(
int code,
const String& content_type,
const String& content) {
328 send(code, (
const char*)content_type.c_str(), content);
331void WebServer::sendContent(
const String& content) {
332 const char * footer =
"\r\n";
333 size_t len = content.length();
335 char * chunkSize = (
char *)malloc(11);
337 sprintf(chunkSize,
"%x%s", len, footer);
338 _currentClient.write(chunkSize, strlen(chunkSize));
342 _currentClient.write(content.c_str(), len);
344 _currentClient.write(footer, 2);
348void WebServer::sendContent_P(PGM_P content) {
349 sendContent_P(content, strlen_P(content));
352void WebServer::sendContent_P(PGM_P content,
size_t size) {
353 const char * footer =
"\r\n";
355 char * chunkSize = (
char *)malloc(11);
357 sprintf(chunkSize,
"%x%s", size, footer);
358 _currentClient.write(chunkSize, strlen(chunkSize));
362 _currentClient.write(content, size);
364 _currentClient.write(footer, 2);
369String WebServer::arg(String name) {
370 for (
int i = 0; i < _currentArgCount; ++i) {
371 if ( _currentArgs[i].key == name )
372 return _currentArgs[i].value;
377String WebServer::arg(
int i) {
378 if (i < _currentArgCount)
379 return _currentArgs[i].value;
383String WebServer::argName(
int i) {
384 if (i < _currentArgCount)
385 return _currentArgs[i].key;
389int WebServer::args() {
390 return _currentArgCount;
393bool WebServer::hasArg(String name) {
394 for (
int i = 0; i < _currentArgCount; ++i) {
395 if (_currentArgs[i].key == name)
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;
410void WebServer::collectHeaders(
const char* headerKeys[],
const size_t headerKeysCount) {
411 _headerKeysCount = headerKeysCount + 1;
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];
421String WebServer::header(
int i) {
422 if (i < _headerKeysCount)
423 return _currentHeaders[i].value;
427String WebServer::headerName(
int i) {
428 if (i < _headerKeysCount)
429 return _currentHeaders[i].key;
433int WebServer::headers() {
434 return _headerKeysCount;
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))
445String WebServer::hostHeader() {
449void WebServer::onFileUpload(THandlerFunction fn) {
450 _fileUploadHandler = fn;
453void WebServer::onNotFound(THandlerFunction fn) {
454 _notFoundHandler = fn;
457void WebServer::_handleRequest() {
458 bool handled =
false;
459 if (!_currentHandler){
460#ifdef DEBUG_ESP_HTTP_SERVER
461 SerialDebug.println(
"request handler not found");
465 handled = _currentHandler->handle(*
this, _currentMethod, _currentUri);
466#ifdef DEBUG_ESP_HTTP_SERVER
468 SerialDebug.println(
"request handler failed to handle request");
474 if(_notFoundHandler) {
478 send(404,
"text/plain", String(
"Not found: ") + _currentUri);
482 _currentUri = String();
485String WebServer::_responseCodeToString(
int 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");