mirror of
https://github.com/thooge/esp32-nmea2000-obp60.git
synced 2025-12-28 21:23:07 +01:00
182 lines
4.3 KiB
C++
182 lines
4.3 KiB
C++
#include "NetworkClient.h"
|
|
|
|
extern "C" {
|
|
#include "puff.h"
|
|
}
|
|
|
|
// Constructor
|
|
NetworkClient::NetworkClient(size_t reserveSize)
|
|
: _doc(reserveSize),
|
|
_valid(false)
|
|
{
|
|
}
|
|
|
|
// Skip GZIP Header an goto DEFLATE content
|
|
int NetworkClient::skipGzipHeader(const uint8_t* data, size_t len) {
|
|
if (len < 10) return -1;
|
|
|
|
if (data[0] != 0x1F || data[1] != 0x8B || data[2] != 8) {
|
|
return -1;
|
|
}
|
|
|
|
size_t pos = 10;
|
|
uint8_t flags = data[3];
|
|
|
|
if (flags & 4) {
|
|
if (pos + 2 > len) return -1;
|
|
uint16_t xlen = data[pos] | (data[pos+1] << 8);
|
|
pos += 2 + xlen;
|
|
}
|
|
|
|
if (flags & 8) {
|
|
while (pos < len && data[pos] != 0) pos++;
|
|
pos++;
|
|
}
|
|
|
|
if (flags & 16) {
|
|
while (pos < len && data[pos] != 0) pos++;
|
|
pos++;
|
|
}
|
|
|
|
if (flags & 2) pos += 2;
|
|
|
|
if (pos >= len) return -1;
|
|
|
|
return pos;
|
|
}
|
|
|
|
// HTTP GET + GZIP Decompression (reading in chunks)
|
|
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)
|
|
uint8_t* buffer = (uint8_t*)malloc(capacity);
|
|
|
|
if (!buffer) {
|
|
if (DEBUG) {Serial.println("Malloc failed (buffer");}
|
|
return false;
|
|
}
|
|
|
|
HTTPClient http;
|
|
|
|
// Timeouts to prevent hanging connections
|
|
http.setConnectTimeout(CONNECTIONTIMEOUT); // Connect 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.addHeader("Accept-Encoding", "gzip");
|
|
|
|
int code = http.GET();
|
|
if (code != HTTP_CODE_OK) {
|
|
Serial.printf("HTTP ERROR: %d\n", code);
|
|
|
|
// Hard reset HTTP + socket
|
|
WiFiClient* tmp = http.getStreamPtr();
|
|
if (tmp) tmp->stop(); // Force close TCP socket
|
|
http.end();
|
|
|
|
free(buffer);
|
|
return false;
|
|
}
|
|
|
|
WiFiClient* stream = http.getStreamPtr();
|
|
|
|
size_t len = 0;
|
|
uint32_t lastData = millis();
|
|
const uint32_t READ_TIMEOUT = READDATATIMEOUT; // Timeout for reading data (can be adjusted in NetworkClient.h)
|
|
|
|
bool complete = false;
|
|
|
|
while (http.connected() && !complete) {
|
|
|
|
size_t avail = stream->available();
|
|
|
|
if (avail == 0) {
|
|
if (millis() - lastData > READ_TIMEOUT) {
|
|
Serial.println("TIMEOUT waiting for data!");
|
|
break;
|
|
}
|
|
delay(1);
|
|
continue;
|
|
}
|
|
|
|
if (len + avail > capacity)
|
|
avail = capacity - len;
|
|
|
|
int read = stream->readBytes(buffer + len, avail);
|
|
len += read;
|
|
lastData = millis();
|
|
|
|
if (DEBUG) {Serial.printf("Read chunk: %d (total: %d)\n", read, (int)len);}
|
|
|
|
if (len < 20) continue; // Not enough data for header
|
|
|
|
int headerOffset = skipGzipHeader(buffer, len);
|
|
if (headerOffset < 0) continue;
|
|
|
|
unsigned long testLen = len * 8; // Dynamic expansion
|
|
uint8_t* test = (uint8_t*)malloc(testLen);
|
|
|
|
if (!test) continue;
|
|
|
|
unsigned long srcLen = len - headerOffset;
|
|
|
|
int res = puff(test, &testLen, buffer + headerOffset, &srcLen);
|
|
if (res == 0) {
|
|
if (DEBUG) {Serial.printf("Decompress OK! Size: %lu bytes\n", testLen);}
|
|
outData = test;
|
|
outLen = testLen;
|
|
complete = true;
|
|
break;
|
|
}
|
|
|
|
free(test);
|
|
}
|
|
|
|
// --- Added: Force-close connection in all cases to avoid stuck TCP sockets ---
|
|
if (stream) stream->stop();
|
|
|
|
http.end();
|
|
free(buffer);
|
|
|
|
if (!complete) {
|
|
Serial.println("Failed to complete decompress.");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// Decompress JSON
|
|
bool NetworkClient::fetchAndDecompressJson(const String& url) {
|
|
|
|
_valid = false;
|
|
|
|
uint8_t* raw = nullptr;
|
|
size_t rawLen = 0;
|
|
|
|
if (!httpGetGzip(url, raw, rawLen)) {
|
|
Serial.println("GZIP download/decompress failed.");
|
|
return false;
|
|
}
|
|
|
|
DeserializationError err = deserializeJson(_doc, raw, rawLen);
|
|
free(raw);
|
|
|
|
if (err) {
|
|
Serial.printf("JSON ERROR: %s\n", err.c_str());
|
|
return false;
|
|
}
|
|
|
|
if (DEBUG) {Serial.println("JSON OK!");}
|
|
_valid = true;
|
|
return true;
|
|
}
|
|
|
|
JsonDocument& NetworkClient::json() {
|
|
return _doc;
|
|
}
|
|
|
|
bool NetworkClient::isValid() const {
|
|
return _valid;
|
|
}
|