Skip to content

fix(rpcclient): serve WebDAV Range requests to prevent large-file corruption - #449

Merged
doronz88 merged 2 commits into
masterfrom
fix/webdav-range-requests
Aug 23, 2026
Merged

doronz88 merged 2 commits into
masterfrom
fix/webdav-range-requests

Conversation

@doronz88

Copy link
Copy Markdown
Owner

Problem

Copying large files over a mounted WebDAV volume produced corrupted files.

Root cause

RpcFsProvider (the bridge that serves a remote target's filesystem over WebDAV) declared content_range=False and ignored the HTTP Range header, answering every GET with 200 OK and the entire file body.

macOS Finder / webdavfs reads large files as a series of byte-range requests (Range: bytes=X-Y). When the server returns the whole file with status 200 instead of 206 + the requested slice, the client writes the full body at offset X — corrupting the result.

Verified against a live rpcserver_ios (vphone): a Range: bytes=1048576-1114111 request returned status 200 with all 5 MiB of the file (body == full file), instead of the 64 KiB requested.

Clean single-request transfers (e.g. httpx.get) were always byte-correct, which is why the bug only showed up with real range-using clients on large files.

Fix

Honor Range requests, mirroring asgi_webdav's own FileSystemProvider:

  • feature = DAVProviderFeature(content_range=True, ...) so Accept-Ranges: bytes is advertised.
  • _do_get computes the response content-range and returns 206 Partial Content (with 416 handling for a failed If-Range), falling back to 200 full-body when there's no range.
  • _body_generator seeks to the range start and streams only the requested bytes.
  • File.seek widened to c_int64 so range offsets past 2 GiB are not truncated.

Verification

  • New regression test test_get_honors_range_request: asserts Accept-Ranges, a 206 with the exact requested bytes and correct Content-Range, and that reassembling sequential ranges reproduces the file byte-for-byte.
  • Full tests/test_webdav.py suite passes (23 passed) against a live rpcserver_ios.
  • Manually confirmed against the vphone: range reassembly, suffix ranges (bytes=-N), and open-ended ranges (bytes=N-) all return 206 with correct bytes; full GET still returns 200 and matches.

https://claude.ai/code/session_01X7BxNXzz1dJmtZngoj5kab

…ruption

The WebDAV provider declared content_range=False and ignored the HTTP Range
header, answering every GET with 200 and the entire file body. macOS Finder /
webdavfs reads large files as a series of byte ranges; receiving the whole file
in response to `Range: bytes=X-Y` makes the client write the full body at offset
X, producing a corrupted file.

Honor Range requests like asgi_webdav's own FileSystemProvider: advertise
Accept-Ranges, return 206 Partial Content with a body generator that seeks to
the requested offset and streams only the requested bytes (with 416 handling for
a failed If-Range). Also widen File.seek to c_int64 so range offsets past 2 GiB
are not truncated.

Claude-Session: https://claude.ai/code/session_01X7BxNXzz1dJmtZngoj5kab
construct-typing 0.8.0 no longer re-exports the ParsedType TypeVar from the
top-level `construct`; it lives in construct-stubs/core.pyi. `from construct
import ParsedType` therefore fails pyright ("unknown import symbol"), turning
the type-check CI job red across all platforms. Import it from construct.core
instead. Type-checking only (guarded by TYPE_CHECKING); runtime is unaffected.

Claude-Session: https://claude.ai/code/session_01X7BxNXzz1dJmtZngoj5kab
@doronz88
doronz88 merged commit 27e573f into master Aug 23, 2026
24 checks passed
@doronz88
doronz88 deleted the fix/webdav-range-requests branch August 23, 2026 06:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant