From 119d2db30b5d9cfab3946d3435473242859ef9fb Mon Sep 17 00:00:00 2001 From: karan6190 Date: Thu, 28 Jun 2018 10:33:15 +0530 Subject: [PATCH 1/3] added OTAWebUpdater --- .../examples/OTAWebUpdater/OTAWebUpdater.ino | 160 +++++ libraries/ArduinoOTA/src/ESP32WebServer.cpp | 537 ++++++++++++++++ libraries/ArduinoOTA/src/ESP32WebServer.h | 192 ++++++ libraries/ArduinoOTA/src/Parsing.cpp | 607 ++++++++++++++++++ 4 files changed, 1496 insertions(+) create mode 100644 libraries/ArduinoOTA/examples/OTAWebUpdater/OTAWebUpdater.ino create mode 100644 libraries/ArduinoOTA/src/ESP32WebServer.cpp create mode 100644 libraries/ArduinoOTA/src/ESP32WebServer.h create mode 100644 libraries/ArduinoOTA/src/Parsing.cpp diff --git a/libraries/ArduinoOTA/examples/OTAWebUpdater/OTAWebUpdater.ino b/libraries/ArduinoOTA/examples/OTAWebUpdater/OTAWebUpdater.ino new file mode 100644 index 00000000000..5358bfc33ce --- /dev/null +++ b/libraries/ArduinoOTA/examples/OTAWebUpdater/OTAWebUpdater.ino @@ -0,0 +1,160 @@ +#include +#include +#include +#include +#include + +const char* host = "ESP32"; +const char* ssid = "xxx"; +const char* password = "xxxx"; + +ESP32WebServer server(80); + +const char* loginIndex = "
" + "" + "" + "" + "
" + "
" + "" + "" + "" + "" + "
" + "
" + "" + "" + "" + "
" + "
" + "" + + "" + "" + "" + "
ESP32 Login Page
" + "
" + + "
Username:
Password:
" + "
" + ""; + + + + + +const char* serverIndex = "" + "
" + "" + "" + "
" + "
progress: 0%
" + ""; +void setup(void) { + Serial.begin(115200); + + // Connect to WiFi network + WiFi.begin(ssid, password); + Serial.println(""); + + // Wait for connection + while (WiFi.status() != WL_CONNECTED) { + delay(500); + Serial.print("."); + } + Serial.println(""); + Serial.print("Connected to "); + Serial.println(ssid); + Serial.print("IP address: "); + Serial.println(WiFi.localIP()); + + /*use mdns for host name resolution*/ + if (!MDNS.begin(host)) { //http://esp32.local + Serial.println("Error setting up MDNS responder!"); + while (1) { + delay(1000); + } + } + Serial.println("mDNS responder started"); + /*return index page which is stored in serverIndex */ + server.on("/", HTTP_GET, []() { + server.sendHeader("Connection", "close"); + server.send(200, "text/html", loginIndex); + }); + server.on("/serverIndex", HTTP_GET, []() { + server.sendHeader("Connection", "close"); + server.send(200, "text/html", serverIndex); + }); + /*handling uploading firmware file */ + server.on("/update", HTTP_POST, []() { + server.sendHeader("Connection", "close"); + server.send(200, "text/plain", (Update.hasError()) ? "FAIL" : "OK"); + ESP.restart(); + }, []() { + HTTPUpload& upload = server.upload(); + if (upload.status == UPLOAD_FILE_START) { + Serial.printf("Update: %s\n", upload.filename.c_str()); + if (!Update.begin(UPDATE_SIZE_UNKNOWN)) { //start with max available size + Update.printError(Serial); + } + } else if (upload.status == UPLOAD_FILE_WRITE) { + /* flashing firmware to ESP*/ + if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) { + Update.printError(Serial); + } + } else if (upload.status == UPLOAD_FILE_END) { + if (Update.end(true)) { //true to set the size to the current progress + Serial.printf("Update Success: %u\nRebooting...\n", upload.totalSize); + } else { + Update.printError(Serial); + } + } + }); + server.begin(); +} + +void loop(void) { + server.handleClient(); + delay(1); +} diff --git a/libraries/ArduinoOTA/src/ESP32WebServer.cpp b/libraries/ArduinoOTA/src/ESP32WebServer.cpp new file mode 100644 index 00000000000..81bd5cac968 --- /dev/null +++ b/libraries/ArduinoOTA/src/ESP32WebServer.cpp @@ -0,0 +1,537 @@ +/* + ESP32WebServer.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 +#include +#include "WiFiServer.h" +#include "WiFiClient.h" +#include "ESP32WebServer.h" +#include "FS.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"; + +ESP32WebServer::ESP32WebServer(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) +{ +} + +ESP32WebServer::ESP32WebServer(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) +{ +} + +ESP32WebServer::~ESP32WebServer() { + if (_currentHeaders) + delete[]_currentHeaders; + _headerKeysCount = 0; + RequestHandler* handler = _firstHandler; + while (handler) { + RequestHandler* next = handler->next(); + delete handler; + handler = next; + } + close(); +} + +void ESP32WebServer::begin() { + _currentStatus = HC_NONE; + _server.begin(); + if(!_headerKeysCount) + collectHeaders(0, 0); +} + +bool ESP32WebServer::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 ESP32WebServer::requestAuthentication(){ + sendHeader("WWW-Authenticate", "Basic realm=\"Login Required\""); + send(401); +} + +void ESP32WebServer::on(const String &uri, ESP32WebServer::THandlerFunction handler) { + on(uri, HTTP_ANY, handler); +} + +void ESP32WebServer::on(const String &uri, HTTPMethod method, ESP32WebServer::THandlerFunction fn) { + on(uri, method, fn, _fileUploadHandler); +} + +void ESP32WebServer::on(const String &uri, HTTPMethod method, ESP32WebServer::THandlerFunction fn, ESP32WebServer::THandlerFunction ufn) { + _addRequestHandler(new FunctionRequestHandler(fn, ufn, uri, method)); +} + +void ESP32WebServer::addHandler(RequestHandler* handler) { + _addRequestHandler(handler); +} + +void ESP32WebServer::_addRequestHandler(RequestHandler* handler) { + if (!_lastHandler) { + _firstHandler = handler; + _lastHandler = handler; + } + else { + _lastHandler->next(handler); + _lastHandler = handler; + } +} + +void ESP32WebServer::serveStatic(const char* uri, FS& fs, const char* path, const char* cache_header) { + _addRequestHandler(new StaticRequestHandler(fs, path, uri, cache_header)); +} + +void ESP32WebServer::handleClient() { + if (_currentStatus == HC_NONE) { + WiFiClient client = _server.available(); + if (!client) { + return; + } + +#ifdef DEBUG_ESP_HTTP_SERVER + log_e("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 ESP32WebServer::close() { + _server.end(); +} + +void ESP32WebServer::stop() { + close(); +} + +void ESP32WebServer::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 ESP32WebServer::setContentLength(size_t contentLength) { + _contentLength = contentLength; +} + +void ESP32WebServer::_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 ESP32WebServer::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 ESP32WebServer::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 ESP32WebServer::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 ESP32WebServer::send(int code, char* content_type, const String& content) { + send(code, (const char*)content_type, content); +} + +void ESP32WebServer::send(int code, const String& content_type, const String& content) { + send(code, (const char*)content_type.c_str(), content); +} + +void ESP32WebServer::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 ESP32WebServer::sendContent_P(PGM_P content) { + sendContent_P(content, strlen_P(content)); +} + +void ESP32WebServer::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); + } + } + size_t chunkSize = 20; + int idx = 0; + while(size != 0){ + if(size < chunkSize){ + _currentClient.write(&content[idx], size); + size = 0; + } else { + _currentClient.write(&content[idx], chunkSize); + size -= chunkSize; + idx += chunkSize; + } + yield(); + } + if(_chunked){ + _currentClient.write(footer, 2); + } +} + + +String ESP32WebServer::arg(String name) { + for (int i = 0; i < _currentArgCount; ++i) { + if ( _currentArgs[i].key == name ) + return _currentArgs[i].value; + } + return String(); +} + +String ESP32WebServer::arg(int i) { + if (i < _currentArgCount) + return _currentArgs[i].value; + return String(); +} + +String ESP32WebServer::argName(int i) { + if (i < _currentArgCount) + return _currentArgs[i].key; + return String(); +} + +int ESP32WebServer::args() { + return _currentArgCount; +} + +bool ESP32WebServer::hasArg(String name) { + for (int i = 0; i < _currentArgCount; ++i) { + if (_currentArgs[i].key == name) + return true; + } + return false; +} + + +String ESP32WebServer::header(String name) { + for (int i = 0; i < _headerKeysCount; ++i) { + if (_currentHeaders[i].key.equalsIgnoreCase(name)) + return _currentHeaders[i].value; + } + return String(); +} + +void ESP32WebServer::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 ESP32WebServer::header(int i) { + if (i < _headerKeysCount) + return _currentHeaders[i].value; + return String(); +} + +String ESP32WebServer::headerName(int i) { + if (i < _headerKeysCount) + return _currentHeaders[i].key; + return String(); +} + +int ESP32WebServer::headers() { + return _headerKeysCount; +} + +bool ESP32WebServer::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 ESP32WebServer::hostHeader() { + return _hostHeader; +} + +void ESP32WebServer::onFileUpload(THandlerFunction fn) { + _fileUploadHandler = fn; +} + +void ESP32WebServer::onNotFound(THandlerFunction fn) { + _notFoundHandler = fn; +} + +void ESP32WebServer::_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 ESP32WebServer::_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 ""; + } +} diff --git a/libraries/ArduinoOTA/src/ESP32WebServer.h b/libraries/ArduinoOTA/src/ESP32WebServer.h new file mode 100644 index 00000000000..2401a35eafc --- /dev/null +++ b/libraries/ArduinoOTA/src/ESP32WebServer.h @@ -0,0 +1,192 @@ +/* + ESP32WebServer.h - 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) +*/ + + +#ifndef ESP32WEBSERVER_H +#define ESP32WEBSERVER_H + +#include +#include + +enum HTTPMethod { HTTP_ANY, HTTP_GET, HTTP_POST, HTTP_PUT, HTTP_PATCH, HTTP_DELETE, HTTP_OPTIONS }; +enum HTTPUploadStatus { UPLOAD_FILE_START, UPLOAD_FILE_WRITE, UPLOAD_FILE_END, + UPLOAD_FILE_ABORTED }; +enum HTTPClientStatus { HC_NONE, HC_WAIT_READ, HC_WAIT_CLOSE }; + +#define HTTP_DOWNLOAD_UNIT_SIZE 1460 +#define HTTP_UPLOAD_BUFLEN 2048 +#define HTTP_MAX_DATA_WAIT 1000 //ms to wait for the client to send the request +#define HTTP_MAX_POST_WAIT 1000 //ms to wait for POST data to arrive +#define HTTP_MAX_SEND_WAIT 5000 //ms to wait for data chunk to be ACKed +#define HTTP_MAX_CLOSE_WAIT 2000 //ms to wait for the client to close the connection + +#define CONTENT_LENGTH_UNKNOWN ((size_t) -1) +#define CONTENT_LENGTH_NOT_SET ((size_t) -2) + +class ESP32WebServer; + +typedef struct { + HTTPUploadStatus status; + String filename; + String name; + String type; + size_t totalSize; // file size + size_t currentSize; // size of data currently in buf + uint8_t buf[HTTP_UPLOAD_BUFLEN]; +} HTTPUpload; + +#include "detail/RequestHandler.h" + +namespace fs { +class FS; +} + +class ESP32WebServer +{ +public: + ESP32WebServer(IPAddress addr, int port = 80); + ESP32WebServer(int port = 80); + ~ESP32WebServer(); + + void begin(); + void handleClient(); + + void close(); + void stop(); + + bool authenticate(const char * username, const char * password); + void requestAuthentication(); + + typedef std::function THandlerFunction; + void on(const String &uri, THandlerFunction handler); + void on(const String &uri, HTTPMethod method, THandlerFunction fn); + void on(const String &uri, HTTPMethod method, THandlerFunction fn, THandlerFunction ufn); + void addHandler(RequestHandler* handler); + void serveStatic(const char* uri, fs::FS& fs, const char* path, const char* cache_header = NULL ); + void onNotFound(THandlerFunction fn); //called when handler is not assigned + void onFileUpload(THandlerFunction fn); //handle file uploads + + String uri() { return _currentUri; } + HTTPMethod method() { return _currentMethod; } + WiFiClient client() { return _currentClient; } + HTTPUpload& upload() { return _currentUpload; } + + String arg(String name); // get request argument value by name + String arg(int i); // get request argument value by number + String argName(int i); // get request argument name by number + int args(); // get arguments count + bool hasArg(String name); // check if argument exists + void collectHeaders(const char* headerKeys[], const size_t headerKeysCount); // set the request headers to collect + String header(String name); // get request header value by name + String header(int i); // get request header value by number + String headerName(int i); // get request header name by number + int headers(); // get header count + bool hasHeader(String name); // check if header exists + + String hostHeader(); // get request host header if available or empty String if not + + // send response to the client + // code - HTTP response code, can be 200 or 404 + // content_type - HTTP content type, like "text/plain" or "image/png" + // content - actual content body + void send(int code, const char* content_type = NULL, const String& content = String("")); + void send(int code, char* content_type, const String& content); + void send(int code, const String& content_type, const String& content); + void send_P(int code, PGM_P content_type, PGM_P content); + void send_P(int code, PGM_P content_type, PGM_P content, size_t contentLength); + + void setContentLength(size_t contentLength); + void sendHeader(const String& name, const String& value, bool first = false); + void sendContent(const String& content); + void sendContent_P(PGM_P content); + void sendContent_P(PGM_P content, size_t size); + + static String urlDecode(const String& text); + +template size_t streamFile(T &file, const String& contentType){ + setContentLength(file.size()); + if (String(file.name()).endsWith(".gz") && + contentType != "application/x-gzip" && + contentType != "application/octet-stream"){ + sendHeader("Content-Encoding", "gzip"); + } + send(200, contentType, ""); + uint8_t buf[20]; + size_t fsize = 0; + while(file.available()){ + int got = file.read(buf, 20); + _currentClient.write(buf, got); + fsize += got; + yield(); + } + return fsize; +} + +protected: + void _addRequestHandler(RequestHandler* handler); + void _handleRequest(); + bool _parseRequest(WiFiClient& client); + void _parseArguments(String data); + static String _responseCodeToString(int code); + bool _parseForm(WiFiClient& client, String boundary, uint32_t len); + bool _parseFormUploadAborted(); + void _uploadWriteByte(uint8_t b); + uint8_t _uploadReadByte(WiFiClient& client); + void _prepareHeader(String& response, int code, const char* content_type, size_t contentLength); + bool _collectHeader(const char* headerName, const char* headerValue); + + struct RequestArgument { + String key; + String value; + }; + + WiFiServer _server; + + WiFiClient _currentClient; + HTTPMethod _currentMethod; + String _currentUri; + uint8_t _currentVersion; + HTTPClientStatus _currentStatus; + unsigned long _statusChange; + + RequestHandler* _currentHandler; + RequestHandler* _firstHandler; + RequestHandler* _lastHandler; + THandlerFunction _notFoundHandler; + THandlerFunction _fileUploadHandler; + + int _currentArgCount; + RequestArgument* _currentArgs; + HTTPUpload _currentUpload; + + int _headerKeysCount; + RequestArgument* _currentHeaders; + size_t _contentLength; + String _responseHeaders; + + String _hostHeader; + bool _chunked; + +}; + + +#endif //ESP32WEBSERVER_H diff --git a/libraries/ArduinoOTA/src/Parsing.cpp b/libraries/ArduinoOTA/src/Parsing.cpp new file mode 100644 index 00000000000..45094944f08 --- /dev/null +++ b/libraries/ArduinoOTA/src/Parsing.cpp @@ -0,0 +1,607 @@ +/* + Parsing.cpp - HTTP request parsing. + + Copyright (c) 2015 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 +#include "WiFiServer.h" +#include "WiFiClient.h" +#include "ESP32WebServer.h" + +//#define DEBUG_ESP_HTTP_SERVER +#ifdef DEBUG_ESP_PORT +#define DEBUG_OUTPUT DEBUG_ESP_PORT +#else +#define DEBUG_OUTPUT Serial +#endif + +static char* readBytesWithTimeout(WiFiClient& client, size_t maxLength, size_t& dataLength, int timeout_ms) +{ + char *buf = nullptr; + dataLength = 0; + while (dataLength < maxLength) { + int tries = timeout_ms; + size_t newLength; + while (!(newLength = client.available()) && tries--) delay(1); + if (!newLength) { + break; + } + if (!buf) { + buf = (char *) malloc(newLength + 1); + if (!buf) { + return nullptr; + } + } + else { + char* newBuf = (char *) realloc(buf, dataLength + newLength + 1); + if (!newBuf) { + free(buf); + return nullptr; + } + buf = newBuf; + } + client.readBytes(buf + dataLength, newLength); + dataLength += newLength; + buf[dataLength] = '\0'; + } + return buf; +} + +bool ESP32WebServer::_parseRequest(WiFiClient& client) { + // Read the first line of HTTP request + String req = client.readStringUntil('\r'); + client.readStringUntil('\n'); + //reset header value + for (int i = 0; i < _headerKeysCount; ++i) { + _currentHeaders[i].value =String(); + } + + // First line of HTTP request looks like "GET /path HTTP/1.1" + // Retrieve the "/path" part by finding the spaces + int addr_start = req.indexOf(' '); + int addr_end = req.indexOf(' ', addr_start + 1); + if (addr_start == -1 || addr_end == -1) { +#ifdef DEBUG_ESP_HTTP_SERVER + DEBUG_OUTPUT.print("Invalid request: "); + DEBUG_OUTPUT.println(req); +#endif + return false; + } + + String methodStr = req.substring(0, addr_start); + String url = req.substring(addr_start + 1, addr_end); + String versionEnd = req.substring(addr_end + 8); + _currentVersion = atoi(versionEnd.c_str()); + String searchStr = ""; + int hasSearch = url.indexOf('?'); + if (hasSearch != -1){ + searchStr = urlDecode(url.substring(hasSearch + 1)); + url = url.substring(0, hasSearch); + } + _currentUri = url; + _chunked = false; + + HTTPMethod method = HTTP_GET; + if (methodStr == "POST") { + method = HTTP_POST; + } else if (methodStr == "DELETE") { + method = HTTP_DELETE; + } else if (methodStr == "OPTIONS") { + method = HTTP_OPTIONS; + } else if (methodStr == "PUT") { + method = HTTP_PUT; + } else if (methodStr == "PATCH") { + method = HTTP_PATCH; + } + _currentMethod = method; + +#ifdef DEBUG_ESP_HTTP_SERVER + DEBUG_OUTPUT.print("method: "); + DEBUG_OUTPUT.print(methodStr); + DEBUG_OUTPUT.print(" url: "); + DEBUG_OUTPUT.print(url); + DEBUG_OUTPUT.print(" search: "); + DEBUG_OUTPUT.println(searchStr); +#endif + + //attach handler + RequestHandler* handler; + for (handler = _firstHandler; handler; handler = handler->next()) { + if (handler->canHandle(_currentMethod, _currentUri)) + break; + } + _currentHandler = handler; + + String formData; + // below is needed only when POST type request + if (method == HTTP_POST || method == HTTP_PUT || method == HTTP_PATCH || method == HTTP_DELETE){ + String boundaryStr; + String headerName; + String headerValue; + bool isForm = false; + bool isEncoded = false; + uint32_t contentLength = 0; + //parse headers + while(1){ + req = client.readStringUntil('\r'); + client.readStringUntil('\n'); + if (req == "") break;//no moar headers + int headerDiv = req.indexOf(':'); + if (headerDiv == -1){ + break; + } + headerName = req.substring(0, headerDiv); + headerValue = req.substring(headerDiv + 1); + headerValue.trim(); + _collectHeader(headerName.c_str(),headerValue.c_str()); + + #ifdef DEBUG_ESP_HTTP_SERVER + DEBUG_OUTPUT.print("headerName: "); + DEBUG_OUTPUT.println(headerName); + DEBUG_OUTPUT.print("headerValue: "); + DEBUG_OUTPUT.println(headerValue); + #endif + + if (headerName.equalsIgnoreCase("Content-Type")){ + if (headerValue.startsWith("text/plain")){ + isForm = false; + } else if (headerValue.startsWith("application/x-www-form-urlencoded")){ + isForm = false; + isEncoded = true; + } else if (headerValue.startsWith("multipart/")){ + boundaryStr = headerValue.substring(headerValue.indexOf('=')+1); + isForm = true; + } + } else if (headerName.equalsIgnoreCase("Content-Length")){ + contentLength = headerValue.toInt(); + } else if (headerName.equalsIgnoreCase("Host")){ + _hostHeader = headerValue; + } + } + + if (!isForm){ + size_t plainLength; + char* plainBuf = readBytesWithTimeout(client, contentLength, plainLength, HTTP_MAX_POST_WAIT); + if (plainLength < contentLength) { + free(plainBuf); + return false; + } + if (contentLength > 0) { + if (searchStr != "") searchStr += '&'; + if(isEncoded){ + //url encoded form + String decoded = urlDecode(plainBuf); + size_t decodedLen = decoded.length(); + memcpy(plainBuf, decoded.c_str(), decodedLen); + plainBuf[decodedLen] = 0; + searchStr += plainBuf; + } + _parseArguments(searchStr); + if(!isEncoded){ + //plain post json or other data + RequestArgument& arg = _currentArgs[_currentArgCount++]; + arg.key = "plain"; + arg.value = String(plainBuf); + } + + #ifdef DEBUG_ESP_HTTP_SERVER + DEBUG_OUTPUT.print("Plain: "); + DEBUG_OUTPUT.println(plainBuf); + #endif + free(plainBuf); + } + } + + if (isForm){ + _parseArguments(searchStr); + if (!_parseForm(client, boundaryStr, contentLength)) { + return false; + } + } + } else { + String headerName; + String headerValue; + //parse headers + while(1){ + req = client.readStringUntil('\r'); + client.readStringUntil('\n'); + if (req == "") break;//no moar headers + int headerDiv = req.indexOf(':'); + if (headerDiv == -1){ + break; + } + headerName = req.substring(0, headerDiv); + headerValue = req.substring(headerDiv + 2); + _collectHeader(headerName.c_str(),headerValue.c_str()); + + #ifdef DEBUG_ESP_HTTP_SERVER + DEBUG_OUTPUT.print("headerName: "); + DEBUG_OUTPUT.println(headerName); + DEBUG_OUTPUT.print("headerValue: "); + DEBUG_OUTPUT.println(headerValue); + #endif + + if (headerName.equalsIgnoreCase("Host")){ + _hostHeader = headerValue; + } + } + _parseArguments(searchStr); + } + client.flush(); + +#ifdef DEBUG_ESP_HTTP_SERVER + DEBUG_OUTPUT.print("Request: "); + DEBUG_OUTPUT.println(url); + DEBUG_OUTPUT.print(" Arguments: "); + DEBUG_OUTPUT.println(searchStr); +#endif + + return true; +} + +bool ESP32WebServer::_collectHeader(const char* headerName, const char* headerValue) { + for (int i = 0; i < _headerKeysCount; i++) { + if (_currentHeaders[i].key.equalsIgnoreCase(headerName)) { + _currentHeaders[i].value=headerValue; + return true; + } + } + return false; +} + +void ESP32WebServer::_parseArguments(String data) { +#ifdef DEBUG_ESP_HTTP_SERVER + DEBUG_OUTPUT.print("args: "); + DEBUG_OUTPUT.println(data); +#endif + if (_currentArgs) + delete[] _currentArgs; + _currentArgs = 0; + if (data.length() == 0) { + _currentArgCount = 0; + _currentArgs = new RequestArgument[1]; + return; + } + _currentArgCount = 1; + + for (int i = 0; i < (int)data.length(); ) { + i = data.indexOf('&', i); + if (i == -1) + break; + ++i; + ++_currentArgCount; + } +#ifdef DEBUG_ESP_HTTP_SERVER + DEBUG_OUTPUT.print("args count: "); + DEBUG_OUTPUT.println(_currentArgCount); +#endif + + _currentArgs = new RequestArgument[_currentArgCount+1]; + int pos = 0; + int iarg; + for (iarg = 0; iarg < _currentArgCount;) { + int equal_sign_index = data.indexOf('=', pos); + int next_arg_index = data.indexOf('&', pos); +#ifdef DEBUG_ESP_HTTP_SERVER + DEBUG_OUTPUT.print("pos "); + DEBUG_OUTPUT.print(pos); + DEBUG_OUTPUT.print("=@ "); + DEBUG_OUTPUT.print(equal_sign_index); + DEBUG_OUTPUT.print(" &@ "); + DEBUG_OUTPUT.println(next_arg_index); +#endif + if ((equal_sign_index == -1) || ((equal_sign_index > next_arg_index) && (next_arg_index != -1))) { +#ifdef DEBUG_ESP_HTTP_SERVER + DEBUG_OUTPUT.print("arg missing value: "); + DEBUG_OUTPUT.println(iarg); +#endif + if (next_arg_index == -1) + break; + pos = next_arg_index + 1; + continue; + } + RequestArgument& arg = _currentArgs[iarg]; + arg.key = data.substring(pos, equal_sign_index); + arg.value = data.substring(equal_sign_index + 1, next_arg_index); +#ifdef DEBUG_ESP_HTTP_SERVER + DEBUG_OUTPUT.print("arg "); + DEBUG_OUTPUT.print(iarg); + DEBUG_OUTPUT.print(" key: "); + DEBUG_OUTPUT.print(arg.key); + DEBUG_OUTPUT.print(" value: "); + DEBUG_OUTPUT.println(arg.value); +#endif + ++iarg; + if (next_arg_index == -1) + break; + pos = next_arg_index + 1; + } + _currentArgCount = iarg; +#ifdef DEBUG_ESP_HTTP_SERVER + DEBUG_OUTPUT.print("args count: "); + DEBUG_OUTPUT.println(_currentArgCount); +#endif + +} + +void ESP32WebServer::_uploadWriteByte(uint8_t b){ + if (_currentUpload.currentSize == HTTP_UPLOAD_BUFLEN){ + if(_currentHandler && _currentHandler->canUpload(_currentUri)) + _currentHandler->upload(*this, _currentUri, _currentUpload); + _currentUpload.totalSize += _currentUpload.currentSize; + _currentUpload.currentSize = 0; + } + _currentUpload.buf[_currentUpload.currentSize++] = b; +} + +uint8_t ESP32WebServer::_uploadReadByte(WiFiClient& client){ + int res = client.read(); + if(res == -1){ + while(!client.available() && client.connected()) + yield(); + res = client.read(); + } + return (uint8_t)res; +} + +bool ESP32WebServer::_parseForm(WiFiClient& client, String boundary, uint32_t len){ + (void) len; +#ifdef DEBUG_ESP_HTTP_SERVER + DEBUG_OUTPUT.print("Parse Form: Boundary: "); + DEBUG_OUTPUT.print(boundary); + DEBUG_OUTPUT.print(" Length: "); + DEBUG_OUTPUT.println(len); +#endif + String line; + int retry = 0; + do { + line = client.readStringUntil('\r'); + ++retry; + } while (line.length() == 0 && retry < 3); + + client.readStringUntil('\n'); + //start reading the form + if (line == ("--"+boundary)){ + RequestArgument* postArgs = new RequestArgument[32]; + int postArgsLen = 0; + while(1){ + String argName; + String argValue; + String argType; + String argFilename; + bool argIsFile = false; + + line = client.readStringUntil('\r'); + client.readStringUntil('\n'); + if (line.length() > 19 && line.substring(0, 19).equalsIgnoreCase("Content-Disposition")){ + int nameStart = line.indexOf('='); + if (nameStart != -1){ + argName = line.substring(nameStart+2); + nameStart = argName.indexOf('='); + if (nameStart == -1){ + argName = argName.substring(0, argName.length() - 1); + } else { + argFilename = argName.substring(nameStart+2, argName.length() - 1); + argName = argName.substring(0, argName.indexOf('"')); + argIsFile = true; +#ifdef DEBUG_ESP_HTTP_SERVER + DEBUG_OUTPUT.print("PostArg FileName: "); + DEBUG_OUTPUT.println(argFilename); +#endif + //use GET to set the filename if uploading using blob + if (argFilename == "blob" && hasArg("filename")) argFilename = arg("filename"); + } +#ifdef DEBUG_ESP_HTTP_SERVER + DEBUG_OUTPUT.print("PostArg Name: "); + DEBUG_OUTPUT.println(argName); +#endif + argType = "text/plain"; + line = client.readStringUntil('\r'); + client.readStringUntil('\n'); + if (line.length() > 12 && line.substring(0, 12).equalsIgnoreCase("Content-Type")){ + argType = line.substring(line.indexOf(':')+2); + //skip next line + client.readStringUntil('\r'); + client.readStringUntil('\n'); + } +#ifdef DEBUG_ESP_HTTP_SERVER + DEBUG_OUTPUT.print("PostArg Type: "); + DEBUG_OUTPUT.println(argType); +#endif + if (!argIsFile){ + while(1){ + line = client.readStringUntil('\r'); + client.readStringUntil('\n'); + if (line.startsWith("--"+boundary)) break; + if (argValue.length() > 0) argValue += "\n"; + argValue += line; + } +#ifdef DEBUG_ESP_HTTP_SERVER + DEBUG_OUTPUT.print("PostArg Value: "); + DEBUG_OUTPUT.println(argValue); + DEBUG_OUTPUT.println(); +#endif + + RequestArgument& arg = postArgs[postArgsLen++]; + arg.key = argName; + arg.value = argValue; + + if (line == ("--"+boundary+"--")){ +#ifdef DEBUG_ESP_HTTP_SERVER + DEBUG_OUTPUT.println("Done Parsing POST"); +#endif + break; + } + } else { + _currentUpload.status = UPLOAD_FILE_START; + _currentUpload.name = argName; + _currentUpload.filename = argFilename; + _currentUpload.type = argType; + _currentUpload.totalSize = 0; + _currentUpload.currentSize = 0; +#ifdef DEBUG_ESP_HTTP_SERVER + DEBUG_OUTPUT.print("Start File: "); + DEBUG_OUTPUT.print(_currentUpload.filename); + DEBUG_OUTPUT.print(" Type: "); + DEBUG_OUTPUT.println(_currentUpload.type); +#endif + if(_currentHandler && _currentHandler->canUpload(_currentUri)) + _currentHandler->upload(*this, _currentUri, _currentUpload); + _currentUpload.status = UPLOAD_FILE_WRITE; + uint8_t argByte = _uploadReadByte(client); +readfile: + while(argByte != 0x0D){ + if (!client.connected()) return _parseFormUploadAborted(); + _uploadWriteByte(argByte); + argByte = _uploadReadByte(client); + } + + argByte = _uploadReadByte(client); + if (!client.connected()) return _parseFormUploadAborted(); + if (argByte == 0x0A){ + argByte = _uploadReadByte(client); + if (!client.connected()) return _parseFormUploadAborted(); + if ((char)argByte != '-'){ + //continue reading the file + _uploadWriteByte(0x0D); + _uploadWriteByte(0x0A); + goto readfile; + } else { + argByte = _uploadReadByte(client); + if (!client.connected()) return _parseFormUploadAborted(); + if ((char)argByte != '-'){ + //continue reading the file + _uploadWriteByte(0x0D); + _uploadWriteByte(0x0A); + _uploadWriteByte((uint8_t)('-')); + goto readfile; + } + } + + uint8_t endBuf[boundary.length()]; + client.readBytes(endBuf, boundary.length()); + + if (strstr((const char*)endBuf, boundary.c_str()) != NULL){ + if(_currentHandler && _currentHandler->canUpload(_currentUri)) + _currentHandler->upload(*this, _currentUri, _currentUpload); + _currentUpload.totalSize += _currentUpload.currentSize; + _currentUpload.status = UPLOAD_FILE_END; + if(_currentHandler && _currentHandler->canUpload(_currentUri)) + _currentHandler->upload(*this, _currentUri, _currentUpload); +#ifdef DEBUG_ESP_HTTP_SERVER + DEBUG_OUTPUT.print("End File: "); + DEBUG_OUTPUT.print(_currentUpload.filename); + DEBUG_OUTPUT.print(" Type: "); + DEBUG_OUTPUT.print(_currentUpload.type); + DEBUG_OUTPUT.print(" Size: "); + DEBUG_OUTPUT.println(_currentUpload.totalSize); +#endif + line = client.readStringUntil(0x0D); + client.readStringUntil(0x0A); + if (line == "--"){ +#ifdef DEBUG_ESP_HTTP_SERVER + DEBUG_OUTPUT.println("Done Parsing POST"); +#endif + break; + } + continue; + } else { + _uploadWriteByte(0x0D); + _uploadWriteByte(0x0A); + _uploadWriteByte((uint8_t)('-')); + _uploadWriteByte((uint8_t)('-')); + uint32_t i = 0; + while(i < boundary.length()){ + _uploadWriteByte(endBuf[i++]); + } + argByte = _uploadReadByte(client); + goto readfile; + } + } else { + _uploadWriteByte(0x0D); + goto readfile; + } + break; + } + } + } + } + + int iarg; + int totalArgs = ((32 - postArgsLen) < _currentArgCount)?(32 - postArgsLen):_currentArgCount; + for (iarg = 0; iarg < totalArgs; iarg++){ + RequestArgument& arg = postArgs[postArgsLen++]; + arg.key = _currentArgs[iarg].key; + arg.value = _currentArgs[iarg].value; + } + if (_currentArgs) delete[] _currentArgs; + _currentArgs = new RequestArgument[postArgsLen]; + for (iarg = 0; iarg < postArgsLen; iarg++){ + RequestArgument& arg = _currentArgs[iarg]; + arg.key = postArgs[iarg].key; + arg.value = postArgs[iarg].value; + } + _currentArgCount = iarg; + if (postArgs) delete[] postArgs; + return true; + } +#ifdef DEBUG_ESP_HTTP_SERVER + DEBUG_OUTPUT.print("Error: line: "); + DEBUG_OUTPUT.println(line); +#endif + return false; +} + +String ESP32WebServer::urlDecode(const String& text) +{ + String decoded = ""; + char temp[] = "0x00"; + unsigned int len = text.length(); + unsigned int i = 0; + while (i < len) + { + char decodedChar; + char encodedChar = text.charAt(i++); + if ((encodedChar == '%') && (i + 1 < len)) + { + temp[2] = text.charAt(i++); + temp[3] = text.charAt(i++); + + decodedChar = strtol(temp, NULL, 16); + } + else { + if (encodedChar == '+') + { + decodedChar = ' '; + } + else { + decodedChar = encodedChar; // normal ascii char + } + } + decoded += decodedChar; + } + return decoded; +} + +bool ESP32WebServer::_parseFormUploadAborted(){ + _currentUpload.status = UPLOAD_FILE_ABORTED; + if(_currentHandler && _currentHandler->canUpload(_currentUri)) + _currentHandler->upload(*this, _currentUri, _currentUpload); + return false; +} From 08d90286b6fcdad3458f36b7f414474ac2896965 Mon Sep 17 00:00:00 2001 From: karan6190 Date: Thu, 28 Jun 2018 10:53:35 +0530 Subject: [PATCH 2/3] added OTAWebUpdater --- libraries/ArduinoOTA/src/ESP32WebServer.cpp | 537 ----------------- libraries/ArduinoOTA/src/ESP32WebServer.h | 192 ------- libraries/ArduinoOTA/src/Parsing.cpp | 607 -------------------- 3 files changed, 1336 deletions(-) delete mode 100644 libraries/ArduinoOTA/src/ESP32WebServer.cpp delete mode 100644 libraries/ArduinoOTA/src/ESP32WebServer.h delete mode 100644 libraries/ArduinoOTA/src/Parsing.cpp diff --git a/libraries/ArduinoOTA/src/ESP32WebServer.cpp b/libraries/ArduinoOTA/src/ESP32WebServer.cpp deleted file mode 100644 index 81bd5cac968..00000000000 --- a/libraries/ArduinoOTA/src/ESP32WebServer.cpp +++ /dev/null @@ -1,537 +0,0 @@ -/* - ESP32WebServer.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 -#include -#include "WiFiServer.h" -#include "WiFiClient.h" -#include "ESP32WebServer.h" -#include "FS.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"; - -ESP32WebServer::ESP32WebServer(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) -{ -} - -ESP32WebServer::ESP32WebServer(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) -{ -} - -ESP32WebServer::~ESP32WebServer() { - if (_currentHeaders) - delete[]_currentHeaders; - _headerKeysCount = 0; - RequestHandler* handler = _firstHandler; - while (handler) { - RequestHandler* next = handler->next(); - delete handler; - handler = next; - } - close(); -} - -void ESP32WebServer::begin() { - _currentStatus = HC_NONE; - _server.begin(); - if(!_headerKeysCount) - collectHeaders(0, 0); -} - -bool ESP32WebServer::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 ESP32WebServer::requestAuthentication(){ - sendHeader("WWW-Authenticate", "Basic realm=\"Login Required\""); - send(401); -} - -void ESP32WebServer::on(const String &uri, ESP32WebServer::THandlerFunction handler) { - on(uri, HTTP_ANY, handler); -} - -void ESP32WebServer::on(const String &uri, HTTPMethod method, ESP32WebServer::THandlerFunction fn) { - on(uri, method, fn, _fileUploadHandler); -} - -void ESP32WebServer::on(const String &uri, HTTPMethod method, ESP32WebServer::THandlerFunction fn, ESP32WebServer::THandlerFunction ufn) { - _addRequestHandler(new FunctionRequestHandler(fn, ufn, uri, method)); -} - -void ESP32WebServer::addHandler(RequestHandler* handler) { - _addRequestHandler(handler); -} - -void ESP32WebServer::_addRequestHandler(RequestHandler* handler) { - if (!_lastHandler) { - _firstHandler = handler; - _lastHandler = handler; - } - else { - _lastHandler->next(handler); - _lastHandler = handler; - } -} - -void ESP32WebServer::serveStatic(const char* uri, FS& fs, const char* path, const char* cache_header) { - _addRequestHandler(new StaticRequestHandler(fs, path, uri, cache_header)); -} - -void ESP32WebServer::handleClient() { - if (_currentStatus == HC_NONE) { - WiFiClient client = _server.available(); - if (!client) { - return; - } - -#ifdef DEBUG_ESP_HTTP_SERVER - log_e("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 ESP32WebServer::close() { - _server.end(); -} - -void ESP32WebServer::stop() { - close(); -} - -void ESP32WebServer::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 ESP32WebServer::setContentLength(size_t contentLength) { - _contentLength = contentLength; -} - -void ESP32WebServer::_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 ESP32WebServer::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 ESP32WebServer::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 ESP32WebServer::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 ESP32WebServer::send(int code, char* content_type, const String& content) { - send(code, (const char*)content_type, content); -} - -void ESP32WebServer::send(int code, const String& content_type, const String& content) { - send(code, (const char*)content_type.c_str(), content); -} - -void ESP32WebServer::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 ESP32WebServer::sendContent_P(PGM_P content) { - sendContent_P(content, strlen_P(content)); -} - -void ESP32WebServer::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); - } - } - size_t chunkSize = 20; - int idx = 0; - while(size != 0){ - if(size < chunkSize){ - _currentClient.write(&content[idx], size); - size = 0; - } else { - _currentClient.write(&content[idx], chunkSize); - size -= chunkSize; - idx += chunkSize; - } - yield(); - } - if(_chunked){ - _currentClient.write(footer, 2); - } -} - - -String ESP32WebServer::arg(String name) { - for (int i = 0; i < _currentArgCount; ++i) { - if ( _currentArgs[i].key == name ) - return _currentArgs[i].value; - } - return String(); -} - -String ESP32WebServer::arg(int i) { - if (i < _currentArgCount) - return _currentArgs[i].value; - return String(); -} - -String ESP32WebServer::argName(int i) { - if (i < _currentArgCount) - return _currentArgs[i].key; - return String(); -} - -int ESP32WebServer::args() { - return _currentArgCount; -} - -bool ESP32WebServer::hasArg(String name) { - for (int i = 0; i < _currentArgCount; ++i) { - if (_currentArgs[i].key == name) - return true; - } - return false; -} - - -String ESP32WebServer::header(String name) { - for (int i = 0; i < _headerKeysCount; ++i) { - if (_currentHeaders[i].key.equalsIgnoreCase(name)) - return _currentHeaders[i].value; - } - return String(); -} - -void ESP32WebServer::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 ESP32WebServer::header(int i) { - if (i < _headerKeysCount) - return _currentHeaders[i].value; - return String(); -} - -String ESP32WebServer::headerName(int i) { - if (i < _headerKeysCount) - return _currentHeaders[i].key; - return String(); -} - -int ESP32WebServer::headers() { - return _headerKeysCount; -} - -bool ESP32WebServer::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 ESP32WebServer::hostHeader() { - return _hostHeader; -} - -void ESP32WebServer::onFileUpload(THandlerFunction fn) { - _fileUploadHandler = fn; -} - -void ESP32WebServer::onNotFound(THandlerFunction fn) { - _notFoundHandler = fn; -} - -void ESP32WebServer::_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 ESP32WebServer::_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 ""; - } -} diff --git a/libraries/ArduinoOTA/src/ESP32WebServer.h b/libraries/ArduinoOTA/src/ESP32WebServer.h deleted file mode 100644 index 2401a35eafc..00000000000 --- a/libraries/ArduinoOTA/src/ESP32WebServer.h +++ /dev/null @@ -1,192 +0,0 @@ -/* - ESP32WebServer.h - 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) -*/ - - -#ifndef ESP32WEBSERVER_H -#define ESP32WEBSERVER_H - -#include -#include - -enum HTTPMethod { HTTP_ANY, HTTP_GET, HTTP_POST, HTTP_PUT, HTTP_PATCH, HTTP_DELETE, HTTP_OPTIONS }; -enum HTTPUploadStatus { UPLOAD_FILE_START, UPLOAD_FILE_WRITE, UPLOAD_FILE_END, - UPLOAD_FILE_ABORTED }; -enum HTTPClientStatus { HC_NONE, HC_WAIT_READ, HC_WAIT_CLOSE }; - -#define HTTP_DOWNLOAD_UNIT_SIZE 1460 -#define HTTP_UPLOAD_BUFLEN 2048 -#define HTTP_MAX_DATA_WAIT 1000 //ms to wait for the client to send the request -#define HTTP_MAX_POST_WAIT 1000 //ms to wait for POST data to arrive -#define HTTP_MAX_SEND_WAIT 5000 //ms to wait for data chunk to be ACKed -#define HTTP_MAX_CLOSE_WAIT 2000 //ms to wait for the client to close the connection - -#define CONTENT_LENGTH_UNKNOWN ((size_t) -1) -#define CONTENT_LENGTH_NOT_SET ((size_t) -2) - -class ESP32WebServer; - -typedef struct { - HTTPUploadStatus status; - String filename; - String name; - String type; - size_t totalSize; // file size - size_t currentSize; // size of data currently in buf - uint8_t buf[HTTP_UPLOAD_BUFLEN]; -} HTTPUpload; - -#include "detail/RequestHandler.h" - -namespace fs { -class FS; -} - -class ESP32WebServer -{ -public: - ESP32WebServer(IPAddress addr, int port = 80); - ESP32WebServer(int port = 80); - ~ESP32WebServer(); - - void begin(); - void handleClient(); - - void close(); - void stop(); - - bool authenticate(const char * username, const char * password); - void requestAuthentication(); - - typedef std::function THandlerFunction; - void on(const String &uri, THandlerFunction handler); - void on(const String &uri, HTTPMethod method, THandlerFunction fn); - void on(const String &uri, HTTPMethod method, THandlerFunction fn, THandlerFunction ufn); - void addHandler(RequestHandler* handler); - void serveStatic(const char* uri, fs::FS& fs, const char* path, const char* cache_header = NULL ); - void onNotFound(THandlerFunction fn); //called when handler is not assigned - void onFileUpload(THandlerFunction fn); //handle file uploads - - String uri() { return _currentUri; } - HTTPMethod method() { return _currentMethod; } - WiFiClient client() { return _currentClient; } - HTTPUpload& upload() { return _currentUpload; } - - String arg(String name); // get request argument value by name - String arg(int i); // get request argument value by number - String argName(int i); // get request argument name by number - int args(); // get arguments count - bool hasArg(String name); // check if argument exists - void collectHeaders(const char* headerKeys[], const size_t headerKeysCount); // set the request headers to collect - String header(String name); // get request header value by name - String header(int i); // get request header value by number - String headerName(int i); // get request header name by number - int headers(); // get header count - bool hasHeader(String name); // check if header exists - - String hostHeader(); // get request host header if available or empty String if not - - // send response to the client - // code - HTTP response code, can be 200 or 404 - // content_type - HTTP content type, like "text/plain" or "image/png" - // content - actual content body - void send(int code, const char* content_type = NULL, const String& content = String("")); - void send(int code, char* content_type, const String& content); - void send(int code, const String& content_type, const String& content); - void send_P(int code, PGM_P content_type, PGM_P content); - void send_P(int code, PGM_P content_type, PGM_P content, size_t contentLength); - - void setContentLength(size_t contentLength); - void sendHeader(const String& name, const String& value, bool first = false); - void sendContent(const String& content); - void sendContent_P(PGM_P content); - void sendContent_P(PGM_P content, size_t size); - - static String urlDecode(const String& text); - -template size_t streamFile(T &file, const String& contentType){ - setContentLength(file.size()); - if (String(file.name()).endsWith(".gz") && - contentType != "application/x-gzip" && - contentType != "application/octet-stream"){ - sendHeader("Content-Encoding", "gzip"); - } - send(200, contentType, ""); - uint8_t buf[20]; - size_t fsize = 0; - while(file.available()){ - int got = file.read(buf, 20); - _currentClient.write(buf, got); - fsize += got; - yield(); - } - return fsize; -} - -protected: - void _addRequestHandler(RequestHandler* handler); - void _handleRequest(); - bool _parseRequest(WiFiClient& client); - void _parseArguments(String data); - static String _responseCodeToString(int code); - bool _parseForm(WiFiClient& client, String boundary, uint32_t len); - bool _parseFormUploadAborted(); - void _uploadWriteByte(uint8_t b); - uint8_t _uploadReadByte(WiFiClient& client); - void _prepareHeader(String& response, int code, const char* content_type, size_t contentLength); - bool _collectHeader(const char* headerName, const char* headerValue); - - struct RequestArgument { - String key; - String value; - }; - - WiFiServer _server; - - WiFiClient _currentClient; - HTTPMethod _currentMethod; - String _currentUri; - uint8_t _currentVersion; - HTTPClientStatus _currentStatus; - unsigned long _statusChange; - - RequestHandler* _currentHandler; - RequestHandler* _firstHandler; - RequestHandler* _lastHandler; - THandlerFunction _notFoundHandler; - THandlerFunction _fileUploadHandler; - - int _currentArgCount; - RequestArgument* _currentArgs; - HTTPUpload _currentUpload; - - int _headerKeysCount; - RequestArgument* _currentHeaders; - size_t _contentLength; - String _responseHeaders; - - String _hostHeader; - bool _chunked; - -}; - - -#endif //ESP32WEBSERVER_H diff --git a/libraries/ArduinoOTA/src/Parsing.cpp b/libraries/ArduinoOTA/src/Parsing.cpp deleted file mode 100644 index 45094944f08..00000000000 --- a/libraries/ArduinoOTA/src/Parsing.cpp +++ /dev/null @@ -1,607 +0,0 @@ -/* - Parsing.cpp - HTTP request parsing. - - Copyright (c) 2015 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 -#include "WiFiServer.h" -#include "WiFiClient.h" -#include "ESP32WebServer.h" - -//#define DEBUG_ESP_HTTP_SERVER -#ifdef DEBUG_ESP_PORT -#define DEBUG_OUTPUT DEBUG_ESP_PORT -#else -#define DEBUG_OUTPUT Serial -#endif - -static char* readBytesWithTimeout(WiFiClient& client, size_t maxLength, size_t& dataLength, int timeout_ms) -{ - char *buf = nullptr; - dataLength = 0; - while (dataLength < maxLength) { - int tries = timeout_ms; - size_t newLength; - while (!(newLength = client.available()) && tries--) delay(1); - if (!newLength) { - break; - } - if (!buf) { - buf = (char *) malloc(newLength + 1); - if (!buf) { - return nullptr; - } - } - else { - char* newBuf = (char *) realloc(buf, dataLength + newLength + 1); - if (!newBuf) { - free(buf); - return nullptr; - } - buf = newBuf; - } - client.readBytes(buf + dataLength, newLength); - dataLength += newLength; - buf[dataLength] = '\0'; - } - return buf; -} - -bool ESP32WebServer::_parseRequest(WiFiClient& client) { - // Read the first line of HTTP request - String req = client.readStringUntil('\r'); - client.readStringUntil('\n'); - //reset header value - for (int i = 0; i < _headerKeysCount; ++i) { - _currentHeaders[i].value =String(); - } - - // First line of HTTP request looks like "GET /path HTTP/1.1" - // Retrieve the "/path" part by finding the spaces - int addr_start = req.indexOf(' '); - int addr_end = req.indexOf(' ', addr_start + 1); - if (addr_start == -1 || addr_end == -1) { -#ifdef DEBUG_ESP_HTTP_SERVER - DEBUG_OUTPUT.print("Invalid request: "); - DEBUG_OUTPUT.println(req); -#endif - return false; - } - - String methodStr = req.substring(0, addr_start); - String url = req.substring(addr_start + 1, addr_end); - String versionEnd = req.substring(addr_end + 8); - _currentVersion = atoi(versionEnd.c_str()); - String searchStr = ""; - int hasSearch = url.indexOf('?'); - if (hasSearch != -1){ - searchStr = urlDecode(url.substring(hasSearch + 1)); - url = url.substring(0, hasSearch); - } - _currentUri = url; - _chunked = false; - - HTTPMethod method = HTTP_GET; - if (methodStr == "POST") { - method = HTTP_POST; - } else if (methodStr == "DELETE") { - method = HTTP_DELETE; - } else if (methodStr == "OPTIONS") { - method = HTTP_OPTIONS; - } else if (methodStr == "PUT") { - method = HTTP_PUT; - } else if (methodStr == "PATCH") { - method = HTTP_PATCH; - } - _currentMethod = method; - -#ifdef DEBUG_ESP_HTTP_SERVER - DEBUG_OUTPUT.print("method: "); - DEBUG_OUTPUT.print(methodStr); - DEBUG_OUTPUT.print(" url: "); - DEBUG_OUTPUT.print(url); - DEBUG_OUTPUT.print(" search: "); - DEBUG_OUTPUT.println(searchStr); -#endif - - //attach handler - RequestHandler* handler; - for (handler = _firstHandler; handler; handler = handler->next()) { - if (handler->canHandle(_currentMethod, _currentUri)) - break; - } - _currentHandler = handler; - - String formData; - // below is needed only when POST type request - if (method == HTTP_POST || method == HTTP_PUT || method == HTTP_PATCH || method == HTTP_DELETE){ - String boundaryStr; - String headerName; - String headerValue; - bool isForm = false; - bool isEncoded = false; - uint32_t contentLength = 0; - //parse headers - while(1){ - req = client.readStringUntil('\r'); - client.readStringUntil('\n'); - if (req == "") break;//no moar headers - int headerDiv = req.indexOf(':'); - if (headerDiv == -1){ - break; - } - headerName = req.substring(0, headerDiv); - headerValue = req.substring(headerDiv + 1); - headerValue.trim(); - _collectHeader(headerName.c_str(),headerValue.c_str()); - - #ifdef DEBUG_ESP_HTTP_SERVER - DEBUG_OUTPUT.print("headerName: "); - DEBUG_OUTPUT.println(headerName); - DEBUG_OUTPUT.print("headerValue: "); - DEBUG_OUTPUT.println(headerValue); - #endif - - if (headerName.equalsIgnoreCase("Content-Type")){ - if (headerValue.startsWith("text/plain")){ - isForm = false; - } else if (headerValue.startsWith("application/x-www-form-urlencoded")){ - isForm = false; - isEncoded = true; - } else if (headerValue.startsWith("multipart/")){ - boundaryStr = headerValue.substring(headerValue.indexOf('=')+1); - isForm = true; - } - } else if (headerName.equalsIgnoreCase("Content-Length")){ - contentLength = headerValue.toInt(); - } else if (headerName.equalsIgnoreCase("Host")){ - _hostHeader = headerValue; - } - } - - if (!isForm){ - size_t plainLength; - char* plainBuf = readBytesWithTimeout(client, contentLength, plainLength, HTTP_MAX_POST_WAIT); - if (plainLength < contentLength) { - free(plainBuf); - return false; - } - if (contentLength > 0) { - if (searchStr != "") searchStr += '&'; - if(isEncoded){ - //url encoded form - String decoded = urlDecode(plainBuf); - size_t decodedLen = decoded.length(); - memcpy(plainBuf, decoded.c_str(), decodedLen); - plainBuf[decodedLen] = 0; - searchStr += plainBuf; - } - _parseArguments(searchStr); - if(!isEncoded){ - //plain post json or other data - RequestArgument& arg = _currentArgs[_currentArgCount++]; - arg.key = "plain"; - arg.value = String(plainBuf); - } - - #ifdef DEBUG_ESP_HTTP_SERVER - DEBUG_OUTPUT.print("Plain: "); - DEBUG_OUTPUT.println(plainBuf); - #endif - free(plainBuf); - } - } - - if (isForm){ - _parseArguments(searchStr); - if (!_parseForm(client, boundaryStr, contentLength)) { - return false; - } - } - } else { - String headerName; - String headerValue; - //parse headers - while(1){ - req = client.readStringUntil('\r'); - client.readStringUntil('\n'); - if (req == "") break;//no moar headers - int headerDiv = req.indexOf(':'); - if (headerDiv == -1){ - break; - } - headerName = req.substring(0, headerDiv); - headerValue = req.substring(headerDiv + 2); - _collectHeader(headerName.c_str(),headerValue.c_str()); - - #ifdef DEBUG_ESP_HTTP_SERVER - DEBUG_OUTPUT.print("headerName: "); - DEBUG_OUTPUT.println(headerName); - DEBUG_OUTPUT.print("headerValue: "); - DEBUG_OUTPUT.println(headerValue); - #endif - - if (headerName.equalsIgnoreCase("Host")){ - _hostHeader = headerValue; - } - } - _parseArguments(searchStr); - } - client.flush(); - -#ifdef DEBUG_ESP_HTTP_SERVER - DEBUG_OUTPUT.print("Request: "); - DEBUG_OUTPUT.println(url); - DEBUG_OUTPUT.print(" Arguments: "); - DEBUG_OUTPUT.println(searchStr); -#endif - - return true; -} - -bool ESP32WebServer::_collectHeader(const char* headerName, const char* headerValue) { - for (int i = 0; i < _headerKeysCount; i++) { - if (_currentHeaders[i].key.equalsIgnoreCase(headerName)) { - _currentHeaders[i].value=headerValue; - return true; - } - } - return false; -} - -void ESP32WebServer::_parseArguments(String data) { -#ifdef DEBUG_ESP_HTTP_SERVER - DEBUG_OUTPUT.print("args: "); - DEBUG_OUTPUT.println(data); -#endif - if (_currentArgs) - delete[] _currentArgs; - _currentArgs = 0; - if (data.length() == 0) { - _currentArgCount = 0; - _currentArgs = new RequestArgument[1]; - return; - } - _currentArgCount = 1; - - for (int i = 0; i < (int)data.length(); ) { - i = data.indexOf('&', i); - if (i == -1) - break; - ++i; - ++_currentArgCount; - } -#ifdef DEBUG_ESP_HTTP_SERVER - DEBUG_OUTPUT.print("args count: "); - DEBUG_OUTPUT.println(_currentArgCount); -#endif - - _currentArgs = new RequestArgument[_currentArgCount+1]; - int pos = 0; - int iarg; - for (iarg = 0; iarg < _currentArgCount;) { - int equal_sign_index = data.indexOf('=', pos); - int next_arg_index = data.indexOf('&', pos); -#ifdef DEBUG_ESP_HTTP_SERVER - DEBUG_OUTPUT.print("pos "); - DEBUG_OUTPUT.print(pos); - DEBUG_OUTPUT.print("=@ "); - DEBUG_OUTPUT.print(equal_sign_index); - DEBUG_OUTPUT.print(" &@ "); - DEBUG_OUTPUT.println(next_arg_index); -#endif - if ((equal_sign_index == -1) || ((equal_sign_index > next_arg_index) && (next_arg_index != -1))) { -#ifdef DEBUG_ESP_HTTP_SERVER - DEBUG_OUTPUT.print("arg missing value: "); - DEBUG_OUTPUT.println(iarg); -#endif - if (next_arg_index == -1) - break; - pos = next_arg_index + 1; - continue; - } - RequestArgument& arg = _currentArgs[iarg]; - arg.key = data.substring(pos, equal_sign_index); - arg.value = data.substring(equal_sign_index + 1, next_arg_index); -#ifdef DEBUG_ESP_HTTP_SERVER - DEBUG_OUTPUT.print("arg "); - DEBUG_OUTPUT.print(iarg); - DEBUG_OUTPUT.print(" key: "); - DEBUG_OUTPUT.print(arg.key); - DEBUG_OUTPUT.print(" value: "); - DEBUG_OUTPUT.println(arg.value); -#endif - ++iarg; - if (next_arg_index == -1) - break; - pos = next_arg_index + 1; - } - _currentArgCount = iarg; -#ifdef DEBUG_ESP_HTTP_SERVER - DEBUG_OUTPUT.print("args count: "); - DEBUG_OUTPUT.println(_currentArgCount); -#endif - -} - -void ESP32WebServer::_uploadWriteByte(uint8_t b){ - if (_currentUpload.currentSize == HTTP_UPLOAD_BUFLEN){ - if(_currentHandler && _currentHandler->canUpload(_currentUri)) - _currentHandler->upload(*this, _currentUri, _currentUpload); - _currentUpload.totalSize += _currentUpload.currentSize; - _currentUpload.currentSize = 0; - } - _currentUpload.buf[_currentUpload.currentSize++] = b; -} - -uint8_t ESP32WebServer::_uploadReadByte(WiFiClient& client){ - int res = client.read(); - if(res == -1){ - while(!client.available() && client.connected()) - yield(); - res = client.read(); - } - return (uint8_t)res; -} - -bool ESP32WebServer::_parseForm(WiFiClient& client, String boundary, uint32_t len){ - (void) len; -#ifdef DEBUG_ESP_HTTP_SERVER - DEBUG_OUTPUT.print("Parse Form: Boundary: "); - DEBUG_OUTPUT.print(boundary); - DEBUG_OUTPUT.print(" Length: "); - DEBUG_OUTPUT.println(len); -#endif - String line; - int retry = 0; - do { - line = client.readStringUntil('\r'); - ++retry; - } while (line.length() == 0 && retry < 3); - - client.readStringUntil('\n'); - //start reading the form - if (line == ("--"+boundary)){ - RequestArgument* postArgs = new RequestArgument[32]; - int postArgsLen = 0; - while(1){ - String argName; - String argValue; - String argType; - String argFilename; - bool argIsFile = false; - - line = client.readStringUntil('\r'); - client.readStringUntil('\n'); - if (line.length() > 19 && line.substring(0, 19).equalsIgnoreCase("Content-Disposition")){ - int nameStart = line.indexOf('='); - if (nameStart != -1){ - argName = line.substring(nameStart+2); - nameStart = argName.indexOf('='); - if (nameStart == -1){ - argName = argName.substring(0, argName.length() - 1); - } else { - argFilename = argName.substring(nameStart+2, argName.length() - 1); - argName = argName.substring(0, argName.indexOf('"')); - argIsFile = true; -#ifdef DEBUG_ESP_HTTP_SERVER - DEBUG_OUTPUT.print("PostArg FileName: "); - DEBUG_OUTPUT.println(argFilename); -#endif - //use GET to set the filename if uploading using blob - if (argFilename == "blob" && hasArg("filename")) argFilename = arg("filename"); - } -#ifdef DEBUG_ESP_HTTP_SERVER - DEBUG_OUTPUT.print("PostArg Name: "); - DEBUG_OUTPUT.println(argName); -#endif - argType = "text/plain"; - line = client.readStringUntil('\r'); - client.readStringUntil('\n'); - if (line.length() > 12 && line.substring(0, 12).equalsIgnoreCase("Content-Type")){ - argType = line.substring(line.indexOf(':')+2); - //skip next line - client.readStringUntil('\r'); - client.readStringUntil('\n'); - } -#ifdef DEBUG_ESP_HTTP_SERVER - DEBUG_OUTPUT.print("PostArg Type: "); - DEBUG_OUTPUT.println(argType); -#endif - if (!argIsFile){ - while(1){ - line = client.readStringUntil('\r'); - client.readStringUntil('\n'); - if (line.startsWith("--"+boundary)) break; - if (argValue.length() > 0) argValue += "\n"; - argValue += line; - } -#ifdef DEBUG_ESP_HTTP_SERVER - DEBUG_OUTPUT.print("PostArg Value: "); - DEBUG_OUTPUT.println(argValue); - DEBUG_OUTPUT.println(); -#endif - - RequestArgument& arg = postArgs[postArgsLen++]; - arg.key = argName; - arg.value = argValue; - - if (line == ("--"+boundary+"--")){ -#ifdef DEBUG_ESP_HTTP_SERVER - DEBUG_OUTPUT.println("Done Parsing POST"); -#endif - break; - } - } else { - _currentUpload.status = UPLOAD_FILE_START; - _currentUpload.name = argName; - _currentUpload.filename = argFilename; - _currentUpload.type = argType; - _currentUpload.totalSize = 0; - _currentUpload.currentSize = 0; -#ifdef DEBUG_ESP_HTTP_SERVER - DEBUG_OUTPUT.print("Start File: "); - DEBUG_OUTPUT.print(_currentUpload.filename); - DEBUG_OUTPUT.print(" Type: "); - DEBUG_OUTPUT.println(_currentUpload.type); -#endif - if(_currentHandler && _currentHandler->canUpload(_currentUri)) - _currentHandler->upload(*this, _currentUri, _currentUpload); - _currentUpload.status = UPLOAD_FILE_WRITE; - uint8_t argByte = _uploadReadByte(client); -readfile: - while(argByte != 0x0D){ - if (!client.connected()) return _parseFormUploadAborted(); - _uploadWriteByte(argByte); - argByte = _uploadReadByte(client); - } - - argByte = _uploadReadByte(client); - if (!client.connected()) return _parseFormUploadAborted(); - if (argByte == 0x0A){ - argByte = _uploadReadByte(client); - if (!client.connected()) return _parseFormUploadAborted(); - if ((char)argByte != '-'){ - //continue reading the file - _uploadWriteByte(0x0D); - _uploadWriteByte(0x0A); - goto readfile; - } else { - argByte = _uploadReadByte(client); - if (!client.connected()) return _parseFormUploadAborted(); - if ((char)argByte != '-'){ - //continue reading the file - _uploadWriteByte(0x0D); - _uploadWriteByte(0x0A); - _uploadWriteByte((uint8_t)('-')); - goto readfile; - } - } - - uint8_t endBuf[boundary.length()]; - client.readBytes(endBuf, boundary.length()); - - if (strstr((const char*)endBuf, boundary.c_str()) != NULL){ - if(_currentHandler && _currentHandler->canUpload(_currentUri)) - _currentHandler->upload(*this, _currentUri, _currentUpload); - _currentUpload.totalSize += _currentUpload.currentSize; - _currentUpload.status = UPLOAD_FILE_END; - if(_currentHandler && _currentHandler->canUpload(_currentUri)) - _currentHandler->upload(*this, _currentUri, _currentUpload); -#ifdef DEBUG_ESP_HTTP_SERVER - DEBUG_OUTPUT.print("End File: "); - DEBUG_OUTPUT.print(_currentUpload.filename); - DEBUG_OUTPUT.print(" Type: "); - DEBUG_OUTPUT.print(_currentUpload.type); - DEBUG_OUTPUT.print(" Size: "); - DEBUG_OUTPUT.println(_currentUpload.totalSize); -#endif - line = client.readStringUntil(0x0D); - client.readStringUntil(0x0A); - if (line == "--"){ -#ifdef DEBUG_ESP_HTTP_SERVER - DEBUG_OUTPUT.println("Done Parsing POST"); -#endif - break; - } - continue; - } else { - _uploadWriteByte(0x0D); - _uploadWriteByte(0x0A); - _uploadWriteByte((uint8_t)('-')); - _uploadWriteByte((uint8_t)('-')); - uint32_t i = 0; - while(i < boundary.length()){ - _uploadWriteByte(endBuf[i++]); - } - argByte = _uploadReadByte(client); - goto readfile; - } - } else { - _uploadWriteByte(0x0D); - goto readfile; - } - break; - } - } - } - } - - int iarg; - int totalArgs = ((32 - postArgsLen) < _currentArgCount)?(32 - postArgsLen):_currentArgCount; - for (iarg = 0; iarg < totalArgs; iarg++){ - RequestArgument& arg = postArgs[postArgsLen++]; - arg.key = _currentArgs[iarg].key; - arg.value = _currentArgs[iarg].value; - } - if (_currentArgs) delete[] _currentArgs; - _currentArgs = new RequestArgument[postArgsLen]; - for (iarg = 0; iarg < postArgsLen; iarg++){ - RequestArgument& arg = _currentArgs[iarg]; - arg.key = postArgs[iarg].key; - arg.value = postArgs[iarg].value; - } - _currentArgCount = iarg; - if (postArgs) delete[] postArgs; - return true; - } -#ifdef DEBUG_ESP_HTTP_SERVER - DEBUG_OUTPUT.print("Error: line: "); - DEBUG_OUTPUT.println(line); -#endif - return false; -} - -String ESP32WebServer::urlDecode(const String& text) -{ - String decoded = ""; - char temp[] = "0x00"; - unsigned int len = text.length(); - unsigned int i = 0; - while (i < len) - { - char decodedChar; - char encodedChar = text.charAt(i++); - if ((encodedChar == '%') && (i + 1 < len)) - { - temp[2] = text.charAt(i++); - temp[3] = text.charAt(i++); - - decodedChar = strtol(temp, NULL, 16); - } - else { - if (encodedChar == '+') - { - decodedChar = ' '; - } - else { - decodedChar = encodedChar; // normal ascii char - } - } - decoded += decodedChar; - } - return decoded; -} - -bool ESP32WebServer::_parseFormUploadAborted(){ - _currentUpload.status = UPLOAD_FILE_ABORTED; - if(_currentHandler && _currentHandler->canUpload(_currentUri)) - _currentHandler->upload(*this, _currentUri, _currentUpload); - return false; -} From 18b8ee928b8e67bb84ee233bf489eba6c54207e3 Mon Sep 17 00:00:00 2001 From: karan6190 Date: Thu, 28 Jun 2018 20:19:46 +0530 Subject: [PATCH 3/3] Updated OTAWebUpdater --- libraries/ArduinoOTA/examples/OTAWebUpdater/OTAWebUpdater.ino | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/ArduinoOTA/examples/OTAWebUpdater/OTAWebUpdater.ino b/libraries/ArduinoOTA/examples/OTAWebUpdater/OTAWebUpdater.ino index 5358bfc33ce..aa8aaf03f4b 100644 --- a/libraries/ArduinoOTA/examples/OTAWebUpdater/OTAWebUpdater.ino +++ b/libraries/ArduinoOTA/examples/OTAWebUpdater/OTAWebUpdater.ino @@ -1,6 +1,6 @@ #include #include -#include +#include #include #include @@ -8,7 +8,7 @@ const char* host = "ESP32"; const char* ssid = "xxx"; const char* password = "xxxx"; -ESP32WebServer server(80); +WebServer server(80); const char* loginIndex = "
" ""