Skip to content

HTTP Service Module

The HTTP service module (main/modules/http/) is the primary remote-access gateway of the firmware. It hosts a single ESP-IDF httpd server instance that simultaneously serves the embedded WebUI, exposes a REST-like command/file API, handles multipart file uploads, upgrades connections to WebSocket, and optionally implements the WebDAV protocol — all on a single TCP port over plain HTTP or TLS (HTTPS).

The module is network-dependent: it is started by ESP3DNetworkServices only after the WiFi stack is ready, and it shuts down cleanly when the network goes away. It is conditionally compiled: most subsystems (HTTPS, SD card, WebDAV, WebSocket, authentication, PSRAM cache, camera, SSDP) are gated behind CMakeLists.txt feature flags that are validated by cmake/sanity_check.cmake.

⚠️ SKU constraint — The HTTP service and SOCKET_CLIENT_SERVICE are mutually exclusive in the default build (CNC-over-WiFi TCP blocks WebUI). See cmake/sanity_check.cmake and docs/features/feature_resource_matrix.md.


Downstream Services

URI Handlers (registered at begin())

HTTP Service (Port 80 / 443)

Network Layer

begin() when WiFi ready

isStreamingActive()

ESP3DNetwork

ESP3DNetworkServices

ESP3DHttpService

esp3dHttpService

ESP3DWebUiPsramCache

webUiPsramCache

PSRAM_FEATURE only

GET /

GET /command

GET /config

GET /favicon.ico

POST /login

GET|POST /files

(flash FS)

GET|POST /sdfiles

SD_CARD_FEATURE

POST /updatefw

UPDATE_FEATURE

GET /ws ↑WS

WEBUI_SERVER_FEATURE

GET /wsdata ↑WS

WS_SERVER_FEATURE

ANY /webdav/?*

12 methods

WEBDAV_FEATURE

GET /description.xml

SSDP_FEATURE

GET /snap

CAMERA_FEATURE

404 handler

ESP3DWebUiService

WebUI WebSocket

ESP3DWsDataService

Data WebSocket

ESP3DAuthenticationService

ESP3DGlobalFileSystem

ESP3DFlash

ESP3DSD

ESP3DGCodeHostService


owns (static)

uses (PSRAM_FEATURE)

creates in streamFile()

holds

1

1

4

0..*

ESP3DHttpService

-bool _started

-httpd_handle_t _server

-uint32_t _port

-bool _webdav_active

-bool _is_https

-char* _chunk

-list<pair> _sockets_list

-PostUploadContext _post_files_upload_ctx

-PostUploadContext _post_sdfiles_upload_ctx

-PostUploadContext _post_updatefw_upload_ctx

-PostUploadContext _post_login_ctx

+begin() : bool

+handle()

+end()

+process(msg)

+streamFile(path, req) : esp_err_t

+getAuthenticationLevel(req) : ESP3DAuthenticationLevel

+isStreamingActive() : bool

+webdavActive(fromSettings) : bool

+sendStringChunk(req, str, autoClose) : esp_err_t

+sendBinaryChunk(req, data, len, autoClose) : esp_err_t

+sendBufferChunked(req, data, len) : esp_err_t

+pushError(errcode, st)

+push(type, fd)

+pop(type, fd)

PostUploadContext

+writeFn fn_ptr

+nextHandler fn_ptr

+uint packetReadSize

+uint packetWriteSize

+list<pair> args

ESP3DWebUiPsramCache

-vector<CacheEntry> _entries

-size_t _total_bytes

+rebuild()

+tryServe(path, req) : bool

+entryCount() : size_t

+totalBytes() : size_t

-_clear()

-_loadFile(fsPath)

-_scanDir(dirPath, prefix, recurse)

CacheEntry

+string path

+char* data

+size_t size

StreamFileGlobalFsGuard

-const char* path_

-bool acquired_

+access() : bool


