diff --git a/lib/update/GwUpdate.cpp b/lib/update/GwUpdate.cpp index 4933222..9b2f11e 100644 --- a/lib/update/GwUpdate.cpp +++ b/lib/update/GwUpdate.cpp @@ -9,6 +9,7 @@ class UpdateParam{ public: String error; size_t uplodaded=0; + bool otherUpdate=false; bool hasError(){ return ! error.isEmpty(); } @@ -23,12 +24,12 @@ bool GwUpdate::delayedRestart(){ vTaskDelete(NULL); },"reset",2000,logger,0,NULL) == pdPASS; } -GwUpdate::GwUpdate(GwLog *log, GwWebServer *webserver, PasswordChecker checker) +GwUpdate::GwUpdate(GwLog *log, GwWebServer *webserver, PasswordChecker ckr) { - this->checker=checker; + this->checker=ckr; this->logger = log; this->server = webserver->getServer(); - server->on("/update", HTTP_POST, [&](AsyncWebServerRequest *request) { + server->on("/api/update", HTTP_POST, [&](AsyncWebServerRequest *request) { // the request handler is triggered after the upload has finished... // create the response, add header, and send response String result="{\"status\":\"OK\"}"; @@ -50,11 +51,13 @@ GwUpdate::GwUpdate(GwLog *log, GwWebServer *webserver, PasswordChecker checker) if (!updateOk){ result=jsonError(String("Update failed: ")+param->error); } + if (! param->otherUpdate) { + updateRunning=0; + } } AsyncWebServerResponse *response = request->beginResponse(200, "application/json",result); response->addHeader("Connection", "close"); request->send(response); - updateRunning=false; if (updateOk) delayedRestart(); }, [&](AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final) { //Upload handler chunks in data @@ -67,29 +70,37 @@ GwUpdate::GwUpdate(GwLog *log, GwWebServer *webserver, PasswordChecker checker) param=new UpdateParam(); request->_tempObject=param; } - if (updateRunning){ + if (updateRunning > 0 && (updateRunning + 60000) >= millis()){ param->error="another update is running"; + param->otherUpdate=true; } else{ - updateRunning=true; + updateRunning=millis(); } if (!param->hasError()) { - if (!request->hasParam("_hash", true)) + AsyncWebParameter *hash=request->getParam("_hash"); + if (! hash){ + hash=request->getParam("_hash",true); + } + if (!hash) { LOG_DEBUG(GwLog::ERROR, "missing _hash in update"); param->error = "missing _hash"; } else { - if (!checker(request->getParam("_hash")->value())) + String shash=hash->value(); + LOG_DEBUG(GwLog::DEBUG,"checking hash %s",shash.c_str()); + if (!checker(shash)) { - LOG_DEBUG(GwLog::ERROR, "invalid _hash in update"); + LOG_DEBUG(GwLog::ERROR, "invalid _hash in update %s",shash.c_str()); param->error = "invalid password"; } } } if (! param->hasError()){ + Update.abort(); //abort any dangling update int cmd=U_FLASH; if (!Update.begin(UPDATE_SIZE_UNKNOWN, cmd)) { // Start with max available size LOG_DEBUG(GwLog::ERROR,"unable to start update %s",Update.errorString()); @@ -107,6 +118,7 @@ GwUpdate::GwUpdate(GwLog *log, GwWebServer *webserver, PasswordChecker checker) { LOG_DEBUG(GwLog::ERROR, "invalid write, expected %d got %d", (int)len, (int)wr); param->error="unable to write"; + Update.abort(); } else{ param->uplodaded+=wr; @@ -115,10 +127,12 @@ GwUpdate::GwUpdate(GwLog *log, GwWebServer *webserver, PasswordChecker checker) if (final && ! param->hasError()) { // if the final flag is set then this is the last frame of data + LOG_DEBUG(GwLog::DEBUG,"finalizing update"); if (!Update.end(true)) { //true to set the size to the current progress LOG_DEBUG(GwLog::ERROR, "unable to end update %s", Update.errorString()); param->error=String("unable to end update:") + String(Update.errorString()); + Update.abort(); } } else diff --git a/lib/update/GwUpdate.h b/lib/update/GwUpdate.h index 0b1e3d8..8b17eb2 100644 --- a/lib/update/GwUpdate.h +++ b/lib/update/GwUpdate.h @@ -7,7 +7,7 @@ class GwUpdate{ AsyncWebServer *server; GwLog *logger; PasswordChecker checker; - bool updateRunning=false; + unsigned long updateRunning=0; public: bool delayedRestart(); GwUpdate(GwLog *log,GwWebServer *webserver, PasswordChecker checker); diff --git a/src/main.cpp b/src/main.cpp index 6136415..f66d556 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -169,7 +169,7 @@ bool checkPass(String hash){ return false; } -GwUpdate updater(&logger,&webserver,checkPass); +GwUpdate updater(&logger,&webserver,&checkPass); void updateNMEACounter(int id,const char *msg,bool incoming,bool fail=false){ //we rely on the msg being long enough diff --git a/tools/testServer.py b/tools/testServer.py index 8b73519..ecbfb4d 100755 --- a/tools/testServer.py +++ b/tools/testServer.py @@ -5,31 +5,57 @@ import sys import http import http.server import urllib.request +import urllib.parse import posixpath import os +import traceback class RequestHandler(http.server.SimpleHTTPRequestHandler): - def do_GET(self): + def do_proxy(self): p=self.path print("path=%s"%p) if p.startswith("/api/"): apiurl=self.server.proxyUrl url=apiurl+p.replace("/api","") + hostname=urllib.parse.urlparse(url).netloc print("proxy to %s"%url) try: - with urllib.request.urlopen(url,timeout=10) as response: + body = None + if self.headers.get('content-length') is not None: + content_len = int(self.headers.get('content-length')) + print("reading %d bytes body"%content_len) + body = self.rfile.read(content_len) + + # set new headers + new_headers = {} + for item in self.headers.items(): + new_headers[item[0]] = item[1] + new_headers['host'] = hostname + try: + del new_headers['accept-encoding'] + except KeyError: + pass + req=urllib.request.Request(url,headers=new_headers,data=body) + with urllib.request.urlopen(req,timeout=50) as response: self.send_response(http.HTTPStatus.OK) - self.send_header("Content-type", response.getheader("Content-type")) - + for h in response.getheaders(): + self.send_header(h[0],h[1]) self.end_headers() shutil.copyfileobj(response,self.wfile) + return True return None self.send_error(http.HTTPStatus.NOT_FOUND, "api not found") return None except Exception as e: + print("Exception: %s"%traceback.format_exc()) self.send_error(http.HTTPStatus.INTERNAL_SERVER_ERROR, "api error %s"%str(e)) return None - super().do_GET() + def do_GET(self): + if not self.do_proxy(): + super().do_GET() + def do_POST(self): + if not self.do_proxy(): + super().do_POST() def translate_path(self, path): """Translate a /-separated PATH to the local filename syntax. diff --git a/web/index.html b/web/index.html index 12e438b..64917e3 100644 --- a/web/index.html +++ b/web/index.html @@ -20,6 +20,7 @@
Config
XDR
Data
+
Update
@@ -74,6 +75,12 @@ +