diff --git a/lib/asyncHTTPrequest/src/asyncHTTPrequest.cpp b/lib/asyncHTTPrequest/src/asyncHTTPrequest.cpp index 1503adb..661f1d0 100644 --- a/lib/asyncHTTPrequest/src/asyncHTTPrequest.cpp +++ b/lib/asyncHTTPrequest/src/asyncHTTPrequest.cpp @@ -175,7 +175,7 @@ void asyncHTTPrequest::close(){ _client->close(); } //************************************************************************************************************** -int asyncHTTPrequest::readyState(){ +int asyncHTTPrequest::readyState() const { return _readyState; } diff --git a/lib/asyncHTTPrequest/src/asyncHTTPrequest.h b/lib/asyncHTTPrequest/src/asyncHTTPrequest.h index 6d02628..9830154 100644 --- a/lib/asyncHTTPrequest/src/asyncHTTPrequest.h +++ b/lib/asyncHTTPrequest/src/asyncHTTPrequest.h @@ -136,7 +136,7 @@ class asyncHTTPrequest { void abort(); // Abort the current operation void close(); // Close the connection - int readyState(); // Return the ready state + int readyState() const; // Return the ready state int respHeaderCount(); // Retrieve count of response headers char* respHeaderName(int index); // Return header name by index diff --git a/src/Afterburner.cpp b/src/Afterburner.cpp index 157b718..4d144ba 100644 --- a/src/Afterburner.cpp +++ b/src/Afterburner.cpp @@ -1567,6 +1567,9 @@ void checkDebugCommands() else if(rxVal == '-') { TxManage.queueOffRequest(); } + else if(rxVal == 'h') { + getWebContent(); + } else if(rxVal == 'r') { ESP.restart(); // reset the esp } diff --git a/src/WiFi/BTCWebServer.cpp b/src/WiFi/BTCWebServer.cpp index 574af78..9365cc7 100644 --- a/src/WiFi/BTCWebServer.cpp +++ b/src/WiFi/BTCWebServer.cpp @@ -41,6 +41,7 @@ #include #include "BrowserUpload.h" #include +#include "WebContentDL.h" extern WiFiManager wm; extern const char* stdHeader; @@ -54,10 +55,11 @@ extern void checkSplashScreenUpdate(); sBrowserUpload BrowserUpload; WebServer server(80); WebSocketsServer webSocket = WebSocketsServer(81); +CWebContentDL WebContentDL; bool bRxWebData = false; // flags for OLED animation bool bTxWebData = false; -bool bUpdateAccessed = false; // flag used to ensure web update always starts via /update. direct accesses to /updatenow will FAIL +bool bUpdateAccessed = false; // flag used to ensure browser update always starts via /update. direct accesses to /updatenow will FAIL bool bFormatAccessed = false; bool bFormatPerformed = false; long _SuppliedFileSize = 0; @@ -83,6 +85,7 @@ void onUploadProgression(); void onRename(); void build404Response(String& content, String file); void build500Response(String& content, String file); +void manageWegContentUpdate(); void initWebServer(void) { @@ -137,6 +140,7 @@ void initWebServer(void) { DebugPort.println("HTTP server started"); + // initWebPageUpdate(); } @@ -145,6 +149,7 @@ bool doWebServer(void) { webSocket.loop(); server.handleClient(); + manageWegContentUpdate(); return true; } @@ -1059,3 +1064,41 @@ content += R"=====(" exists, but cannot be streamed? )====="; } +static int webContentState = 0; + +void getWebContent() { + webContentState = 1; +// WebContentDL.get("index.html.gz"); + // getWebContent("favicon.ico"); +} + + +void manageWegContentUpdate() +{ + switch(webContentState) { + case 1: + DebugPort.println("Requesting index.html.gz from Afterburner web site"); + WebContentDL.get("index.html.gz"); + webContentState++; + break; + case 2: + WebContentDL.process(); + if(!WebContentDL.busy()) { + DebugPort.println("Completed index.html.gz from Afterburner web site"); + webContentState++; + } + break; + case 3: + DebugPort.println("Requesting favicon.ico from Afterburner web site"); + WebContentDL.get("favicon.ico"); + webContentState++; + break; + case 4: + WebContentDL.process(); + if(!WebContentDL.busy()) { + DebugPort.println("Completed favicon.ico from Afterburner web site"); + webContentState = 0; + } + break; + } +} diff --git a/src/WiFi/BTCWebServer.h b/src/WiFi/BTCWebServer.h index cff697c..a33dd37 100644 --- a/src/WiFi/BTCWebServer.h +++ b/src/WiFi/BTCWebServer.h @@ -34,5 +34,8 @@ bool sendWebSocketString(const char* Str); bool isWebSocketClientChange(); void listSPIFFS(const char * dirname, uint8_t levels, String& HTMLreport, int withHTMLanchors=0); +void getWebContent(); +void getWebContent(const char* filename); + #endif diff --git a/src/WiFi/WebContentDL.cpp b/src/WiFi/WebContentDL.cpp new file mode 100644 index 0000000..00fc5cf --- /dev/null +++ b/src/WiFi/WebContentDL.cpp @@ -0,0 +1,150 @@ +// seek a web page update from the afterburner web server + +#include "WebContentDL.h" +#include "../Utility/DebugPort.h" + + +void WebPageDataCB(void* pClass, asyncHTTPrequest* request, size_t available) +{ + CWebContentDL* pParent = (CWebContentDL*)pClass; + while(available) { + int read = pParent->queueDLdata(available, request); + if(read >= 0) + available -= read; + else + break; + } +} + + +void WebPageRequestCB(void* pClass, asyncHTTPrequest* request, int readyState) +{ + CWebContentDL* pParent = (CWebContentDL*)pClass; + if(readyState == 4){ + while(request->available()) { + pParent->queueDLdata(request->available(), request); + } + pParent->finalise(); + + request->close(); + } +} + +CWebContentDL::CWebContentDL() +{ + // _request.setDebug(true); + _request.onReadyStateChange(WebPageRequestCB, this); + _request.onData(WebPageDataCB, this); + _queue = xQueueCreate(10, sizeof(sQueueEntry)); + _fileActive = false; + _bytecount = 0; + _queuecount = 0; +} + +CWebContentDL::~CWebContentDL() +{ + vQueueDelete(_queue); +} + +bool +CWebContentDL::busy() const +{ + return _fileActive || (_request.readyState() != 0 && _request.readyState() != 4) ; +} + + +void CWebContentDL::get(const char* filename) +{ + if(_request.readyState() == 0 || _request.readyState() == 4){ + // ensure leading forward slash, required for SPIFFS + _filename = ""; + if(filename[0] != '/') _filename = "/"; + _filename += filename; + // replace with sanitised name + filename = _filename.c_str(); + + DebugPort.printf("Loading file to SPIFFS: '%s'\r\n", filename); + if(SPIFFS.exists(filename)) { + DebugPort.println("Removing existing file from SPIFFS"); + SPIFFS.remove(filename); + } + + _file = SPIFFS.open(filename, "w"); // Open the file for writing in SPIFFS (create if it doesn't exist) + _fileActive = true; + _bytecount = 0; + _queuecount = 0; + + String URL = "http://afterburner.mrjones.id.au/fota/web"; + URL += filename; + _request.open("GET", URL.c_str()); + _request.send(); + } +} + +void CWebContentDL::process() +{ + sQueueEntry entry; + while(xQueueReceive(_queue, &entry, 0)) { + int16_t len = entry.len; + if(len == -1) { + if(_file) { + _file.close(); + _fileActive = false; + } + DebugPort.printf("Downloaded %s (%d bytes) - CLOSED OK\r\n", _filename.c_str(), _bytecount); + + } + else if(len > 0) { + if(_file) { + if(_file.write(entry.data, len) != len) { // Write the received bytes to the file + _file.close(); + _fileActive = false; + DebugPort.printf("Web content downlod - FILE_WRITE error: removing %s\r\n", _filename.c_str()); + SPIFFS.remove(_filename.c_str()); // remove the bad file from SPIFFS + } + else { + _bytecount += len; + } + } + } + // DebugPort.printf("Len=%d Queuecount=%d/%d total=%d\r\n", entry.len, entry.count, queuecount, webpagecount); + } +} + +int16_t +CWebContentDL::queueDLdata(int size, asyncHTTPrequest* request) +{ + sQueueEntry entry; + + if(size > sizeof(entry.data)) + size = sizeof(entry.data); + + int16_t read = request->responseRead(entry.data, size); + + if(read > 0) { + // available -= read; + entry.len = read; + entry.count = ++_queuecount; + + BaseType_t awoken; + xQueueSendFromISR(_queue, &entry, &awoken); + } + else { + DebugPort.println(" page read error?"); + } + + return read; +} + +void +CWebContentDL::finalise() +{ + sQueueEntry entry; + + entry.len = -1; + entry.count = -1; + BaseType_t awoken; + xQueueSendFromISR(_queue, &entry, &awoken); +} + + diff --git a/src/WiFi/WebContentDL.h b/src/WiFi/WebContentDL.h new file mode 100644 index 0000000..70033d1 --- /dev/null +++ b/src/WiFi/WebContentDL.h @@ -0,0 +1,38 @@ +// seek a web page update from the afterburner web server + +#include "../../asyncHTTPrequest/src/asyncHTTPrequest.h" +#include +#include "freertos/queue.h" +#include + +#pragma pack(push, 1) +struct sQueueEntry { + int16_t len; + int16_t count; + uint8_t data[2044]; +}; +#pragma pack(pop) + +class CWebContentDL { + std::string _filename; + asyncHTTPrequest _request; + bool _fileActive; + File _file; + int _bytecount; + int _queuecount; + QueueHandle_t _queue; +public: + CWebContentDL(); + ~CWebContentDL(); + void get(const char* filename); + void process(); + // callback handlers + int16_t queueDLdata(int size, asyncHTTPrequest* request); + void finalise(); + bool busy() const; +}; + +void WebPageRequestCB(void* optParm, asyncHTTPrequest* request, int readyState); +void WebPageDataCB(void* optParm, asyncHTTPrequest*, size_t available); + +