diff --git a/src/interfaces/nextcloud/webdav_handler.rs b/src/interfaces/nextcloud/webdav_handler.rs index 204c9bce..135872cb 100644 --- a/src/interfaces/nextcloud/webdav_handler.rs +++ b/src/interfaces/nextcloud/webdav_handler.rs @@ -381,10 +381,18 @@ async fn handle_head( // ETag comes from `FileDto::etag` — see the same comment block on // the GET handler. HEAD and GET must agree byte-for-byte; pulling // both from the same DTO field guarantees that. + // + // We deliberately do NOT set `Content-Length: file.size` here even + // though RFC 7231 §4.3.2 says HEAD SHOULD return the same headers + // GET would. Our body is `Body::empty()`, so declaring a non-zero + // Content-Length tells the client "20 bytes are coming" — and on a + // keep-alive connection the client waits forever for them. Hyper + // derives `Content-Length: 0` from the empty body, which is honest + // about what's actually on the wire. Clients that need the file + // size use PROPFIND (which is what NC and Sabre clients do). Ok(Response::builder() .status(StatusCode::OK) .header(header::CONTENT_TYPE, file.mime_type.as_ref()) - .header(header::CONTENT_LENGTH, file.size) .header(header::ETAG, format!("\"{}\"", file.etag)) .header(header::LAST_MODIFIED, modified_at.to_rfc2822()) .body(Body::empty()) diff --git a/tests/webdav/lib/dav_helpers.sh b/tests/webdav/lib/dav_helpers.sh index f5911269..d6840b4e 100755 --- a/tests/webdav/lib/dav_helpers.sh +++ b/tests/webdav/lib/dav_helpers.sh @@ -84,6 +84,14 @@ dav_curl() { curl -s -H "Authorization: Bearer $TOKEN" "$@" } +# Return the HTTP status code of a `PROPFIND Depth: 0` against the +# given NC URL. Used by existence assertions ("did this collection +# silently get auto-created?") where the only thing the caller cares +# about is the status (404 → absent, 207 → present). +nc_status_propfind_depth0() { + nc_curl -o /dev/null -w "%{http_code}" -X PROPFIND -H "Depth: 0" "$1" +} + # Count `` (or ``) children in a multistatus # body. Case-insensitive on the namespace prefix because OxiCloud's # two DAV surfaces use different cases: the NC handler emits