From 1f07cd0d0a4f598dd655f64fa9a1f4b617c31d95 Mon Sep 17 00:00:00 2001 From: Matthias Hertel Date: Tue, 20 Jul 2021 22:09:55 +0200 Subject: [PATCH 01/10] WebServer eTag implementation improvements --- .../src/ESP8266WebServer-impl.h | 16 ++++- .../ESP8266WebServer/src/ESP8266WebServer.h | 6 ++ .../src/detail/RequestHandlersImpl.h | 70 +++++++++++++++---- 3 files changed, 76 insertions(+), 16 deletions(-) diff --git a/libraries/ESP8266WebServer/src/ESP8266WebServer-impl.h b/libraries/ESP8266WebServer/src/ESP8266WebServer-impl.h index 8c5cdf401e..bf4b9e1ed0 100644 --- a/libraries/ESP8266WebServer/src/ESP8266WebServer-impl.h +++ b/libraries/ESP8266WebServer/src/ESP8266WebServer-impl.h @@ -68,6 +68,13 @@ template void ESP8266WebServerTemplate::enableCORS(bool enable) { _corsEnabled = enable; } + +template +void ESP8266WebServerTemplate::enableETag(bool enable, ETagFunction fn) { + _eTagEnabled = enable; + _eTagFunction = fn; +} + template void ESP8266WebServerTemplate::begin() { close(); @@ -264,10 +271,11 @@ void ESP8266WebServerTemplate::serveStatic(const char* uri, FS& fs, file.close(); } - if(is_file) + if(is_file) { _addRequestHandler(new StaticFileRequestHandler(fs, path, uri, cache_header)); - else + } else { _addRequestHandler(new StaticDirectoryRequestHandler(fs, path, uri, cache_header)); + } } template @@ -436,6 +444,10 @@ void ESP8266WebServerTemplate::_prepareHeader(String& response, int sendHeader(String(F("Keep-Alive")), String(F("timeout=")) + HTTP_MAX_CLOSE_WAIT); } + if (_eTagEnabled) { + // sendHeader(String(F("Access-Control-Allow-Origin")), String("*")); + } + response += _responseHeaders; response += "\r\n"; _responseHeaders = ""; diff --git a/libraries/ESP8266WebServer/src/ESP8266WebServer.h b/libraries/ESP8266WebServer/src/ESP8266WebServer.h index eeb98c15a7..334612d30a 100644 --- a/libraries/ESP8266WebServer/src/ESP8266WebServer.h +++ b/libraries/ESP8266WebServer/src/ESP8266WebServer.h @@ -114,6 +114,8 @@ class ESP8266WebServerTemplate void requestAuthentication(HTTPAuthMethod mode = BASIC_AUTH, const char* realm = NULL, const String& authFailMsg = String("") ); typedef std::function THandlerFunction; + typedef std::function ETagFunction; + void on(const Uri &uri, THandlerFunction handler); void on(const Uri &uri, HTTPMethod method, THandlerFunction fn); void on(const Uri &uri, HTTPMethod method, THandlerFunction fn, THandlerFunction ufn); @@ -122,6 +124,7 @@ class ESP8266WebServerTemplate void onNotFound(THandlerFunction fn); //called when handler is not assigned void onFileUpload(THandlerFunction fn); //handle file uploads void enableCORS(bool enable); + void enableETag(bool enable, ETagFunction fn = nullptr); const String& uri() const { return _currentUri; } HTTPMethod method() const { return _currentMethod; } @@ -271,6 +274,9 @@ class ESP8266WebServerTemplate } } + bool _eTagEnabled = false; + ETagFunction _eTagFunction = nullptr; + protected: void _addRequestHandler(RequestHandlerType* handler); void _handleRequest(); diff --git a/libraries/ESP8266WebServer/src/detail/RequestHandlersImpl.h b/libraries/ESP8266WebServer/src/detail/RequestHandlersImpl.h index c88c830320..dea6cfb76b 100644 --- a/libraries/ESP8266WebServer/src/detail/RequestHandlersImpl.h +++ b/libraries/ESP8266WebServer/src/detail/RequestHandlersImpl.h @@ -9,6 +9,25 @@ namespace esp8266webserver { +// calculate an eTag for a file in filesystem +static String calcETag(FS &fs, const String path) { + String result; + + // calculate eTag using md5 checksum + uint8_t md5_buf[16]; + File f = fs.open(path, "r"); + MD5Builder calcMD5; + calcMD5.begin(); + calcMD5.addStream(f, f.size()); + calcMD5.calculate(); + calcMD5.getBytes(md5_buf); + f.close(); + // create a minimal-length eTag using base64 byte[]->text encoding. + result = "\"" + base64::encode(md5_buf, 16, false) + "\""; + return(result); +} // calcETag + + template class FunctionRequestHandler : public RequestHandler { using WebServerType = ESP8266WebServerTemplate; @@ -92,6 +111,7 @@ class StaticRequestHandler : public RequestHandler { }; +// serve all files within a given directory template class StaticDirectoryRequestHandler : public StaticRequestHandler { @@ -117,6 +137,7 @@ class StaticDirectoryRequestHandler : public StaticRequestHandler { DEBUGV("DirectoryRequestHandler::handle: request=%s _uri=%s\r\n", requestUri.c_str(), SRH::_uri.c_str()); String path; + String eTagCode; path.reserve(SRH::_path.length() + requestUri.length() + 32); path = SRH::_path; @@ -156,10 +177,28 @@ class StaticDirectoryRequestHandler : public StaticRequestHandler { return false; } + if (server._eTagEnabled) { + if (server._eTagFunction) { + eTagCode = (server._eTagFunction)(SRH::_fs, path); + } else { + eTagCode = calcETag(SRH::_fs, path); + } + + if (server.header("If-None-Match") == eTagCode) { + server.send(304); + return true; + } + } + if (SRH::_cache_header.length() != 0) server.sendHeader("Cache-Control", SRH::_cache_header); + if ((server._eTagEnabled) && (eTagCode.length() > 0)) { + server.sendHeader("ETag", eTagCode); + } + server.streamFile(f, contentType, requestMethod); + return true; } @@ -167,6 +206,8 @@ class StaticDirectoryRequestHandler : public StaticRequestHandler { size_t _baseUriLength; }; + +// Serve a specific, single file template class StaticFileRequestHandler : @@ -180,13 +221,6 @@ public StaticRequestHandler { : StaticRequestHandler{fs, path, uri, cache_header} { - File f = SRH::_fs.open(path, "r"); - MD5Builder calcMD5; - calcMD5.begin(); - calcMD5.addStream(f, f.size()); - calcMD5.calculate(); - calcMD5.getBytes(_ETag_md5); - f.close(); } bool canHandle(HTTPMethod requestMethod, const String& requestUri) override { @@ -197,11 +231,17 @@ public StaticRequestHandler { if (!canHandle(requestMethod, requestUri)) return false; - const String etag = "\"" + base64::encode(_ETag_md5, 16, false) + "\""; - - if(server.header("If-None-Match") == etag){ - server.send(304); - return true; + if (server._eTagEnabled) { + if (server._eTagFunction) { + _eTagCode = (server._eTagFunction)(SRH::_fs, SRH::_path); + } else if (_eTagCode.isEmpty()) { + _eTagCode = calcETag(SRH::_fs, SRH::_path); + } + + if (server.header("If-None-Match") == _eTagCode) { + server.send(304); + return true; + } } File f = SRH::_fs.open(SRH::_path, "r"); @@ -217,14 +257,16 @@ public StaticRequestHandler { if (SRH::_cache_header.length() != 0) server.sendHeader("Cache-Control", SRH::_cache_header); - server.sendHeader("ETag", etag); + if ((server._eTagEnabled) && (_eTagCode.length() > 0)) { + server.sendHeader("ETag", _eTagCode); + } server.streamFile(f, mime::getContentType(SRH::_path), requestMethod); return true; } protected: - uint8_t _ETag_md5[16]; + String _eTagCode; // eTag as used in http header for this single file }; } // namespace From 4f1c5d502783242727b4f2c5451759f28e082630 Mon Sep 17 00:00:00 2001 From: Matthias Hertel Date: Tue, 20 Jul 2021 22:20:05 +0200 Subject: [PATCH 02/10] cleanup --- libraries/ESP8266WebServer/src/ESP8266WebServer-impl.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/libraries/ESP8266WebServer/src/ESP8266WebServer-impl.h b/libraries/ESP8266WebServer/src/ESP8266WebServer-impl.h index bf4b9e1ed0..42c8394e9d 100644 --- a/libraries/ESP8266WebServer/src/ESP8266WebServer-impl.h +++ b/libraries/ESP8266WebServer/src/ESP8266WebServer-impl.h @@ -444,10 +444,6 @@ void ESP8266WebServerTemplate::_prepareHeader(String& response, int sendHeader(String(F("Keep-Alive")), String(F("timeout=")) + HTTP_MAX_CLOSE_WAIT); } - if (_eTagEnabled) { - // sendHeader(String(F("Access-Control-Allow-Origin")), String("*")); - } - response += _responseHeaders; response += "\r\n"; _responseHeaders = ""; From b5e48de36e61f9366a4fd835e258d63ec4318d73 Mon Sep 17 00:00:00 2001 From: Matthias Hertel Date: Wed, 21 Jul 2021 08:19:15 +0200 Subject: [PATCH 03/10] fixing mac build --- libraries/ESP8266WebServer/src/ESP8266WebServer-impl.h | 3 ++- libraries/ESP8266WebServer/src/detail/RequestHandlersImpl.h | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/libraries/ESP8266WebServer/src/ESP8266WebServer-impl.h b/libraries/ESP8266WebServer/src/ESP8266WebServer-impl.h index 42c8394e9d..b9ca0fe535 100644 --- a/libraries/ESP8266WebServer/src/ESP8266WebServer-impl.h +++ b/libraries/ESP8266WebServer/src/ESP8266WebServer-impl.h @@ -72,7 +72,7 @@ void ESP8266WebServerTemplate::enableCORS(bool enable) { template void ESP8266WebServerTemplate::enableETag(bool enable, ETagFunction fn) { _eTagEnabled = enable; - _eTagFunction = fn; + _eTagFunction = fn; } template @@ -444,6 +444,7 @@ void ESP8266WebServerTemplate::_prepareHeader(String& response, int sendHeader(String(F("Keep-Alive")), String(F("timeout=")) + HTTP_MAX_CLOSE_WAIT); } + response += _responseHeaders; response += "\r\n"; _responseHeaders = ""; diff --git a/libraries/ESP8266WebServer/src/detail/RequestHandlersImpl.h b/libraries/ESP8266WebServer/src/detail/RequestHandlersImpl.h index dea6cfb76b..ea016328be 100644 --- a/libraries/ESP8266WebServer/src/detail/RequestHandlersImpl.h +++ b/libraries/ESP8266WebServer/src/detail/RequestHandlersImpl.h @@ -181,7 +181,7 @@ class StaticDirectoryRequestHandler : public StaticRequestHandler { if (server._eTagFunction) { eTagCode = (server._eTagFunction)(SRH::_fs, path); } else { - eTagCode = calcETag(SRH::_fs, path); + eTagCode = esp8266webserver::calcETag(SRH::_fs, path); } if (server.header("If-None-Match") == eTagCode) { @@ -235,7 +235,7 @@ public StaticRequestHandler { if (server._eTagFunction) { _eTagCode = (server._eTagFunction)(SRH::_fs, SRH::_path); } else if (_eTagCode.isEmpty()) { - _eTagCode = calcETag(SRH::_fs, SRH::_path); + _eTagCode = esp8266webserver::calcETag(SRH::_fs, SRH::_path); } if (server.header("If-None-Match") == _eTagCode) { From 5636d5faf6ed9c9e7f9e28736e19414df939fd6f Mon Sep 17 00:00:00 2001 From: Matthias Hertel Date: Wed, 21 Jul 2021 14:20:06 +0200 Subject: [PATCH 04/10] review fixes --- .../ESP8266WebServer/src/detail/RequestHandlersImpl.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/libraries/ESP8266WebServer/src/detail/RequestHandlersImpl.h b/libraries/ESP8266WebServer/src/detail/RequestHandlersImpl.h index ea016328be..bb06033dea 100644 --- a/libraries/ESP8266WebServer/src/detail/RequestHandlersImpl.h +++ b/libraries/ESP8266WebServer/src/detail/RequestHandlersImpl.h @@ -9,8 +9,9 @@ namespace esp8266webserver { -// calculate an eTag for a file in filesystem -static String calcETag(FS &fs, const String path) { +// calculate an ETag for a file in filesystem based on md5 checksum +// that can be used in the http headers - include quotes. +static String calcETag(FS &fs, const String &path) { String result; // calculate eTag using md5 checksum @@ -266,7 +267,7 @@ public StaticRequestHandler { } protected: - String _eTagCode; // eTag as used in http header for this single file + String _eTagCode; // ETag code calculated for this file as used in http header include quotes. }; } // namespace From fbd861ec80c1ace00b71ed0ba34bed09d675011e Mon Sep 17 00:00:00 2001 From: Matthias Hertel Date: Fri, 23 Jul 2021 00:31:19 +0200 Subject: [PATCH 05/10] WebServer example added --- .../examples/WebServer/README.md | 199 ++++++++++++ .../examples/WebServer/WebServer.ino | 284 ++++++++++++++++++ .../examples/WebServer/builtinfiles.h | 63 ++++ .../examples/WebServer/data/files.htm | 65 ++++ .../examples/WebServer/data/index.htm | 24 ++ .../examples/WebServer/data/style.css | 10 + .../examples/WebServer/secrets.h | 13 + 7 files changed, 658 insertions(+) create mode 100644 libraries/ESP8266WebServer/examples/WebServer/README.md create mode 100644 libraries/ESP8266WebServer/examples/WebServer/WebServer.ino create mode 100644 libraries/ESP8266WebServer/examples/WebServer/builtinfiles.h create mode 100644 libraries/ESP8266WebServer/examples/WebServer/data/files.htm create mode 100644 libraries/ESP8266WebServer/examples/WebServer/data/index.htm create mode 100644 libraries/ESP8266WebServer/examples/WebServer/data/style.css create mode 100644 libraries/ESP8266WebServer/examples/WebServer/secrets.h diff --git a/libraries/ESP8266WebServer/examples/WebServer/README.md b/libraries/ESP8266WebServer/examples/WebServer/README.md new file mode 100644 index 0000000000..d404a4a602 --- /dev/null +++ b/libraries/ESP8266WebServer/examples/WebServer/README.md @@ -0,0 +1,199 @@ +# WebServer example documentation and hints + +This example shows different techniques on how to use and extend the ESP8266WebServer for specific purposes + +It is a small project in it's own and has some files to use on the web server to show how to use simple REST based services. + +It requires some space for a filesystem and runs fine on ESP8266 NodeMCU board with 4 MByte flash using the following options: +* Flash Size Option 4MB (FS:2MB) +* Debug Port Serial +* MMU 32+32 balanced + +It features + +* http access to the web server +* deliver all files from the file system +* deliver special built-in files +* implement services (list files, sysinfo) +* uploading files using drag & drop +* listing and deleting files using a SPA application +* Example of SPA and Web Service application +* Only files in the root folder are supported for simplicity - no directories. + + + + +## Implementing a web server + +The ESP8266WebServer library offers a simple path to implement a web server on a ESP8266 board. + +The advantage on using the ESP8266WebServer instead of the plain simple WiFiServer is that the ESP8266WebServer +takes much care about the http protocol conventions and features and allows easily access to parameters. +It offers plug-in capabilities by registering specific functionalities that will be outlined below. + + +### Initialization + +In the setup() function in the webserver.ino sketch file the following steps are implemented to make the webserver available on the local network. + +* Create a webserver listening to port 80 for http requests. +* Initialize the access to the filesystem in the free flash memory (typically 2MByte). +* Connect to the local WiFi network. Here is only a straight-forward implementation hard-coding network name and passphrase. You may consider to use something like the WiFiManager library. +* Register the device in DNS using a known hostname. +* Registering several plug-ins (see below). +* Starting the web server. + + +### Running + +In the loop() function the web server will be given time to receive and send network packages by calling +`server.handleClient();`. + + + +## Registering simple functions to implement RESTful services + +Registering function is the simplest integration mechanism available to add functionality. The server offers the `on(path, function)` methods that take the URL and the function as parameters. + +There are 2 functions implemented that get registered to handle incoming GET requests for given URLs. + +The JSON data format is used often for such services as it is the "natural" data format of the browser using javascript. + +When the **handleSysInfo()** function is registered and a browser requests for the function will be called and can collect the requested information. + +> ```CPP +> server.on("/$sysinfo", handleSysInfo); +> ``` + +The result in this case is a JSON object that is assembled in the result String variable and the returned as a response to the client also giving the information about the data format. + +You can try this request in a browser by opening in the address bar. + +> ```CPP +> server.on("/$sysinfo", handleList); +> ``` + +The function **handleList()** is registered the same way to return the list of files in the file system also returning a JSON object including name, size and the last modification timestamp. + +You can try this request in a browser by opening in the address bar. + + +## Registering a function to send out some static content from a String + +This is an example of registering a inline function in the web server. +The 2. parameter of the on() method is a so called CPP lamda function (without a name) +that actually has only one line of functionality by sending a string as result to the client. + +> ```CPP +> server.on("/$upload.htm", []() { +> server.send(200, "text/html", FPSTR(uploadContent)); +> }); +> ``` + +Here the text from a static String with html code is returned instead of a file from the filesystem. +The content of this string can be found in the file `builtinfiles.h`. It contains a small html+javascript implementation +that allows uploading new files into the empty filesystem. + +Just open and drag some files from the data folder on the drop area. + + +## Registering a function to handle requests to the server without a path + +Often servers are addressed by using the base URL like where no further path details is given. +Of course we like the user to be redirected to something usable. Therefore the `handleRoot()` function is registered: + +> ```CPP +> server.on("/$upload.htm", handleRoot); +> ``` + +The `handleRoot()` function checks the filesystem for the file named **/index.htm** and creates a redirect to this file when the file exists. +Otherwise the redirection goes to the built-in **/$upload.htm** web page. + + + +## Using the serveStatic plug-in + +The **serveStatic** plug in is part of the library and handles delivering files from the filesystem to the client. It can be customized in some ways. + +> ```CPP +> server.enableCORS(true); +> server.enableETag(true); +> server.serveStatic("/", LittleFS, "/"); +> ``` + + +### Cross-Origin Ressource Sharing (CORS) + +The `enableCORS(true)` function adds a `Access-Control-Allow-Origin: *` http-header to all responses to the client +to inform that it is allowed to call URLs and services on this server from other web sites. + +The feature is disabled by default (in the current version) and when you like to disable this then you should call `enableCORS(false)` during setup. + +* Web sites providing high sensitive information like online banking this is disabled most of the times. +* Web sites providing advertising information or reusable scripts / images this is enabled. + + +### ETag support + +The `enableETag(true)` function adds a ETag http header to the responses to the client that come from files from the filesystem +to enable better use of the cache in the browser. + +When enabled by default the server reads the file content and creates a checksum using the md5 and base64 algorithm her called the ETag value +that changes whenever the file contains something different. + +Once a browser has got the content of a file from the server including the ETag information it will add that ETag value is added in the following requests for the same resource. +Now the server can answer with a 'use the version from the cache' when the new calculated ETag value is equal to the ETag value in the request. + +The calculation of the ETag value requires some time and processing but sending content is always slower. +So when you have the situation that a browser will use a web server multiple times this mechanism saves network and computing and makes web pages more responsive. + +In the source code you can find another version of an algorithm to calculate a ETag value that uses the date&time from the filesystem. +This is a simpler and faster way but with a low risk of dismissing a file update as the timestamp is based on seconds and local time. +This can be enabled on demand, see inline comments. + + +## Registering a full-featured handler as plug-in + +The example also implements the class `FileServerHandler` derived from the class `RequestHandler` to plug in functionality +that can handle more complex requests without giving a fixed URL. +It implements uploading and deleting files in the file system that is not implemented by the standard server.serveStatic functionality. + +This class has to implements several functions and works in a more detailed way: + +* The `canHandle()` method can inspect the given http method and url to decide weather the RequestFileHandler can handle the incoming request or not. + + In this case the RequestFileHandler will return true when the request method is an POST for upload or a DELETE for deleting files. + + The regular GET requests will be ignored and therefore handled by the also registered server.serveStatic handler. + +* The function `handle()` then implements the real deletion of the file. + +* The `canUpload()`and `upload()` methods work similar while the `upload()` method is called multiple times to create, append data and close the new file. + + +## Registering a special handler for "file not found" + +Any other incoming request that was not handled by the registered plug-ins above can be detected by registering + +> ```CPP +> // handle cases when file is not found +> server.onNotFound([]() { +> // standard not found in browser. +> server.send(404, "text/html", FPSTR(notFoundContent)); +> }); +> ``` + +This allows sending back an "friendly" result for the browser. Here a sim ple html page is created from a static string. +You can easily change the html code in the file `builtinfiles.h`. + + +## customizations + +You may like to change the hostname and the timezone in the lines: + +> ```CPP +> #define HOSTNAME "webserver" +> #define TIMEZONE "CET-1CEST,M3.5.0,M10.5.0/3" +> ``` + + diff --git a/libraries/ESP8266WebServer/examples/WebServer/WebServer.ino b/libraries/ESP8266WebServer/examples/WebServer/WebServer.ino new file mode 100644 index 0000000000..f6446d5bce --- /dev/null +++ b/libraries/ESP8266WebServer/examples/WebServer/WebServer.ino @@ -0,0 +1,284 @@ +/** + * @file WebServer.ino + * @brief Example implementation using the ESP8266 WebServer. + * + * See also README.md for instructions and hints. + * + * Changelog: + * 21.07.2021 creation, first version + */ + +#include +#include + +#include "secrets.h" // add WLAN Credentials in here. + +#include // File System for Web Server Files +#include // This file system is used. + +#define TRACE(...) Serial.printf(__VA_ARGS__); + +// name of the server. You reach it using http://webserver +#define HOSTNAME "webserver" + +// local time zone definition (Berlin) +#define TIMEZONE "CET-1CEST,M3.5.0,M10.5.0/3" + +// need a WebServer for http access on port 80. +ESP8266WebServer server(80); + +// The text of builtin files are in this header file +#include "builtinfiles.h" + + +// ===== Simple functions used to answer simple GET requests ===== + +// This function is called when the WebServer was requested without giving a filename. +// This will redirect to the file index.htm when it is existing otherwise to the built-in $upload.htm page +void handleRedirect() +{ + TRACE("Redirect..."); + String url = "/index.htm"; + + if (!LittleFS.exists(url)) { + url = "/$update.htm"; + } + + server.sendHeader("Location", url, true); + server.send(302); + server.client().stop(); +} // handleRedirect() + + +// This function is called when the WebServer was requested to list all existing files in the filesystem. +// a JSON array with file information is returned. +void handleListFiles() +{ + Dir dir = LittleFS.openDir("/"); + String result; + + result += "[\n"; + while (dir.next()) { + if (result.length() > 4) + result += ","; + result += " {"; + result += " \"name\": \"" + dir.fileName() + "\", "; + result += " \"size\": " + String(dir.fileSize()) + ", "; + result += " \"time\": " + String(dir.fileTime()); + result += " }\n"; + // jc.addProperty("size", dir.fileSize()); + } // while + result += "]"; + server.sendHeader("Cache-Control", "no-cache"); + server.send(200, "text/javascript; charset=utf-8", result); +} // handleListFiles() + + +// This function is called when the sysInfo service was requested. +void handleSysInfo() +{ + String result; + + FSInfo fs_info; + LittleFS.info(fs_info); + + result += "{\n"; + result += " \"flashSize\": " + String(ESP.getFlashChipSize()) + ",\n"; + result += " \"freeHeap\": " + String(ESP.getFreeHeap()) + ",\n"; + result += " \"fsTotalBytes\": " + String(fs_info.totalBytes) + ",\n"; + result += " \"fsUsedBytes\": " + String(fs_info.usedBytes) + ",\n"; + result += "}"; + + server.sendHeader("Cache-Control", "no-cache"); + server.send(200, "text/javascript; charset=utf-8", result); +} // handleSysInfo() + + +// ===== Request Handler class used to answer more complex requests ===== + +// The FileServerHandler is registered to the web server to support DELETE and UPLOAD of files into the filesystem. +class FileServerHandler : public RequestHandler +{ +public: + /** + * @brief Construct a new File Server Handler object + * @param fs The file system to be used. + * @param path Path to the root folder in the file system that is used for + * Serving static data down and upload. + * @param cache_header Cache Header to be used in replies. + */ + FileServerHandler() + { + TRACE("FileServerHandler is registered\n"); + } + + + /** + @brief check wether the request can be handled by this implementation. + @param requestMethod method of the http request line. + @param requestUri request ressource from the http request line. + @return true when method can be handled. + */ + bool canHandle(HTTPMethod requestMethod, const String &_uri) override + { + return ((requestMethod == HTTP_POST) || (requestMethod == HTTP_DELETE)); + } // canHandle() + + + bool canUpload(const String &uri) override + { + // only allow upload on root fs level. + return (uri == "/"); + } // canUpload() + + + bool handle(ESP8266WebServer &server, HTTPMethod requestMethod, const String &requestUri) override + { + // ensure that filename starts with '/' + String fName = requestUri; + if (!fName.startsWith("/")) { + fName = "/" + fName; + } + + // LOGGER_RAW("File:handle(%s)", requestUri.c_str()); + if (requestMethod == HTTP_POST) { + // all done in upload. no other forms. + + } else if (requestMethod == HTTP_DELETE) { + if (LittleFS.exists(fName)) { + LittleFS.remove(fName); + } + } // if + + server.send(200); // all done. + return (true); + } // handle() + + + void upload(ESP8266WebServer &_server, const String &_requestUri, HTTPUpload &upload) override + { + // ensure that filename starts with '/' + String fName = upload.filename; + if (!fName.startsWith("/")) { + fName = "/" + fName; + } + + // LOGGER_TRACE("upload...<%s>", fName.c_str()); + if (fName.indexOf('#') > 0) { + // LOGGER_TRACE("no #..."); + } else if (fName.indexOf('$') > 0) { + // LOGGER_TRACE("no $..."); + + } else if (upload.status == UPLOAD_FILE_START) { + if (LittleFS.exists(fName)) { + LittleFS.remove(fName); + } // if + _fsUploadFile = LittleFS.open(fName, "w"); + + } else if (upload.status == UPLOAD_FILE_WRITE) { + if (_fsUploadFile) + _fsUploadFile.write(upload.buf, upload.currentSize); + yield(); + + } else if (upload.status == UPLOAD_FILE_END) { + if (_fsUploadFile) { + _fsUploadFile.close(); + } + } // if + } // upload() + +protected: + File _fsUploadFile; +}; + + +/** + * Setup everything to make the webserver work. + */ +void setup(void) +{ + delay(3000); + + // Use Serial port for some trace information from the example + Serial.begin(115200); + Serial.setDebugOutput(false); + + TRACE("Starting WebServer example...\n"); + + TRACE("Mounting the filesystem...\n"); + if (!LittleFS.begin()) { + TRACE("could not mount the filesystem...\n"); + delay(2000); + ESP.restart(); + } + + // start WiFI + WiFi.mode(WIFI_STA); + if (strlen(ssid) == 0) { + WiFi.begin(); + } else { + WiFi.begin(ssid, passPhrase); + } + + // allow to address the device by the given name e.g. http://webserver + WiFi.setHostname(HOSTNAME); + + TRACE("Connect to WiFi...\n"); + while (WiFi.status() != WL_CONNECTED) { + delay(500); + TRACE("."); + } + TRACE("connected.\n"); + + // Ask for the current time using NTP request builtin into ESP firmware. + TRACE("Setup ntp...\n"); + configTime(TIMEZONE, "pool.ntp.org"); + + TRACE("Register service handlers...\n"); + + // serve a built-in htm page + server.on("/$upload.htm", []() { + server.send(200, "text/html", FPSTR(uploadContent)); + }); + + // register a redirect handler when only domain name is given. + server.on("/", HTTP_GET, handleRedirect); + + // register a REST service returning the list of files in the root folder + server.on("/$list", HTTP_GET, handleListFiles); + + // register a REST service returning some more system level information + server.on("/$sysinfo", HTTP_GET, handleSysInfo); + + // UPLOAD and DELETE of files in the file system using a request handler. + server.addHandler(new FileServerHandler()); + + // enable CORS header in webserver results + server.enableCORS(true); + + // enable ETAG header in webserver results from serveStatic handler + server.enableETag(true); + + // serve all static files + server.serveStatic("/", LittleFS, "/"); + + // handle cases when file is not found + server.onNotFound([]() { + // standard not found in browser. + server.send(404, "text/html", FPSTR(notFoundContent)); + }); + + server.begin(); + TRACE("setup done.\n"); + TRACE("hostname=%s\n", WiFi.getHostname()); +} // setup + + +// handle all give time to all Elements and active components. +void loop(void) +{ + server.handleClient(); + // MDNS.update(); +} // loop() + +// end. diff --git a/libraries/ESP8266WebServer/examples/WebServer/builtinfiles.h b/libraries/ESP8266WebServer/examples/WebServer/builtinfiles.h new file mode 100644 index 0000000000..4c13c1f2f3 --- /dev/null +++ b/libraries/ESP8266WebServer/examples/WebServer/builtinfiles.h @@ -0,0 +1,63 @@ +/** + * @file builtinfiles.h + * @brief This file is part of the WebServer example for the ESP8266WebServer. + * + * This file contains long, multiline text variables for all builtin resources. + */ + +// used for $upload.htm +static const char uploadContent[] PROGMEM = +R"==( + + + + + + + Upload + + + +

Upload

+ +
+
Drop files here...
+ + + +)=="; + +// used for $upload.htm +static const char notFoundContent[] PROGMEM = R"==( + + + Ressource not found + + +

The ressource was not found.

+

Start again

+ +)=="; diff --git a/libraries/ESP8266WebServer/examples/WebServer/data/files.htm b/libraries/ESP8266WebServer/examples/WebServer/data/files.htm new file mode 100644 index 0000000000..4eae798980 --- /dev/null +++ b/libraries/ESP8266WebServer/examples/WebServer/data/files.htm @@ -0,0 +1,65 @@ + + + + Files + + + + +

