feat: Add WebDAV Class 1 server for SD card access#1030
feat: Add WebDAV Class 1 server for SD card access#1030dexif wants to merge 2 commits intocrosspoint-reader:masterfrom
Conversation
Implement WebDAV support so the device can be mounted as a network drive in Windows Explorer, macOS Finder, Linux Nautilus, and mobile file managers. Methods: OPTIONS, PROPFIND, GET, HEAD, PUT, DELETE, MKCOL, MOVE, COPY, LOCK, UNLOCK. All handled via onNotFound() to avoid conflicts with existing routes. The WebServer library is vendored into lib/WebServer/ because the framework version buffers the entire PUT body in RAM before calling the handler. On ESP32-C3 (~150KB free heap) this makes file uploads over ~100KB impossible. The vendored copy has a minimal patch in Parsing.cpp (~14 lines) that skips body buffering for PUT requests with no registered handler, allowing the WebDAV handler to stream the body from the TCP buffer in 4KB chunks.
📝 WalkthroughWalkthroughA new WebServer library for ESP32 is introduced with full HTTP request parsing, routing, authentication, and file upload support. Additionally, WebDAV protocol support is integrated into the application with handlers for standard WebDAV operations on the filesystem. Changes
Sequence DiagramssequenceDiagram
participant Client
participant WebServer
participant Parser as Request Parser
participant Handler as Request Handler
participant FS as File System
Client->>WebServer: TCP connection (handleClient)
WebServer->>Parser: _parseRequest(WiFiClient)
Parser->>Parser: Read request line
Parser->>Parser: Parse headers
Parser->>Handler: Find handler via URI/method
alt POST/PUT with form data
Parser->>Parser: Parse multipart boundary
Parser->>FS: Stream file upload with buffering
FS-->>Parser: Write confirmation
else GET with query string
Parser->>Parser: Parse URL arguments
end
Parser-->>WebServer: Request complete
WebServer->>Handler: Execute handler function
Handler->>FS: Read static file (optional)
FS-->>Handler: File stream
Handler->>WebServer: Send response with headers
WebServer->>Client: HTTP response with content
sequenceDiagram
participant Client
participant WebServer
participant DAV as WebDAVHandler
participant FS as File System
Client->>WebServer: WebDAV request (PROPFIND/PUT/DELETE)
WebServer->>DAV: handleRequest()
DAV->>DAV: Route by HTTP method
alt PROPFIND
DAV->>FS: List directory contents
FS-->>DAV: Directory entries
DAV->>DAV: Build multistatus XML response
else PUT (upload)
DAV->>FS: Write file stream
FS-->>DAV: Write confirmation
DAV->>DAV: Clear EPUB cache if needed
else DELETE
DAV->>FS: Remove file/empty directory
FS-->>DAV: Deletion confirmation
end
DAV-->>WebServer: Response ready
WebServer->>Client: HTTP response (200/207/403/404/etc)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes 🚥 Pre-merge checks | ✅ 2✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Tip Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord. Comment |
There was a problem hiding this comment.
Actionable comments posted: 10
🧹 Nitpick comments (1)
lib/WebServer/src/WebServer.h (1)
216-216: Align the header-guard end comment with the actual guard.Minor nit, but it avoids confusion during maintenance.
🧹 Suggested fix
-#endif //ESP8266WEBSERVER_H +#endif //WEBSERVER_H🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@lib/WebServer/src/WebServer.h` at line 216, The closing `#endif` comment in WebServer.h currently reads "//ESP8266WEBSERVER_H" but doesn't match the actual header guard macro; open WebServer.h, locate the top guard macro (the `#ifndef/`#define symbol used for the include guard) and update the trailing comment on the final `#endif` to exactly match that macro name (e.g., change the comment to the same identifier found at the top) so the end-comment aligns with the real guard (reference the macro symbol at the top of WebServer.h and the existing trailing comment "ESP8266WEBSERVER_H").
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (17)
lib/WebServer/library.jsonlib/WebServer/src/HTTP_Method.hlib/WebServer/src/Parsing.cpplib/WebServer/src/Uri.hlib/WebServer/src/WebServer.cpplib/WebServer/src/WebServer.hlib/WebServer/src/detail/RequestHandler.hlib/WebServer/src/detail/RequestHandlersImpl.hlib/WebServer/src/detail/mimetable.cpplib/WebServer/src/detail/mimetable.hlib/WebServer/src/uri/UriBraces.hlib/WebServer/src/uri/UriGlob.hlib/WebServer/src/uri/UriRegex.hsrc/network/CrossPointWebServer.cppsrc/network/CrossPointWebServer.hsrc/network/WebDAVHandler.cppsrc/network/WebDAVHandler.h
🧰 Additional context used
🧬 Code graph analysis (9)
lib/WebServer/src/detail/RequestHandler.h (5)
lib/WebServer/src/detail/RequestHandlersImpl.h (5)
canHandle(26-31)canHandle(77-85)canUpload(33-38)handle(40-47)handle(87-125)lib/WebServer/src/uri/UriBraces.h (1)
canHandle(28-63)lib/WebServer/src/uri/UriGlob.h (1)
canHandle(17-19)lib/WebServer/src/uri/UriRegex.h (1)
canHandle(25-41)lib/WebServer/src/WebServer.cpp (5)
WebServer(42-64)WebServer(66-88)WebServer(90-100)pathArg(543-547)pathArg(543-543)
lib/WebServer/src/uri/UriRegex.h (4)
lib/WebServer/src/Uri.h (1)
Uri(7-27)lib/WebServer/src/WebServer.h (1)
uri(96-96)lib/WebServer/src/uri/UriGlob.h (2)
clone(13-15)canHandle(17-19)lib/WebServer/src/detail/RequestHandlersImpl.h (2)
canHandle(26-31)canHandle(77-85)
src/network/WebDAVHandler.h (2)
src/network/WebDAVHandler.cpp (43)
WebDAVHandler(21-21)handleRequest(23-63)handleRequest(23-23)handleOptions(67-75)handleOptions(67-67)handlePropfind(79-167)handlePropfind(79-79)handleGet(204-237)handleGet(204-204)handleHead(241-271)handleHead(241-241)handlePut(275-337)handlePut(275-275)handleDelete(341-390)handleDelete(341-341)handleMkcol(394-430)handleMkcol(394-394)handleMove(434-496)handleMove(434-434)handleCopy(500-586)handleCopy(500-500)handleLock(590-611)handleLock(590-590)handleUnlock(613-616)handleUnlock(613-613)getRequestPath(620-637)getRequestPath(620-620)getDestinationPath(639-668)getDestinationPath(639-639)urlEncodePath(670-695)urlEncodePath(670-670)isProtectedPath(697-713)isProtectedPath(697-697)getDepth(715-721)getDepth(715-715)getOverwrite(723-727)getOverwrite(723-723)clearEpubCacheIfNeeded(729-734)clearEpubCacheIfNeeded(729-729)sendPropEntry(169-200)sendPropEntry(169-170)getMimeType(736-754)getMimeType(736-736)lib/Epub/Epub/hyphenation/LanguageRegistry.h (1)
const(14-19)
lib/WebServer/src/WebServer.cpp (4)
lib/WebServer/src/WebServer.h (4)
WebServer(70-213)uri(96-96)method(97-97)WiFiClient(98-98)lib/WebServer/src/detail/RequestHandler.h (2)
next(15-15)next(16-16)lib/WebServer/src/detail/mimetable.h (1)
mime(5-44)lib/WebServer/src/Parsing.cpp (2)
_parseRequest(76-252)_parseRequest(76-76)
src/network/CrossPointWebServer.h (2)
src/network/WebDAVHandler.cpp (1)
WebDAVHandler(21-21)src/network/WebDAVHandler.h (1)
WebDAVHandler(5-38)
lib/WebServer/src/Uri.h (5)
lib/WebServer/src/WebServer.h (1)
uri(96-96)lib/WebServer/src/uri/UriBraces.h (3)
clone(12-14)initPathArgs(16-26)canHandle(28-63)lib/WebServer/src/uri/UriGlob.h (2)
clone(13-15)canHandle(17-19)lib/WebServer/src/uri/UriRegex.h (3)
clone(13-15)initPathArgs(17-42)canHandle(25-41)lib/WebServer/src/detail/RequestHandlersImpl.h (2)
canHandle(26-31)canHandle(77-85)
lib/WebServer/src/Parsing.cpp (3)
lib/WebServer/src/WebServer.h (1)
method(97-97)lib/WebServer/src/WebServer.cpp (8)
arg(549-559)arg(549-549)arg(561-565)arg(561-561)argName(567-571)argName(567-567)hasArg(577-587)hasArg(577-577)lib/WebServer/src/detail/mimetable.h (1)
mime(5-44)
lib/WebServer/src/detail/RequestHandlersImpl.h (6)
lib/WebServer/src/detail/mimetable.h (1)
mime(5-44)lib/WebServer/src/detail/RequestHandler.h (1)
RequestHandler(7-28)lib/WebServer/src/WebServer.h (4)
WebServer(70-213)uri(96-96)method(97-97)fs(66-68)lib/WebServer/src/Uri.h (1)
Uri(7-27)lib/WebServer/src/uri/UriGlob.h (1)
canHandle(17-19)lib/WebServer/src/uri/UriRegex.h (1)
canHandle(25-41)
lib/WebServer/src/WebServer.h (2)
lib/WebServer/src/WebServer.cpp (47)
WebServer(42-64)WebServer(66-88)WebServer(90-100)begin(102-106)begin(102-102)begin(108-112)begin(108-108)close(354-359)close(354-354)on(255-257)on(255-255)on(259-261)on(259-259)on(263-265)on(263-263)onNotFound(641-643)onNotFound(641-641)arg(549-559)arg(549-549)arg(561-565)arg(561-561)collectHeaders(598-607)collectHeaders(598-598)header(590-596)header(590-590)header(609-613)header(609-609)headerName(615-619)headerName(615-615)send(429-441)send(429-429)send(443-445)send(443-443)send(447-449)send(447-447)send(451-458)send(451-451)sendHeader(365-377)sendHeader(365-365)_responseCodeToString(678-722)_responseCodeToString(678-678)_prepareHeader(395-427)_prepareHeader(395-395)_getRandomHexString(229-236)_getRandomHexString(229-229)_extractParam(114-119)_extractParam(114-114)lib/WebServer/src/Parsing.cpp (4)
urlDecode(563-592)urlDecode(563-563)_collectHeader(254-262)_collectHeader(254-254)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: build
🔇 Additional comments (23)
lib/WebServer/library.json (1)
1-6: LGTM — minimal, valid PlatformIO manifest.lib/WebServer/src/HTTP_Method.h (1)
1-9: LGTM — clean method alias and sentinel.lib/WebServer/src/Uri.h (1)
1-27: LGTM — solid base URI abstraction.lib/WebServer/src/Parsing.cpp (9)
44-74: LGTM — timeout-based buffered read is clear and safe on realloc failure.
177-211: Good WebDAV streaming guard for PUT bodies.
254-262: LGTM — header collection is straightforward.
264-311: LGTM — argument parsing is consistent with URL-decoding expectations.
313-321: LGTM — upload buffering and callback cadence look correct.
323-362: LGTM — retry loop handles transientread()failures sensibly.
364-561: LGTM — multipart parsing and upload lifecycle logic is intact.
563-592: LGTM — URL decode utility is clear and correct.
594-599: LGTM — upload abort path cleanly notifies handler.src/network/CrossPointWebServer.h (1)
12-76: LGTM — WebDAV handler ownership is clear and RAII-safe.src/network/CrossPointWebServer.cpp (3)
222-222: Good cleanup of WebDAV handler during stop.
314-316: Nice: WebDAV gets first right-of-refusal before 404.
162-166: No action needed—collected headers are sufficient.WebDAVHandler only reads three headers from incoming requests:
Destination(line 640),Depth(line 716), andOverwrite(line 724). All three are in the collected list. WebDAVHandler does not accessContent-Length,Content-Type, or any other headers beyond those being collected, so the current header collection is complete and correct.lib/WebServer/src/detail/mimetable.cpp (1)
1-33: LGTM — MIME table is comprehensive and ordered as expected.lib/WebServer/src/detail/mimetable.h (1)
1-43: LGTM — header matches the MIME table definition.src/network/WebDAVHandler.h (1)
5-37: API surface looks consistent.
Clean separation between dispatch and per-method handlers; utilities are logically grouped.lib/WebServer/src/detail/RequestHandler.h (1)
7-29: Base handler interface looks solid.
Clean chaining API and path-arg accessors.lib/WebServer/src/uri/UriRegex.h (1)
4-40: UriRegex is not used in this project; std::regex concern is moot.The CrossPointWebServer implementation uses simple string-based routing (e.g.,
server->on("/", HTTP_GET, ...)), not the Uri pattern-matching classes. While UriRegex and its std::regex dependency exist in the bundled WebServer library, they are never instantiated or invoked by the application code. No action needed.Likely an incorrect or invalid review comment.
lib/WebServer/src/WebServer.h (2)
33-62: LGTM for enums, constants, and HTTPUpload layout.The sizing macros and upload struct look consistent with the expected single-client, embedded constraints.
73-95: Routing/handler API surface looks solid.The overload set for
on(...), plusonNotFound/onFileUpload, provides clean registration points for WebDAV and static routes.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@lib/WebServer/src/detail/RequestHandlersImpl.h`:
- Around line 77-83: canHandle currently allows prefix collisions (e.g., "/foo"
matching "/foobar") because it only checks startsWith(_uri); change the
predicate so when _isFile is true require exact equality (requestUri == _uri),
and when not _isFile require either exact match or that requestUri starts with
_uri followed by a path separator (e.g., requestUri == _uri ||
requestUri.startsWith(_uri + "/")). Update the logic in canHandle to use
_isFile, _uri, requestUri and HTTP_GET accordingly so static routes only match
the exact path or a proper subpath.
In `@lib/WebServer/src/Parsing.cpp`:
- Around line 220-233: The header value parsing drops the first character when
headers are in "Header:Value" form because it uses substring(headerDiv + 2);
change the logic in the headers loop to extract headerValue starting at
headerDiv + 1 (i.e., substring(headerDiv + 1)) and then trim a single leading
space if present (so both "Header:Value" and "Header: Value" are handled).
Update the variables/locations referenced (req, headerDiv, headerName,
headerValue) and continue to call _collectHeader(headerName.c_str(),
headerValue.c_str()) with the corrected value.
In `@lib/WebServer/src/uri/UriBraces.h`:
- Around line 16-26: The placeholder counting in initPathArgs incorrectly
ignores matches at index 0 because it checks start > 0; update the loop in
UriBraces::initPathArgs to treat any non-negative index as a valid match (use
start >= 0 or check for start != -1), increment numParams when start >= 0, then
advance start by 2 and continue while start >= 0; this will ensure
pathArgs.resize(numParams) allocates slots for placeholders at the beginning and
prevents canHandle from writing out of bounds.
In `@lib/WebServer/src/uri/UriGlob.h`:
- Around line 4-19: The current UriGlob class unconditionally uses fnmatch
(included via fnmatch.h) which breaks builds on platforms without fnmatch; wrap
the include and the UriGlob class implementation with a compile-time guard
(e.g., `#ifdef` HAVE_FNMATCH / `#endif`) or introduce a portability macro (e.g.,
USE_FNMATCH) and provide a fallback: if fnmatch is unavailable, either implement
a lightweight glob matcher used by UriGlob::canHandle(const String &requestUri,
std::vector<String> &pathArgs) or implement UriGlob as a thin wrapper that
always returns false from canHandle and documents the limitation; keep
UriGlob::clone() consistent with whichever implementation is chosen and ensure
the guard symbol is documented in the build system so toolchains without fnmatch
can compile.
In `@lib/WebServer/src/WebServer.cpp`:
- Around line 147-154: The code uses a signed 8-bit char for the length
(toencodeLen) causing potential overflow and wrong allocations; change to an
unsigned size type (e.g., size_t or int) when computing
strlen(username)+strlen(password)+1 and use that type for allocations and when
calling base64_encode_expected_len, updating any related variables (toencodeLen,
toencode, encoded) in WebServer.cpp so buffer sizes are computed correctly and
safely; ensure null-checks and allocations use the new size type consistently.
- Around line 197-210: The current digest hash only handles GET/POST/PUT/DELETE
leading to WebDAV methods failing; update the logic around _currentMethod that
builds _H2 so it uses the actual HTTP method string instead of the hardcoded
if/else chain—e.g., derive a methodName from _currentMethod (or use an existing
helper like httpMethodToString/_request->method() if available) that returns
"GET", "POST", "PROPFIND", "MKCOL", "MOVE", etc., then compute _H2 =
md5str(String(methodName) + ':' + _uri) (fallback to "GET" only if methodName is
unknown); reference md5str, _currentMethod, _uri and _H2 when making the change.
In `@lib/WebServer/src/WebServer.h`:
- Around line 2-4: The file header in WebServer.h incorrectly states the server
"Supports only one simultaneous client, knows how to handle GET and POST";
update that top-of-file comment to accurately describe current capabilities of
the WebServer class—mention its concurrency support (single/multi-client as
implemented), and list the actual HTTP methods supported (as implemented by
methods like handleClient() and handleRequest()), and any important behavior
(e.g., request parsing, routing, or payload handling) so integrators aren’t
misled; keep the wording brief and factual to match the current implementation.
- Around line 96-100: The upload() accessor dereferences the std::unique_ptr
_currentUpload unsafely; update WebServer::upload() to guard against null by
returning a safe fallback (for example a reference to a static empty HTTPUpload
instance) or change the API to return a pointer/optional so callers must handle
absence, and ensure callers that expect an active upload still function; refer
to symbols upload(), _currentUpload, HTTPUpload and update WebServer::upload()
implementation (or add clear documentation on the precondition) so dereferencing
a null unique_ptr is never possible.
In `@src/network/WebDAVHandler.cpp`:
- Around line 697-712: isProtectedPath currently only examines the final path
segment so nested hidden directories like "/.hidden/file" bypass protection;
update isProtectedPath to iterate over every path segment (splitting on '/' or
scanning between '/' chars) and for each non-empty segment check if
segment.startsWith(".") or equals any entry in HIDDEN_ITEMS (using HIDDEN_ITEMS
and HIDDEN_ITEMS_COUNT) and return true if so, otherwise return false; keep
existing behavior for empty paths.
- Around line 319-336: The PUT handler currently uses client.readBytes(...) and
file.write(...) without verifying they processed the full requested amount, so
partial reads or short writes produce a truncated file while still returning
201/204; update the upload loop in WebDAVHandler::PUT to check that bytesRead ==
toRead (and break with an error on timeout/0), check the return value of
file.write (or bytesWritten) equals bytesRead, and on any mismatch or failure
close and remove the partial file (or rollback) and send an error response
(e.g., 408/500) instead of 201/204; reference the loop using client.readBytes,
file.write, remaining/contentLength, file.close, and _server->send to locate and
implement these checks and proper cleanup.
---
Nitpick comments:
In `@lib/WebServer/src/WebServer.h`:
- Line 216: The closing `#endif` comment in WebServer.h currently reads
"//ESP8266WEBSERVER_H" but doesn't match the actual header guard macro; open
WebServer.h, locate the top guard macro (the `#ifndef/`#define symbol used for the
include guard) and update the trailing comment on the final `#endif` to exactly
match that macro name (e.g., change the comment to the same identifier found at
the top) so the end-comment aligns with the real guard (reference the macro
symbol at the top of WebServer.h and the existing trailing comment
"ESP8266WEBSERVER_H").
| bool canHandle(HTTPMethod requestMethod, String requestUri) override { | ||
| if (requestMethod != HTTP_GET) | ||
| return false; | ||
|
|
||
| if ((_isFile && requestUri != _uri) || !requestUri.startsWith(_uri)) | ||
| return false; | ||
|
|
There was a problem hiding this comment.
Avoid prefix collisions in static routing.
Line 81 allows /foo to match /foobar, which can resolve unintended files. Add a boundary check so only /foo or /foo/... matches.
🛠️ Proposed fix
bool canHandle(HTTPMethod requestMethod, String requestUri) override {
if (requestMethod != HTTP_GET)
return false;
- if ((_isFile && requestUri != _uri) || !requestUri.startsWith(_uri))
- return false;
+ if (_isFile) {
+ if (requestUri != _uri)
+ return false;
+ } else {
+ String base = _uri;
+ if (!base.endsWith("/"))
+ base += "/";
+ if (!(requestUri == _uri || requestUri.startsWith(base)))
+ return false;
+ }
return true;
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| bool canHandle(HTTPMethod requestMethod, String requestUri) override { | |
| if (requestMethod != HTTP_GET) | |
| return false; | |
| if ((_isFile && requestUri != _uri) || !requestUri.startsWith(_uri)) | |
| return false; | |
| bool canHandle(HTTPMethod requestMethod, String requestUri) override { | |
| if (requestMethod != HTTP_GET) | |
| return false; | |
| if (_isFile) { | |
| if (requestUri != _uri) | |
| return false; | |
| } else { | |
| String base = _uri; | |
| if (!base.endsWith("/")) | |
| base += "/"; | |
| if (!(requestUri == _uri || requestUri.startsWith(base))) | |
| return false; | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@lib/WebServer/src/detail/RequestHandlersImpl.h` around lines 77 - 83,
canHandle currently allows prefix collisions (e.g., "/foo" matching "/foobar")
because it only checks startsWith(_uri); change the predicate so when _isFile is
true require exact equality (requestUri == _uri), and when not _isFile require
either exact match or that requestUri starts with _uri followed by a path
separator (e.g., requestUri == _uri || requestUri.startsWith(_uri + "/")).
Update the logic in canHandle to use _isFile, _uri, requestUri and HTTP_GET
accordingly so static routes only match the exact path or a proper subpath.
| String headerName; | ||
| String headerValue; | ||
| //parse headers | ||
| while(1){ | ||
| req = client.readStringUntil('\r'); | ||
| client.readStringUntil('\n'); | ||
| if (req == "") break;//no moar headers | ||
| int headerDiv = req.indexOf(':'); | ||
| if (headerDiv == -1){ | ||
| break; | ||
| } | ||
| headerName = req.substring(0, headerDiv); | ||
| headerValue = req.substring(headerDiv + 2); | ||
| _collectHeader(headerName.c_str(),headerValue.c_str()); |
There was a problem hiding this comment.
Fix header value parsing when there's no space after “:”.
HTTP allows Header:Value (no space). Using substring(headerDiv + 2) will drop the first character in that case. Align with the POST/PUT branch by trimming after +1.
🛠️ Suggested fix
- headerValue = req.substring(headerDiv + 2);
- _collectHeader(headerName.c_str(),headerValue.c_str());
+ headerValue = req.substring(headerDiv + 1);
+ headerValue.trim();
+ _collectHeader(headerName.c_str(), headerValue.c_str());📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| String headerName; | |
| String headerValue; | |
| //parse headers | |
| while(1){ | |
| req = client.readStringUntil('\r'); | |
| client.readStringUntil('\n'); | |
| if (req == "") break;//no moar headers | |
| int headerDiv = req.indexOf(':'); | |
| if (headerDiv == -1){ | |
| break; | |
| } | |
| headerName = req.substring(0, headerDiv); | |
| headerValue = req.substring(headerDiv + 2); | |
| _collectHeader(headerName.c_str(),headerValue.c_str()); | |
| String headerName; | |
| String headerValue; | |
| //parse headers | |
| while(1){ | |
| req = client.readStringUntil('\r'); | |
| client.readStringUntil('\n'); | |
| if (req == "") break;//no moar headers | |
| int headerDiv = req.indexOf(':'); | |
| if (headerDiv == -1){ | |
| break; | |
| } | |
| headerName = req.substring(0, headerDiv); | |
| headerValue = req.substring(headerDiv + 1); | |
| headerValue.trim(); | |
| _collectHeader(headerName.c_str(), headerValue.c_str()); |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@lib/WebServer/src/Parsing.cpp` around lines 220 - 233, The header value
parsing drops the first character when headers are in "Header:Value" form
because it uses substring(headerDiv + 2); change the logic in the headers loop
to extract headerValue starting at headerDiv + 1 (i.e., substring(headerDiv +
1)) and then trim a single leading space if present (so both "Header:Value" and
"Header: Value" are handled). Update the variables/locations referenced (req,
headerDiv, headerName, headerValue) and continue to call
_collectHeader(headerName.c_str(), headerValue.c_str()) with the corrected
value.
| void initPathArgs(std::vector<String> &pathArgs) override final { | ||
| int numParams = 0, start = 0; | ||
| do { | ||
| start = _uri.indexOf("{}", start); | ||
| if (start > 0) { | ||
| numParams++; | ||
| start += 2; | ||
| } | ||
| } while (start > 0); | ||
| pathArgs.resize(numParams); | ||
| } |
There was a problem hiding this comment.
Fix placeholder counting at index 0 (prevents OOB).
Line 20 only counts matches where start > 0, so patterns beginning with {} allocate zero args and canHandle writes out of bounds.
🛠️ Proposed fix
- void initPathArgs(std::vector<String> &pathArgs) override final {
- int numParams = 0, start = 0;
- do {
- start = _uri.indexOf("{}", start);
- if (start > 0) {
- numParams++;
- start += 2;
- }
- } while (start > 0);
- pathArgs.resize(numParams);
- }
+ void initPathArgs(std::vector<String> &pathArgs) override final {
+ int numParams = 0;
+ int start = 0;
+ while ((start = _uri.indexOf("{}", start)) >= 0) {
+ numParams++;
+ start += 2;
+ }
+ pathArgs.resize(numParams);
+ }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@lib/WebServer/src/uri/UriBraces.h` around lines 16 - 26, The placeholder
counting in initPathArgs incorrectly ignores matches at index 0 because it
checks start > 0; update the loop in UriBraces::initPathArgs to treat any
non-negative index as a valid match (use start >= 0 or check for start != -1),
increment numParams when start >= 0, then advance start by 2 and continue while
start >= 0; this will ensure pathArgs.resize(numParams) allocates slots for
placeholders at the beginning and prevents canHandle from writing out of bounds.
| #include "Uri.h" | ||
| #include <fnmatch.h> | ||
|
|
||
| class UriGlob : public Uri { | ||
|
|
||
| public: | ||
| explicit UriGlob(const char *uri) : Uri(uri) {}; | ||
| explicit UriGlob(const String &uri) : Uri(uri) {}; | ||
|
|
||
| Uri* clone() const override final { | ||
| return new UriGlob(_uri); | ||
| }; | ||
|
|
||
| bool canHandle(const String &requestUri, __attribute__((unused)) std::vector<String> &pathArgs) override final { | ||
| return fnmatch(_uri.c_str(), requestUri.c_str(), 0) == 0; | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check fnmatch usage and any compatibility guards/fallbacks
rg -n "fnmatch|UriGlob" lib/WebServer -g '*.h' -g '*.cpp'Repository: crosspoint-reader/crosspoint-reader
Length of output: 560
🏁 Script executed:
#!/bin/bash
# Check for any conditional compilation or platform-specific code
rg -n "#ifdef|#if|fnmatch|ESP32" lib/WebServer --context 2 -g '*.h' -g '*.cpp'Repository: crosspoint-reader/crosspoint-reader
Length of output: 2695
🏁 Script executed:
#!/bin/bash
# Look for include guards or platform detection
rg -n "include.*fnmatch|HAVE_FNMATCH|_GNU_SOURCE" -g '*.h' -g '*.cpp'Repository: crosspoint-reader/crosspoint-reader
Length of output: 133
Add compatibility guards for fnmatch or provide a fallback implementation.
fnmatch.h is included unconditionally, but this POSIX function is not available on all embedded platforms (notably some ESP32 toolchains). The code will fail to compile if fnmatch is unavailable. Guard the include and class definition with #ifdef or provide an alternative glob matcher.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@lib/WebServer/src/uri/UriGlob.h` around lines 4 - 19, The current UriGlob
class unconditionally uses fnmatch (included via fnmatch.h) which breaks builds
on platforms without fnmatch; wrap the include and the UriGlob class
implementation with a compile-time guard (e.g., `#ifdef` HAVE_FNMATCH / `#endif`) or
introduce a portability macro (e.g., USE_FNMATCH) and provide a fallback: if
fnmatch is unavailable, either implement a lightweight glob matcher used by
UriGlob::canHandle(const String &requestUri, std::vector<String> &pathArgs) or
implement UriGlob as a thin wrapper that always returns false from canHandle and
documents the limitation; keep UriGlob::clone() consistent with whichever
implementation is chosen and ensure the guard symbol is documented in the build
system so toolchains without fnmatch can compile.
| char toencodeLen = strlen(username)+strlen(password)+1; | ||
| char *toencode = new char[toencodeLen + 1]; | ||
| if(toencode == NULL){ | ||
| authReq = ""; | ||
| return false; | ||
| } | ||
| char *encoded = new char[base64_encode_expected_len(toencodeLen)+1]; | ||
| if(encoded == NULL){ |
There was a problem hiding this comment.
Basic auth length uses char, risking overflow.
Line 147 stores the length in a char, which can overflow for longer credentials and under-allocate buffers. Use size_t or int.
🛠️ Proposed fix
- char toencodeLen = strlen(username)+strlen(password)+1;
+ size_t toencodeLen = strlen(username) + strlen(password) + 1;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@lib/WebServer/src/WebServer.cpp` around lines 147 - 154, The code uses a
signed 8-bit char for the length (toencodeLen) causing potential overflow and
wrong allocations; change to an unsigned size type (e.g., size_t or int) when
computing strlen(username)+strlen(password)+1 and use that type for allocations
and when calling base64_encode_expected_len, updating any related variables
(toencodeLen, toencode, encoded) in WebServer.cpp so buffer sizes are computed
correctly and safely; ensure null-checks and allocations use the new size type
consistently.
| String _H1 = md5str(String(username) + ':' + _realm + ':' + String(password)); | ||
| log_v("Hash of user:realm:pass=%s", _H1.c_str()); | ||
| String _H2 = ""; | ||
| if(_currentMethod == HTTP_GET){ | ||
| _H2 = md5str(String(F("GET:")) + _uri); | ||
| }else if(_currentMethod == HTTP_POST){ | ||
| _H2 = md5str(String(F("POST:")) + _uri); | ||
| }else if(_currentMethod == HTTP_PUT){ | ||
| _H2 = md5str(String(F("PUT:")) + _uri); | ||
| }else if(_currentMethod == HTTP_DELETE){ | ||
| _H2 = md5str(String(F("DELETE:")) + _uri); | ||
| }else{ | ||
| _H2 = md5str(String(F("GET:")) + _uri); | ||
| } |
There was a problem hiding this comment.
Digest auth misses WebDAV methods.
Line 200 only hashes GET/POST/PUT/DELETE, so WebDAV methods (PROPFIND, MKCOL, MOVE, etc.) will always fail Digest authentication.
🛠️ Proposed fix
- String _H2 = "";
- if(_currentMethod == HTTP_GET){
- _H2 = md5str(String(F("GET:")) + _uri);
- }else if(_currentMethod == HTTP_POST){
- _H2 = md5str(String(F("POST:")) + _uri);
- }else if(_currentMethod == HTTP_PUT){
- _H2 = md5str(String(F("PUT:")) + _uri);
- }else if(_currentMethod == HTTP_DELETE){
- _H2 = md5str(String(F("DELETE:")) + _uri);
- }else{
- _H2 = md5str(String(F("GET:")) + _uri);
- }
+ String methodName;
+ switch (_currentMethod) {
+ case HTTP_GET: methodName = F("GET"); break;
+ case HTTP_POST: methodName = F("POST"); break;
+ case HTTP_PUT: methodName = F("PUT"); break;
+ case HTTP_DELETE: methodName = F("DELETE"); break;
+ case HTTP_HEAD: methodName = F("HEAD"); break;
+ case HTTP_OPTIONS: methodName = F("OPTIONS"); break;
+ case HTTP_PROPFIND: methodName = F("PROPFIND"); break;
+ case HTTP_MKCOL: methodName = F("MKCOL"); break;
+ case HTTP_MOVE: methodName = F("MOVE"); break;
+ case HTTP_COPY: methodName = F("COPY"); break;
+ case HTTP_LOCK: methodName = F("LOCK"); break;
+ case HTTP_UNLOCK: methodName = F("UNLOCK"); break;
+ default: methodName = F("GET"); break;
+ }
+ String _H2 = md5str(methodName + ':' + _uri);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| String _H1 = md5str(String(username) + ':' + _realm + ':' + String(password)); | |
| log_v("Hash of user:realm:pass=%s", _H1.c_str()); | |
| String _H2 = ""; | |
| if(_currentMethod == HTTP_GET){ | |
| _H2 = md5str(String(F("GET:")) + _uri); | |
| }else if(_currentMethod == HTTP_POST){ | |
| _H2 = md5str(String(F("POST:")) + _uri); | |
| }else if(_currentMethod == HTTP_PUT){ | |
| _H2 = md5str(String(F("PUT:")) + _uri); | |
| }else if(_currentMethod == HTTP_DELETE){ | |
| _H2 = md5str(String(F("DELETE:")) + _uri); | |
| }else{ | |
| _H2 = md5str(String(F("GET:")) + _uri); | |
| } | |
| String methodName; | |
| switch (_currentMethod) { | |
| case HTTP_GET: methodName = F("GET"); break; | |
| case HTTP_POST: methodName = F("POST"); break; | |
| case HTTP_PUT: methodName = F("PUT"); break; | |
| case HTTP_DELETE: methodName = F("DELETE"); break; | |
| case HTTP_HEAD: methodName = F("HEAD"); break; | |
| case HTTP_OPTIONS: methodName = F("OPTIONS"); break; | |
| case HTTP_PROPFIND: methodName = F("PROPFIND"); break; | |
| case HTTP_MKCOL: methodName = F("MKCOL"); break; | |
| case HTTP_MOVE: methodName = F("MOVE"); break; | |
| case HTTP_COPY: methodName = F("COPY"); break; | |
| case HTTP_LOCK: methodName = F("LOCK"); break; | |
| case HTTP_UNLOCK: methodName = F("UNLOCK"); break; | |
| default: methodName = F("GET"); break; | |
| } | |
| String _H2 = md5str(methodName + ':' + _uri); |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@lib/WebServer/src/WebServer.cpp` around lines 197 - 210, The current digest
hash only handles GET/POST/PUT/DELETE leading to WebDAV methods failing; update
the logic around _currentMethod that builds _H2 so it uses the actual HTTP
method string instead of the hardcoded if/else chain—e.g., derive a methodName
from _currentMethod (or use an existing helper like
httpMethodToString/_request->method() if available) that returns "GET", "POST",
"PROPFIND", "MKCOL", "MOVE", etc., then compute _H2 = md5str(String(methodName)
+ ':' + _uri) (fallback to "GET" only if methodName is unknown); reference
md5str, _currentMethod, _uri and _H2 when making the change.
| WebServer.h - Dead simple web-server. | ||
| Supports only one simultaneous client, knows how to handle GET and POST. | ||
|
|
There was a problem hiding this comment.
Update the header comment to reflect current method support.
The file header says the server only handles GET/POST, which is now outdated and potentially misleading for integrators.
✍️ Suggested wording
- Supports only one simultaneous client, knows how to handle GET and POST.
+ Supports only one simultaneous client and handles multiple HTTP methods (e.g., GET/POST/PUT/DELETE/OPTIONS).📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| WebServer.h - Dead simple web-server. | |
| Supports only one simultaneous client, knows how to handle GET and POST. | |
| WebServer.h - Dead simple web-server. | |
| Supports only one simultaneous client and handles multiple HTTP methods (e.g., GET/POST/PUT/DELETE/OPTIONS). | |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@lib/WebServer/src/WebServer.h` around lines 2 - 4, The file header in
WebServer.h incorrectly states the server "Supports only one simultaneous
client, knows how to handle GET and POST"; update that top-of-file comment to
accurately describe current capabilities of the WebServer class—mention its
concurrency support (single/multi-client as implemented), and list the actual
HTTP methods supported (as implemented by methods like handleClient() and
handleRequest()), and any important behavior (e.g., request parsing, routing, or
payload handling) so integrators aren’t misled; keep the wording brief and
factual to match the current implementation.
| String uri() { return _currentUri; } | ||
| HTTPMethod method() { return _currentMethod; } | ||
| virtual WiFiClient client() { return _currentClient; } | ||
| HTTPUpload& upload() { return *_currentUpload; } | ||
|
|
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Search for _currentUpload in WebServer.cpp to understand initialization and usage
rg -n '_currentUpload' lib/WebServer/src/WebServer.cpp | head -50Repository: crosspoint-reader/crosspoint-reader
Length of output: 110
🏁 Script executed:
#!/bin/bash
# Search for upload() method calls in the entire codebase
rg -n '\.upload\s*\(' --type cpp --type hRepository: crosspoint-reader/crosspoint-reader
Length of output: 61
🏁 Script executed:
#!/bin/bash
# Find the constructor of WebServer to see initialization
rg -n 'WebServer::WebServer|WebServer(' lib/WebServer/src/WebServer.cpp | head -20Repository: crosspoint-reader/crosspoint-reader
Length of output: 191
🏁 Script executed:
#!/bin/bash
# Get more context around _currentUpload usage
rg -n -B5 -A5 '_currentUpload' lib/WebServer/src/WebServer.cppRepository: crosspoint-reader/crosspoint-reader
Length of output: 289
🏁 Script executed:
#!/bin/bash
# Check the header file for _currentUpload declaration
rg -n '_currentUpload' lib/WebServer/src/WebServer.hRepository: crosspoint-reader/crosspoint-reader
Length of output: 182
🏁 Script executed:
#!/bin/bash
# Search for upload() method calls - simpler regex
rg -n '\.upload\(\)' --type cppRepository: crosspoint-reader/crosspoint-reader
Length of output: 61
🏁 Script executed:
#!/bin/bash
# Find the WebServer constructor(s) to see initialization
rg -n 'WebServer::' lib/WebServer/src/WebServer.cpp | head -20Repository: crosspoint-reader/crosspoint-reader
Length of output: 1348
🏁 Script executed:
#!/bin/bash
# Search in all cpp/h files for upload() usage
find lib -name '*.cpp' -o -name '*.h' | xargs grep -n 'upload()'Repository: crosspoint-reader/crosspoint-reader
Length of output: 162
🏁 Script executed:
#!/bin/bash
# Look for constructor implementation more broadly
sed -n '1,100p' lib/WebServer/src/WebServer.cppRepository: crosspoint-reader/crosspoint-reader
Length of output: 2906
🏁 Script executed:
#!/bin/bash
# Check complete constructor initialization lists
sed -n '42,88p' lib/WebServer/src/WebServer.cppRepository: crosspoint-reader/crosspoint-reader
Length of output: 1149
🏁 Script executed:
#!/bin/bash
# Search for where _currentUpload is assigned a value
rg -n '_currentUpload\s*=' lib/WebServer/src/WebServer.cppRepository: crosspoint-reader/crosspoint-reader
Length of output: 61
🏁 Script executed:
#!/bin/bash
# Look for HTTPUpload allocation/creation
rg -n 'new HTTPUpload|make_unique<HTTPUpload>' lib/WebServer/src/WebServer.cppRepository: crosspoint-reader/crosspoint-reader
Length of output: 61
🏁 Script executed:
#!/bin/bash
# Broader search for HTTPUpload creation/allocation
rg -n 'HTTPUpload' lib/WebServer/src/WebServer.cpp | head -30Repository: crosspoint-reader/crosspoint-reader
Length of output: 61
🏁 Script executed:
#!/bin/bash
# Look at handleClient implementation since that's where upload might be used
sed -n '286,400p' lib/WebServer/src/WebServer.cppRepository: crosspoint-reader/crosspoint-reader
Length of output: 3037
🏁 Script executed:
#!/bin/bash
# Check if there's any guard before dereferencing _currentUpload
rg -n -B10 '\*_currentUpload' lib/WebServer/src/WebServer.cppRepository: crosspoint-reader/crosspoint-reader
Length of output: 61
🏁 Script executed:
#!/bin/bash
# Check the upload() method context and visibility
sed -n '90,110p' lib/WebServer/src/WebServer.hRepository: crosspoint-reader/crosspoint-reader
Length of output: 1422
🏁 Script executed:
#!/bin/bash
# Search entire repo for HTTPUpload to see if it's allocated anywhere
find . -type f \( -name '*.cpp' -o -name '*.h' \) | xargs grep -l 'HTTPUpload' | head -10Repository: crosspoint-reader/crosspoint-reader
Length of output: 273
🏁 Script executed:
#!/bin/bash
# Look for RequestHandler implementations that might allocate HTTPUpload
rg -n 'HTTPUpload' lib/WebServer/src/detail/RequestHandlersImpl.h | head -20Repository: crosspoint-reader/crosspoint-reader
Length of output: 166
🏁 Script executed:
#!/bin/bash
# Check Parsing.cpp for _currentUpload initialization
rg -n '_currentUpload' lib/WebServer/src/Parsing.cpp -B5 -A5Repository: crosspoint-reader/crosspoint-reader
Length of output: 3127
🏁 Script executed:
#!/bin/bash
# Search for _handleRequest implementation
rg -n '_handleRequest' lib/WebServer/src/WebServer.cpp | head -5Repository: crosspoint-reader/crosspoint-reader
Length of output: 149
🏁 Script executed:
#!/bin/bash
# Look for where upload handlers are called
rg -n '\.upload\(' lib/WebServer/src/ -B3 -A3Repository: crosspoint-reader/crosspoint-reader
Length of output: 61
Add null safety check to upload() accessor or document the precondition.
upload() dereferences _currentUpload unconditionally, but _currentUpload is a std::unique_ptr that starts as null and is only allocated during multipart file upload parsing (line 439 in Parsing.cpp). After request completion, it's reset to null (line 346 in WebServer.cpp). Since upload() is public, callers outside the upload handler context will crash with a null pointer dereference. Either add a null check and return a safe fallback, or clearly document that upload() must only be called from within an upload handler.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@lib/WebServer/src/WebServer.h` around lines 96 - 100, The upload() accessor
dereferences the std::unique_ptr _currentUpload unsafely; update
WebServer::upload() to guard against null by returning a safe fallback (for
example a reference to a static empty HTTPUpload instance) or change the API to
return a pointer/optional so callers must handle absence, and ensure callers
that expect an active upload still function; refer to symbols upload(),
_currentUpload, HTTPUpload and update WebServer::upload() implementation (or add
clear documentation on the precondition) so dereferencing a null unique_ptr is
never possible.
|
I like the idea, but the implementation seems pretty heavy. Maybe it could be easier to add FTP support instead? |
I made a pull request to the library (once they approve the fix, we can remove the library files). There aren't really many changes, as I tried to avoid pulling in third-party libraries. However, I had to patch the existing one to be able to load files larger than 100 KB. And I tested it and everything works great. It seems to me that ftp will be significantly more expensive in terms of resources and code. |
| // Collect WebDAV headers and create handler | ||
| const char* davHeaders[] = {"Depth", "Destination", "Overwrite", "If", "Lock-Token", "Timeout"}; | ||
| server->collectHeaders(davHeaders, 6); | ||
| davHandler.reset(new WebDAVHandler(server.get())); |
There was a problem hiding this comment.
This automatically starts the WebDav server when "File Transfer" is used afaics. Is that something we want? I assumed we would have a 4th option in "File Transfer" menu as WebDav Server.
There was a problem hiding this comment.
Essentially, we're already running the web server. In this case, I don't see the point in creating a separate section for enabling webdav, given that processing is happening there as well.
There was a problem hiding this comment.
Webserver is already pretty slow and resource intensive, wouldnt this put more pressure on it?
There was a problem hiding this comment.
I can’t imagine what could create load here. Essentially, it’s just a few additional request handlers.
Your current platformio.ini does not use actual Arduino core. If your PR in espressif32 Arduino core is accepted you have to change to switch from your currently used Arduino core 2.0.17 to actual 3.3.x |
If the pull request to the framework is accepted, I was planning to work on the update. |
- isProtectedPath: check every path segment, not just the last one,
to block access to e.g. /.hidden/file or /System Volume Information/foo
- handlePut: verify readBytes/write results and remove partial file
on failure instead of returning 201/204 for truncated uploads
|
I'll open a new pull request. It will include library updates and a webdav implementation. Just to make it a clean pull request. |
## Summary - Upgrade platform from espressif32 6.12.0 (Arduino Core 2.0.17) to pioarduino 55.03.37 (Arduino Core 3.3.7, ESP-IDF 5.5.2) - Add WebDAV Class 1 server (RFC 4918) - SD card can be mounted as a network drive - I also slightly fixed the SDK and also made a [pull request ](open-x4-epaper/community-sdk#21) First PR #1030 (was closed because the implementation was based on an old version of the libraries) Issue #439 --------- Co-authored-by: Dave Allie <[email protected]>
## Summary - Upgrade platform from espressif32 6.12.0 (Arduino Core 2.0.17) to pioarduino 55.03.37 (Arduino Core 3.3.7, ESP-IDF 5.5.2) - Add WebDAV Class 1 server (RFC 4918) - SD card can be mounted as a network drive - I also slightly fixed the SDK and also made a [pull request ](open-x4-epaper/community-sdk#21) First PR crosspoint-reader#1030 (was closed because the implementation was based on an old version of the libraries) Issue crosspoint-reader#439 --------- Co-authored-by: Dave Allie <[email protected]>
## Summary - Upgrade platform from espressif32 6.12.0 (Arduino Core 2.0.17) to pioarduino 55.03.37 (Arduino Core 3.3.7, ESP-IDF 5.5.2) - Add WebDAV Class 1 server (RFC 4918) - SD card can be mounted as a network drive - I also slightly fixed the SDK and also made a [pull request ](open-x4-epaper/community-sdk#21) First PR crosspoint-reader#1030 (was closed because the implementation was based on an old version of the libraries) Issue crosspoint-reader#439 --------- Co-authored-by: Dave Allie <[email protected]>
## Summary - Upgrade platform from espressif32 6.12.0 (Arduino Core 2.0.17) to pioarduino 55.03.37 (Arduino Core 3.3.7, ESP-IDF 5.5.2) - Add WebDAV Class 1 server (RFC 4918) - SD card can be mounted as a network drive - I also slightly fixed the SDK and also made a [pull request ](open-x4-epaper/community-sdk#21) First PR crosspoint-reader#1030 (was closed because the implementation was based on an old version of the libraries) Issue crosspoint-reader#439 --------- Co-authored-by: Dave Allie <[email protected]>
## Summary - Upgrade platform from espressif32 6.12.0 (Arduino Core 2.0.17) to pioarduino 55.03.37 (Arduino Core 3.3.7, ESP-IDF 5.5.2) - Add WebDAV Class 1 server (RFC 4918) - SD card can be mounted as a network drive - I also slightly fixed the SDK and also made a [pull request ](open-x4-epaper/community-sdk#21) First PR crosspoint-reader#1030 (was closed because the implementation was based on an old version of the libraries) Issue crosspoint-reader#439 --------- Co-authored-by: Dave Allie <[email protected]>
## Summary - Upgrade platform from espressif32 6.12.0 (Arduino Core 2.0.17) to pioarduino 55.03.37 (Arduino Core 3.3.7, ESP-IDF 5.5.2) - Add WebDAV Class 1 server (RFC 4918) - SD card can be mounted as a network drive - I also slightly fixed the SDK and also made a [pull request ](open-x4-epaper/community-sdk#21) First PR crosspoint-reader#1030 (was closed because the implementation was based on an old version of the libraries) Issue crosspoint-reader#439 --------- Co-authored-by: Dave Allie <[email protected]>
## Summary - Upgrade platform from espressif32 6.12.0 (Arduino Core 2.0.17) to pioarduino 55.03.37 (Arduino Core 3.3.7, ESP-IDF 5.5.2) - Add WebDAV Class 1 server (RFC 4918) - SD card can be mounted as a network drive - I also slightly fixed the SDK and also made a [pull request ](open-x4-epaper/community-sdk#21) First PR crosspoint-reader#1030 (was closed because the implementation was based on an old version of the libraries) Issue crosspoint-reader#439 --------- Co-authored-by: Dave Allie <[email protected]>
## Summary - Upgrade platform from espressif32 6.12.0 (Arduino Core 2.0.17) to pioarduino 55.03.37 (Arduino Core 3.3.7, ESP-IDF 5.5.2) - Add WebDAV Class 1 server (RFC 4918) - SD card can be mounted as a network drive - I also slightly fixed the SDK and also made a [pull request ](open-x4-epaper/community-sdk#21) First PR crosspoint-reader#1030 (was closed because the implementation was based on an old version of the libraries) Issue crosspoint-reader#439 --------- Co-authored-by: Dave Allie <[email protected]>
## Summary - Upgrade platform from espressif32 6.12.0 (Arduino Core 2.0.17) to pioarduino 55.03.37 (Arduino Core 3.3.7, ESP-IDF 5.5.2) - Add WebDAV Class 1 server (RFC 4918) - SD card can be mounted as a network drive - I also slightly fixed the SDK and also made a [pull request ](open-x4-epaper/community-sdk#21) First PR crosspoint-reader#1030 (was closed because the implementation was based on an old version of the libraries) Issue crosspoint-reader#439 --------- Co-authored-by: Dave Allie <[email protected]>
Summary
PR for issue #439
Implement WebDAV support so the device can be mounted as a network drive in Windows Explorer, macOS Finder, Linux Nautilus, and mobile file managers.
Methods: OPTIONS, PROPFIND, GET, HEAD, PUT, DELETE, MKCOL, MOVE, COPY, LOCK, UNLOCK. All handled via onNotFound() to avoid conflicts with existing routes.
Additional Context
The WebServer library is vendored into lib/WebServer/ because the framework version buffers the entire PUT body in RAM before calling the handler. On ESP32-C3 (~150KB free heap) this makes file uploads over ~100KB impossible. The vendored copy has a minimal patch in Parsing.cpp (~14 lines) that skips body buffering for PUT requests with no registered handler, allowing the WebDAV handler to stream the body from the TCP buffer in 4KB chunks.