From 2af6af2bf0e5f85cf566d3be219f2989690317ed Mon Sep 17 00:00:00 2001 From: cschwinne Date: Sun, 17 Feb 2019 17:11:10 +0100 Subject: [PATCH] Added HTTP OTA update via ESPAsyncWebServer --- wled00/data/msg.htm | 6 +- wled00/html_other.h | 2 +- wled00/src/dependencies/espalexa/Espalexa.h | 22 +- .../webserver/ESP8266HTTPUpdateServer.cpp | 105 --- .../webserver/ESP8266HTTPUpdateServer.h | 74 --- .../dependencies/webserver/ESP8266WebServer.h | 29 - wled00/src/dependencies/webserver/LICENSE | 504 -------------- wled00/src/dependencies/webserver/Parsing.cpp | 613 ------------------ wled00/src/dependencies/webserver/README.md | 11 - .../src/dependencies/webserver/WebServer.cpp | 529 --------------- wled00/src/dependencies/webserver/WebServer.h | 236 ------- .../webserver/detail/RequestHandler.h | 19 - .../webserver/detail/RequestHandlersImpl.h | 191 ------ wled00/wled00.ino | 8 +- wled00/wled03_set.ino | 2 +- wled00/wled18_server.ino | 67 +- 16 files changed, 66 insertions(+), 2352 deletions(-) delete mode 100644 wled00/src/dependencies/webserver/ESP8266HTTPUpdateServer.cpp delete mode 100644 wled00/src/dependencies/webserver/ESP8266HTTPUpdateServer.h delete mode 100644 wled00/src/dependencies/webserver/ESP8266WebServer.h delete mode 100644 wled00/src/dependencies/webserver/LICENSE delete mode 100644 wled00/src/dependencies/webserver/Parsing.cpp delete mode 100644 wled00/src/dependencies/webserver/README.md delete mode 100644 wled00/src/dependencies/webserver/WebServer.cpp delete mode 100644 wled00/src/dependencies/webserver/WebServer.h delete mode 100644 wled00/src/dependencies/webserver/detail/RequestHandler.h delete mode 100644 wled00/src/dependencies/webserver/detail/RequestHandlersImpl.h diff --git a/wled00/data/msg.htm b/wled00/data/msg.htm index 844c4d5ee..93179399e 100644 --- a/wled00/data/msg.htm +++ b/wled00/data/msg.htm @@ -23,7 +23,7 @@ --tCol: #328CC1; --cFn: Verdana; } - button { + .bt { background: var(--bCol); color: var(--tCol); border: 0.3ch solid var(--bCol); @@ -34,6 +34,9 @@ margin: 8px; margin-top: 12px; } + input[type=file] { + font-size: 16px; + } body { font-family: var(--cFn), sans-serif; text-align: center; @@ -47,6 +50,7 @@

Sample message.

+Sample detail. \ No newline at end of file diff --git a/wled00/html_other.h b/wled00/html_other.h index 4846a245d..4c776f6c1 100644 --- a/wled00/html_other.h +++ b/wled00/html_other.h @@ -12,7 +12,7 @@ const char PAGE_msg[] PROGMEM = R"=====( WLED Message -%CSS%button{background:var(--bCol);color:var(--tCol);font-family:var(--cFn),sans-serif;border:.3ch solid var(--bCol);display:inline-block;filter:drop-shadow(-5px -5px 5px var(--sCol));font-size:20px;margin:8px;margin-top:12px}body{font-family:var(--cFn),sans-serif;text-align:center;background:var(--cCol);color:var(--tCol);line-height:200%%;margin:0;background-attachment:fixed} +%CSS%.bt{background:var(--bCol);color:var(--tCol);font-family:var(--cFn),sans-serif;border:.3ch solid var(--bCol);display:inline-block;filter:drop-shadow(-5px -5px 5px var(--sCol));font-size:20px;margin:8px;margin-top:12px}input[type=file]{font-size:16px}body{font-family:var(--cFn),sans-serif;text-align:center;background:var(--cCol);color:var(--tCol);line-height:200%%;margin:0;background-attachment:fixed}

%MSG%)====="; diff --git a/wled00/src/dependencies/espalexa/Espalexa.h b/wled00/src/dependencies/espalexa/Espalexa.h index f3b42d1d3..831b754b9 100644 --- a/wled00/src/dependencies/espalexa/Espalexa.h +++ b/wled00/src/dependencies/espalexa/Espalexa.h @@ -10,7 +10,7 @@ */ /* * @title Espalexa library - * @version 2.3.3 + * @version 2.3.4 * @author Christian Schwinne * @license MIT * @contributors d-999 @@ -37,7 +37,7 @@ #else #ifdef ARDUINO_ARCH_ESP32 #include - #include "../webserver/WebServer.h" //if you get an error here please update to ESP32 arduino core 1.0.0 + #include //if you get an error here please update to ESP32 arduino core 1.0.0 #else #include #include @@ -46,7 +46,7 @@ #include #ifdef ESPALEXA_DEBUG - #pragma message "Espalexa 2.3.3 debug mode" + #pragma message "Espalexa 2.3.4 debug mode" #define EA_DEBUG(x) Serial.print (x) #define EA_DEBUGLN(x) Serial.println (x) #else @@ -80,6 +80,7 @@ private: String escapedMac=""; //lowercase mac address //private member functions + //device JSON string: color+temperature device emulates LCT015, dimmable device LWB010, (TODO: on/off Plug 01, color temperature device LWT010, color device LST001) String deviceJsonString(uint8_t deviceId) { if (deviceId < 1 || deviceId > currentDeviceCount) return "{}"; //error @@ -89,7 +90,9 @@ private: json += "\",\"manufacturername\":\"OpenSource\",\"swversion\":\"0.1\",\"name\":\""; json += dev->getName(); json += "\",\"uniqueid\":\""+ WiFi.macAddress() +"-"+ (deviceId+1) ; - json += "\",\"modelid\":\"LST001\",\"state\":{\"on\":"; + json += "\",\"modelid\":\""; + json += dev->isColorDevice() ? "LCT015" : "LWB010"; + json += "\",\"state\":{\"on\":"; json += boolString(dev->getValue()) +",\"bri\":"+ (String)(dev->getLastValue()-1) ; if (dev->isColorDevice()) { @@ -112,7 +115,7 @@ private: } res += "\r\nFree Heap: " + (String)ESP.getFreeHeap(); res += "\r\nUptime: " + (String)millis(); - res += "\r\n\r\nEspalexa library v2.3.3 by Christian Schwinne 2019"; + res += "\r\n\r\nEspalexa library v2.3.4 by Christian Schwinne 2019"; server->send(200, "text/plain", res); } @@ -146,7 +149,7 @@ private: "http://"+ String(s) +":80/" "" "urn:schemas-upnp-org:device:Basic:1" - "Philips hue ("+ String(s) +")" + "Espalexa ("+ String(s) +")" "Royal Philips Electronics" "http://www.philips.com" "Philips hue Personal Wireless Lighting" @@ -164,13 +167,6 @@ private: " 24" " hue_logo_0.png" " " - " " - " image/png" - " 120" - " 120" - " 24" - " hue_logo_3.png" - " " "" "" ""; diff --git a/wled00/src/dependencies/webserver/ESP8266HTTPUpdateServer.cpp b/wled00/src/dependencies/webserver/ESP8266HTTPUpdateServer.cpp deleted file mode 100644 index 6c4a67327..000000000 --- a/wled00/src/dependencies/webserver/ESP8266HTTPUpdateServer.cpp +++ /dev/null @@ -1,105 +0,0 @@ -#include -#include -#include -#ifdef ARDUINO_ARCH_ESP32 -#include "WebServer.h" -#include -#else -#include -#endif -#include -#include "ESP8266HTTPUpdateServer.h" - - -const char* ESP8266HTTPUpdateServer::_serverIndex = -R"(