Files on Server

+ +

These files are available on the server to be opened or delete:

+
+
+ + + + + \ No newline at end of file diff --git a/libraries/ESP8266WebServer/examples/WebServer/data/index.htm b/libraries/ESP8266WebServer/examples/WebServer/data/index.htm new file mode 100644 index 0000000000..434f2c1d64 --- /dev/null +++ b/libraries/ESP8266WebServer/examples/WebServer/data/index.htm @@ -0,0 +1,24 @@ + + + + HomePage + + + + +

Homepage of the WebServer Example

+ +

The following pages are available:

+ + +

The following REST services are available:

+
    +
  • /$sysinfo - Some system level information
  • +
  • /$list - Array of all files
  • +
+ + \ No newline at end of file diff --git a/libraries/ESP8266WebServer/examples/WebServer/data/style.css b/libraries/ESP8266WebServer/examples/WebServer/data/style.css new file mode 100644 index 0000000000..4730fef58a --- /dev/null +++ b/libraries/ESP8266WebServer/examples/WebServer/data/style.css @@ -0,0 +1,10 @@ +html, body { + color: #111111; font-family: Arial, ui-sans-serif, sans-serif; font-size: 1em; background-color: #f0f0f0; +} + +#list > div { + margin: 0 0 0.5rem 0; +} + +a { color: inherit; cursor: pointer; } + diff --git a/libraries/ESP8266WebServer/examples/WebServer/secrets.h b/libraries/ESP8266WebServer/examples/WebServer/secrets.h new file mode 100644 index 0000000000..62c3093278 --- /dev/null +++ b/libraries/ESP8266WebServer/examples/WebServer/secrets.h @@ -0,0 +1,13 @@ +// Secrets for your local home network + +// This is a "hard way" to configure your local WiFi network name and passphrase +// into the source code and the uploaded sketch. +// +// Using the WiFi Manager is preferred and avoids reprogramming when your network changes. +// See https://homeding.github.io/#page=/wifimanager.md + +// ssid and passPhrase can be used when compiling for a specific environment as a 2. option. + +// add you wifi network name and PassPhrase or use WiFi Manager +const char *ssid = ""; +const char *passPhrase = ""; From b36a7c32f922c76ddd35fb37f1ab48fdd440d069 Mon Sep 17 00:00:00 2001 From: Matthias Hertel Date: Fri, 23 Jul 2021 08:21:03 +0200 Subject: [PATCH 06/10] fixing unused parameters and cleanup --- .../examples/WebServer/WebServer.ino | 36 +++++++++---------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/libraries/ESP8266WebServer/examples/WebServer/WebServer.ino b/libraries/ESP8266WebServer/examples/WebServer/WebServer.ino index f6446d5bce..b4121fe17e 100644 --- a/libraries/ESP8266WebServer/examples/WebServer/WebServer.ino +++ b/libraries/ESP8266WebServer/examples/WebServer/WebServer.ino @@ -16,7 +16,11 @@ #include // File System for Web Server Files #include // This file system is used. -#define TRACE(...) Serial.printf(__VA_ARGS__); +// mark parameters not used in example +#define UNUSED __attribute__((unused)) + +// TRACE output simplified, can be deactivated here +#define TRACE(...) Serial.printf(__VA_ARGS__) // name of the server. You reach it using http://webserver #define HOSTNAME "webserver" @@ -46,7 +50,6 @@ void handleRedirect() server.sendHeader("Location", url, true); server.send(302); - server.client().stop(); } // handleRedirect() @@ -119,8 +122,9 @@ public: @param requestUri request ressource from the http request line. @return true when method can be handled. */ - bool canHandle(HTTPMethod requestMethod, const String &_uri) override + bool canHandle(HTTPMethod requestMethod, const String UNUSED &_uri) override { + // can handle POST for uploads and DELETE. return ((requestMethod == HTTP_POST) || (requestMethod == HTTP_DELETE)); } // canHandle() @@ -140,7 +144,6 @@ public: fName = "/" + fName; } - // LOGGER_RAW("File:handle(%s)", requestUri.c_str()); if (requestMethod == HTTP_POST) { // all done in upload. no other forms. @@ -155,7 +158,8 @@ public: } // handle() - void upload(ESP8266WebServer &_server, const String &_requestUri, HTTPUpload &upload) override + // uploading process + void upload(ESP8266WebServer UNUSED &server, const String UNUSED &_requestUri, HTTPUpload &upload) override { // ensure that filename starts with '/' String fName = upload.filename; @@ -163,24 +167,20 @@ public: fName = "/" + fName; } - // LOGGER_TRACE("upload...<%s>", fName.c_str()); - if (fName.indexOf('#') > 0) { - // LOGGER_TRACE("no #..."); - } else if (fName.indexOf('$') > 0) { - // LOGGER_TRACE("no $..."); - - } else if (upload.status == UPLOAD_FILE_START) { + if (upload.status == UPLOAD_FILE_START) { + // Open the file if (LittleFS.exists(fName)) { LittleFS.remove(fName); } // if _fsUploadFile = LittleFS.open(fName, "w"); } else if (upload.status == UPLOAD_FILE_WRITE) { + // Write received bytes if (_fsUploadFile) _fsUploadFile.write(upload.buf, upload.currentSize); - yield(); } else if (upload.status == UPLOAD_FILE_END) { + // Close the file if (_fsUploadFile) { _fsUploadFile.close(); } @@ -197,7 +197,7 @@ protected: */ void setup(void) { - delay(3000); + delay(3000); // wait for serial monitor to start completely. // Use Serial port for some trace information from the example Serial.begin(115200); @@ -244,10 +244,8 @@ void setup(void) // register a redirect handler when only domain name is given. server.on("/", HTTP_GET, handleRedirect); - // register a REST service returning the list of files in the root folder + // register some REST services server.on("/$list", HTTP_GET, handleListFiles); - - // register a REST service returning some more system level information server.on("/$sysinfo", HTTP_GET, handleSysInfo); // UPLOAD and DELETE of files in the file system using a request handler. @@ -269,16 +267,14 @@ void setup(void) }); server.begin(); - TRACE("setup done.\n"); TRACE("hostname=%s\n", WiFi.getHostname()); } // setup -// handle all give time to all Elements and active components. +// run the server... void loop(void) { server.handleClient(); - // MDNS.update(); } // loop() // end. From f727bb3664f203b2f8bbc1fbe56fed6e889c6853 Mon Sep 17 00:00:00 2001 From: Matthias Hertel Date: Fri, 23 Jul 2021 08:42:59 +0200 Subject: [PATCH 07/10] CRLF into LF --- .../examples/WebServer/README.md | 398 ++++++------- .../examples/WebServer/WebServer.ino | 560 +++++++++--------- .../examples/WebServer/builtinfiles.h | 126 ++-- .../examples/WebServer/data/files.htm | 128 ++-- .../examples/WebServer/data/index.htm | 46 +- .../examples/WebServer/data/style.css | 20 +- .../examples/WebServer/secrets.h | 26 +- 7 files changed, 652 insertions(+), 652 deletions(-) diff --git a/libraries/ESP8266WebServer/examples/WebServer/README.md b/libraries/ESP8266WebServer/examples/WebServer/README.md index d404a4a602..68999100ab 100644 --- a/libraries/ESP8266WebServer/examples/WebServer/README.md +++ b/libraries/ESP8266WebServer/examples/WebServer/README.md @@ -1,199 +1,199 @@ -# WebServer example documentation and hints - -This example shows different techniques on how to use and extend the ESP8266WebServer for specific purposes - -It is a small project in it's own and has some files to use on the web server to show how to use simple REST based services. - -It requires some space for a filesystem and runs fine on ESP8266 NodeMCU board with 4 MByte flash using the following options: -* Flash Size Option 4MB (FS:2MB) -* Debug Port Serial -* MMU 32+32 balanced - -It features - -* http access to the web server -* deliver all files from the file system -* deliver special built-in files -* implement services (list files, sysinfo) -* uploading files using drag & drop -* listing and deleting files using a SPA application -* Example of SPA and Web Service application -* Only files in the root folder are supported for simplicity - no directories. - - - - -## Implementing a web server - -The ESP8266WebServer library offers a simple path to implement a web server on a ESP8266 board. - -The advantage on using the ESP8266WebServer instead of the plain simple WiFiServer is that the ESP8266WebServer -takes much care about the http protocol conventions and features and allows easily access to parameters. -It offers plug-in capabilities by registering specific functionalities that will be outlined below. - - -### Initialization - -In the setup() function in the webserver.ino sketch file the following steps are implemented to make the webserver available on the local network. - -* Create a webserver listening to port 80 for http requests. -* Initialize the access to the filesystem in the free flash memory (typically 2MByte). -* Connect to the local WiFi network. Here is only a straight-forward implementation hard-coding network name and passphrase. You may consider to use something like the WiFiManager library. -* Register the device in DNS using a known hostname. -* Registering several plug-ins (see below). -* Starting the web server. - - -### Running - -In the loop() function the web server will be given time to receive and send network packages by calling -`server.handleClient();`. - - - -## Registering simple functions to implement RESTful services - -Registering function is the simplest integration mechanism available to add functionality. The server offers the `on(path, function)` methods that take the URL and the function as parameters. - -There are 2 functions implemented that get registered to handle incoming GET requests for given URLs. - -The JSON data format is used often for such services as it is the "natural" data format of the browser using javascript. - -When the **handleSysInfo()** function is registered and a browser requests for the function will be called and can collect the requested information. - -> ```CPP -> server.on("/$sysinfo", handleSysInfo); -> ``` - -The result in this case is a JSON object that is assembled in the result String variable and the returned as a response to the client also giving the information about the data format. - -You can try this request in a browser by opening in the address bar. - -> ```CPP -> server.on("/$sysinfo", handleList); -> ``` - -The function **handleList()** is registered the same way to return the list of files in the file system also returning a JSON object including name, size and the last modification timestamp. - -You can try this request in a browser by opening in the address bar. - - -## Registering a function to send out some static content from a String - -This is an example of registering a inline function in the web server. -The 2. parameter of the on() method is a so called CPP lamda function (without a name) -that actually has only one line of functionality by sending a string as result to the client. - -> ```CPP -> server.on("/$upload.htm", []() { -> server.send(200, "text/html", FPSTR(uploadContent)); -> }); -> ``` - -Here the text from a static String with html code is returned instead of a file from the filesystem. -The content of this string can be found in the file `builtinfiles.h`. It contains a small html+javascript implementation -that allows uploading new files into the empty filesystem. - -Just open and drag some files from the data folder on the drop area. - - -## Registering a function to handle requests to the server without a path - -Often servers are addressed by using the base URL like where no further path details is given. -Of course we like the user to be redirected to something usable. Therefore the `handleRoot()` function is registered: - -> ```CPP -> server.on("/$upload.htm", handleRoot); -> ``` - -The `handleRoot()` function checks the filesystem for the file named **/index.htm** and creates a redirect to this file when the file exists. -Otherwise the redirection goes to the built-in **/$upload.htm** web page. - - - -## Using the serveStatic plug-in - -The **serveStatic** plug in is part of the library and handles delivering files from the filesystem to the client. It can be customized in some ways. - -> ```CPP -> server.enableCORS(true); -> server.enableETag(true); -> server.serveStatic("/", LittleFS, "/"); -> ``` - - -### Cross-Origin Ressource Sharing (CORS) - -The `enableCORS(true)` function adds a `Access-Control-Allow-Origin: *` http-header to all responses to the client -to inform that it is allowed to call URLs and services on this server from other web sites. - -The feature is disabled by default (in the current version) and when you like to disable this then you should call `enableCORS(false)` during setup. - -* Web sites providing high sensitive information like online banking this is disabled most of the times. -* Web sites providing advertising information or reusable scripts / images this is enabled. - - -### ETag support - -The `enableETag(true)` function adds a ETag http header to the responses to the client that come from files from the filesystem -to enable better use of the cache in the browser. - -When enabled by default the server reads the file content and creates a checksum using the md5 and base64 algorithm her called the ETag value -that changes whenever the file contains something different. - -Once a browser has got the content of a file from the server including the ETag information it will add that ETag value is added in the following requests for the same resource. -Now the server can answer with a 'use the version from the cache' when the new calculated ETag value is equal to the ETag value in the request. - -The calculation of the ETag value requires some time and processing but sending content is always slower. -So when you have the situation that a browser will use a web server multiple times this mechanism saves network and computing and makes web pages more responsive. - -In the source code you can find another version of an algorithm to calculate a ETag value that uses the date&time from the filesystem. -This is a simpler and faster way but with a low risk of dismissing a file update as the timestamp is based on seconds and local time. -This can be enabled on demand, see inline comments. - - -## Registering a full-featured handler as plug-in - -The example also implements the class `FileServerHandler` derived from the class `RequestHandler` to plug in functionality -that can handle more complex requests without giving a fixed URL. -It implements uploading and deleting files in the file system that is not implemented by the standard server.serveStatic functionality. - -This class has to implements several functions and works in a more detailed way: - -* The `canHandle()` method can inspect the given http method and url to decide weather the RequestFileHandler can handle the incoming request or not. - - In this case the RequestFileHandler will return true when the request method is an POST for upload or a DELETE for deleting files. - - The regular GET requests will be ignored and therefore handled by the also registered server.serveStatic handler. - -* The function `handle()` then implements the real deletion of the file. - -* The `canUpload()`and `upload()` methods work similar while the `upload()` method is called multiple times to create, append data and close the new file. - - -## Registering a special handler for "file not found" - -Any other incoming request that was not handled by the registered plug-ins above can be detected by registering - -> ```CPP -> // handle cases when file is not found -> server.onNotFound([]() { -> // standard not found in browser. -> server.send(404, "text/html", FPSTR(notFoundContent)); -> }); -> ``` - -This allows sending back an "friendly" result for the browser. Here a sim ple html page is created from a static string. -You can easily change the html code in the file `builtinfiles.h`. - - -## customizations - -You may like to change the hostname and the timezone in the lines: - -> ```CPP -> #define HOSTNAME "webserver" -> #define TIMEZONE "CET-1CEST,M3.5.0,M10.5.0/3" -> ``` - - +# WebServer example documentation and hints + +This example shows different techniques on how to use and extend the ESP8266WebServer for specific purposes + +It is a small project in it's own and has some files to use on the web server to show how to use simple REST based services. + +It requires some space for a filesystem and runs fine on ESP8266 NodeMCU board with 4 MByte flash using the following options: +* Flash Size Option 4MB (FS:2MB) +* Debug Port Serial +* MMU 32+32 balanced + +It features + +* http access to the web server +* deliver all files from the file system +* deliver special built-in files +* implement services (list files, sysinfo) +* uploading files using drag & drop +* listing and deleting files using a SPA application +* Example of SPA and Web Service application +* Only files in the root folder are supported for simplicity - no directories. + + + + +## Implementing a web server + +The ESP8266WebServer library offers a simple path to implement a web server on a ESP8266 board. + +The advantage on using the ESP8266WebServer instead of the plain simple WiFiServer is that the ESP8266WebServer +takes much care about the http protocol conventions and features and allows easily access to parameters. +It offers plug-in capabilities by registering specific functionalities that will be outlined below. + + +### Initialization + +In the setup() function in the webserver.ino sketch file the following steps are implemented to make the webserver available on the local network. + +* Create a webserver listening to port 80 for http requests. +* Initialize the access to the filesystem in the free flash memory (typically 2MByte). +* Connect to the local WiFi network. Here is only a straight-forward implementation hard-coding network name and passphrase. You may consider to use something like the WiFiManager library. +* Register the device in DNS using a known hostname. +* Registering several plug-ins (see below). +* Starting the web server. + + +### Running + +In the loop() function the web server will be given time to receive and send network packages by calling +`server.handleClient();`. + + + +## Registering simple functions to implement RESTful services + +Registering function is the simplest integration mechanism available to add functionality. The server offers the `on(path, function)` methods that take the URL and the function as parameters. + +There are 2 functions implemented that get registered to handle incoming GET requests for given URLs. + +The JSON data format is used often for such services as it is the "natural" data format of the browser using javascript. + +When the **handleSysInfo()** function is registered and a browser requests for the function will be called and can collect the requested information. + +> ```CPP +> server.on("/$sysinfo", handleSysInfo); +> ``` + +The result in this case is a JSON object that is assembled in the result String variable and the returned as a response to the client also giving the information about the data format. + +You can try this request in a browser by opening in the address bar. + +> ```CPP +> server.on("/$sysinfo", handleList); +> ``` + +The function **handleList()** is registered the same way to return the list of files in the file system also returning a JSON object including name, size and the last modification timestamp. + +You can try this request in a browser by opening in the address bar. + + +## Registering a function to send out some static content from a String + +This is an example of registering a inline function in the web server. +The 2. parameter of the on() method is a so called CPP lamda function (without a name) +that actually has only one line of functionality by sending a string as result to the client. + +> ```CPP +> server.on("/$upload.htm", []() { +> server.send(200, "text/html", FPSTR(uploadContent)); +> }); +> ``` + +Here the text from a static String with html code is returned instead of a file from the filesystem. +The content of this string can be found in the file `builtinfiles.h`. It contains a small html+javascript implementation +that allows uploading new files into the empty filesystem. + +Just open and drag some files from the data folder on the drop area. + + +## Registering a function to handle requests to the server without a path + +Often servers are addressed by using the base URL like where no further path details is given. +Of course we like the user to be redirected to something usable. Therefore the `handleRoot()` function is registered: + +> ```CPP +> server.on("/$upload.htm", handleRoot); +> ``` + +The `handleRoot()` function checks the filesystem for the file named **/index.htm** and creates a redirect to this file when the file exists. +Otherwise the redirection goes to the built-in **/$upload.htm** web page. + + + +## Using the serveStatic plug-in + +The **serveStatic** plug in is part of the library and handles delivering files from the filesystem to the client. It can be customized in some ways. + +> ```CPP +> server.enableCORS(true); +> server.enableETag(true); +> server.serveStatic("/", LittleFS, "/"); +> ``` + + +### Cross-Origin Ressource Sharing (CORS) + +The `enableCORS(true)` function adds a `Access-Control-Allow-Origin: *` http-header to all responses to the client +to inform that it is allowed to call URLs and services on this server from other web sites. + +The feature is disabled by default (in the current version) and when you like to disable this then you should call `enableCORS(false)` during setup. + +* Web sites providing high sensitive information like online banking this is disabled most of the times. +* Web sites providing advertising information or reusable scripts / images this is enabled. + + +### ETag support + +The `enableETag(true)` function adds a ETag http header to the responses to the client that come from files from the filesystem +to enable better use of the cache in the browser. + +When enabled by default the server reads the file content and creates a checksum using the md5 and base64 algorithm her called the ETag value +that changes whenever the file contains something different. + +Once a browser has got the content of a file from the server including the ETag information it will add that ETag value is added in the following requests for the same resource. +Now the server can answer with a 'use the version from the cache' when the new calculated ETag value is equal to the ETag value in the request. + +The calculation of the ETag value requires some time and processing but sending content is always slower. +So when you have the situation that a browser will use a web server multiple times this mechanism saves network and computing and makes web pages more responsive. + +In the source code you can find another version of an algorithm to calculate a ETag value that uses the date&time from the filesystem. +This is a simpler and faster way but with a low risk of dismissing a file update as the timestamp is based on seconds and local time. +This can be enabled on demand, see inline comments. + + +## Registering a full-featured handler as plug-in + +The example also implements the class `FileServerHandler` derived from the class `RequestHandler` to plug in functionality +that can handle more complex requests without giving a fixed URL. +It implements uploading and deleting files in the file system that is not implemented by the standard server.serveStatic functionality. + +This class has to implements several functions and works in a more detailed way: + +* The `canHandle()` method can inspect the given http method and url to decide weather the RequestFileHandler can handle the incoming request or not. + + In this case the RequestFileHandler will return true when the request method is an POST for upload or a DELETE for deleting files. + + The regular GET requests will be ignored and therefore handled by the also registered server.serveStatic handler. + +* The function `handle()` then implements the real deletion of the file. + +* The `canUpload()`and `upload()` methods work similar while the `upload()` method is called multiple times to create, append data and close the new file. + + +## Registering a special handler for "file not found" + +Any other incoming request that was not handled by the registered plug-ins above can be detected by registering + +> ```CPP +> // handle cases when file is not found +> server.onNotFound([]() { +> // standard not found in browser. +> server.send(404, "text/html", FPSTR(notFoundContent)); +> }); +> ``` + +This allows sending back an "friendly" result for the browser. Here a sim ple html page is created from a static string. +You can easily change the html code in the file `builtinfiles.h`. + + +## customizations + +You may like to change the hostname and the timezone in the lines: + +> ```CPP +> #define HOSTNAME "webserver" +> #define TIMEZONE "CET-1CEST,M3.5.0,M10.5.0/3" +> ``` + + diff --git a/libraries/ESP8266WebServer/examples/WebServer/WebServer.ino b/libraries/ESP8266WebServer/examples/WebServer/WebServer.ino index b4121fe17e..0bb0575208 100644 --- a/libraries/ESP8266WebServer/examples/WebServer/WebServer.ino +++ b/libraries/ESP8266WebServer/examples/WebServer/WebServer.ino @@ -1,280 +1,280 @@ -/** - * @file WebServer.ino - * @brief Example implementation using the ESP8266 WebServer. - * - * See also README.md for instructions and hints. - * - * Changelog: - * 21.07.2021 creation, first version - */ - -#include -#include - -#include "secrets.h" // add WLAN Credentials in here. - -#include // File System for Web Server Files -#include // This file system is used. - -// mark parameters not used in example -#define UNUSED __attribute__((unused)) - -// TRACE output simplified, can be deactivated here -#define TRACE(...) Serial.printf(__VA_ARGS__) - -// name of the server. You reach it using http://webserver -#define HOSTNAME "webserver" - -// local time zone definition (Berlin) -#define TIMEZONE "CET-1CEST,M3.5.0,M10.5.0/3" - -// need a WebServer for http access on port 80. -ESP8266WebServer server(80); - -// The text of builtin files are in this header file -#include "builtinfiles.h" - - -// ===== Simple functions used to answer simple GET requests ===== - -// This function is called when the WebServer was requested without giving a filename. -// This will redirect to the file index.htm when it is existing otherwise to the built-in $upload.htm page -void handleRedirect() -{ - TRACE("Redirect..."); - String url = "/index.htm"; - - if (!LittleFS.exists(url)) { - url = "/$update.htm"; - } - - server.sendHeader("Location", url, true); - server.send(302); -} // handleRedirect() - - -// This function is called when the WebServer was requested to list all existing files in the filesystem. -// a JSON array with file information is returned. -void handleListFiles() -{ - Dir dir = LittleFS.openDir("/"); - String result; - - result += "[\n"; - while (dir.next()) { - if (result.length() > 4) - result += ","; - result += " {"; - result += " \"name\": \"" + dir.fileName() + "\", "; - result += " \"size\": " + String(dir.fileSize()) + ", "; - result += " \"time\": " + String(dir.fileTime()); - result += " }\n"; - // jc.addProperty("size", dir.fileSize()); - } // while - result += "]"; - server.sendHeader("Cache-Control", "no-cache"); - server.send(200, "text/javascript; charset=utf-8", result); -} // handleListFiles() - - -// This function is called when the sysInfo service was requested. -void handleSysInfo() -{ - String result; - - FSInfo fs_info; - LittleFS.info(fs_info); - - result += "{\n"; - result += " \"flashSize\": " + String(ESP.getFlashChipSize()) + ",\n"; - result += " \"freeHeap\": " + String(ESP.getFreeHeap()) + ",\n"; - result += " \"fsTotalBytes\": " + String(fs_info.totalBytes) + ",\n"; - result += " \"fsUsedBytes\": " + String(fs_info.usedBytes) + ",\n"; - result += "}"; - - server.sendHeader("Cache-Control", "no-cache"); - server.send(200, "text/javascript; charset=utf-8", result); -} // handleSysInfo() - - -// ===== Request Handler class used to answer more complex requests ===== - -// The FileServerHandler is registered to the web server to support DELETE and UPLOAD of files into the filesystem. -class FileServerHandler : public RequestHandler -{ -public: - /** - * @brief Construct a new File Server Handler object - * @param fs The file system to be used. - * @param path Path to the root folder in the file system that is used for - * Serving static data down and upload. - * @param cache_header Cache Header to be used in replies. - */ - FileServerHandler() - { - TRACE("FileServerHandler is registered\n"); - } - - - /** - @brief check wether the request can be handled by this implementation. - @param requestMethod method of the http request line. - @param requestUri request ressource from the http request line. - @return true when method can be handled. - */ - bool canHandle(HTTPMethod requestMethod, const String UNUSED &_uri) override - { - // can handle POST for uploads and DELETE. - return ((requestMethod == HTTP_POST) || (requestMethod == HTTP_DELETE)); - } // canHandle() - - - bool canUpload(const String &uri) override - { - // only allow upload on root fs level. - return (uri == "/"); - } // canUpload() - - - bool handle(ESP8266WebServer &server, HTTPMethod requestMethod, const String &requestUri) override - { - // ensure that filename starts with '/' - String fName = requestUri; - if (!fName.startsWith("/")) { - fName = "/" + fName; - } - - if (requestMethod == HTTP_POST) { - // all done in upload. no other forms. - - } else if (requestMethod == HTTP_DELETE) { - if (LittleFS.exists(fName)) { - LittleFS.remove(fName); - } - } // if - - server.send(200); // all done. - return (true); - } // handle() - - - // uploading process - void upload(ESP8266WebServer UNUSED &server, const String UNUSED &_requestUri, HTTPUpload &upload) override - { - // ensure that filename starts with '/' - String fName = upload.filename; - if (!fName.startsWith("/")) { - fName = "/" + fName; - } - - if (upload.status == UPLOAD_FILE_START) { - // Open the file - if (LittleFS.exists(fName)) { - LittleFS.remove(fName); - } // if - _fsUploadFile = LittleFS.open(fName, "w"); - - } else if (upload.status == UPLOAD_FILE_WRITE) { - // Write received bytes - if (_fsUploadFile) - _fsUploadFile.write(upload.buf, upload.currentSize); - - } else if (upload.status == UPLOAD_FILE_END) { - // Close the file - if (_fsUploadFile) { - _fsUploadFile.close(); - } - } // if - } // upload() - -protected: - File _fsUploadFile; -}; - - -/** - * Setup everything to make the webserver work. - */ -void setup(void) -{ - delay(3000); // wait for serial monitor to start completely. - - // Use Serial port for some trace information from the example - Serial.begin(115200); - Serial.setDebugOutput(false); - - TRACE("Starting WebServer example...\n"); - - TRACE("Mounting the filesystem...\n"); - if (!LittleFS.begin()) { - TRACE("could not mount the filesystem...\n"); - delay(2000); - ESP.restart(); - } - - // start WiFI - WiFi.mode(WIFI_STA); - if (strlen(ssid) == 0) { - WiFi.begin(); - } else { - WiFi.begin(ssid, passPhrase); - } - - // allow to address the device by the given name e.g. http://webserver - WiFi.setHostname(HOSTNAME); - - TRACE("Connect to WiFi...\n"); - while (WiFi.status() != WL_CONNECTED) { - delay(500); - TRACE("."); - } - TRACE("connected.\n"); - - // Ask for the current time using NTP request builtin into ESP firmware. - TRACE("Setup ntp...\n"); - configTime(TIMEZONE, "pool.ntp.org"); - - TRACE("Register service handlers...\n"); - - // serve a built-in htm page - server.on("/$upload.htm", []() { - server.send(200, "text/html", FPSTR(uploadContent)); - }); - - // register a redirect handler when only domain name is given. - server.on("/", HTTP_GET, handleRedirect); - - // register some REST services - server.on("/$list", HTTP_GET, handleListFiles); - server.on("/$sysinfo", HTTP_GET, handleSysInfo); - - // UPLOAD and DELETE of files in the file system using a request handler. - server.addHandler(new FileServerHandler()); - - // enable CORS header in webserver results - server.enableCORS(true); - - // enable ETAG header in webserver results from serveStatic handler - server.enableETag(true); - - // serve all static files - server.serveStatic("/", LittleFS, "/"); - - // handle cases when file is not found - server.onNotFound([]() { - // standard not found in browser. - server.send(404, "text/html", FPSTR(notFoundContent)); - }); - - server.begin(); - TRACE("hostname=%s\n", WiFi.getHostname()); -} // setup - - -// run the server... -void loop(void) -{ - server.handleClient(); -} // loop() - -// end. +/** + * @file WebServer.ino + * @brief Example implementation using the ESP8266 WebServer. + * + * See also README.md for instructions and hints. + * + * Changelog: + * 21.07.2021 creation, first version + */ + +#include +#include + +#include "secrets.h" // add WLAN Credentials in here. + +#include // File System for Web Server Files +#include // This file system is used. + +// mark parameters not used in example +#define UNUSED __attribute__((unused)) + +// TRACE output simplified, can be deactivated here +#define TRACE(...) Serial.printf(__VA_ARGS__) + +// name of the server. You reach it using http://webserver +#define HOSTNAME "webserver" + +// local time zone definition (Berlin) +#define TIMEZONE "CET-1CEST,M3.5.0,M10.5.0/3" + +// need a WebServer for http access on port 80. +ESP8266WebServer server(80); + +// The text of builtin files are in this header file +#include "builtinfiles.h" + + +// ===== Simple functions used to answer simple GET requests ===== + +// This function is called when the WebServer was requested without giving a filename. +// This will redirect to the file index.htm when it is existing otherwise to the built-in $upload.htm page +void handleRedirect() +{ + TRACE("Redirect..."); + String url = "/index.htm"; + + if (!LittleFS.exists(url)) { + url = "/$update.htm"; + } + + server.sendHeader("Location", url, true); + server.send(302); +} // handleRedirect() + + +// This function is called when the WebServer was requested to list all existing files in the filesystem. +// a JSON array with file information is returned. +void handleListFiles() +{ + Dir dir = LittleFS.openDir("/"); + String result; + + result += "[\n"; + while (dir.next()) { + if (result.length() > 4) + result += ","; + result += " {"; + result += " \"name\": \"" + dir.fileName() + "\", "; + result += " \"size\": " + String(dir.fileSize()) + ", "; + result += " \"time\": " + String(dir.fileTime()); + result += " }\n"; + // jc.addProperty("size", dir.fileSize()); + } // while + result += "]"; + server.sendHeader("Cache-Control", "no-cache"); + server.send(200, "text/javascript; charset=utf-8", result); +} // handleListFiles() + + +// This function is called when the sysInfo service was requested. +void handleSysInfo() +{ + String result; + + FSInfo fs_info; + LittleFS.info(fs_info); + + result += "{\n"; + result += " \"flashSize\": " + String(ESP.getFlashChipSize()) + ",\n"; + result += " \"freeHeap\": " + String(ESP.getFreeHeap()) + ",\n"; + result += " \"fsTotalBytes\": " + String(fs_info.totalBytes) + ",\n"; + result += " \"fsUsedBytes\": " + String(fs_info.usedBytes) + ",\n"; + result += "}"; + + server.sendHeader("Cache-Control", "no-cache"); + server.send(200, "text/javascript; charset=utf-8", result); +} // handleSysInfo() + + +// ===== Request Handler class used to answer more complex requests ===== + +// The FileServerHandler is registered to the web server to support DELETE and UPLOAD of files into the filesystem. +class FileServerHandler : public RequestHandler +{ +public: + /** + * @brief Construct a new File Server Handler object + * @param fs The file system to be used. + * @param path Path to the root folder in the file system that is used for + * Serving static data down and upload. + * @param cache_header Cache Header to be used in replies. + */ + FileServerHandler() + { + TRACE("FileServerHandler is registered\n"); + } + + + /** + @brief check wether the request can be handled by this implementation. + @param requestMethod method of the http request line. + @param requestUri request ressource from the http request line. + @return true when method can be handled. + */ + bool canHandle(HTTPMethod requestMethod, const String UNUSED &_uri) override + { + // can handle POST for uploads and DELETE. + return ((requestMethod == HTTP_POST) || (requestMethod == HTTP_DELETE)); + } // canHandle() + + + bool canUpload(const String &uri) override + { + // only allow upload on root fs level. + return (uri == "/"); + } // canUpload() + + + bool handle(ESP8266WebServer &server, HTTPMethod requestMethod, const String &requestUri) override + { + // ensure that filename starts with '/' + String fName = requestUri; + if (!fName.startsWith("/")) { + fName = "/" + fName; + } + + if (requestMethod == HTTP_POST) { + // all done in upload. no other forms. + + } else if (requestMethod == HTTP_DELETE) { + if (LittleFS.exists(fName)) { + LittleFS.remove(fName); + } + } // if + + server.send(200); // all done. + return (true); + } // handle() + + + // uploading process + void upload(ESP8266WebServer UNUSED &server, const String UNUSED &_requestUri, HTTPUpload &upload) override + { + // ensure that filename starts with '/' + String fName = upload.filename; + if (!fName.startsWith("/")) { + fName = "/" + fName; + } + + if (upload.status == UPLOAD_FILE_START) { + // Open the file + if (LittleFS.exists(fName)) { + LittleFS.remove(fName); + } // if + _fsUploadFile = LittleFS.open(fName, "w"); + + } else if (upload.status == UPLOAD_FILE_WRITE) { + // Write received bytes + if (_fsUploadFile) + _fsUploadFile.write(upload.buf, upload.currentSize); + + } else if (upload.status == UPLOAD_FILE_END) { + // Close the file + if (_fsUploadFile) { + _fsUploadFile.close(); + } + } // if + } // upload() + +protected: + File _fsUploadFile; +}; + + +/** + * Setup everything to make the webserver work. + */ +void setup(void) +{ + delay(3000); // wait for serial monitor to start completely. + + // Use Serial port for some trace information from the example + Serial.begin(115200); + Serial.setDebugOutput(false); + + TRACE("Starting WebServer example...\n"); + + TRACE("Mounting the filesystem...\n"); + if (!LittleFS.begin()) { + TRACE("could not mount the filesystem...\n"); + delay(2000); + ESP.restart(); + } + + // start WiFI + WiFi.mode(WIFI_STA); + if (strlen(ssid) == 0) { + WiFi.begin(); + } else { + WiFi.begin(ssid, passPhrase); + } + + // allow to address the device by the given name e.g. http://webserver + WiFi.setHostname(HOSTNAME); + + TRACE("Connect to WiFi...\n"); + while (WiFi.status() != WL_CONNECTED) { + delay(500); + TRACE("."); + } + TRACE("connected.\n"); + + // Ask for the current time using NTP request builtin into ESP firmware. + TRACE("Setup ntp...\n"); + configTime(TIMEZONE, "pool.ntp.org"); + + TRACE("Register service handlers...\n"); + + // serve a built-in htm page + server.on("/$upload.htm", []() { + server.send(200, "text/html", FPSTR(uploadContent)); + }); + + // register a redirect handler when only domain name is given. + server.on("/", HTTP_GET, handleRedirect); + + // register some REST services + server.on("/$list", HTTP_GET, handleListFiles); + server.on("/$sysinfo", HTTP_GET, handleSysInfo); + + // UPLOAD and DELETE of files in the file system using a request handler. + server.addHandler(new FileServerHandler()); + + // enable CORS header in webserver results + server.enableCORS(true); + + // enable ETAG header in webserver results from serveStatic handler + server.enableETag(true); + + // serve all static files + server.serveStatic("/", LittleFS, "/"); + + // handle cases when file is not found + server.onNotFound([]() { + // standard not found in browser. + server.send(404, "text/html", FPSTR(notFoundContent)); + }); + + server.begin(); + TRACE("hostname=%s\n", WiFi.getHostname()); +} // setup + + +// run the server... +void loop(void) +{ + server.handleClient(); +} // loop() + +// end. diff --git a/libraries/ESP8266WebServer/examples/WebServer/builtinfiles.h b/libraries/ESP8266WebServer/examples/WebServer/builtinfiles.h index 4c13c1f2f3..210b18c1a5 100644 --- a/libraries/ESP8266WebServer/examples/WebServer/builtinfiles.h +++ b/libraries/ESP8266WebServer/examples/WebServer/builtinfiles.h @@ -1,63 +1,63 @@ -/** - * @file builtinfiles.h - * @brief This file is part of the WebServer example for the ESP8266WebServer. - * - * This file contains long, multiline text variables for all builtin resources. - */ - -// used for $upload.htm -static const char uploadContent[] PROGMEM = -R"==( - - - - - - - Upload - - - -

Upload

- -
-
Drop files here...
- - - -)=="; - -// used for $upload.htm -static const char notFoundContent[] PROGMEM = R"==( - - - Ressource not found - - -

The ressource was not found.

-

Start again

- -)=="; +/** + * @file builtinfiles.h + * @brief This file is part of the WebServer example for the ESP8266WebServer. + * + * This file contains long, multiline text variables for all builtin resources. + */ + +// used for $upload.htm +static const char uploadContent[] PROGMEM = +R"==( + + + + + + + Upload + + + +

Upload

+ +
+
Drop files here...
+ + + +)=="; + +// used for $upload.htm +static const char notFoundContent[] PROGMEM = R"==( + + + Ressource not found + + +

The ressource was not found.

+

Start again

+ +)=="; diff --git a/libraries/ESP8266WebServer/examples/WebServer/data/files.htm b/libraries/ESP8266WebServer/examples/WebServer/data/files.htm index 4eae798980..c05c22fc78 100644 --- a/libraries/ESP8266WebServer/examples/WebServer/data/files.htm +++ b/libraries/ESP8266WebServer/examples/WebServer/data/files.htm @@ -1,65 +1,65 @@ - - - - Files - - - - -

