Files
meshtastic_firmware/src/mesh/http/WebServer.cpp
T
JorropoandGitHub cf0cb9087a Remove some deadcode (#11034)
* chore: remove empty handleWebResponse() stub in PiWebServer

* chore: remove unused PowerStatus::knowsUSB()

* chore: remove orphaned fsListFiles() declaration

* chore: remove stale #if 0 SPI-comms test in SX126xInterface

* chore: remove stale #if 0 cert-delete debug block in WebServer
2026-07-16 18:57:50 -05:00

261 lines
7.7 KiB
C++

#include "configuration.h"
#if !MESHTASTIC_EXCLUDE_WEBSERVER
#include "NodeDB.h"
#include "graphics/Screen.h"
#include "main.h"
#include "mesh/http/WebServer.h"
#include "mesh/wifi/WiFiAPClient.h"
#include "sleep.h"
#include <HTTPBodyParser.hpp>
#include <HTTPMultipartBodyParser.hpp>
#include <HTTPURLEncodedBodyParser.hpp>
#include <Throttle.h>
#include <WebServer.h>
#include <WiFi.h>
#if HAS_ETHERNET && defined(ARCH_ESP32)
#include <ETH.h>
#endif // HAS_ETHERNET
#ifdef ARCH_ESP32
#include "esp_task_wdt.h"
#endif
// Persistent Data Storage
#include <Preferences.h>
Preferences prefs;
/*
Including the esp32_https_server library will trigger a compile time error. I've
tracked it down to a reoccurrance of this bug:
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=57824
The work around is described here:
https://forums.xilinx.com/t5/Embedded-Development-Tools/Error-with-Standard-Libaries-in-Zynq/td-p/450032
Long story short is we need "#undef str" before including the esp32_https_server.
- Jm Casler (jm@casler.org) Oct 2020
*/
#undef str
// Includes for the https server
// https://github.com/fhessel/esp32_https_server
#include <HTTPRequest.hpp>
#include <HTTPResponse.hpp>
#include <HTTPSServer.hpp>
#include <HTTPServer.hpp>
#include <SSLCert.hpp>
// The HTTPS Server comes in a separate namespace. For easier use, include it here.
using namespace httpsserver;
#include "mesh/http/ContentHandler.h"
static const uint32_t ACTIVE_THRESHOLD_MS = 5000;
static const uint32_t MEDIUM_THRESHOLD_MS = 30000;
static const int32_t ACTIVE_INTERVAL_MS = 50;
static const int32_t MEDIUM_INTERVAL_MS = 200;
static const int32_t IDLE_INTERVAL_MS = 1000;
// Maximum concurrent HTTPS connections (reduced from default 4 to save memory)
static const uint8_t MAX_HTTPS_CONNECTIONS = 2;
// Minimum free heap required for SSL handshake (~40KB for mbedTLS contexts)
static const uint32_t MIN_HEAP_FOR_SSL = 40000;
static SSLCert *cert;
static HTTPSServer *secureServer;
static HTTPServer *insecureServer;
volatile bool isWebServerReady;
volatile bool isCertReady;
static void handleWebResponse()
{
if (isWifiAvailable()) {
if (isWebServerReady) {
// Check heap before HTTPS processing - SSL requires significant memory
if (secureServer) {
uint32_t freeHeap = ESP.getFreeHeap();
if (freeHeap >= MIN_HEAP_FOR_SSL) {
secureServer->loop();
} else {
// Skip HTTPS when memory is low to prevent SSL setup failures
static uint32_t lastHeapWarning = 0;
if (lastHeapWarning == 0 || !Throttle::isWithinTimespanMs(lastHeapWarning, 30000)) {
LOG_WARN("Low heap (%u bytes), skipping HTTPS processing", freeHeap);
lastHeapWarning = millis();
}
}
}
insecureServer->loop();
}
}
}
static void taskCreateCert(void *parameter)
{
prefs.begin("MeshtasticHTTPS", false);
LOG_INFO("Checking if we have a saved SSL Certificate");
size_t pkLen = prefs.getBytesLength("PK");
size_t certLen = prefs.getBytesLength("cert");
if (pkLen && certLen) {
LOG_INFO("Existing SSL Certificate found!");
uint8_t *pkBuffer = new uint8_t[pkLen];
prefs.getBytes("PK", pkBuffer, pkLen);
uint8_t *certBuffer = new uint8_t[certLen];
prefs.getBytes("cert", certBuffer, certLen);
cert = new SSLCert(certBuffer, certLen, pkBuffer, pkLen);
LOG_DEBUG("Retrieved Private Key: %d Bytes", cert->getPKLength());
LOG_DEBUG("Retrieved Certificate: %d Bytes", cert->getCertLength());
} else {
LOG_INFO("Creating the certificate. This may take a while. Please wait");
yield();
cert = new SSLCert();
yield();
int createCertResult = createSelfSignedCert(*cert, KEYSIZE_2048, "CN=meshtastic.local,O=Meshtastic,C=US",
"20190101000000", "20300101000000");
yield();
if (createCertResult != 0) {
LOG_ERROR("Creating the certificate failed");
} else {
LOG_INFO("Creating the certificate was successful");
LOG_DEBUG("Created Private Key: %d Bytes", cert->getPKLength());
LOG_DEBUG("Created Certificate: %d Bytes", cert->getCertLength());
prefs.putBytes("PK", (uint8_t *)cert->getPKData(), cert->getPKLength());
prefs.putBytes("cert", (uint8_t *)cert->getCertData(), cert->getCertLength());
}
}
isCertReady = true;
// Must delete self, can't just fall out
vTaskDelete(NULL);
}
void createSSLCert()
{
if (isWifiAvailable() && !isCertReady) {
bool runLoop = false;
// Create a new process just to handle creating the cert.
// This is a workaround for Bug: https://github.com/fhessel/esp32_https_server/issues/48
// jm@casler.org (Oct 2020)
xTaskCreate(taskCreateCert, /* Task function. */
"createCert", /* String with name of task. */
// 16384, /* Stack size in bytes. */
8192, /* Stack size in bytes. */
NULL, /* Parameter passed as input of the task */
16, /* Priority of the task. */
NULL); /* Task handle. */
LOG_DEBUG("Waiting for SSL Cert to be generated");
while (!isCertReady) {
if ((millis() / 500) % 2) {
if (runLoop) {
LOG_DEBUG(".");
yield();
esp_task_wdt_reset();
#if HAS_SCREEN
if (millis() / 1000 >= 3) {
if (screen)
screen->setSSLFrames();
}
#endif
}
runLoop = false;
} else {
runLoop = true;
}
}
LOG_INFO("SSL Cert Ready!");
}
}
WebServerThread *webServerThread;
WebServerThread::WebServerThread() : concurrency::OSThread("WebServer")
{
if (!config.network.wifi_enabled && !config.network.eth_enabled) {
disable();
}
lastActivityTime = millis();
}
void WebServerThread::markActivity()
{
lastActivityTime = millis();
}
int32_t WebServerThread::getAdaptiveInterval()
{
uint32_t currentTime = millis();
uint32_t timeSinceActivity;
if (currentTime >= lastActivityTime) {
timeSinceActivity = currentTime - lastActivityTime;
} else {
timeSinceActivity = (UINT32_MAX - lastActivityTime) + currentTime + 1;
}
if (timeSinceActivity < ACTIVE_THRESHOLD_MS) {
return ACTIVE_INTERVAL_MS;
} else if (timeSinceActivity < MEDIUM_THRESHOLD_MS) {
return MEDIUM_INTERVAL_MS;
} else {
return IDLE_INTERVAL_MS;
}
}
int32_t WebServerThread::runOnce()
{
if (!config.network.wifi_enabled && !config.network.eth_enabled) {
disable();
}
handleWebResponse();
if (requestRestart && (millis() / 1000) > requestRestart) {
ESP.restart();
}
return getAdaptiveInterval();
}
void initWebServer()
{
LOG_DEBUG("Init Web Server");
// We can now use the new certificate to setup our server as usual.
secureServer = new HTTPSServer(cert, 443, MAX_HTTPS_CONNECTIONS);
insecureServer = new HTTPServer();
registerHandlers(insecureServer, secureServer);
if (secureServer) {
LOG_INFO("Start Secure Web Server");
secureServer->start();
}
LOG_INFO("Start Insecure Web Server");
insecureServer->start();
if (insecureServer->isRunning()) {
LOG_INFO("Web Servers Ready! :-) ");
isWebServerReady = true;
} else {
LOG_ERROR("Web Servers Failed! ;-( ");
}
}
#endif