main/modules/http/
├── esp3d_http_service.h # Class declaration, enums, PostUploadContext
├── esp3d_http_service.cpp # Lifecycle + core utilities
├── esp3d_webui_psram_cache.h # PSRAM cache declaration (PSRAM_FEATURE)
├── esp3d_webui_psram_cache.cpp # PSRAM cache implementation
└── handlers/
├── flash/
│ ├── esp3d_files.cpp # GET|POST /files — flash FS browser
│ └── esp3d_upload_files.cpp # Multipart upload → flash write
├── sd/
│ ├── esp3d_sdfiles.cpp # GET|POST /sdfiles — SD card browser
│ └── esp3d_upload_sdfiles.cpp
├── updatefw/
│ └── esp3d_updatefw.cpp # POST /updatefw — OTA / partition raw write
├── webdav/
│ ├── esp3d_webdav_propfind.cpp
│ └── ... # One file per WebDAV method
└── ... # command, config, root, login, ws handlers

No

Yes

Yes

No

Yes

Yes

No

No

No

Yes

Yes

No

Yes

No

begin called by

ESP3DNetworkServices

HTTP enabled

in NVS?

return true - no-op

PSRAM_FEATURE?

Allocate _chunk in SPIRAM

Read HTTP port from NVS settings

Count URI handlers

per feature flags

PSRAM_FEATURE?

webUiPsramCache.rebuild

before httpd_start

HTTPS_FEATURE?

_loadCerts from

/fs/cert/server.crt + .key

Certs loaded?

httpd_ssl_start

HTTPS on configured port

httpd_start

HTTP fallback - allows cert upload

Server started OK?

log error, free certs, return false

Register URI handlers

per feature flags

WEBUI_SERVER

feature?

esp3dWsWebUiService.begin

WS_SERVER

feature?

esp3dWsServerDataService.begin

_started = true, return true

The PSRAM cache is rebuilt before httpd_start() and handler registration: no request can be served yet, so tryServe() cannot walk the entry list while rebuild() changes it. The rebuild only needs the flash filesystem; it is done even if the server then fails to start (the cache is simply ready for the next begin()).

  1. Stops esp3dWsWebUiService and esp3dWsServerDataService.
  2. Unregisters all URI handlers.
  3. Calls httpd_ssl_stop (HTTPS) or httpd_stop (HTTP) and frees TLS cert/key buffers.
  4. Clears all active WebUI authentication sessions (clearSessions(ESP3DClientType::webui)).
  5. Resets _server = nullptr and _started = false.

Called from ESP3DNetworkServices::handle() on a timer tick. Delegates to esp3dWsWebUiService.handle() and esp3dWsServerDataService.handle() for zombie-client cleanup.


URIMethod(s)Feature GuardHandlerAuth Required
/GETalwaysroot_get_handlerNo
/favicon.icoGETalwaysfavicon_ico_handlerNo
/commandGETalwayscommand_handlerYes
/configGETalwaysconfig_handlerNo
/filesGETalwaysfiles_handlerYes
/filesPOSTalwayspost_multipart_handler → flash uploadYes
/loginPOSTalwayspost_multipart_handler → login_handlerNo
/sdfilesGETESP3D_SD_CARD_FEATUREsdfiles_handlerYes
/sdfilesPOSTESP3D_SD_CARD_FEATUREpost_multipart_handler → SD uploadYes
/updatefwPOSTESP3D_UPDATE_FEATUREpost_multipart_handler → FW/partition writeAdmin
/wsGET ↑WSESP3D_WEBUI_SERVER_FEATUREwebsocket_webui_handlerNo
/wsdataGET ↑WSESP3D_WS_SERVER_SERVICE_FEATUREwebsocket_data_handlerNo
/snapGETESP3D_CAMERA_FEATUREsnap_handlerYes
/description.xmlGETESP3D_SSDP_FEATUREdescription_xml_handlerNo
/webdav/?*GET, PUT, DELETE, MKCOL, PROPFIND, OPTIONS, HEAD, MOVE, COPY, LOCK, UNLOCK, PROPPATCHESP3D_WEBDAV_SERVICES_FEATUREwebdav_*_handlerYes
(catch-all)anyalwaysfile_not_found_handler (404)No

WebDAV uses httpd_uri_match_wildcard URI matching. When ESP3D_WEBDAV_SERVICES_FEATURE is disabled, exact-match mode is used to avoid interference with WebSocket route selection on some IDF builds.