Files on Server

- -

These files are available on the server to be opened or delete:

-
-
- - - - + + + + Files + + + + +

Files on Server

+ +

These files are available on the server to be opened or delete:

+
+
+ + + + \ No newline at end of file diff --git a/libraries/ESP8266WebServer/examples/WebServer/data/index.htm b/libraries/ESP8266WebServer/examples/WebServer/data/index.htm index 434f2c1d64..89188f5203 100644 --- a/libraries/ESP8266WebServer/examples/WebServer/data/index.htm +++ b/libraries/ESP8266WebServer/examples/WebServer/data/index.htm @@ -1,24 +1,24 @@ - - - - HomePage - - - - -

Homepage of the WebServer Example

- -

The following pages are available:

- - -

The following REST services are available:

-
    -
  • /$sysinfo - Some system level information
  • -
  • /$list - Array of all files
  • -
- + + + + HomePage + + + + +

Homepage of the WebServer Example

+ +

The following pages are available:

+ + +

The following REST services are available:

+
    +
  • /$sysinfo - Some system level information
  • +
  • /$list - Array of all files
  • +
+ \ No newline at end of file diff --git a/libraries/ESP8266WebServer/examples/WebServer/data/style.css b/libraries/ESP8266WebServer/examples/WebServer/data/style.css index 4730fef58a..95ac48e727 100644 --- a/libraries/ESP8266WebServer/examples/WebServer/data/style.css +++ b/libraries/ESP8266WebServer/examples/WebServer/data/style.css @@ -1,10 +1,10 @@ -html, body { - color: #111111; font-family: Arial, ui-sans-serif, sans-serif; font-size: 1em; background-color: #f0f0f0; -} - -#list > div { - margin: 0 0 0.5rem 0; -} - -a { color: inherit; cursor: pointer; } - +html, body { + color: #111111; font-family: Arial, ui-sans-serif, sans-serif; font-size: 1em; background-color: #f0f0f0; +} + +#list > div { + margin: 0 0 0.5rem 0; +} + +a { color: inherit; cursor: pointer; } + diff --git a/libraries/ESP8266WebServer/examples/WebServer/secrets.h b/libraries/ESP8266WebServer/examples/WebServer/secrets.h index 62c3093278..094e0dfa7d 100644 --- a/libraries/ESP8266WebServer/examples/WebServer/secrets.h +++ b/libraries/ESP8266WebServer/examples/WebServer/secrets.h @@ -1,13 +1,13 @@ -// Secrets for your local home network - -// This is a "hard way" to configure your local WiFi network name and passphrase -// into the source code and the uploaded sketch. -// -// Using the WiFi Manager is preferred and avoids reprogramming when your network changes. -// See https://homeding.github.io/#page=/wifimanager.md - -// ssid and passPhrase can be used when compiling for a specific environment as a 2. option. - -// add you wifi network name and PassPhrase or use WiFi Manager -const char *ssid = ""; -const char *passPhrase = ""; +// Secrets for your local home network + +// This is a "hard way" to configure your local WiFi network name and passphrase +// into the source code and the uploaded sketch. +// +// Using the WiFi Manager is preferred and avoids reprogramming when your network changes. +// See https://homeding.github.io/#page=/wifimanager.md + +// ssid and passPhrase can be used when compiling for a specific environment as a 2. option. + +// add you wifi network name and PassPhrase or use WiFi Manager +const char *ssid = "KHMH"; +const char *passPhrase = "hk-2012FD2926"; From eb04578f0a69745b83cc32daac2579baac93d635 Mon Sep 17 00:00:00 2001 From: Matthias Hertel Date: Fri, 23 Jul 2021 09:04:54 +0200 Subject: [PATCH 08/10] fixing style --- .../examples/WebServer/WebServer.ino | 45 +++++++------------ 1 file changed, 17 insertions(+), 28 deletions(-) diff --git a/libraries/ESP8266WebServer/examples/WebServer/WebServer.ino b/libraries/ESP8266WebServer/examples/WebServer/WebServer.ino index 0bb0575208..f81e72d244 100644 --- a/libraries/ESP8266WebServer/examples/WebServer/WebServer.ino +++ b/libraries/ESP8266WebServer/examples/WebServer/WebServer.ino @@ -39,8 +39,7 @@ ESP8266WebServer server(80); // This function is called when the WebServer was requested without giving a filename. // This will redirect to the file index.htm when it is existing otherwise to the built-in $upload.htm page -void handleRedirect() -{ +void handleRedirect() { TRACE("Redirect..."); String url = "/index.htm"; @@ -55,15 +54,15 @@ void handleRedirect() // This function is called when the WebServer was requested to list all existing files in the filesystem. // a JSON array with file information is returned. -void handleListFiles() -{ +void handleListFiles() { Dir dir = LittleFS.openDir("/"); String result; result += "[\n"; while (dir.next()) { - if (result.length() > 4) + if (result.length() > 4) { result += ","; + } result += " {"; result += " \"name\": \"" + dir.fileName() + "\", "; result += " \"size\": " + String(dir.fileSize()) + ", "; @@ -78,8 +77,7 @@ void handleListFiles() // This function is called when the sysInfo service was requested. -void handleSysInfo() -{ +void handleSysInfo() { String result; FSInfo fs_info; @@ -100,18 +98,15 @@ void handleSysInfo() // ===== Request Handler class used to answer more complex requests ===== // The FileServerHandler is registered to the web server to support DELETE and UPLOAD of files into the filesystem. -class FileServerHandler : public RequestHandler -{ +class FileServerHandler : public RequestHandler { public: /** - * @brief Construct a new File Server Handler object - * @param fs The file system to be used. - * @param path Path to the root folder in the file system that is used for - * Serving static data down and upload. - * @param cache_header Cache Header to be used in replies. + @brief Construct a new File Server Handler object + @param fs The file system to be used. + @param path Path to the root folder in the file system that is used for serving static data down and upload. + @param cache_header Cache Header to be used in replies. */ - FileServerHandler() - { + FileServerHandler() { TRACE("FileServerHandler is registered\n"); } @@ -122,22 +117,19 @@ public: @param requestUri request ressource from the http request line. @return true when method can be handled. */ - bool canHandle(HTTPMethod requestMethod, const String UNUSED &_uri) override - { + bool canHandle(HTTPMethod requestMethod, const String UNUSED &_uri) override { // can handle POST for uploads and DELETE. return ((requestMethod == HTTP_POST) || (requestMethod == HTTP_DELETE)); } // canHandle() - bool canUpload(const String &uri) override - { + bool canUpload(const String &uri) override { // only allow upload on root fs level. return (uri == "/"); } // canUpload() - bool handle(ESP8266WebServer &server, HTTPMethod requestMethod, const String &requestUri) override - { + bool handle(ESP8266WebServer &server, HTTPMethod requestMethod, const String &requestUri) override { // ensure that filename starts with '/' String fName = requestUri; if (!fName.startsWith("/")) { @@ -159,8 +151,7 @@ public: // uploading process - void upload(ESP8266WebServer UNUSED &server, const String UNUSED &_requestUri, HTTPUpload &upload) override - { + void upload(ESP8266WebServer UNUSED &server, const String UNUSED &_requestUri, HTTPUpload &upload) override { // ensure that filename starts with '/' String fName = upload.filename; if (!fName.startsWith("/")) { @@ -195,8 +186,7 @@ protected: /** * Setup everything to make the webserver work. */ -void setup(void) -{ +void setup(void) { delay(3000); // wait for serial monitor to start completely. // Use Serial port for some trace information from the example @@ -272,8 +262,7 @@ void setup(void) // run the server... -void loop(void) -{ +void loop(void) { server.handleClient(); } // loop() From 4b9a0da7429bf7f9fbd576d54be052df87b917ac Mon Sep 17 00:00:00 2001 From: Matthias Hertel Date: Fri, 23 Jul 2021 09:16:39 +0200 Subject: [PATCH 09/10] fixing style --- .../examples/WebServer/WebServer.ino | 149 ++++++++---------- 1 file changed, 70 insertions(+), 79 deletions(-) diff --git a/libraries/ESP8266WebServer/examples/WebServer/WebServer.ino b/libraries/ESP8266WebServer/examples/WebServer/WebServer.ino index f81e72d244..49d78a68f3 100644 --- a/libraries/ESP8266WebServer/examples/WebServer/WebServer.ino +++ b/libraries/ESP8266WebServer/examples/WebServer/WebServer.ino @@ -1,12 +1,10 @@ -/** - * @file WebServer.ino - * @brief Example implementation using the ESP8266 WebServer. - * - * See also README.md for instructions and hints. - * - * Changelog: - * 21.07.2021 creation, first version - */ +// @file WebServer.ino +// @brief Example implementation using the ESP8266 WebServer. +// +// See also README.md for instructions and hints. +// +// Changelog: +// 21.07.2021 creation, first version #include #include @@ -99,93 +97,86 @@ void handleSysInfo() { // The FileServerHandler is registered to the web server to support DELETE and UPLOAD of files into the filesystem. class FileServerHandler : public RequestHandler { -public: - /** - @brief Construct a new File Server Handler object - @param fs The file system to be used. - @param path Path to the root folder in the file system that is used for serving static data down and upload. - @param cache_header Cache Header to be used in replies. - */ - FileServerHandler() { - TRACE("FileServerHandler is registered\n"); - } - - - /** - @brief check wether the request can be handled by this implementation. - @param requestMethod method of the http request line. - @param requestUri request ressource from the http request line. - @return true when method can be handled. - */ - bool canHandle(HTTPMethod requestMethod, const String UNUSED &_uri) override { - // can handle POST for uploads and DELETE. - return ((requestMethod == HTTP_POST) || (requestMethod == HTTP_DELETE)); - } // canHandle() + public: + // @brief Construct a new File Server Handler object + // @param fs The file system to be used. + // @param path Path to the root folder in the file system that is used for serving static data down and upload. + // @param cache_header Cache Header to be used in replies. + FileServerHandler() { + TRACE("FileServerHandler is registered\n"); + } - bool canUpload(const String &uri) override { - // only allow upload on root fs level. - return (uri == "/"); - } // canUpload() + // @brief check incoming request. Can handle POST for uploads and DELETE. + // @param requestMethod method of the http request line. + // @param requestUri request ressource from the http request line. + // @return true when method can be handled. + bool canHandle(HTTPMethod requestMethod, const String UNUSED &_uri) override { + return ((requestMethod == HTTP_POST) || (requestMethod == HTTP_DELETE)); + } // canHandle() - bool handle(ESP8266WebServer &server, HTTPMethod requestMethod, const String &requestUri) override { - // ensure that filename starts with '/' - String fName = requestUri; - if (!fName.startsWith("/")) { - fName = "/" + fName; - } + bool canUpload(const String &uri) override { + // only allow upload on root fs level. + return (uri == "/"); + } // canUpload() - if (requestMethod == HTTP_POST) { - // all done in upload. no other forms. - } else if (requestMethod == HTTP_DELETE) { - if (LittleFS.exists(fName)) { - LittleFS.remove(fName); + bool handle(ESP8266WebServer &server, HTTPMethod requestMethod, const String &requestUri) override { + // ensure that filename starts with '/' + String fName = requestUri; + if (!fName.startsWith("/")) { + fName = "/" + fName; } - } // if - - server.send(200); // all done. - return (true); - } // handle() + if (requestMethod == HTTP_POST) { + // all done in upload. no other forms. - // uploading process - void upload(ESP8266WebServer UNUSED &server, const String UNUSED &_requestUri, HTTPUpload &upload) override { - // ensure that filename starts with '/' - String fName = upload.filename; - if (!fName.startsWith("/")) { - fName = "/" + fName; - } - - if (upload.status == UPLOAD_FILE_START) { - // Open the file - if (LittleFS.exists(fName)) { - LittleFS.remove(fName); + } else if (requestMethod == HTTP_DELETE) { + if (LittleFS.exists(fName)) { + LittleFS.remove(fName); + } } // if - _fsUploadFile = LittleFS.open(fName, "w"); - } else if (upload.status == UPLOAD_FILE_WRITE) { - // Write received bytes - if (_fsUploadFile) - _fsUploadFile.write(upload.buf, upload.currentSize); + server.send(200); // all done. + return (true); + } // handle() + - } else if (upload.status == UPLOAD_FILE_END) { - // Close the file - if (_fsUploadFile) { - _fsUploadFile.close(); + // uploading process + void upload(ESP8266WebServer UNUSED &server, const String UNUSED &_requestUri, HTTPUpload &upload) override { + // ensure that filename starts with '/' + String fName = upload.filename; + if (!fName.startsWith("/")) { + fName = "/" + fName; } - } // if - } // upload() -protected: - File _fsUploadFile; + if (upload.status == UPLOAD_FILE_START) { + // Open the file + if (LittleFS.exists(fName)) { + LittleFS.remove(fName); + } // if + _fsUploadFile = LittleFS.open(fName, "w"); + + } else if (upload.status == UPLOAD_FILE_WRITE) { + // Write received bytes + if (_fsUploadFile) + _fsUploadFile.write(upload.buf, upload.currentSize); + + } else if (upload.status == UPLOAD_FILE_END) { + // Close the file + if (_fsUploadFile) { + _fsUploadFile.close(); + } + } // if + } // upload() + + protected: + File _fsUploadFile; }; -/** - * Setup everything to make the webserver work. - */ +// Setup everything to make the webserver work. void setup(void) { delay(3000); // wait for serial monitor to start completely. From e2ad93972bed2dce557388ef62d6cf685da4c5ca Mon Sep 17 00:00:00 2001 From: Matthias Hertel Date: Fri, 23 Jul 2021 09:22:53 +0200 Subject: [PATCH 10/10] fixing style --- .../ESP8266WebServer/examples/WebServer/WebServer.ino | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/libraries/ESP8266WebServer/examples/WebServer/WebServer.ino b/libraries/ESP8266WebServer/examples/WebServer/WebServer.ino index 49d78a68f3..08107586fd 100644 --- a/libraries/ESP8266WebServer/examples/WebServer/WebServer.ino +++ b/libraries/ESP8266WebServer/examples/WebServer/WebServer.ino @@ -1,8 +1,8 @@ // @file WebServer.ino // @brief Example implementation using the ESP8266 WebServer. -// +// // See also README.md for instructions and hints. -// +// // Changelog: // 21.07.2021 creation, first version @@ -160,8 +160,9 @@ class FileServerHandler : public RequestHandler { } else if (upload.status == UPLOAD_FILE_WRITE) { // Write received bytes - if (_fsUploadFile) + if (_fsUploadFile) { _fsUploadFile.write(upload.buf, upload.currentSize); + } } else if (upload.status == UPLOAD_FILE_END) { // Close the file