WLED Software Update


Get the latest binaries on the project GitHub page!
-Unsure which binary is correct? Go to the /build subpage for the details of this version.
-Be sure to upload a valid .bin file for your ESP! Otherwise you'll need USB recovery!
-



-
- - -
-)"; -const char* ESP8266HTTPUpdateServer::_failedResponse = R"(Update Failed!)"; -const char* ESP8266HTTPUpdateServer::_successResponse = R"(Update Successful! Rebooting, please wait for redirect...)"; - -ESP8266HTTPUpdateServer::ESP8266HTTPUpdateServer(bool serial_debug) -{ - _serial_output = serial_debug; - _server = NULL; - _username = NULL; - _password = NULL; - _authenticated = false; -} - -#ifdef ARDUINO_ARCH_ESP32 -void ESP8266HTTPUpdateServer::setup(WebServer *server, const char * path, const char * username, const char * password) -#else -void ESP8266HTTPUpdateServer::setup(ESP8266WebServer *server, const char * path, const char * username, const char * password) -#endif -{ - _server = server; - _username = (char *)username; - _password = (char *)password; - - // handler for the /update form page - _server->on(path, HTTP_GET, [&](){ - if(_username != NULL && _password != NULL && !_server->authenticate(_username, _password)) - return _server->requestAuthentication(); - _server->send(200, "text/html", _serverIndex); - }); - - // handler for the /update form POST (once file upload finishes) - _server->on(path, HTTP_POST, [&](){ - if(!_authenticated) - return _server->requestAuthentication(); - _server->send(200, "text/html", Update.hasError() ? _failedResponse : _successResponse); - ESP.restart(); - },[&](){ - // handler for the file upload, get's the sketch bytes, and writes - // them through the Update object - HTTPUpload& upload = _server->upload(); - if(upload.status == UPLOAD_FILE_START){ - if (_serial_output) - Serial.setDebugOutput(true); - - _authenticated = (_username == NULL || _password == NULL || _server->authenticate(_username, _password)); - if(!_authenticated){ - if (_serial_output) - Serial.printf("Unauthenticated Update\n"); - return; - } - #ifndef ARDUINO_ARCH_ESP32 - WiFiUDP::stopAll(); - #endif - if (_serial_output) - Serial.printf("Update: %s\n", upload.filename.c_str()); - #ifdef ARDUINO_ARCH_ESP32 - uint32_t maxSketchSpace = 0x100000; //dirty workaround, limit to 1MB - #else - uint32_t maxSketchSpace = (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000; - #endif - if(!Update.begin(maxSketchSpace)){//start with max available size - if (_serial_output) Update.printError(Serial); - } - } else if(_authenticated && upload.status == UPLOAD_FILE_WRITE){ - if (_serial_output) Serial.printf("."); - if(Update.write(upload.buf, upload.currentSize) != upload.currentSize){ - if (_serial_output) Update.printError(Serial); - - } - } else if(_authenticated && upload.status == UPLOAD_FILE_END){ - if(Update.end(true)){ //true to set the size to the current progress - if (_serial_output) Serial.printf("Update Success: %u\nRebooting...\n", upload.totalSize); - } else { - if (_serial_output) Update.printError(Serial); - } - if (_serial_output) Serial.setDebugOutput(false); - } else if(_authenticated && upload.status == UPLOAD_FILE_ABORTED){ - Update.end(); - if (_serial_output) Serial.println("Update was aborted"); - } - delay(0); - }); -} diff --git a/wled00/src/dependencies/webserver/ESP8266HTTPUpdateServer.h b/wled00/src/dependencies/webserver/ESP8266HTTPUpdateServer.h deleted file mode 100644 index 363d3bccf..000000000 --- a/wled00/src/dependencies/webserver/ESP8266HTTPUpdateServer.h +++ /dev/null @@ -1,74 +0,0 @@ -#ifndef __HTTP_UPDATE_SERVER_H -#define __HTTP_UPDATE_SERVER_H - -#ifdef ARDUINO_ARCH_ESP32 -class WebServer; - -class ESP8266HTTPUpdateServer -{ - private: - bool _serial_output; - WebServer *_server; - static const char *_serverIndex; - static const char *_failedResponse; - static const char *_successResponse; - char * _username; - char * _password; - bool _authenticated; - public: - ESP8266HTTPUpdateServer(bool serial_debug=false); - - void setup(WebServer *server) - { - setup(server, NULL, NULL); - } - - void setup(WebServer *server, const char * path) - { - setup(server, path, NULL, NULL); - } - - void setup(WebServer *server, const char * username, const char * password) - { - setup(server, "/update", username, password); - } - - void setup(WebServer *server, const char * path, const char * username, const char * password); -}; -#else -class ESP8266WebServer; - -class ESP8266HTTPUpdateServer -{ - private: - bool _serial_output; - ESP8266WebServer *_server; - static const char *_serverIndex; - static const char *_failedResponse; - static const char *_successResponse; - char * _username; - char * _password; - bool _authenticated; - public: - ESP8266HTTPUpdateServer(bool serial_debug=false); - - void setup(ESP8266WebServer *server) - { - setup(server, NULL, NULL); - } - - void setup(ESP8266WebServer *server, const char * path) - { - setup(server, path, NULL, NULL); - } - - void setup(ESP8266WebServer *server, const char * username, const char * password) - { - setup(server, "/update", username, password); - } - - void setup(ESP8266WebServer *server, const char * path, const char * username, const char * password); -}; -#endif - -#endif diff --git a/wled00/src/dependencies/webserver/ESP8266WebServer.h b/wled00/src/dependencies/webserver/ESP8266WebServer.h deleted file mode 100644 index 72abfe7bc..000000000 --- a/wled00/src/dependencies/webserver/ESP8266WebServer.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - ESP8266WebServer.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 ESP8266WEBSERVER_H -#define ESP8266WEBSERVER_H - -#include "WebServer.h" - -#endif //ESP8266WEBSERVER_H diff --git a/wled00/src/dependencies/webserver/LICENSE b/wled00/src/dependencies/webserver/LICENSE deleted file mode 100644 index 19e307187..000000000 --- a/wled00/src/dependencies/webserver/LICENSE +++ /dev/null @@ -1,504 +0,0 @@ - GNU LESSER GENERAL PUBLIC LICENSE - Version 2.1, February 1999 - - Copyright (C) 1991, 1999 Free Software Foundation, Inc. - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - -(This is the first released version of the Lesser GPL. It also counts - as the successor of the GNU Library Public License, version 2, hence - the version number 2.1.) - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -Licenses are intended to guarantee your freedom to share and change -free software--to make sure the software is free for all its users. - - This license, the Lesser General Public License, applies to some -specially designated software packages--typically libraries--of the -Free Software Foundation and other authors who decide to use it. You -can use it too, but we suggest you first think carefully about whether -this license or the ordinary General Public License is the better -strategy to use in any particular case, based on the explanations below. - - When we speak of free software, we are referring to freedom of use, -not price. Our General Public Licenses are designed to make sure that -you have the freedom to distribute copies of free software (and charge -for this service if you wish); that you receive source code or can get -it if you want it; that you can change the software and use pieces of -it in new free programs; and that you are informed that you can do -these things. - - To protect your rights, we need to make restrictions that forbid -distributors to deny you these rights or to ask you to surrender these -rights. These restrictions translate to certain responsibilities for -you if you distribute copies of the library or if you modify it. - - For example, if you distribute copies of the library, whether gratis -or for a fee, you must give the recipients all the rights that we gave -you. You must make sure that they, too, receive or can get the source -code. If you link other code with the library, you must provide -complete object files to the recipients, so that they can relink them -with the library after making changes to the library and recompiling -it. And you must show them these terms so they know their rights. - - We protect your rights with a two-step method: (1) we copyright the -library, and (2) we offer you this license, which gives you legal -permission to copy, distribute and/or modify the library. - - To protect each distributor, we want to make it very clear that -there is no warranty for the free library. Also, if the library is -modified by someone else and passed on, the recipients should know -that what they have is not the original version, so that the original -author's reputation will not be affected by problems that might be -introduced by others. - - Finally, software patents pose a constant threat to the existence of -any free program. We wish to make sure that a company cannot -effectively restrict the users of a free program by obtaining a -restrictive license from a patent holder. Therefore, we insist that -any patent license obtained for a version of the library must be -consistent with the full freedom of use specified in this license. - - Most GNU software, including some libraries, is covered by the -ordinary GNU General Public License. This license, the GNU Lesser -General Public License, applies to certain designated libraries, and -is quite different from the ordinary General Public License. We use -this license for certain libraries in order to permit linking those -libraries into non-free programs. - - When a program is linked with a library, whether statically or using -a shared library, the combination of the two is legally speaking a -combined work, a derivative of the original library. The ordinary -General Public License therefore permits such linking only if the -entire combination fits its criteria of freedom. The Lesser General -Public License permits more lax criteria for linking other code with -the library. - - We call this license the "Lesser" General Public License because it -does Less to protect the user's freedom than the ordinary General -Public License. It also provides other free software developers Less -of an advantage over competing non-free programs. These disadvantages -are the reason we use the ordinary General Public License for many -libraries. However, the Lesser license provides advantages in certain -special circumstances. - - For example, on rare occasions, there may be a special need to -encourage the widest possible use of a certain library, so that it becomes -a de-facto standard. To achieve this, non-free programs must be -allowed to use the library. A more frequent case is that a free -library does the same job as widely used non-free libraries. In this -case, there is little to gain by limiting the free library to free -software only, so we use the Lesser General Public License. - - In other cases, permission to use a particular library in non-free -programs enables a greater number of people to use a large body of -free software. For example, permission to use the GNU C Library in -non-free programs enables many more people to use the whole GNU -operating system, as well as its variant, the GNU/Linux operating -system. - - Although the Lesser General Public License is Less protective of the -users' freedom, it does ensure that the user of a program that is -linked with the Library has the freedom and the wherewithal to run -that program using a modified version of the Library. - - The precise terms and conditions for copying, distribution and -modification follow. Pay close attention to the difference between a -"work based on the library" and a "work that uses the library". The -former contains code derived from the library, whereas the latter must -be combined with the library in order to run. - - GNU LESSER GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License Agreement applies to any software library or other -program which contains a notice placed by the copyright holder or -other authorized party saying it may be distributed under the terms of -this Lesser General Public License (also called "this License"). -Each licensee is addressed as "you". - - A "library" means a collection of software functions and/or data -prepared so as to be conveniently linked with application programs -(which use some of those functions and data) to form executables. - - The "Library", below, refers to any such software library or work -which has been distributed under these terms. A "work based on the -Library" means either the Library or any derivative work under -copyright law: that is to say, a work containing the Library or a -portion of it, either verbatim or with modifications and/or translated -straightforwardly into another language. (Hereinafter, translation is -included without limitation in the term "modification".) - - "Source code" for a work means the preferred form of the work for -making modifications to it. For a library, complete source code means -all the source code for all modules it contains, plus any associated -interface definition files, plus the scripts used to control compilation -and installation of the library. - - Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running a program using the Library is not restricted, and output from -such a program is covered only if its contents constitute a work based -on the Library (independent of the use of the Library in a tool for -writing it). Whether that is true depends on what the Library does -and what the program that uses the Library does. - - 1. You may copy and distribute verbatim copies of the Library's -complete source code as you receive it, in any medium, provided that -you conspicuously and appropriately publish on each copy an -appropriate copyright notice and disclaimer of warranty; keep intact -all the notices that refer to this License and to the absence of any -warranty; and distribute a copy of this License along with the -Library. - - You may charge a fee for the physical act of transferring a copy, -and you may at your option offer warranty protection in exchange for a -fee. - - 2. You may modify your copy or copies of the Library or any portion -of it, thus forming a work based on the Library, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) The modified work must itself be a software library. - - b) You must cause the files modified to carry prominent notices - stating that you changed the files and the date of any change. - - c) You must cause the whole of the work to be licensed at no - charge to all third parties under the terms of this License. - - d) If a facility in the modified Library refers to a function or a - table of data to be supplied by an application program that uses - the facility, other than as an argument passed when the facility - is invoked, then you must make a good faith effort to ensure that, - in the event an application does not supply such function or - table, the facility still operates, and performs whatever part of - its purpose remains meaningful. - - (For example, a function in a library to compute square roots has - a purpose that is entirely well-defined independent of the - application. Therefore, Subsection 2d requires that any - application-supplied function or table used by this function must - be optional: if the application does not supply it, the square - root function must still compute square roots.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Library, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Library, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote -it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Library. - -In addition, mere aggregation of another work not based on the Library -with the Library (or with a work based on the Library) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may opt to apply the terms of the ordinary GNU General Public -License instead of this License to a given copy of the Library. To do -this, you must alter all the notices that refer to this License, so -that they refer to the ordinary GNU General Public License, version 2, -instead of to this License. (If a newer version than version 2 of the -ordinary GNU General Public License has appeared, then you can specify -that version instead if you wish.) Do not make any other change in -these notices. - - Once this change is made in a given copy, it is irreversible for -that copy, so the ordinary GNU General Public License applies to all -subsequent copies and derivative works made from that copy. - - This option is useful when you wish to copy part of the code of -the Library into a program that is not a library. - - 4. You may copy and distribute the Library (or a portion or -derivative of it, under Section 2) in object code or executable form -under the terms of Sections 1 and 2 above provided that you accompany -it with the complete corresponding machine-readable source code, which -must be distributed under the terms of Sections 1 and 2 above on a -medium customarily used for software interchange. - - If distribution of object code is made by offering access to copy -from a designated place, then offering equivalent access to copy the -source code from the same place satisfies the requirement to -distribute the source code, even though third parties are not -compelled to copy the source along with the object code. - - 5. A program that contains no derivative of any portion of the -Library, but is designed to work with the Library by being compiled or -linked with it, is called a "work that uses the Library". Such a -work, in isolation, is not a derivative work of the Library, and -therefore falls outside the scope of this License. - - However, linking a "work that uses the Library" with the Library -creates an executable that is a derivative of the Library (because it -contains portions of the Library), rather than a "work that uses the -library". The executable is therefore covered by this License. -Section 6 states terms for distribution of such executables. - - When a "work that uses the Library" uses material from a header file -that is part of the Library, the object code for the work may be a -derivative work of the Library even though the source code is not. -Whether this is true is especially significant if the work can be -linked without the Library, or if the work is itself a library. The -threshold for this to be true is not precisely defined by law. - - If such an object file uses only numerical parameters, data -structure layouts and accessors, and small macros and small inline -functions (ten lines or less in length), then the use of the object -file is unrestricted, regardless of whether it is legally a derivative -work. (Executables containing this object code plus portions of the -Library will still fall under Section 6.) - - Otherwise, if the work is a derivative of the Library, you may -distribute the object code for the work under the terms of Section 6. -Any executables containing that work also fall under Section 6, -whether or not they are linked directly with the Library itself. - - 6. As an exception to the Sections above, you may also combine or -link a "work that uses the Library" with the Library to produce a -work containing portions of the Library, and distribute that work -under terms of your choice, provided that the terms permit -modification of the work for the customer's own use and reverse -engineering for debugging such modifications. - - You must give prominent notice with each copy of the work that the -Library is used in it and that the Library and its use are covered by -this License. You must supply a copy of this License. If the work -during execution displays copyright notices, you must include the -copyright notice for the Library among them, as well as a reference -directing the user to the copy of this License. Also, you must do one -of these things: - - a) Accompany the work with the complete corresponding - machine-readable source code for the Library including whatever - changes were used in the work (which must be distributed under - Sections 1 and 2 above); and, if the work is an executable linked - with the Library, with the complete machine-readable "work that - uses the Library", as object code and/or source code, so that the - user can modify the Library and then relink to produce a modified - executable containing the modified Library. (It is understood - that the user who changes the contents of definitions files in the - Library will not necessarily be able to recompile the application - to use the modified definitions.) - - b) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (1) uses at run time a - copy of the library already present on the user's computer system, - rather than copying library functions into the executable, and (2) - will operate properly with a modified version of the library, if - the user installs one, as long as the modified version is - interface-compatible with the version that the work was made with. - - c) Accompany the work with a written offer, valid for at - least three years, to give the same user the materials - specified in Subsection 6a, above, for a charge no more - than the cost of performing this distribution. - - d) If distribution of the work is made by offering access to copy - from a designated place, offer equivalent access to copy the above - specified materials from the same place. - - e) Verify that the user has already received a copy of these - materials or that you have already sent this user a copy. - - For an executable, the required form of the "work that uses the -Library" must include any data and utility programs needed for -reproducing the executable from it. However, as a special exception, -the materials to be distributed need not include anything that is -normally distributed (in either source or binary form) with the major -components (compiler, kernel, and so on) of the operating system on -which the executable runs, unless that component itself accompanies -the executable. - - It may happen that this requirement contradicts the license -restrictions of other proprietary libraries that do not normally -accompany the operating system. Such a contradiction means you cannot -use both them and the Library together in an executable that you -distribute. - - 7. You may place library facilities that are a work based on the -Library side-by-side in a single library together with other library -facilities not covered by this License, and distribute such a combined -library, provided that the separate distribution of the work based on -the Library and of the other library facilities is otherwise -permitted, and provided that you do these two things: - - a) Accompany the combined library with a copy of the same work - based on the Library, uncombined with any other library - facilities. This must be distributed under the terms of the - Sections above. - - b) Give prominent notice with the combined library of the fact - that part of it is a work based on the Library, and explaining - where to find the accompanying uncombined form of the same work. - - 8. You may not copy, modify, sublicense, link with, or distribute -the Library except as expressly provided under this License. Any -attempt otherwise to copy, modify, sublicense, link with, or -distribute the Library is void, and will automatically terminate your -rights under this License. However, parties who have received copies, -or rights, from you under this License will not have their licenses -terminated so long as such parties remain in full compliance. - - 9. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Library or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Library (or any work based on the -Library), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Library or works based on it. - - 10. Each time you redistribute the Library (or any work based on the -Library), the recipient automatically receives a license from the -original licensor to copy, distribute, link with or modify the Library -subject to these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties with -this License. - - 11. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Library at all. For example, if a patent -license would not permit royalty-free redistribution of the Library by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Library. - -If any portion of this section is held invalid or unenforceable under any -particular circumstance, the balance of the section is intended to apply, -and the section as a whole is intended to apply in other circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 12. If the distribution and/or use of the Library is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Library under this License may add -an explicit geographical distribution limitation excluding those countries, -so that distribution is permitted only in or among countries not thus -excluded. In such case, this License incorporates the limitation as if -written in the body of this License. - - 13. The Free Software Foundation may publish revised and/or new -versions of the Lesser General Public License from time to time. -Such new versions will be similar in spirit to the present version, -but may differ in detail to address new problems or concerns. - -Each version is given a distinguishing version number. If the Library -specifies a version number of this License which applies to it and -"any later version", you have the option of following the terms and -conditions either of that version or of any later version published by -the Free Software Foundation. If the Library does not specify a -license version number, you may choose any version ever published by -the Free Software Foundation. - - 14. If you wish to incorporate parts of the Library into other free -programs whose distribution conditions are incompatible with these, -write to the author to ask for permission. For software which is -copyrighted by the Free Software Foundation, write to the Free -Software Foundation; we sometimes make exceptions for this. Our -decision will be guided by the two goals of preserving the free status -of all derivatives of our free software and of promoting the sharing -and reuse of software generally. - - NO WARRANTY - - 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO -WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. -EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR -OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY -KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE -LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME -THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN -WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY -AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU -FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR -CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE -LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING -RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A -FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF -SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Libraries - - If you develop a new library, and you want it to be of the greatest -possible use to the public, we recommend making it free software that -everyone can redistribute and change. You can do so by permitting -redistribution under these terms (or, alternatively, under the terms of the -ordinary General Public License). - - To apply these terms, attach the following notices to the library. It is -safest to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least the -"copyright" line and a pointer to where the full notice is found. - - {description} - Copyright (C) {year} {fullname} - - 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 Street, Fifth Floor, Boston, MA 02110-1301 - USA - -Also add information on how to contact you by electronic and paper mail. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the library, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the - library `Frob' (a library for tweaking knobs) written by James Random - Hacker. - - {signature of Ty Coon}, 1 April 1990 - Ty Coon, President of Vice - -That's all there is to it! diff --git a/wled00/src/dependencies/webserver/Parsing.cpp b/wled00/src/dependencies/webserver/Parsing.cpp deleted file mode 100644 index 2c2c0cbcf..000000000 --- a/wled00/src/dependencies/webserver/Parsing.cpp +++ /dev/null @@ -1,613 +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 -#ifdef ARDUINO_ARCH_ESP32 //only use this library if building for ESP32 - -#include "WiFiServer.h" -#include "WiFiClient.h" -#include "WebServer.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 WebServer::_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 = 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); - } else { - // No content - but we can still have arguments in the URL. - _parseArguments(searchStr); - } - } - - 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 WebServer::_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 WebServer::_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 = urlDecode(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 WebServer::_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 WebServer::_uploadReadByte(WiFiClient& client){ - int res = client.read(); - if(res == -1){ - while(!client.available() && client.connected()) - yield(); - res = client.read(); - } - return (uint8_t)res; -} - -bool WebServer::_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 WebServer::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 WebServer::_parseFormUploadAborted(){ - _currentUpload.status = UPLOAD_FILE_ABORTED; - if(_currentHandler && _currentHandler->canUpload(_currentUri)) - _currentHandler->upload(*this, _currentUri, _currentUpload); - return false; -} -#endif diff --git a/wled00/src/dependencies/webserver/README.md b/wled00/src/dependencies/webserver/README.md deleted file mode 100644 index dc2178cf1..000000000 --- a/wled00/src/dependencies/webserver/README.md +++ /dev/null @@ -1,11 +0,0 @@ -Notice by Aircoookie: Port of the ESP8266HTTPUpdateServer for ESP32 is also included in this directory. - -# WebServer -ESP8266/ESP32 WebServer library - -This is an experimental port of the ESP8266WebServer library that should work -on ESP8266 and ESP32. This is NOT an official repo supported by Espressif. Do -not depend on this code for anything important or expect it to be updated. Once -the official repo is created, this repo will be deleted. - -Added Travis CI diff --git a/wled00/src/dependencies/webserver/WebServer.cpp b/wled00/src/dependencies/webserver/WebServer.cpp deleted file mode 100644 index 83785bef2..000000000 --- a/wled00/src/dependencies/webserver/WebServer.cpp +++ /dev/null @@ -1,529 +0,0 @@ -/* - 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 -#ifdef ARDUINO_ARCH_ESP32 //only use this library if building for ESP32 - -#include -#include "WiFiServer.h" -#include "WiFiClient.h" -#include "WebServer.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"; - -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() { -#ifdef ESP8266 - _server.stop(); -#else - // TODO add ESP32 WiFiServer::stop() - _server.end(); -#endif -} - -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_P(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(); -} - -//Modified by Aircoookie to work for WLED -void WebServer::collectHeaders(String headerKey) { - _headerKeysCount = 2; - if (_currentHeaders) delete[]_currentHeaders; - _currentHeaders = new RequestArgument[2]; - _currentHeaders[0].key = AUTHORIZATION_HEADER; - _currentHeaders[1].key = headerKey; -} - -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 ""; - } -} -#endif \ No newline at end of file diff --git a/wled00/src/dependencies/webserver/WebServer.h b/wled00/src/dependencies/webserver/WebServer.h deleted file mode 100644 index 5a9ed0de7..000000000 --- a/wled00/src/dependencies/webserver/WebServer.h +++ /dev/null @@ -1,236 +0,0 @@ -/* - WebServer.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 WEBSERVER_H -#define WEBSERVER_H - -#include -#ifdef ESP8266 -#define WebServer ESP8266WebServer -#include -#else -#include -#define write_P write -#endif - -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 - -#ifndef HTTP_UPLOAD_BUFLEN -#define HTTP_UPLOAD_BUFLEN 2048 -#endif - -#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 WebServer; - -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 WebServer -{ -public: - WebServer(IPAddress addr, int port = 80); - WebServer(int port = 80); - ~WebServer(); - - 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(String headerKey); // 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); - -#ifdef ESP8266 -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, ""); - return _currentClient.write(file); -} -#else -template size_t streamFile(T &file, const String& contentType){ -#define STREAMFILE_BUFSIZE 2*1460 - 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 = (uint8_t *)malloc(STREAMFILE_BUFSIZE); - if (buf == NULL) { - //DBG_OUTPUT_PORT.printf("streamFile malloc failed"); - return 0; - } - size_t totalBytesOut = 0; - while (client().connected() && (file.available() > 0)) { - int bytesOut; - int bytesIn = file.read(buf, STREAMFILE_BUFSIZE); - if (bytesIn <= 0) break; - while (1) { - bytesOut = 0; - if (!client().connected()) break; - bytesOut = client().write(buf, bytesIn); - if (bytesIn == bytesOut) break; - - //DBG_OUTPUT_PORT.printf("bytesIn %d != bytesOut %d\r\n", - //bytesIn, bytesOut); - delay(1); - } - totalBytesOut += bytesOut; - yield(); - } - if (totalBytesOut != file.size()) { - //DBG_OUTPUT_PORT.printf("file size %d bytes out %d\r\n", - // file.size(), totalBytesOut); - } - free(buf); - return totalBytesOut; -} -#endif - -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 //WEBSERVER_H diff --git a/wled00/src/dependencies/webserver/detail/RequestHandler.h b/wled00/src/dependencies/webserver/detail/RequestHandler.h deleted file mode 100644 index c1cc909d8..000000000 --- a/wled00/src/dependencies/webserver/detail/RequestHandler.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef REQUESTHANDLER_H -#define REQUESTHANDLER_H - -class RequestHandler { -public: - virtual ~RequestHandler() { } - virtual bool canHandle(HTTPMethod method, String uri) { (void) method; (void) uri; return false; } - virtual bool canUpload(String uri) { (void) uri; return false; } - virtual bool handle(WebServer& server, HTTPMethod requestMethod, String requestUri) { (void) server; (void) requestMethod; (void) requestUri; return false; } - virtual void upload(WebServer& server, String requestUri, HTTPUpload& upload) { (void) server; (void) requestUri; (void) upload; } - - RequestHandler* next() { return _next; } - void next(RequestHandler* r) { _next = r; } - -private: - RequestHandler* _next = nullptr; -}; - -#endif //REQUESTHANDLER_H diff --git a/wled00/src/dependencies/webserver/detail/RequestHandlersImpl.h b/wled00/src/dependencies/webserver/detail/RequestHandlersImpl.h deleted file mode 100644 index e337bc110..000000000 --- a/wled00/src/dependencies/webserver/detail/RequestHandlersImpl.h +++ /dev/null @@ -1,191 +0,0 @@ -#ifndef REQUESTHANDLERSIMPL_H -#define REQUESTHANDLERSIMPL_H - -#include "RequestHandler.h" - -#ifdef ESP8266 -// Table of extension->MIME strings stored in PROGMEM, needs to be global due to GCC section typing rules -static const struct {const char endsWith[16]; const char mimeType[32];} mimeTable[] ICACHE_RODATA_ATTR = { -#else -static const struct {const char endsWith[16]; const char mimeType[32];} mimeTable[] = { -#endif - { ".html", "text/html" }, - { ".htm", "text/html" }, - { ".css", "text/css" }, - { ".txt", "text/plain" }, - { ".js", "application/javascript" }, - { ".json", "application/json" }, - { ".png", "image/png" }, - { ".gif", "image/gif" }, - { ".jpg", "image/jpeg" }, - { ".ico", "image/x-icon" }, - { ".svg", "image/svg+xml" }, - { ".ttf", "application/x-font-ttf" }, - { ".otf", "application/x-font-opentype" }, - { ".woff", "application/font-woff" }, - { ".woff2", "application/font-woff2" }, - { ".eot", "application/vnd.ms-fontobject" }, - { ".sfnt", "application/font-sfnt" }, - { ".xml", "text/xml" }, - { ".pdf", "application/pdf" }, - { ".zip", "application/zip" }, - { ".gz", "application/x-gzip" }, - { ".appcache", "text/cache-manifest" }, - { "", "application/octet-stream" } }; - -class FunctionRequestHandler : public RequestHandler { -public: - FunctionRequestHandler(WebServer::THandlerFunction fn, WebServer::THandlerFunction ufn, const String &uri, HTTPMethod method) - : _fn(fn) - , _ufn(ufn) - , _uri(uri) - , _method(method) - { - } - - bool canHandle(HTTPMethod requestMethod, String requestUri) override { - if (_method != HTTP_ANY && _method != requestMethod) - return false; - - if (requestUri != _uri) - return false; - - return true; - } - - bool canUpload(String requestUri) override { - if (!_ufn || !canHandle(HTTP_POST, requestUri)) - return false; - - return true; - } - - bool handle(WebServer& server, HTTPMethod requestMethod, String requestUri) override { - (void) server; - if (!canHandle(requestMethod, requestUri)) - return false; - - _fn(); - return true; - } - - void upload(WebServer& server, String requestUri, HTTPUpload& upload) override { - (void) server; - (void) upload; - if (canUpload(requestUri)) - _ufn(); - } - -protected: - WebServer::THandlerFunction _fn; - WebServer::THandlerFunction _ufn; - String _uri; - HTTPMethod _method; -}; - -class StaticRequestHandler : public RequestHandler { -public: - StaticRequestHandler(FS& fs, const char* path, const char* uri, const char* cache_header) - : _fs(fs) - , _uri(uri) - , _path(path) - , _cache_header(cache_header) - { - _isFile = fs.exists(path); -#ifdef ESP8266 - DEBUGV("StaticRequestHandler: path=%s uri=%s isFile=%d, cache_header=%s\r\n", path, uri, _isFile, cache_header); -#else -#ifdef DEBUG_ESP_HTTP_SERVER - DEBUG_OUTPUT.printf("StaticRequestHandler: path=%s uri=%s isFile=%d, cache_header=%s\r\n", path, uri, _isFile, cache_header); -#endif -#endif - _baseUriLength = _uri.length(); - } - - bool canHandle(HTTPMethod requestMethod, String requestUri) override { - if (requestMethod != HTTP_GET) - return false; - - if ((_isFile && requestUri != _uri) || !requestUri.startsWith(_uri)) - return false; - - return true; - } - - bool handle(WebServer& server, HTTPMethod requestMethod, String requestUri) override { - if (!canHandle(requestMethod, requestUri)) - return false; - -#ifdef ESP8266 - DEBUGV("StaticRequestHandler::handle: request=%s _uri=%s\r\n", requestUri.c_str(), _uri.c_str()); -#else -#ifdef DEBUG_ESP_HTTP_SERVER - DEBUG_OUTPUT.printf("StaticRequestHandler::handle: request=%s _uri=%s\r\n", requestUri.c_str(), _uri.c_str()); -#endif -#endif - - String path(_path); - - if (!_isFile) { - // Base URI doesn't point to a file. - // If a directory is requested, look for index file. - if (requestUri.endsWith("/")) requestUri += "index.htm"; - - // Append whatever follows this URI in request to get the file path. - path += requestUri.substring(_baseUriLength); - } -#ifdef ESP8266 - DEBUGV("StaticRequestHandler::handle: path=%s, isFile=%d\r\n", path.c_str(), _isFile); -#else -#ifdef DEBUG_ESP_HTTP_SERVER - DEBUG_OUTPUT.printf("StaticRequestHandler::handle: path=%s, isFile=%d\r\n", path.c_str(), _isFile); -#endif -#endif - - String contentType = getContentType(path); - - // look for gz file, only if the original specified path is not a gz. So part only works to send gzip via content encoding when a non compressed is asked for - // if you point the the path to gzip you will serve the gzip as content type "application/x-gzip", not text or javascript etc... - if (!path.endsWith(".gz") && !_fs.exists(path)) { - String pathWithGz = path + ".gz"; - if(_fs.exists(pathWithGz)) - path += ".gz"; - } - - File f = _fs.open(path, "r"); - if (!f) - return false; - - if (_cache_header.length() != 0) - server.sendHeader("Cache-Control", _cache_header); - - server.streamFile(f, contentType); - return true; - } - - static String getContentType(const String& path) { - char buff[sizeof(mimeTable[0].mimeType)]; - // Check all entries but last one for match, return if found - for (size_t i=0; i < sizeof(mimeTable)/sizeof(mimeTable[0])-1; i++) { - strcpy_P(buff, mimeTable[i].endsWith); - if (path.endsWith(buff)) { - strcpy_P(buff, mimeTable[i].mimeType); - return String(buff); - } - } - // Fall-through and just return default type - strcpy_P(buff, mimeTable[sizeof(mimeTable)/sizeof(mimeTable[0])-1].mimeType); - return String(buff); - } - -protected: - FS _fs; - String _uri; - String _path; - String _cache_header; - bool _isFile; - size_t _baseUriLength; -}; - - -#endif //REQUESTHANDLERSIMPL_H diff --git a/wled00/wled00.ino b/wled00/wled00.ino index 535977284..2ce6af434 100644 --- a/wled00/wled00.ino +++ b/wled00/wled00.ino @@ -34,7 +34,6 @@ #include #include #include - #include #include /*#ifndef WLED_DISABLE_INFRARED #include @@ -52,12 +51,12 @@ #endif #endif +#include #include #include #include #ifndef WLED_DISABLE_OTA #include - //#include "src/dependencies/webserver/ESP8266HTTPUpdateServer.h" #endif #include "src/dependencies/time/Time.h" #include "src/dependencies/time/TimeLib.h" @@ -81,7 +80,7 @@ //version code in format yymmddb (b = daily build) -#define VERSION 1902162 +#define VERSION 1902172 char versionString[] = "0.8.4-dev"; @@ -399,6 +398,8 @@ uint16_t olen = 0; String messageHead, messageSub; uint32_t optionType; +bool doReboot = false; //flag to initiate reboot from async handlers + //server library objects AsyncWebServer server(80); HTTPClient* hueClient = NULL; @@ -519,6 +520,7 @@ void loop() { handleOverlays(); yield(); + if (doReboot) reset(); if (!realtimeActive) //block stuff if WARLS/Adalight is enabled { diff --git a/wled00/wled03_set.ino b/wled00/wled03_set.ino index e166b3c5c..cf0351527 100644 --- a/wled00/wled03_set.ino +++ b/wled00/wled03_set.ino @@ -282,7 +282,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage) { clearEEPROM(); serveMessage(request, 200, "All Settings erased.", "Connect to WLED-AP to setup again",255); - reset(); + doReboot = true; } bool pwdCorrect = !otaLock; //always allow access if ota not locked diff --git a/wled00/wled18_server.ino b/wled00/wled18_server.ino index b7607aabc..5134402a2 100644 --- a/wled00/wled18_server.ino +++ b/wled00/wled18_server.ino @@ -28,14 +28,14 @@ void initServer() }); server.on("/reset", HTTP_GET, [](AsyncWebServerRequest *request){ - serveMessage(request, 200,"Rebooting now...","(takes ~20 seconds, wait for auto-redirect)",79); - reset(); + serveMessage(request, 200,"Rebooting now...","Please wait ~15 seconds...",132); + doReboot = true; }); server.on("/settings/wifi", HTTP_POST, [](AsyncWebServerRequest *request){ if (!(wifiLock && otaLock)) handleSettingsSet(request, 1); serveMessage(request, 200,"WiFi settings saved.","Rebooting now...",255); - reset(); + doReboot = true; }); server.on("/settings/leds", HTTP_POST, [](AsyncWebServerRequest *request){ @@ -66,14 +66,10 @@ void initServer() server.on("/settings/sec", HTTP_POST, [](AsyncWebServerRequest *request){ handleSettingsSet(request, 6); - serveMessage(request, 200,"Security settings saved.","Rebooting now... (takes ~20 seconds, wait for auto-redirect)",139); - reset(); + serveMessage(request, 200,"Security settings saved.","Rebooting now, please wait ~15 seconds...",132); + doReboot = true; }); - /*server.on("/json", HTTP_ANY, [](AsyncWebServerRequest *request){ - request->send(500, "application/json", "{\"error\":\"Not implemented\"}"); - });*/ - server.on("/json/effects", HTTP_GET, [](AsyncWebServerRequest *request){ request->send_P(200, "application/json", JSON_mode_names); }); @@ -81,6 +77,10 @@ void initServer() server.on("/json/palettes", HTTP_GET, [](AsyncWebServerRequest *request){ request->send_P(200, "application/json", JSON_palette_names); }); + + server.on("/json", HTTP_ANY, [](AsyncWebServerRequest *request){ + request->send(500, "application/json", "{\"error\":\"Not implemented\"}"); + }); server.on("/version", HTTP_GET, [](AsyncWebServerRequest *request){ request->send(200, "text/plain", (String)VERSION); @@ -133,7 +133,38 @@ void initServer() #endif //init ota page #ifndef WLED_DISABLE_OTA - //httpUpdater.setup(&server); + server.on("/update", HTTP_GET, [](AsyncWebServerRequest *request){ + serveMessage(request, 200, "WLED Software Update", "Your installed version: " + String(versionString) + "
Download the latest binary: " + "" + "
" + "
", 254); + }); + + server.on("/update", HTTP_POST, [](AsyncWebServerRequest *request){ + if (Update.hasError()) + { + serveMessage(request, 500, "Failed updating firmware!", "Please check your file and retry!", 254); return; + } + serveMessage(request, 200, "Successfully updated firmware!", "Please wait while the module reboots...", 132); + doReboot = true; + },[](AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final){ + if(!index){ + DEBUG_PRINTLN("OTA Update Start"); + #ifndef ARDUINO_ARCH_ESP32 + Update.runAsync(true); + #endif + Update.begin((ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000); + } + if(!Update.hasError()) Update.write(data, len); + if(final){ + if(Update.end(true)){ + DEBUG_PRINTLN("Update Success"); + } else { + DEBUG_PRINTLN("Update Failed"); + } + } + }); + #else server.on("/update", HTTP_GET, [](AsyncWebServerRequest *request){ serveMessage(request, 500, "Not implemented", "OTA updates are unsupported in this build.", 254); @@ -152,7 +183,6 @@ void initServer() }); } - //this ceased working somehow server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){ serveIndexOrWelcome(request); }); @@ -168,13 +198,6 @@ void initServer() { request->send(200); return; } - - //workaround for subpage issue - /*if (request->url().length() == 1) - { - serveIndexOrWelcome(request); - return; - }*/ if(!handleSet(request, request->url())){ #ifndef WLED_DISABLE_ALEXA @@ -271,18 +294,18 @@ String msgProcessor(const String& var) if (optionType < 60) //redirect to settings after optionType seconds { messageBody += ""; - } else if (optionType < 120) //redirect back after optionType-60 seconds + } else if (optionType < 120) //redirect back after optionType-60 seconds, unused { - messageBody += ""; + //messageBody += ""; } else if (optionType < 180) //reload parent after optionType-120 seconds { messageBody += ""; } else if (optionType == 253) { - messageBody += "

"; //button to settings + messageBody += "

"; //button to settings } else if (optionType == 254) { - messageBody += "

"; + messageBody += "

"; } return messageBody; }