Authentication is entirely optional at compile time (ESP3D_AUTHENTICATION_FEATURE). When disabled, every request is treated as admin.

Three levels are defined in esp3d_authentication_types.h:

  • guest — unauthenticated
  • user — password-protected, reduced privileges
  • admin — full access

Found and SUBMIT=YES

Yes and password OK

No: user and password OK

Missing or SUBMIT not YES

admin creds match

user creds match

No match

Valid cookie and record found

No timeout

Within timeout

Expired

No cookie or no record

No existing session

Existing session

getAuthenticationLevel called

POST args present?

SUBMIT + USER + PASSWORD

USER == admin?

admin

user

Authorization header?

Basic auth

ESPSESSIONID cookie

present?

Session timeout

configured?

Refresh last_time

return record.level

Clear session record

Set-Cookie Max-Age=0

guest

Level != guest:

create/update session

create_session_id

createRecord

Set-Cookie: ESPSESSIONID=...

Update record.level

and last_time

Sessions are stored in ESP3DAuthenticationService (see authentication.md). Session IDs are 24-character strings derived from the client IP and socket ID. Cookies use HttpOnly; SameSite=Strict attributes.

⚠️ HTTPS security headers — When running over TLS, Strict-Transport-Security (max-age=31536000) and Content-Security-Policy: upgrade-insecure-requests are added to every response. The CSP header causes the browser to transparently upgrade ws:// → wss:// for all sub-requests emitted by the WebUI JS, including WebSocket connections constructed from the host IP without a scheme prefix.


All file uploads (flash, SD, firmware) share a single post_multipart_handler entry point. The target is selected by the PostUploadContext stored in httpd_uri_t::user_ctx.

POST request arrives

post_multipart_handler

user_ctx =

PostUploadContext ptr

getBoundaryString from

Content-Type header

Read multipart chunks

via httpd_req_recv

Parse boundary,

headers, body segments

Non-file fields stored

into ctx.args list

File segment:

call ctx.writeFn

upload_to_flash_handler

flashFs.write

upload_to_sd_handler

sd.write

upload_to_updatefw_handler

OTA or raw partition

call ctx.nextHandler

e.g. files_handler

JSON response to client

StateMeaning
upload_startFirst chunk for this file — open/create target
file_writeIntermediate chunk — append data
upload_endLast chunk — close/commit target
upload_abortedTransport error — discard and clean up
CodeMeaning
no_errorSuccess
authentication_failedAuth check rejected the request
file_create_failedCould not create target file
write_failedWrite I/O error
not_enough_spaceFilesystem full
start_update_failedOTA esp_ota_begin failed
mount_sd_failedSD card not accessible
memory_allocationmalloc returned null
access_deniedStreaming active — writes blocked
wrong_sizeUploaded size mismatch
update_failedOTA esp_ota_end or validation failed

Errors are sent as ERROR:<code>:<message>\n over the WebUI WebSocket via pushError(), which calls esp3dWsWebUiService.pushText(): like the stream output, each recipient’s session is checked before it receives the text (see websocket_server.md).

Four PostUploadContext instances are declared as static members of ESP3DHttpService:

ContextURIwriteFnnextHandler
_post_files_upload_ctxPOST /filesupload_to_flash_handlerfiles_handler
_post_sdfiles_upload_ctxPOST /sdfilesupload_to_sd_handlersdfiles_handler
_post_updatefw_upload_ctxPOST /updatefwupload_to_updatefw_handlerupdatefw_handler
_post_login_ctxPOST /loginNULLlogin_handler

The writeFn = NULL in the login context means no file data is written; only the args list (SUBMIT, USER, PASSWORD fields) is populated for getAuthenticationLevel() to consume.


Used by root_get_handler and files_handler for serving static content from flash.

Yes

No

Cache hit

Miss

.br exists and HTTPS

.gz exists

plain only

destructor

streamFile called

path arg

provided?

Use path directly

Decode URL query string

to filename

PSRAM_FEATURE?

tryServe from cache

Send from SPIRAM

return ESP_OK

StreamFileGlobalFsGuard

globalFs.accessFS

Probe compressed variants

Open .br file

Content-Encoding: br

Open .gz file

Content-Encoding: gzip

Open plain file

Set Content-Type

