mirror of
https://github.com/thooge/esp32-nmea2000-obp60.git
synced 2026-08-18 18:32:30 +02:00
Fix NetworkCleint.cpp in case of bad connections
This commit is contained in:
+143
-10
@@ -51,14 +51,16 @@ int NetworkClient::skipGzipHeader(const uint8_t* data, size_t len) {
|
|||||||
// HTTP GET + GZIP Decompression (reading in chunks)
|
// HTTP GET + GZIP Decompression (reading in chunks)
|
||||||
bool NetworkClient::httpGetGzip(const String& url, uint8_t*& outData, size_t& outLen) {
|
bool NetworkClient::httpGetGzip(const String& url, uint8_t*& outData, size_t& outLen) {
|
||||||
|
|
||||||
const size_t capacity = READLIMIT; // Read limit for data (can be adjusted in NetworkClient.h)
|
const size_t capacity = READLIMIT; // Read limit for data (can be adjusted in NetworkClient.h)
|
||||||
uint8_t* buffer = (uint8_t*)malloc(capacity);
|
uint8_t* buffer = (uint8_t*)malloc(capacity);
|
||||||
|
|
||||||
|
// If not with WiFi connectetd then return without any activities
|
||||||
if (!gwWifi.clientConnected()) {
|
if (!gwWifi.clientConnected()) {
|
||||||
if (DEBUGING) {Serial.println("No WiFi connection");}
|
if (DEBUGING) {Serial.println("No WiFi connection");}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If frame buffer not correct allocated then return without any activities
|
||||||
if (!buffer) {
|
if (!buffer) {
|
||||||
if (DEBUGING) {Serial.println("Malloc failed buffer");}
|
if (DEBUGING) {Serial.println("Malloc failed buffer");}
|
||||||
return false;
|
return false;
|
||||||
@@ -71,20 +73,51 @@ bool NetworkClient::httpGetGzip(const String& url, uint8_t*& outData, size_t& ou
|
|||||||
http.setTimeout(TCPREADTIMEOUT); // Read timeout in ms (can be adjusted in NetworkClient.h)
|
http.setTimeout(TCPREADTIMEOUT); // Read timeout in ms (can be adjusted in NetworkClient.h)
|
||||||
|
|
||||||
http.begin(url);
|
http.begin(url);
|
||||||
|
|
||||||
|
// NEW: force server to close the connection after the response (prevents "stuck" keep-alive reads)
|
||||||
|
http.addHeader("Connection", "close");
|
||||||
|
|
||||||
|
// NEW: request gzip, but we will only decompress if the server actually answers with gzip
|
||||||
http.addHeader("Accept-Encoding", "gzip");
|
http.addHeader("Accept-Encoding", "gzip");
|
||||||
|
|
||||||
|
// NEW: register headers BEFORE GET() (more reliable with Arduino HTTPClient)
|
||||||
|
if (DEBUGING) {
|
||||||
|
// We need follow key words
|
||||||
|
const char* keys[] = {
|
||||||
|
"Content-Encoding",
|
||||||
|
"Transfer-Encoding",
|
||||||
|
"Content-Length"
|
||||||
|
};
|
||||||
|
// Read header
|
||||||
|
http.collectHeaders(keys, 3);
|
||||||
|
}
|
||||||
|
|
||||||
int code = http.GET();
|
int code = http.GET();
|
||||||
if (code != HTTP_CODE_OK) {
|
if (code != HTTP_CODE_OK) {
|
||||||
Serial.printf("HTTP ERROR: %d\n", code);
|
Serial.printf("HTTP Client ERROR: %d (%s)\n", code, http.errorToString(code).c_str());
|
||||||
|
|
||||||
// Hard reset HTTP + socket
|
// Hard reset HTTP + socket
|
||||||
WiFiClient* tmp = http.getStreamPtr();
|
WiFiClient* tmp = http.getStreamPtr();
|
||||||
if (tmp) tmp->stop(); // Force close TCP socket
|
if (tmp) tmp->stop(); // Force close TCP socket
|
||||||
http.end();
|
|
||||||
|
|
||||||
|
http.end();
|
||||||
free(buffer);
|
free(buffer);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
else{
|
||||||
|
|
||||||
|
if (DEBUGING) {
|
||||||
|
String ce = http.header("Content-Encoding");
|
||||||
|
String te = http.header("Transfer-Encoding");
|
||||||
|
String cl = http.header("Content-Length");
|
||||||
|
|
||||||
|
// Print header informations
|
||||||
|
Serial.printf("Content-Encoding=%s Transfer-Encoding=%s Content-Length=%s\n",
|
||||||
|
ce.c_str(),
|
||||||
|
te.c_str(),
|
||||||
|
cl.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
WiFiClient* stream = http.getStreamPtr();
|
WiFiClient* stream = http.getStreamPtr();
|
||||||
|
|
||||||
@@ -93,25 +126,120 @@ bool NetworkClient::httpGetGzip(const String& url, uint8_t*& outData, size_t& ou
|
|||||||
const uint32_t READ_TIMEOUT = READDATATIMEOUT; // Timeout for reading data (can be adjusted in NetworkClient.h)
|
const uint32_t READ_TIMEOUT = READDATATIMEOUT; // Timeout for reading data (can be adjusted in NetworkClient.h)
|
||||||
|
|
||||||
bool complete = false;
|
bool complete = false;
|
||||||
|
bool aborting = false; // NEW: remember if we must force-close socket
|
||||||
|
|
||||||
while (http.connected() && !complete) {
|
// NEW: detect if server really sent gzip
|
||||||
|
String ce = http.header("Content-Encoding");
|
||||||
|
bool isGzip = ce.equalsIgnoreCase("gzip");
|
||||||
|
|
||||||
size_t avail = stream->available();
|
// NEW: if not gzip, we will not try to decompress (prevents false "Decompress OK" / random success)
|
||||||
|
// You can either handle plain JSON here or just fail-fast.
|
||||||
|
if (!isGzip) {
|
||||||
|
if (DEBUGING) {
|
||||||
|
Serial.println("Server response is NOT gzip (Content-Encoding != gzip).");
|
||||||
|
Serial.println("Either disable Accept-Encoding: gzip or add plain-body handling here.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Plain-body handling (recommended): read full body into outData as-is ---
|
||||||
|
// NEW: try to read Content-Length bytes if available (more robust)
|
||||||
|
int total = http.getSize(); // returns Content-Length, or -1 if unknown/chunked
|
||||||
|
if (total > 0 && (size_t)total > capacity) {
|
||||||
|
Serial.println("Plain response exceeds READLIMIT.");
|
||||||
|
aborting = true;
|
||||||
|
} else {
|
||||||
|
// Read until we have all bytes (Content-Length) or until connection closes + buffer drains
|
||||||
|
while ((http.connected() || (stream && stream->available())) && !aborting) {
|
||||||
|
size_t avail = stream ? stream->available() : 0;
|
||||||
|
if (avail == 0) {
|
||||||
|
if (millis() - lastData > READ_TIMEOUT) {
|
||||||
|
Serial.println("TIMEOUT waiting for data (plain)!");
|
||||||
|
aborting = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
delay(1);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (len >= capacity) {
|
||||||
|
Serial.println("READLIMIT reached, aborting (plain).");
|
||||||
|
aborting = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (len + avail > capacity)
|
||||||
|
avail = capacity - len;
|
||||||
|
|
||||||
|
int read = stream->readBytes(buffer + len, avail);
|
||||||
|
if (read > 0) {
|
||||||
|
len += (size_t)read;
|
||||||
|
lastData = millis();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (total > 0 && (int)len >= total) {
|
||||||
|
break; // we got full body
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aborting) {
|
||||||
|
// --- Added: Force-close connection only if aborted to avoid TCP RST storms ---
|
||||||
|
if (stream) stream->stop(); // Force close TCP socket
|
||||||
|
http.end();
|
||||||
|
free(buffer);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return plain body to caller
|
||||||
|
outData = (uint8_t*)malloc(len);
|
||||||
|
if (!outData) {
|
||||||
|
Serial.println("Malloc failed outData (plain).");
|
||||||
|
// --- Added: Force-close connection only if aborted to avoid TCP RST storms ---
|
||||||
|
if (stream) stream->stop(); // Force close TCP socket
|
||||||
|
http.end();
|
||||||
|
free(buffer);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
memcpy(outData, buffer, len);
|
||||||
|
outLen = len;
|
||||||
|
|
||||||
|
http.end();
|
||||||
|
free(buffer);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- GZIP path (only if Content-Encoding is gzip) ---
|
||||||
|
while ((http.connected() || (stream && stream->available())) && !complete) {
|
||||||
|
|
||||||
|
size_t avail = stream ? stream->available() : 0;
|
||||||
|
|
||||||
if (avail == 0) {
|
if (avail == 0) {
|
||||||
if (millis() - lastData > READ_TIMEOUT) {
|
if (millis() - lastData > READ_TIMEOUT) {
|
||||||
Serial.println("TIMEOUT waiting for data!");
|
Serial.println("TIMEOUT waiting for data!");
|
||||||
|
aborting = true; // NEW: mark abnormal exit
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
delay(1);
|
delay(1);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NEW: safety check if buffer limit is reached
|
||||||
|
if (len >= capacity) {
|
||||||
|
Serial.println("READLIMIT reached, aborting.");
|
||||||
|
aborting = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (len + avail > capacity)
|
if (len + avail > capacity)
|
||||||
avail = capacity - len;
|
avail = capacity - len;
|
||||||
|
|
||||||
int read = stream->readBytes(buffer + len, avail);
|
int read = stream->readBytes(buffer + len, avail);
|
||||||
len += read;
|
if (read <= 0) {
|
||||||
|
// NEW: avoid tight loop if read returns zero
|
||||||
|
delay(1);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
len += (size_t)read;
|
||||||
lastData = millis();
|
lastData = millis();
|
||||||
|
|
||||||
if (DEBUGING) {Serial.printf("Read chunk: %d (total: %d)\n", read, (int)len);}
|
if (DEBUGING) {Serial.printf("Read chunk: %d (total: %d)\n", read, (int)len);}
|
||||||
@@ -124,7 +252,12 @@ bool NetworkClient::httpGetGzip(const String& url, uint8_t*& outData, size_t& ou
|
|||||||
unsigned long testLen = len * 8; // Dynamic expansion
|
unsigned long testLen = len * 8; // Dynamic expansion
|
||||||
uint8_t* test = (uint8_t*)malloc(testLen);
|
uint8_t* test = (uint8_t*)malloc(testLen);
|
||||||
|
|
||||||
if (!test) continue;
|
if (!test) {
|
||||||
|
// NEW: abort if allocation fails to prevent endless retry loop
|
||||||
|
Serial.println("Malloc failed test buffer, aborting.");
|
||||||
|
aborting = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
unsigned long srcLen = len - headerOffset;
|
unsigned long srcLen = len - headerOffset;
|
||||||
|
|
||||||
@@ -132,7 +265,7 @@ bool NetworkClient::httpGetGzip(const String& url, uint8_t*& outData, size_t& ou
|
|||||||
if (res == 0) {
|
if (res == 0) {
|
||||||
if (DEBUGING) {Serial.printf("Decompress OK! Size: %lu bytes\n", testLen);}
|
if (DEBUGING) {Serial.printf("Decompress OK! Size: %lu bytes\n", testLen);}
|
||||||
outData = test;
|
outData = test;
|
||||||
outLen = testLen;
|
outLen = (size_t)testLen;
|
||||||
complete = true;
|
complete = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -140,8 +273,8 @@ bool NetworkClient::httpGetGzip(const String& url, uint8_t*& outData, size_t& ou
|
|||||||
free(test);
|
free(test);
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Added: Force-close connection in all cases to avoid stuck TCP sockets ---
|
// --- Added: Force-close connection only if aborted to avoid TCP RST storms ---
|
||||||
if (stream) stream->stop();
|
if (aborting && stream) stream->stop(); // NEW: stop() only on abnormal termination
|
||||||
|
|
||||||
http.end();
|
http.end();
|
||||||
free(buffer);
|
free(buffer);
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
#include <WiFi.h>
|
#include <WiFi.h>
|
||||||
#include <HTTPClient.h>
|
#include <HTTPClient.h>
|
||||||
|
|
||||||
#define DEBUGING false // Debug flag for NetworkClient for more live information
|
#define DEBUGING true // Debug flag for NetworkClient for more live information
|
||||||
#define READLIMIT 200000 // HTTP read limit in byte for gzip content (can be adjusted)
|
#define READLIMIT 200000 // HTTP read limit in byte for gzip content (can be adjusted)
|
||||||
#define CONNECTIONTIMEOUT 3000 // Timeout in ms for HTTP connection
|
#define CONNECTIONTIMEOUT 3000 // Timeout in ms for HTTP connection
|
||||||
#define TCPREADTIMEOUT 2000 // Timeout in ms for read HTTP client stack
|
#define TCPREADTIMEOUT 2000 // Timeout in ms for read HTTP client stack
|
||||||
|
|||||||
+8
-4
@@ -21,8 +21,10 @@ lib_deps =
|
|||||||
ttlappalainen_NMEA2000=https://github.com/wellenvogel/NMEA2000.git#20251126
|
ttlappalainen_NMEA2000=https://github.com/wellenvogel/NMEA2000.git#20251126
|
||||||
ttlappalainen/NMEA0183 @ 1.10.1
|
ttlappalainen/NMEA0183 @ 1.10.1
|
||||||
ArduinoJson @ 6.18.5
|
ArduinoJson @ 6.18.5
|
||||||
AsyncTCP-esphome @ 2.0.1
|
; AsyncTCP-esphome @ 2.0.1
|
||||||
ottowinter/ESPAsyncWebServer-esphome@2.0.1
|
; ottowinter/ESPAsyncWebServer-esphome@2.0.1
|
||||||
|
AsyncTCP-esphome @ 2.1.1
|
||||||
|
ottowinter/ESPAsyncWebServer-esphome@3.4.0
|
||||||
FS
|
FS
|
||||||
Preferences
|
Preferences
|
||||||
ESPmDNS
|
ESPmDNS
|
||||||
@@ -34,8 +36,10 @@ lib_deps=
|
|||||||
ttlappalainen_NMEA2000=symlink://../NMEA2000
|
ttlappalainen_NMEA2000=symlink://../NMEA2000
|
||||||
ttlappalainen/NMEA0183 @ 1.10.1
|
ttlappalainen/NMEA0183 @ 1.10.1
|
||||||
ArduinoJson @ 6.18.5
|
ArduinoJson @ 6.18.5
|
||||||
AsyncTCP-esphome @ 2.0.1
|
; AsyncTCP-esphome @ 2.0.1
|
||||||
ottowinter/ESPAsyncWebServer-esphome@2.0.1
|
; ottowinter/ESPAsyncWebServer-esphome@2.0.1
|
||||||
|
AsyncTCP-esphome @ 2.1.1
|
||||||
|
ottowinter/ESPAsyncWebServer-esphome@3.4.0
|
||||||
FS
|
FS
|
||||||
Preferences
|
Preferences
|
||||||
ESPmDNS
|
ESPmDNS
|
||||||
|
|||||||
Reference in New Issue
Block a user