diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000000..1a5467e1a2 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,7 @@ +{ + "files.associations": { + "__config": "cpp", + "fstream": "cpp", + "locale": "cpp" + } +} \ No newline at end of file diff --git a/libraries/ESP8266WebServer/src/ESP8266WebServer-impl.h b/libraries/ESP8266WebServer/src/ESP8266WebServer-impl.h index 0fcb3c3970..9ea77718f8 100644 --- a/libraries/ESP8266WebServer/src/ESP8266WebServer-impl.h +++ b/libraries/ESP8266WebServer/src/ESP8266WebServer-impl.h @@ -255,7 +255,18 @@ void ESP8266WebServerTemplate::_addRequestHandler(RequestHandlerType template void ESP8266WebServerTemplate::serveStatic(const char* uri, FS& fs, const char* path, const char* cache_header) { - _addRequestHandler(new StaticRequestHandler(fs, path, uri, cache_header)); + bool is_file = false; + + if (fs.exists(path)) { + File file = fs.open(path, "r"); + is_file = file && file.isFile(); + file.close(); + } + + if(is_file) + _addRequestHandler(new StaticFileRequestHandler(fs, path, uri, cache_header)); + else + _addRequestHandler(new StaticDirectoryRequestHandler(fs, path, uri, cache_header)); } template @@ -607,29 +618,18 @@ const String& ESP8266WebServerTemplate::header(const String& name) c return emptyString; } -template -void ESP8266WebServerTemplate::collectHeaders(const char* headerKeys[], const size_t headerKeysCount) { - _headerKeysCount = headerKeysCount + 1; - if (_currentHeaders) - delete[]_currentHeaders; - _currentHeaders = new RequestArgument[_headerKeysCount]; - _currentHeaders[0].key = FPSTR(AUTHORIZATION_HEADER); - for (int i = 1; i < _headerKeysCount; i++){ - _currentHeaders[i].key = headerKeys[i-1]; - } -} template -void ESP8266WebServerETagTemplate::collectHeaders(const char* headerKeys[], const size_t headerKeysCount) { - WST::_headerKeysCount = headerKeysCount + 2; - if (WST::_currentHeaders){ - delete[] WST::_currentHeaders; +void ESP8266WebServerTemplate::collectHeaders(const char* headerKeys[], const size_t headerKeysCount) { + _headerKeysCount = headerKeysCount + 2; + if (_currentHeaders){ + delete[] _currentHeaders; } - WST::_currentHeaders = new typename WST::RequestArgument[WST::_headerKeysCount]; - WST::_currentHeaders[0].key = FPSTR(AUTHORIZATION_HEADER); - WST::_currentHeaders[1].key = FPSTR(ETAG_HEADER); - for (int i = 2; i < WST::_headerKeysCount; i++){ - WST::_currentHeaders[i].key = headerKeys[i-2]; + _currentHeaders = new RequestArgument[_headerKeysCount]; + _currentHeaders[0].key = FPSTR(AUTHORIZATION_HEADER); + _currentHeaders[1].key = FPSTR(ETAG_HEADER); + for (int i = 2; i < _headerKeysCount; i++){ + _currentHeaders[i].key = headerKeys[i-2]; } } @@ -846,37 +846,4 @@ String ESP8266WebServerTemplate::responseCodeToString(const int code return String(r); } -template -void ESP8266WebServerETagTemplate::serveStaticETag(const char* uri, FS& fs, const char* path, const char* cache_header){ - WST::_addRequestHandler(new StaticRequestETagHandler(fs, path, uri, cache_header)); -} - -template -void ESP8266WebServerETagTemplate::serveStaticETag(const char* uri, FS& fs, const char * path) { - - File toHash = fs.open(path, "r"); - - if(toHash){ - - MD5Builder calcMD5; - calcMD5.begin(); - - calcMD5.addStream(toHash, toHash.size()); - - toHash.close(); - - calcMD5.calculate(); - - uint8_t buff[16]; - - calcMD5.getBytes(buff); - - const String etag = "\"" + base64::encode(buff, 16, false) + "\""; - - serveStaticETag(uri, fs, path, etag.c_str()); - - } - -} - } // namespace diff --git a/libraries/ESP8266WebServer/src/ESP8266WebServer.h b/libraries/ESP8266WebServer/src/ESP8266WebServer.h index 28036ed8d5..d7199c4a9a 100644 --- a/libraries/ESP8266WebServer/src/ESP8266WebServer.h +++ b/libraries/ESP8266WebServer/src/ESP8266WebServer.h @@ -300,20 +300,6 @@ class ESP8266WebServerTemplate HookFunction _hook; }; - -template -class ESP8266WebServerETagTemplate : public ESP8266WebServerTemplate -{ - using WST = ESP8266WebServerTemplate; - - using WST::WST; - - public: - void collectHeaders(const char* headerKeys[], const size_t headerKeysCount); - void serveStaticETag(const char* uri, FS& fs, const char* path, const char* cache_header); - void serveStaticETag(const char* uri, FS& fs, const char* path); -}; - } // namespace #include "ESP8266WebServer-impl.h" @@ -322,6 +308,4 @@ class ESP8266WebServerETagTemplate : public ESP8266WebServerTemplate using ESP8266WebServer = esp8266webserver::ESP8266WebServerTemplate; using RequestHandler = esp8266webserver::RequestHandler; -using ESP8266WebServerETag = esp8266webserver::ESP8266WebServerETagTemplate; - #endif //ESP8266WEBSERVER_H \ No newline at end of file diff --git a/libraries/ESP8266WebServer/src/detail/RequestHandlersImpl.h b/libraries/ESP8266WebServer/src/detail/RequestHandlersImpl.h index 1ea64c0c11..b15b54a000 100644 --- a/libraries/ESP8266WebServer/src/detail/RequestHandlersImpl.h +++ b/libraries/ESP8266WebServer/src/detail/RequestHandlersImpl.h @@ -72,27 +72,41 @@ class StaticRequestHandler : public RequestHandler { , _path(path) , _cache_header(cache_header) { - if (fs.exists(path)) { - File file = fs.open(path, "r"); - _isFile = file && file.isFile(); - file.close(); - } - else { - _isFile = false; - } - DEBUGV("StaticRequestHandler: path=%s uri=%s isFile=%d, cache_header=%s\r\n", path, uri, _isFile, cache_header == __null ? "" : cache_header); - _baseUriLength = _uri.length(); } - bool canHandle(HTTPMethod requestMethod, const String& requestUri) override { - if ((requestMethod != HTTP_GET) && (requestMethod != HTTP_HEAD)) - return false; + bool validMethod(HTTPMethod requestMethod){ + return (requestMethod == HTTP_GET) || (requestMethod == HTTP_HEAD); + } - if ((_isFile && requestUri != _uri) || !requestUri.startsWith(_uri)) - return false; + /* Deprecated version. Please use mime::getContentType instead */ + static String getContentType(const String& path) __attribute__((deprecated)) { + return mime::getContentType(path); + } - return true; +protected: + FS _fs; + String _uri; + String _path; + String _cache_header; +}; + + +template +class StaticDirectoryRequestHandler : public StaticRequestHandler { + + using SRH = StaticRequestHandler; + using WebServerType = ESP8266WebServerTemplate; + +public: + StaticDirectoryRequestHandler(FS& fs, const char* path, const char* uri, const char* cache_header) + : + SRH(fs, path, uri, cache_header), + _baseUriLength{SRH::_uri.length()} + {} + + bool canHandle(HTTPMethod requestMethod, const String& requestUri) override { + return SRH::validMethod(requestMethod) && requestUri.startsWith(SRH::_uri); } bool handle(WebServerType& server, HTTPMethod requestMethod, const String& requestUri) override { @@ -100,42 +114,40 @@ class StaticRequestHandler : public RequestHandler { if (!canHandle(requestMethod, requestUri)) return false; - DEBUGV("StaticRequestHandler::handle: request=%s _uri=%s\r\n", requestUri.c_str(), _uri.c_str()); + DEBUGV("DirectoryRequestHandler::handle: request=%s _uri=%s\r\n", requestUri.c_str(), SRH::_uri.c_str()); String path; - path.reserve(_path.length() + requestUri.length() + 32); - path = _path; - - if (!_isFile) { + path.reserve(SRH::_path.length() + requestUri.length() + 32); + path = SRH::_path; - // Append whatever follows this URI in request to get the file path. - path += requestUri.substring(_baseUriLength); + // Append whatever follows this URI in request to get the file path. + path += requestUri.substring(_baseUriLength); - // Base URI doesn't point to a file. - // If a directory is requested, look for index file. - if (path.endsWith("/")) - path += F("index.htm"); + // Base URI doesn't point to a file. + // If a directory is requested, look for index file. + if (path.endsWith("/")) + path += F("index.htm"); - // If neither nor .gz exist, and is a file.htm, try it with file.html instead - // For the normal case this will give a search order of index.htm, index.htm.gz, index.html, index.html.gz - if (!_fs.exists(path) && !_fs.exists(path + ".gz") && path.endsWith(".htm")) { - path += 'l'; - } + // If neither nor .gz exist, and is a file.htm, try it with file.html instead + // For the normal case this will give a search order of index.htm, index.htm.gz, index.html, index.html.gz + if (!SRH::_fs.exists(path) && !SRH::_fs.exists(path + ".gz") && path.endsWith(".htm")) { + path += 'l'; } - DEBUGV("StaticRequestHandler::handle: path=%s, isFile=%d\r\n", path.c_str(), _isFile); + + DEBUGV("DirectoryRequestHandler::handle: path=%s\r\n", path.c_str()); String contentType = mime::getContentType(path); using namespace mime; // 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(FPSTR(mimeTable[gz].endsWith)) && !_fs.exists(path)) { + if (!path.endsWith(FPSTR(mimeTable[gz].endsWith)) && !SRH::_fs.exists(path)) { String pathWithGz = path + FPSTR(mimeTable[gz].endsWith); - if(_fs.exists(pathWithGz)) + if(SRH::_fs.exists(pathWithGz)) path += FPSTR(mimeTable[gz].endsWith); } - File f = _fs.open(path, "r"); + File f = SRH::_fs.open(path, "r"); if (!f) return false; @@ -144,70 +156,75 @@ class StaticRequestHandler : public RequestHandler { return false; } - if (_cache_header.length() != 0) - server.sendHeader("Cache-Control", _cache_header); + if (SRH::_cache_header.length() != 0) + server.sendHeader("Cache-Control", SRH::_cache_header); server.streamFile(f, contentType, requestMethod); return true; } - /* Deprecated version. Please use mime::getContentType instead */ - static String getContentType(const String& path) __attribute__((deprecated)) { - return mime::getContentType(path); - } - protected: - FS _fs; - String _uri; - String _path; - String _cache_header; - bool _isFile; size_t _baseUriLength; }; template -class StaticRequestETagHandler +class StaticFileRequestHandler : public StaticRequestHandler { using SRH = StaticRequestHandler; + using WebServerType = ESP8266WebServerTemplate; - using SRH::SRH; +public: + StaticFileRequestHandler(FS& fs, const char* path, const char* uri, const char* cache_header) + : + 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(); + } - using WebServerType = ESP8266WebServerTemplate; + bool canHandle(HTTPMethod requestMethod, const String& requestUri) override { + return SRH::validMethod(requestMethod) && requestUri == SRH::_uri; + } - public: bool handle(WebServerType& server, HTTPMethod requestMethod, const String & requestUri) override { - if (!SRH::canHandle(requestMethod, requestUri)){ + if (!canHandle(requestMethod, requestUri)) return false; - } - if(server.header("If-None-Match") == SRH::_cache_header){ - // Serial.println("Sending 304!!!"); + const String etag = "\"" + base64::encode(_ETag_md5, 16, false) + "\""; + + if(server.header("If-None-Match") == etag){ server.send(304); return true; } - File f = SRH::_fs.open(SRH::_path, "r"); - if (!f){ + if (!f) return false; - } if (!f.isFile()) { f.close(); return false; } - if (SRH::_cache_header.length() != 0){ - server.sendHeader("ETag", SRH::_cache_header); - } + if (SRH::_cache_header.length() != 0) + server.sendHeader("Cache-Control", SRH::_cache_header); - server.streamFile(f, SRH::getContentType(SRH::_path), requestMethod); + server.sendHeader("ETag", etag); + + server.streamFile(f, mime::getContentType(SRH::_path), requestMethod); return true; } - + +protected: + uint8_t _ETag_md5[16]; }; } // namespace