from file extension

fread loop in STREAM_CHUNK_SIZE pieces

httpd_resp_send_chunk

+ taskYIELD between chunks

Send terminating null chunk

globalFs.releaseFS

StreamFileGlobalFsGuard is a RAII wrapper (anonymous namespace) that guarantees globalFs.releaseFS() is called on every exit path, including early returns during the chunked send loop.

Compressed variant priority:

  1. .br (Brotli) — served only over HTTPS (_is_https flag), since browsers only advertise Accept-Encoding: br on secure connections.
  2. .gz (Gzip) — served when available on both HTTP and HTTPS.
  3. Plain file — fallback.

PSRAM boards — _chunk is allocated in SPIRAM via heap_caps_malloc(STREAM_CHUNK_SIZE, MALLOC_CAP_SPIRAM). On non-PSRAM boards it is a static .bss array. STREAM_CHUNK_SIZE is defined per-board in boards/*/components/bsp/tasks_def.h.


Active only when ESP3D_PSRAM_FEATURE is defined. Preloads read-mostly WebUI assets from flash into SPIRAM at HTTP service startup so they can be served without touching the flash bus while a G-code stream is running (see gcode_host_streaming_flow.md).

Asset TypeLocations scannedName filter
Main page<mp>/index.html[.gz]
Preferences<mp>/preferences.json[.gz]
Themes<mp>/themes/ and <mp>/theme-*
Extensions<mp>/extensions/ and <mp>/esp3dext-*/ (full subtree, unfiltered)
Language packs<mp>/languages/ and <mp>/lang-*.json[.gz]

Extension subdirectories (esp3dext-<name>/) are cached in full — including nested assets/ folders — via _scanDir(..., recurse=true).

SPIRAM heapglobalFs / flashFswebUiPsramCacheESP3DHttpServiceSPIRAM heapglobalFs / flashFswebUiPsramCacheESP3DHttpServiceloop[For each candidate path / pattern]On each GET request for a cached path:alt[Cache hit][Cache miss]rebuild()_clear() - heap_caps_free all entriesaccessFS(mount_point)stat(path)entry_statheap_caps_malloc(size, MALLOC_CAP_SPIRAM)open → fread → closepush_back CacheEntryreleaseFS()tryServe(path, req)Search _entries (.gz preferred over plain)httpd_resp_send from SPIRAMreturn truereturn false - caller falls back to flash

A cache hit is sent in one shot with httpd_resp_send(), which sets Content-Length from the buffer size. It does not use sendBufferChunked(): chunk framing and a Content-Length header in the same response are illegal (RFC 9112).

The cache is shared by the httpd task and the tasks that write to the flash filesystem, so its entry list is protected by a pthread_mutex_t (initialized in the constructor, at static-init time, before app_main()):

  • tryServe() looks the entry up under the lock, increments its in_use counter and copies the data pointer and size, then releases the lock before the network send. After the send it takes the lock again, finds the entry by its data pointer and decrements in_use.
  • rebuild(), _clear() and invalidate() free a buffer only when in_use == 0. An entry that is being sent is only detached (removed = true, path cleared); tryServe() frees the buffer when its send ends. _purgeDetached() then erases entries whose buffer was released.
  • entryCount() / totalBytes() read without the lock (telemetry only).
TriggerCall
begin()rebuild() (before httpd_start())
Upload to flash (/files POST, upload_end)rebuild()
WebDAV PUT, DELETE, MKCOL, MOVE, COPY on flash (success)rebuild()
WebSocket file upload end (esp3d_ws_data_service.cpp)invalidate(full_path)
preferences.json written or created by ESP3DJsonSettingsinvalidate("<mount>/preferences.json")

invalidate(path) drops one entry, using the same path convention as tryServe() (for example /fs/preferences.json). It is a safe no-op when the path is not cached or the cache is empty (feature off, no PSRAM), and can be called from any task.


Implemented in handlers/flash/esp3d_files.cpp. Responds to GET (directory listing + file operations) and POST (file upload via post_multipart_handler).

Query parameters: path (URL-decoded), action (delete / deletedir / createdir), filename.

