diff --git a/src/ESPAsyncWebServer.h b/src/ESPAsyncWebServer.h index fae9829..db4807b 100644 --- a/src/ESPAsyncWebServer.h +++ b/src/ESPAsyncWebServer.h @@ -186,12 +186,14 @@ class AsyncWebServerRequest { void send(AsyncWebServerResponse *response); void send(int code, String contentType=String(), String content=String()); void send(FS &fs, String path, String contentType=String(), bool download=false); + void send(File content, String contentType=String(), bool download=false); void send(Stream &stream, String contentType, size_t len); void send(String contentType, size_t len, AwsResponseFiller callback); void sendChunked(String contentType, AwsResponseFiller callback); AsyncWebServerResponse *beginResponse(int code, String contentType=String(), String content=String()); AsyncWebServerResponse *beginResponse(FS &fs, String path, String contentType=String(), bool download=false); + AsyncWebServerResponse *beginResponse(File content, String contentType=String(), bool download=false); AsyncWebServerResponse *beginResponse(Stream &stream, String contentType, size_t len); AsyncWebServerResponse *beginResponse(String contentType, size_t len, AwsResponseFiller callback); AsyncWebServerResponse *beginChunkedResponse(String contentType, AwsResponseFiller callback); diff --git a/src/WebHandlerImpl.h b/src/WebHandlerImpl.h index eb26a09..158cb60 100644 --- a/src/WebHandlerImpl.h +++ b/src/WebHandlerImpl.h @@ -26,8 +26,8 @@ class AsyncStaticWebHandler: public AsyncWebHandler { private: - String _getPath(AsyncWebServerRequest *request, const bool withStats); - bool _fileExists(const String path, const bool withStats); + bool _getFile(AsyncWebServerRequest *request); + bool _fileExists(AsyncWebServerRequest *request, const String path); uint8_t _countBits(const uint8_t value); protected: FS _fs; @@ -39,28 +39,7 @@ class AsyncStaticWebHandler: public AsyncWebHandler { uint8_t _gzipStats; uint8_t _fileStats; public: - AsyncStaticWebHandler(FS& fs, const char* path, const char* uri, const char* cache_header) - : _fs(fs), _uri(uri), _path(path), _cache_header(cache_header){ - // Ensure leading '/' - if (_uri.length() == 0 || _uri[0] != '/') _uri = "/" + _uri; - if (_path.length() == 0 || _path[0] != '/') _path = "/" + _path; - - // If uri or path ends with '/' we assume a hint that this is a directory to improve performance. - // However - if they both do not end '/' we, can't assume they are files, they can still be directory. - bool isUriDir = _uri[_uri.length()-1] == '/'; - bool isPathDir = _path[_path.length()-1] == '/'; - _isDir = isUriDir || isPathDir; - - // If we serving directory - remove the trailing '/' so we can handle default file - // Notice that root will be "" not "/" - if (_isDir && isUriDir) _uri = _uri.substring(0, _uri.length()-1); - if (_isDir && isPathDir) _path = _path.substring(0, _path.length()-1); - - // Reset stats - _gzipFirst = false; - _gzipStats = 0; - _fileStats = 0; - } + AsyncStaticWebHandler(FS& fs, const char* path, const char* uri, const char* cache_header); bool canHandle(AsyncWebServerRequest *request); void handleRequest(AsyncWebServerRequest *request); }; diff --git a/src/WebHandlers.cpp b/src/WebHandlers.cpp index cbf1842..81a7f15 100644 --- a/src/WebHandlers.cpp +++ b/src/WebHandlers.cpp @@ -21,21 +21,44 @@ #include "ESPAsyncWebServer.h" #include "WebHandlerImpl.h" +AsyncStaticWebHandler::AsyncStaticWebHandler(FS& fs, const char* path, const char* uri, const char* cache_header) + : _fs(fs), _uri(uri), _path(path), _cache_header(cache_header) +{ + // Ensure leading '/' + if (_uri.length() == 0 || _uri[0] != '/') _uri = "/" + _uri; + if (_path.length() == 0 || _path[0] != '/') _path = "/" + _path; + + // If uri or path ends with '/' we assume a hint that this is a directory to improve performance. + // However - if they both do not end '/' we, can't assume they are files, they can still be directory. + bool isUriDir = _uri[_uri.length()-1] == '/'; + bool isPathDir = _path[_path.length()-1] == '/'; + _isDir = isUriDir || isPathDir; + + // If we serving directory - remove the trailing '/' so we can handle default file + // Notice that root will be "" not "/" + if (_isDir && isUriDir) _uri = _uri.substring(0, _uri.length()-1); + if (_isDir && isPathDir) _path = _path.substring(0, _path.length()-1); + + // Reset stats + _gzipFirst = false; + _gzipStats = 0; + _fileStats = 0; +} + bool AsyncStaticWebHandler::canHandle(AsyncWebServerRequest *request) { if (request->method() == HTTP_GET && request->url().startsWith(_uri) && - _getPath(request, true).length()) { + _getFile(request)) { DEBUGF("[AsyncStaticWebHandler::canHandle] TRUE\n"); return true; - } return false; } -String AsyncStaticWebHandler::_getPath(AsyncWebServerRequest *request, const bool withStats) +bool AsyncStaticWebHandler::_getFile(AsyncWebServerRequest *request) { // Remove the found uri String path = request->url().substring(_uri.length()); @@ -46,19 +69,18 @@ String AsyncStaticWebHandler::_getPath(AsyncWebServerRequest *request, const boo path = _path + path; // Do we have a file or .gz file - if (!canSkipFileCheck) if (_fileExists(path, withStats)) return path; + if (!canSkipFileCheck && _fileExists(request, path)) + return true; // Try to add default page, ensure there is a trailing '/' ot the path. - if (path.length() == 0 || path[path.length()-1] != '/') path += "/"; + if (path.length() == 0 || path[path.length()-1] != '/') + path += "/"; path += "index.htm"; - if (_fileExists(path, withStats)) return path; - - // No file - return empty string - return String(); + return_fileExists(request, path); } -bool AsyncStaticWebHandler::_fileExists(const String path, const bool withStats) +bool AsyncStaticWebHandler::_fileExists(AsyncWebServerRequest *request, const String path) { bool fileFound = false; bool gzipFound = false; @@ -66,16 +88,24 @@ bool AsyncStaticWebHandler::_fileExists(const String path, const bool withStats) String gzip = path + ".gz"; if (_gzipFirst) { - gzipFound = _fs.exists(gzip); - if (!gzipFound) fileFound = _fs.exists(path); + request->_tempFile = _fs.open(gzip, "r"); + gzipFound = request->_tempFile == true; + if (!gzipFound){ + request->_tempFile = _fs.open(path, "r"); + fileFound = request->_tempFile == true; + } } else { - fileFound = _fs.exists(path); - if (!fileFound) gzipFound = _fs.exists(gzip); + request->_tempFile = _fs.open(path, "r"); + fileFound = request->_tempFile == true; + if (!fileFound){ + request->_tempFile = _fs.open(gzip, "r"); + gzipFound = request->_tempFile == true; + } } bool found = fileFound || gzipFound; - if (withStats && found) { + if (found) { _gzipStats = (_gzipStats << 1) + gzipFound ? 1 : 0; _fileStats = (_fileStats << 1) + fileFound ? 1 : 0; _gzipFirst = _countBits(_gzipStats) > _countBits(_fileStats); @@ -94,15 +124,12 @@ uint8_t AsyncStaticWebHandler::_countBits(const uint8_t value) void AsyncStaticWebHandler::handleRequest(AsyncWebServerRequest *request) { - String path = _getPath(request, false); - - if (path.length()) { - AsyncWebServerResponse * response = new AsyncFileResponse(_fs, path); + if (request->_tempFile == true) { + AsyncWebServerResponse * response = new AsyncFileResponse(request->_tempFile); if (_cache_header.length() != 0) response->addHeader("Cache-Control", _cache_header); request->send(response); } else { request->send(404); } - path = String(); } diff --git a/src/WebRequest.cpp b/src/WebRequest.cpp index 527f507..1b6aa49 100644 --- a/src/WebRequest.cpp +++ b/src/WebRequest.cpp @@ -651,6 +651,12 @@ AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(FS &fs, String pat return NULL; } +AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(File content, String contentType, bool download){ + if(content == true) + return new AsyncFileResponse(content, contentType, download); + return NULL; +} + AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(Stream &stream, String contentType, size_t len){ return new AsyncStreamResponse(stream, contentType, len); } @@ -679,6 +685,12 @@ void AsyncWebServerRequest::send(FS &fs, String path, String contentType, bool d } else send(404); } +void AsyncWebServerRequest::send(File content, String contentType, bool download){ + if(content == true){ + send(beginResponse(content, contentType, download)); + } else send(404); +} + void AsyncWebServerRequest::send(Stream &stream, String contentType, size_t len){ send(beginResponse(stream, contentType, len)); } diff --git a/src/WebResponseImpl.h b/src/WebResponseImpl.h index 59400f6..e9e64ff 100644 --- a/src/WebResponseImpl.h +++ b/src/WebResponseImpl.h @@ -48,6 +48,7 @@ class AsyncFileResponse: public AsyncAbstractResponse { void _setContentType(String path); public: AsyncFileResponse(FS &fs, String path, String contentType=String(), bool download=false); + AsyncFileResponse(File content, String contentType=String(), bool download=false); ~AsyncFileResponse(); bool _sourceValid(){ return !!(_content); } size_t _fillBuffer(uint8_t *buf, size_t maxLen); diff --git a/src/WebResponses.cpp b/src/WebResponses.cpp index b3b1b7d..2148996 100644 --- a/src/WebResponses.cpp +++ b/src/WebResponses.cpp @@ -376,6 +376,31 @@ AsyncFileResponse::AsyncFileResponse(FS &fs, String path, String contentType, bo _contentLength = _content.size(); } +AsyncFileResponse::AsyncFileResponse(File content, String contentType, bool download){ + _code = 200; + _content = content; + _path = String(_content.name()); + _contentLength = _content.size(); + int filenameStart = path.lastIndexOf('/') + 1; + char buf[26+path.length()-filenameStart]; + char* filename = (char*)path.c_str() + filenameStart; + + if(!download && _path.endsWith(".gz")) + addHeader("Content-Encoding", "gzip"); + + if(contentType == "") + _setContentType(_path); + else + _contentType = contentType; + + if(download) { + snprintf(buf, sizeof (buf), "attachment; filename='%s'", filename); + } else { + snprintf(buf, sizeof (buf), "inline; filename='%s'", filename); + } + addHeader("Content-Disposition", buf); +} + size_t AsyncFileResponse::_fillBuffer(uint8_t *data, size_t len){ _content.read(data, len); return len;