From 3e0b70af10f35c0f1f3280fac5776a365e497ab2 Mon Sep 17 00:00:00 2001 From: btsimonh Date: Fri, 24 May 2024 21:02:09 +0100 Subject: [PATCH] Ufs serve fix - makes it work better on esp32cam (ai thinker style) (#21486) * Add yield() in file listing, so watchdog does not trigger with very large folders * update UFSServe function to be like download function. This removes the use of server.streamFile, and provides a much more reliable download. Make UFSServe always use this function, regardless of auth requirement. (i.e. no use of the original in the original class, as it still uses streaming.) Note that UFSServe is enab;ed with #define UFILESYS_STATIC_SERVING --- .../xdrv_50_filesystem.ino | 76 ++++++++++++++++--- 1 file changed, 65 insertions(+), 11 deletions(-) diff --git a/tasmota/tasmota_xdrv_driver/xdrv_50_filesystem.ino b/tasmota/tasmota_xdrv_driver/xdrv_50_filesystem.ino index 2ade8983b..c7eb25f68 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_50_filesystem.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_50_filesystem.ino @@ -889,15 +889,20 @@ void UFSRename(void) { */ #include "detail/RequestHandlersImpl.h" +//#define SERVING_DEBUG + // class to allow us to request auth when required. // StaticRequestHandler is in the above header class StaticRequestHandlerAuth : public StaticRequestHandler { public: - StaticRequestHandlerAuth(FS& fs, const char* path, const char* uri, const char* cache_header): + StaticRequestHandlerAuth(FS& fs, const char* path, const char* uri, const char* cache_header, bool requireAuth): StaticRequestHandler(fs, path, uri, cache_header) { + _requireAuth = requireAuth; } + bool _requireAuth; + // we replace the handle method, // and look for authentication only if we would serve the file. // if we check earlier, then we will reject as unauth even though a later @@ -906,8 +911,10 @@ public: if (!canHandle(requestMethod, requestUri)) return false; - log_v("StaticRequestHandler::handle: request=%s _uri=%s\r\n", requestUri.c_str(), _uri.c_str()); - + //log_v("StaticRequestHandler::handle: request=%s _uri=%s\r\n", requestUri.c_str(), _uri.c_str()); +#ifdef SERVING_DEBUG + AddLog(LOG_LEVEL_DEBUG, PSTR("UFS: ::handle: request=%s _uri=%s"), requestUri.c_str(), _uri.c_str()); +#endif String path(_path); if (!_isFile) { @@ -919,8 +926,9 @@ public: // Append whatever follows this URI in request to get the file path. path += requestUri.substring(_baseUriLength); } - log_v("StaticRequestHandler::handle: path=%s, isFile=%d\r\n", path.c_str(), _isFile); - +#ifdef SERVING_DEBUG + AddLog(LOG_LEVEL_DEBUG, PSTR("UFS: ::handle: path=%s, isFile=%d"), path.c_str(), _isFile); +#endif String contentType = getContentType(path); // look for gz file, only if the original specified path is not a gz. So part only works to send gzip via content encoding when a non compressed is asked for @@ -932,11 +940,17 @@ public: } File f = _fs.open(path, "r"); - if (!f || !f.available()) + if (!f || !f.available()){ + AddLog(LOG_LEVEL_DEBUG, PSTR("UFS: ::handler missing file?")); return false; - - if (!WebAuthenticate()) { + } +#ifdef SERVING_DEBUG + AddLog(LOG_LEVEL_DEBUG, PSTR("UFS: ::handler file open %d"), f.available()); +#endif + if (_requireAuth && !WebAuthenticate()) { +#ifdef SERVING_DEBUG AddLog(LOG_LEVEL_ERROR, PSTR("UFS: serv of %s denied"), requestUri.c_str()); +#endif server.requestAuthentication(); return true; } @@ -944,7 +958,45 @@ public: if (_cache_header.length() != 0) server.sendHeader("Cache-Control", _cache_header); - server.streamFile(f, contentType); +#ifdef SERVING_DEBUG + AddLog(LOG_LEVEL_DEBUG, PSTR("UFS: ::handler sending")); +#endif + uint8_t buff[512]; + uint32_t bread; + uint32_t flen = f.available(); + WiFiClient download_Client = server.client(); + server.setContentLength(flen); + server.send(200, contentType, ""); + + // transfer is about 150kb/s + uint32_t cnt = 0; + while (f.available()) { + bread = f.read(buff, sizeof(buff)); + cnt += bread; +#ifdef SERVING_DEBUG + //AddLog(LOG_LEVEL_DEBUG, PSTR("UFS: ::handler sending %d/%d"), cnt, flen); +#endif + uint32_t bw = download_Client.write((const char*)buff, bread); + if (!bw) { break; } + yield(); + } +#ifdef SERVING_DEBUG + AddLog(LOG_LEVEL_DEBUG, PSTR("UFS: ::handler sent %d/%d"), cnt, flen); +#endif + + if (cnt != flen){ + AddLog(LOG_LEVEL_ERROR, PSTR("UFS: ::handler incomplete file send: sent %d/%d"), cnt, flen); + } + + // It does seem that on lesser ESP32, this causes a problem? A lockup... + //server.streamFile(f, contentType); + + f.close(); + download_Client.stop(); + +#ifdef SERVING_DEBUG + AddLog(LOG_LEVEL_DEBUG, PSTR("UFS: ::handler done")); +#endif return true; } }; @@ -959,9 +1011,9 @@ void UFSServe(void) { if (Webserver) { // fail if no Webserver yet. StaticRequestHandlerAuth *staticHandler; if (noauth && *noauth == '1'){ - staticHandler = (StaticRequestHandlerAuth *) new StaticRequestHandler(*ffsp, fpath, url, (char *)nullptr); + staticHandler = new StaticRequestHandlerAuth(*ffsp, fpath, url, (char *)nullptr, false); } else { - staticHandler = new StaticRequestHandlerAuth(*ffsp, fpath, url, (char *)nullptr); + staticHandler = new StaticRequestHandlerAuth(*ffsp, fpath, url, (char *)nullptr, true); } if (staticHandler) { //Webserver->serveStatic(url, *ffsp, fpath); @@ -1319,6 +1371,8 @@ void UfsListDir(char *path, uint8_t depth) { HtmlEscape(name).c_str(), tstr.c_str(), entry.size(), delpath, editpath); } entry.close(); + + yield(); // trigger watchdog reset } } dir.close();