{
"files": [
{"name": "index.html.gz", "size": "142 KB", "time": "2024-01-15 10:30:00"},
{"name": "themes", "size": "-1"}
],
"path": "/",
"occupation": "42",
"status": "ok",
"total": "1.50 MB",
"used": "630 KB"
}
  • Directories always use "size": "-1".
  • The time field is only included when ESP3D_TIMESTAMP_FEATURE is defined.
  • Mount prefix stripping: The handler normalizes paths such as /fs/fs/… (doubled mount prefix from some WebUI versions) before any filesystem call. All repeated occurrences of the flash mount point prefix are stripped until none remain.

Implemented in handlers/sd/esp3d_sdfiles.cpp. Structurally identical to the flash handler but operates on the sd singleton (ESP3DSD) instead of flashFs. Guarded by ESP3D_SD_CARD_FEATURE.

Space reporting uses uint64_t instead of size_t to accommodate large SD cards that exceed 4 GB.


Implemented across handlers/webdav/. Guarded by ESP3D_WEBDAV_SERVICES_FEATURE. Operates on globalFs so it spans both flash and SD card via the global filesystem abstraction (see filesystem.md).

HTTP MethodHandlerSemantics
GETwebdav_get_handlerDownload a file
PUTwebdav_put_handlerUpload / overwrite a file
DELETEwebdav_delete_handlerDelete file or empty directory
MKCOLwebdav_mkcol_handlerCreate a directory
PROPFINDwebdav_propfind_handlerList properties (Depth: 0 or Depth: 1)
PROPPATCHwebdav_proppatch_handlerProperty update stub (returns 200)
OPTIONSwebdav_options_handlerAdvertise allowed methods
HEADwebdav_head_handlerFile metadata without body
MOVEwebdav_move_handlerRename / move
COPYwebdav_copy_handlerCopy a file
LOCKwebdav_lock_handlerAdvisory lock (stub for clients that require it)
UNLOCKwebdav_unlock_handlerRelease an advisory lock
HandlerBehavior
GETNo manual Content-Length: the file is streamed with httpd_resp_send_chunk(), so the httpd frames the response as Transfer-Encoding: chunked. Both headers together are illegal (RFC 9112) and strict WebDAV clients truncate or reject the file.
HEADSuccess: httpd_resp_send(req, NULL, file_size), so the httpd emits Content-Length with an empty body (RFC 9110). Errors are sent with an empty message, since a HEAD response must not carry a body.
PUTContent-Length is parsed with strtoull() and fully validated (not atoi(), which accepts garbage and overflows above INT_MAX). A missing or invalid value is answered with 411 Length Required instead of silently creating an empty file. The body is not drained; the WebDAV headers carry Connection: close, so the socket closes after the response.
COPYRefused with 403 when source and destination are the same path (with Overwrite: T the destination opened w would truncate the source being read). Free space is checked on the destination volume, which can be the other filesystem (flash vs SD).
MOVERefused with 403 when source and destination are the same path (the overwrite step would delete the source before the rename).
PROPFINDThe request path goes through the shared esp3d_webdav_vfs_path_from_req_uri() helper, like the other handlers: it decodes the URI and rejects path traversal (.., backslash) by returning an empty path, answered with 400.

Same-path checks use strcasecmp(), because FAT is case-insensitive. http_send_response() now knows status 411.

Depth: 1 on a directory emits one <D:response> XML element per direct child, obtained via globalFs.readdir(). Infinity depth or any value other than 0 / 1 is capped to 1. Each element carries getlastmodified, creationdate, resourcetype, getcontentlength (files only), and displayname.

WebDAV can be toggled at runtime without restarting the server via webdavActive(fromSettings=true), which re-reads the esp3d_webdav_on NVS key.

globalFsESP3DHttpServiceWebDAV ClientglobalFsESP3DHttpServiceWebDAV Clientloop[Each directory entry]PROPFIND /webdav/themes/ Depth: 1webdav_propfind_handlerwebdavActive() checkgetAuthenticationLevel check_clearPayload (discard PROPFIND XML body)Parse Depth header, cap infinity → 1accessFS("/themes/")truestat("/themes/")entry_stat (directory)207 Multi-Status + XML headerresponse element for /themes/ itselfopendir("/themes/")stat(entry)entry_statresponse element (chunked send)closedirreleaseFSXML footer + terminating null chunk

