mirror of
https://github.com/meshtastic/firmware.git
synced 2026-11-07 06:44:33 -05:00
- WiFiServerAPI/ethServerAPI apiPort and ethApiServer's listener are create/destroy cycles that repeat across WiFi teardown and W5500 chip resets; the manual delete+null bookkeeping becomes reset(). (ethTlsApiServer's listener is left for a follow-up: that file is already touched by the partial-init fix PR and converting it here would conflict.) - ContentHandler::handleFormUpload held its body parser raw with delete on four separate exit paths of a per-request handler; any future early return was a silent leak. unique_ptr removes all four. - The portduino ch341Hal global becomes unique_ptr. The LoRa-error recovery loop's delete/null/new sequence was correct only by hand-preserved ordering; it becomes reset()/make_unique. RadioLibHAL keeps a non-owning raw pointer, as before. No behavior change.
111 lines
3.8 KiB
C++
111 lines
3.8 KiB
C++
#include "configuration.h"
|
|
|
|
#if HAS_ETHERNET && defined(HAS_ETHERNET_API)
|
|
|
|
#include "concurrency/OSThread.h"
|
|
#include "ethApiHandlers.h"
|
|
#include "ethApiServer.h"
|
|
#include <Arduino.h>
|
|
#include <memory>
|
|
|
|
#ifdef USE_ARDUINO_ETHERNET
|
|
#include <Ethernet.h>
|
|
#else
|
|
#include <RAK13800_W5100S.h>
|
|
#endif
|
|
|
|
// Adaptive poll intervals (mirror mesh/http/WebServer.cpp ESP32 pattern).
|
|
static constexpr uint32_t ACTIVE_THRESHOLD_MS = 5000;
|
|
static constexpr uint32_t MEDIUM_THRESHOLD_MS = 30000;
|
|
static constexpr int32_t ACTIVE_INTERVAL_MS = 20;
|
|
static constexpr int32_t MEDIUM_INTERVAL_MS = 100;
|
|
static constexpr int32_t IDLE_INTERVAL_MS = 500;
|
|
|
|
static std::unique_ptr<EthernetServer> apiServer;
|
|
|
|
// Adapter that exposes an EthernetClient through the transport-agnostic
|
|
// IStreamReadWrite interface so the handlers in ethApiHandlers.cpp can drive
|
|
// it the same way they drive the TLS transport.
|
|
class EthernetClientStream : public IStreamReadWrite
|
|
{
|
|
public:
|
|
explicit EthernetClientStream(EthernetClient &c) : c_(c) {}
|
|
|
|
size_t write(uint8_t b) override { return c_.write(b); }
|
|
size_t write(const uint8_t *buf, size_t len) override { return c_.write(buf, len); }
|
|
|
|
int available() override { return c_.available(); }
|
|
int read() override { return c_.read(); }
|
|
int read(uint8_t *buf, size_t len) override { return c_.read(buf, len); }
|
|
|
|
bool connected() override { return c_.connected(); }
|
|
void flush() override { c_.flush(); }
|
|
IPAddress remoteIP() override { return c_.remoteIP(); }
|
|
|
|
private:
|
|
EthernetClient &c_;
|
|
};
|
|
|
|
// Dedicated OSThread so accept() runs on sub-second cadence. The Ethernet
|
|
// client periodic ticks every 5s which is fine for NTP/MQTT but cripples a
|
|
// chatty web client doing many small back-to-back requests (6.5s TTFB observed
|
|
// before; W5500 sockets get exhausted faster than the periodic can drain).
|
|
class EthApiServerThread : public concurrency::OSThread
|
|
{
|
|
public:
|
|
EthApiServerThread() : concurrency::OSThread("EthApiServer") { lastActivityMs = millis(); }
|
|
|
|
protected:
|
|
int32_t runOnce() override
|
|
{
|
|
if (apiServer) {
|
|
EthernetClient client = apiServer->accept();
|
|
if (client) {
|
|
lastActivityMs = millis();
|
|
EthernetClientStream stream(client);
|
|
handleApiClient(stream);
|
|
client.stop();
|
|
}
|
|
}
|
|
|
|
uint32_t since = millis() - lastActivityMs;
|
|
if (since < ACTIVE_THRESHOLD_MS)
|
|
return ACTIVE_INTERVAL_MS;
|
|
if (since < MEDIUM_THRESHOLD_MS)
|
|
return MEDIUM_INTERVAL_MS;
|
|
return IDLE_INTERVAL_MS;
|
|
}
|
|
|
|
private:
|
|
uint32_t lastActivityMs;
|
|
};
|
|
|
|
static EthApiServerThread *apiThread = nullptr;
|
|
|
|
void initEthApiServer()
|
|
{
|
|
// Bind the listener (idempotent - deInitEthApiServer() drops apiServer on a
|
|
// W5500 reset, and this rebinds it on the restart path).
|
|
if (!apiServer) {
|
|
apiServer = std::make_unique<EthernetServer>(ETH_API_PORT);
|
|
apiServer->begin();
|
|
LOG_INFO("ETH API: server listening on TCP port %d (phase 2.0, OSThread @ 20ms)", ETH_API_PORT);
|
|
}
|
|
// The worker is created once and kept for the lifetime of the process. It
|
|
// idles harmlessly while apiServer is null (runOnce guards on it), so we
|
|
// never delete it from another thread's runOnce - that would corrupt the
|
|
// scheduler's thread list mid-iteration.
|
|
if (!apiThread)
|
|
apiThread = new EthApiServerThread(); // OSThread base auto-registers with the scheduler
|
|
}
|
|
|
|
void deInitEthApiServer()
|
|
{
|
|
// A W5500 chip reset wipes the hardware socket table, so the listener is now
|
|
// bound to a dead socket. Drop it (the worker stays alive and idles) so the
|
|
// next initEthApiServer() from reconnectETH's restart path rebinds TCP/80.
|
|
apiServer.reset();
|
|
}
|
|
|
|
#endif // HAS_ETHERNET && HAS_ETHERNET_API
|