123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548 |
- /*
- WebServer.cpp - 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)
- */
- #include "WebServer.h"
- #include <Arduino.h>
- #include <libb64/cencode.h>
- #include "FS.h"
- #include "WiFiClient.h"
- #include "WiFiServer.h"
- #include "detail/RequestHandlersImpl.h"
- //#define DEBUG_ESP_HTTP_SERVER
- #ifdef DEBUG_ESP_PORT
- #define DEBUG_OUTPUT DEBUG_ESP_PORT
- #else
- #define DEBUG_OUTPUT Serial
- #endif
- const char* AUTHORIZATION_HEADER = "Authorization";
- WebServer::WebServer(IPAddress addr, int port)
- : _server(addr, port),
- _currentMethod(HTTP_ANY),
- _currentVersion(0),
- _currentStatus(HC_NONE),
- _statusChange(0),
- _currentHandler(0),
- _firstHandler(0),
- _lastHandler(0),
- _currentArgCount(0),
- _currentArgs(0),
- _headerKeysCount(0),
- _currentHeaders(0),
- _contentLength(0),
- _chunked(false) {}
- WebServer::WebServer(int port)
- : _server(port),
- _currentMethod(HTTP_ANY),
- _currentVersion(0),
- _currentStatus(HC_NONE),
- _statusChange(0),
- _currentHandler(0),
- _firstHandler(0),
- _lastHandler(0),
- _currentArgCount(0),
- _currentArgs(0),
- _headerKeysCount(0),
- _currentHeaders(0),
- _contentLength(0),
- _chunked(false) {}
- WebServer::~WebServer() {
- if (_currentHeaders) delete[] _currentHeaders;
- _headerKeysCount = 0;
- RequestHandler* handler = _firstHandler;
- while (handler) {
- RequestHandler* next = handler->next();
- delete handler;
- handler = next;
- }
- close();
- }
- void WebServer::begin() {
- _currentStatus = HC_NONE;
- _server.begin();
- if (!_headerKeysCount) collectHeaders(0, 0);
- }
- bool WebServer::authenticate(const char* username, const char* password) {
- if (hasHeader(AUTHORIZATION_HEADER)) {
- String authReq = header(AUTHORIZATION_HEADER);
- if (authReq.startsWith("Basic")) {
- authReq = authReq.substring(6);
- authReq.trim();
- char toencodeLen = strlen(username) + strlen(password) + 1;
- char* toencode = new char[toencodeLen + 1];
- if (toencode == NULL) {
- authReq = String();
- return false;
- }
- char* encoded =
- new char[base64_encode_expected_len(toencodeLen) + 1];
- if (encoded == NULL) {
- authReq = String();
- delete[] toencode;
- return false;
- }
- sprintf(toencode, "%s:%s", username, password);
- if (base64_encode_chars(toencode, toencodeLen, encoded) > 0 &&
- authReq.equals(encoded)) {
- authReq = String();
- delete[] toencode;
- delete[] encoded;
- return true;
- }
- delete[] toencode;
- delete[] encoded;
- }
- authReq = String();
- }
- return false;
- }
- void WebServer::requestAuthentication() {
- sendHeader("WWW-Authenticate", "Basic realm=\"Login Required\"");
- send(401);
- }
- void WebServer::on(const String& uri, WebServer::THandlerFunction handler) {
- on(uri, HTTP_ANY, handler);
- }
- void WebServer::on(const String& uri, HTTPMethod method,
- WebServer::THandlerFunction fn) {
- on(uri, method, fn, _fileUploadHandler);
- }
- void WebServer::on(const String& uri, HTTPMethod method,
- WebServer::THandlerFunction fn,
- WebServer::THandlerFunction ufn) {
- _addRequestHandler(new FunctionRequestHandler(fn, ufn, uri, method));
- }
- void WebServer::addHandler(RequestHandler* handler) {
- _addRequestHandler(handler);
- }
- void WebServer::_addRequestHandler(RequestHandler* handler) {
- if (!_lastHandler) {
- _firstHandler = handler;
- _lastHandler = handler;
- } else {
- _lastHandler->next(handler);
- _lastHandler = handler;
- }
- }
- void WebServer::serveStatic(const char* uri, FS& fs, const char* path,
- const char* cache_header) {
- _addRequestHandler(new StaticRequestHandler(fs, path, uri, cache_header));
- }
- void WebServer::handleClient() {
- if (_currentStatus == HC_NONE) {
- WiFiClient client = _server.available();
- if (!client) {
- return;
- }
- #ifdef DEBUG_ESP_HTTP_SERVER
- DEBUG_OUTPUT.println("New client");
- #endif
- _currentClient = client;
- _currentStatus = HC_WAIT_READ;
- _statusChange = millis();
- }
- if (!_currentClient.connected()) {
- _currentClient = WiFiClient();
- _currentStatus = HC_NONE;
- return;
- }
- // Wait for data from client to become available
- if (_currentStatus == HC_WAIT_READ) {
- if (!_currentClient.available()) {
- if (millis() - _statusChange > HTTP_MAX_DATA_WAIT) {
- _currentClient = WiFiClient();
- _currentStatus = HC_NONE;
- }
- yield();
- return;
- }
- if (!_parseRequest(_currentClient)) {
- _currentClient = WiFiClient();
- _currentStatus = HC_NONE;
- return;
- }
- _currentClient.setTimeout(HTTP_MAX_SEND_WAIT);
- _contentLength = CONTENT_LENGTH_NOT_SET;
- _handleRequest();
- if (!_currentClient.connected()) {
- _currentClient = WiFiClient();
- _currentStatus = HC_NONE;
- return;
- } else {
- _currentStatus = HC_WAIT_CLOSE;
- _statusChange = millis();
- return;
- }
- }
- if (_currentStatus == HC_WAIT_CLOSE) {
- if (millis() - _statusChange > HTTP_MAX_CLOSE_WAIT) {
- _currentClient = WiFiClient();
- _currentStatus = HC_NONE;
- } else {
- yield();
- return;
- }
- }
- }
- void WebServer::close() { _server.end(); }
- void WebServer::stop() { close(); }
- void WebServer::sendHeader(const String& name, const String& value,
- bool first) {
- String headerLine = name;
- headerLine += ": ";
- headerLine += value;
- headerLine += "\r\n";
- if (first) {
- _responseHeaders = headerLine + _responseHeaders;
- } else {
- _responseHeaders += headerLine;
- }
- }
- void WebServer::setContentLength(size_t contentLength) {
- _contentLength = contentLength;
- }
- void WebServer::_prepareHeader(String& response, int code,
- const char* content_type, size_t contentLength) {
- response = "HTTP/1." + String(_currentVersion) + " ";
- response += String(code);
- response += " ";
- response += _responseCodeToString(code);
- response += "\r\n";
- if (!content_type) content_type = "text/html";
- sendHeader("Content-Type", content_type, true);
- if (_contentLength == CONTENT_LENGTH_NOT_SET) {
- sendHeader("Content-Length", String(contentLength));
- } else if (_contentLength != CONTENT_LENGTH_UNKNOWN) {
- sendHeader("Content-Length", String(_contentLength));
- } else if (_contentLength == CONTENT_LENGTH_UNKNOWN &&
- _currentVersion) { // HTTP/1.1 or above client
- // let's do chunked
- _chunked = true;
- sendHeader("Accept-Ranges", "none");
- sendHeader("Transfer-Encoding", "chunked");
- }
- sendHeader("Connection", "close");
- response += _responseHeaders;
- response += "\r\n";
- _responseHeaders = String();
- }
- void WebServer::send(int code, const char* content_type,
- const String& content) {
- String header;
- // Can we asume the following?
- // if(code == 200 && content.length() == 0 && _contentLength ==
- // CONTENT_LENGTH_NOT_SET)
- // _contentLength = CONTENT_LENGTH_UNKNOWN;
- _prepareHeader(header, code, content_type, content.length());
- _currentClient.write(header.c_str(), header.length());
- if (content.length()) sendContent(content);
- }
- void WebServer::send_P(int code, PGM_P content_type, PGM_P content) {
- size_t contentLength = 0;
- if (content != NULL) {
- contentLength = strlen_P(content);
- }
- String header;
- char type[64];
- memccpy_P((void*)type, (PGM_VOID_P)content_type, 0, sizeof(type));
- _prepareHeader(header, code, (const char*)type, contentLength);
- _currentClient.write(header.c_str(), header.length());
- sendContent_P(content);
- }
- void WebServer::send_P(int code, PGM_P content_type, PGM_P content,
- size_t contentLength) {
- String header;
- char type[64];
- memccpy_P((void*)type, (PGM_VOID_P)content_type, 0, sizeof(type));
- _prepareHeader(header, code, (const char*)type, contentLength);
- sendContent(header);
- sendContent_P(content, contentLength);
- }
- void WebServer::send(int code, char* content_type, const String& content) {
- send(code, (const char*)content_type, content);
- }
- void WebServer::send(int code, const String& content_type,
- const String& content) {
- send(code, (const char*)content_type.c_str(), content);
- }
- void WebServer::sendContent(const String& content) {
- const char* footer = "\r\n";
- size_t len = content.length();
- if (_chunked) {
- char* chunkSize = (char*)malloc(11);
- if (chunkSize) {
- sprintf(chunkSize, "%x%s", len, footer);
- _currentClient.write(chunkSize, strlen(chunkSize));
- free(chunkSize);
- }
- }
- _currentClient.write(content.c_str(), len);
- if (_chunked) {
- _currentClient.write(footer, 2);
- }
- }
- void WebServer::sendContent_P(PGM_P content) {
- sendContent_P(content, strlen_P(content));
- }
- void WebServer::sendContent_P(PGM_P content, size_t size) {
- const char* footer = "\r\n";
- if (_chunked) {
- char* chunkSize = (char*)malloc(11);
- if (chunkSize) {
- sprintf(chunkSize, "%x%s", size, footer);
- _currentClient.write(chunkSize, strlen(chunkSize));
- free(chunkSize);
- }
- }
- _currentClient.write(content, size);
- if (_chunked) {
- _currentClient.write(footer, 2);
- }
- }
- String WebServer::arg(String name) {
- for (int i = 0; i < _currentArgCount; ++i) {
- if (_currentArgs[i].key == name) return _currentArgs[i].value;
- }
- return String();
- }
- String WebServer::arg(int i) {
- if (i < _currentArgCount) return _currentArgs[i].value;
- return String();
- }
- String WebServer::argName(int i) {
- if (i < _currentArgCount) return _currentArgs[i].key;
- return String();
- }
- int WebServer::args() { return _currentArgCount; }
- bool WebServer::hasArg(String name) {
- for (int i = 0; i < _currentArgCount; ++i) {
- if (_currentArgs[i].key == name) return true;
- }
- return false;
- }
- String WebServer::header(String name) {
- for (int i = 0; i < _headerKeysCount; ++i) {
- if (_currentHeaders[i].key.equalsIgnoreCase(name))
- return _currentHeaders[i].value;
- }
- return String();
- }
- void WebServer::collectHeaders(const char* headerKeys[],
- const size_t headerKeysCount) {
- _headerKeysCount = headerKeysCount + 1;
- if (_currentHeaders) delete[] _currentHeaders;
- _currentHeaders = new RequestArgument[_headerKeysCount];
- _currentHeaders[0].key = AUTHORIZATION_HEADER;
- for (int i = 1; i < _headerKeysCount; i++) {
- _currentHeaders[i].key = headerKeys[i - 1];
- }
- }
- String WebServer::header(int i) {
- if (i < _headerKeysCount) return _currentHeaders[i].value;
- return String();
- }
- String WebServer::headerName(int i) {
- if (i < _headerKeysCount) return _currentHeaders[i].key;
- return String();
- }
- int WebServer::headers() { return _headerKeysCount; }
- bool WebServer::hasHeader(String name) {
- for (int i = 0; i < _headerKeysCount; ++i) {
- if ((_currentHeaders[i].key.equalsIgnoreCase(name)) &&
- (_currentHeaders[i].value.length() > 0))
- return true;
- }
- return false;
- }
- String WebServer::hostHeader() { return _hostHeader; }
- void WebServer::onFileUpload(THandlerFunction fn) { _fileUploadHandler = fn; }
- void WebServer::onNotFound(THandlerFunction fn) { _notFoundHandler = fn; }
- void WebServer::_handleRequest() {
- bool handled = false;
- if (!_currentHandler) {
- #ifdef DEBUG_ESP_HTTP_SERVER
- DEBUG_OUTPUT.println("request handler not found");
- #endif
- } else {
- handled = _currentHandler->handle(*this, _currentMethod, _currentUri);
- #ifdef DEBUG_ESP_HTTP_SERVER
- if (!handled) {
- DEBUG_OUTPUT.println("request handler failed to handle request");
- }
- #endif
- }
- if (!handled) {
- if (_notFoundHandler) {
- _notFoundHandler();
- } else {
- send(404, "text/plain", String("Not found: ") + _currentUri);
- }
- }
- _currentUri = String();
- }
- String WebServer::_responseCodeToString(int code) {
- switch (code) {
- case 100:
- return F("Continue");
- case 101:
- return F("Switching Protocols");
- case 200:
- return F("OK");
- case 201:
- return F("Created");
- case 202:
- return F("Accepted");
- case 203:
- return F("Non-Authoritative Information");
- case 204:
- return F("No Content");
- case 205:
- return F("Reset Content");
- case 206:
- return F("Partial Content");
- case 300:
- return F("Multiple Choices");
- case 301:
- return F("Moved Permanently");
- case 302:
- return F("Found");
- case 303:
- return F("See Other");
- case 304:
- return F("Not Modified");
- case 305:
- return F("Use Proxy");
- case 307:
- return F("Temporary Redirect");
- case 400:
- return F("Bad Request");
- case 401:
- return F("Unauthorized");
- case 402:
- return F("Payment Required");
- case 403:
- return F("Forbidden");
- case 404:
- return F("Not Found");
- case 405:
- return F("Method Not Allowed");
- case 406:
- return F("Not Acceptable");
- case 407:
- return F("Proxy Authentication Required");
- case 408:
- return F("Request Time-out");
- case 409:
- return F("Conflict");
- case 410:
- return F("Gone");
- case 411:
- return F("Length Required");
- case 412:
- return F("Precondition Failed");
- case 413:
- return F("Request Entity Too Large");
- case 414:
- return F("Request-URI Too Large");
- case 415:
- return F("Unsupported Media Type");
- case 416:
- return F("Requested range not satisfiable");
- case 417:
- return F("Expectation Failed");
- case 500:
- return F("Internal Server Error");
- case 501:
- return F("Not Implemented");
- case 502:
- return F("Bad Gateway");
- case 503:
- return F("Service Unavailable");
- case 504:
- return F("Gateway Time-out");
- case 505:
- return F("HTTP Version not supported");
- default:
- return "";
- }
- }
|