Guarded by ESP3D_HTTPS_FEATURE. Uses esp_https_server on top of mbedTLS.

fail

ok

missing

found

fail

ok

_loadCerts called

_freeCerts - release any

previously loaded buffers

globalFs.accessFS

/fs/cert/

return false → HTTP fallback

stat server.crt

and server.key

releaseFS

heap_caps_malloc × 2

MALLOC_CAP_SPIRAM

(size + 1 for null terminator)

_freeCerts, releaseFS

fread cert buffer

fread key buffer

releaseFS

return true → HTTPS server

  • Certificates are stored in SPIRAM for the full lifetime of the server instance.
  • The +1 null terminator is required by the mbedTLS PEM parser.
  • If no certificate is found at startup, the server falls back to plain HTTP so the user can upload a certificate via the WebUI without needing a serial connection.
  • On end() or start failure, _freeCerts() always releases both buffers to prevent leaks across begin() retries.
HeaderValuePurpose
Strict-Transport-Securitymax-age=31536000HSTS — browser auto-upgrades future HTTP requests
Content-Security-Policyupgrade-insecure-requestsWebUI sub-requests (XHR, WebSocket) silently upgraded to TLS

The HTTP service acts as the transport layer for WebSocket connections. It owns the httpd server handle; the WebSocket services (see websocket_server.md) own the client state.

esp3dWsServerDataServiceesp3dWsWebUiServiceESP3DHttpService (httpd)Browseresp3dWsServerDataServiceesp3dWsWebUiServiceESP3DHttpService (httpd)BrowserPlain HTTP closes are NOT forwarded.\nFile descriptor recycling guard prevents\nfalse positive session drops.GET /ws\nUpgrade: websocket\nSec-WebSocket-Protocol: webui-v3websocket_webui_handler(req)101 Switching ProtocolsGET /wsdata Upgrade: websocketwebsocket_data_handler(req)101 Switching ProtocolsTCP close (socketFd N)close_fn(hd, socketFd N)httpd_ws_get_fd_info → classify socketonClose(N) if WS or known to webui serviceonClose(N) if WS or known to wsdata service

Socket type classification (httpd_ws_get_fd_info + checking each service’s client registry) prevents forwarding plain HTTP close_fn events to WebSocket services. This matters because the httpd reuses file descriptors: a new HTTP connection can receive the same fd as a recently closed WebSocket.


isStreamingActive() queries gcodeHostService.getState() and returns true when a G-code stream is in the processing state.

Effect on HTTP handlers:

  • Flash and SD file writes are rejected immediately (not queued or deferred) by returning ESP3DUploadError::access_denied. This prevents contention between HTTP upload I/O and the G-code streaming task on the shared flash/SD bus.
  • File reads for static WebUI content are served from the PSRAM cache (when ESP3D_PSRAM_FEATURE is active), bypassing the flash bus entirely.

All handlers share these helpers on ESP3DHttpService:

MethodDescription
httpd_resp_set_http_hdr(req)Sets User-Agent and Host response headers; adds HSTS + CSP on HTTPS
sendStringChunk(req, str, autoClose)Sends a null-terminated string as one chunked response segment
sendBinaryChunk(req, data, len, autoClose)Sends a raw binary buffer as one chunked segment
sendBufferChunked(req, data, len)Slices a large buffer into STREAM_CHUNK_SIZE chunks with taskYIELD between each
hasArg(req, name) / getArg(req, name)Reads form fields parsed into the active PostUploadContext::args list
pushError(errcode, msg)Broadcasts ERROR:<code>:<msg>\n over the WebUI WebSocket
getBoundaryString(req)Extracts the MIME boundary from the Content-Type header using a static fixed-size buffer
streamFile(path, req)Serves a file from flash, preferring PSRAM cache then .br then .gz then plain

flashFsAuthServiceESP3DHttpServiceESP-IDF httpdBrowser / ToolflashFsAuthServiceESP3DHttpServiceESP-IDF httpdBrowser / Toolloop[Each directory entry]GET /files?path=/&action=delete&filename=old.gcodefiles_handler(req)httpd_resp_set_http_hdr(req)getAuthenticationLevel(req)adminParse query string (path, action, filename)accessFS()trueremove("/old.gcode")truegetSpaceInfo() → total, usedopendir("/")stat(entry)sendStringChunk(JSON fragment)releaseFS()sendStringChunk(JSON footer)send_chunk(NULL, 0) - end chunked transfer200 OK + JSON body
CMake OptionWhen ON
ESP3D_WEBUI_SERVER_FEATURERegisters /ws; starts esp3dWsWebUiService
ESP3D_WS_SERVER_SERVICE_FEATURERegisters /wsdata; starts esp3dWsServerDataService
ESP3D_WEBDAV_SERVICES_FEATURERegisters 12 WebDAV handlers; enables wildcard URI matching
ESP3D_SD_CARD_FEATURERegisters /sdfiles (GET + POST)
ESP3D_UPDATE_FEATURERegisters POST /updatefw; exposes setUpdateTargetPartition()
ESP3D_HTTPS_FEATUREUses esp_https_server; enables cert loading + security headers
ESP3D_SSDP_FEATURERegisters GET /description.xml
ESP3D_CAMERA_FEATURERegisters GET /snap
ESP3D_PSRAM_FEATUREEnables SPIRAM _chunk buffer and webUiPsramCache
ESP3D_AUTHENTICATION_FEATUREEnables session management, Basic Auth, cookie handling
ESP3D_TIMESTAMP_FEATUREAdds "time" field to file listing JSON responses
Config KeyMinimumReason
CONFIG_HTTPD_WS_SUPPORTmust be y when WebUI or WS data is enabledWebSocket upgrade support in httpd
CONFIG_HTTPD_MAX_REQ_HDR_LEN≥ 1024 when WEBUI_SERVER_FEATURE is enabledWS upgrade headers (Origin, Sec-WebSocket-Key, etc.)

Both constraints are enforced at compile time with #error directives.

Setting IndexTypePurpose
esp3d_http_onbyte (ESP3DState)Enable / disable the HTTP service
esp3d_http_portuint32HTTP listening port (default 80)
esp3d_https_portuint32HTTPS listening port (default 443)
esp3d_webdav_onbyte (ESP3DState)Enable / disable WebDAV (readable at runtime via webdavActive(true))
esp3d_ws_onbyte (ESP3DState)Enable / disable the data WebSocket server

See docs/guides/esp32_memory_constraints.md for the full fragmentation playbook.

ResourceSize / Allocation strategy
_chunkSTREAM_CHUNK_SIZE bytes — static .bss (non-PSRAM) or SPIRAM (PSRAM). Single buffer reused for all file streaming. Never reallocated during operation.
PostUploadContext::argsstd::list<std::pair<std::string,std::string>> — populated per request, cleared between uploads. Contains only small text fields.
TLS cert + key buffersSPIRAM allocations sized to file content +1 byte. Allocated in _loadCerts(), freed in _freeCerts() (called by end() and on any start failure).
PSRAM cache entriesEach CacheEntry::data is an individual SPIRAM allocation. Allocation failure for a single file is logged and skipped; the cache degrades gracefully rather than aborting.
WebDAV response_bodyBuilt as std::string per PROPFIND entry and immediately flushed as a chunk. Peak allocation is proportional to one directory entry, not the full listing.

ModuleRelationship
network.mdESP3DNetworkServices starts and stops the HTTP service when WiFi comes up/down
wifi.mdesp3dWifiClient.getLocalIpString() used to populate the Host response header
websocket_server.mdesp3dWsWebUiService and esp3dWsServerDataService share the httpd server handle owned by this module
authentication.mdesp3dAuthenthicationService manages session records and credential validation for all authenticated endpoints
filesystem.mdglobalFs, flashFs, and sd provide all file I/O for handlers and the PSRAM cache
gcode_host.mdgcodeHostService.getState() provides the streaming-active guard that protects flash/SD writes
ssdp.mddescription_xml_handler generates the SSDP device description XML
mdns.mdRegistered independently at the network layer; HTTP service does not interact with mDNS directly
settings.mdNVS-backed port numbers, enable flags, and runtime toggles (WebDAV, WS)