I shipped WebDAVClient 2.7.0 to NuGet today. Most of the the changes fill in WebDAV protocol surface that the library never had, plus a few spec-compliance bug fixes that mattered the moment you pointed the client at a strict server.
Here’s the rundown.
WebDAV protocol coverage
The big theme this release. The library could already do the basic file/folder dance (PROPFIND/GET/PUT/MKCOL/MOVE/COPY/DELETE) but it stopped there – anything past §9.4 of RFC 4918 was a “use CustomHeaders and hope” situation. Version 2.7 closes most of those gaps.
- LOCK / UNLOCK (RFC 4918 §9.10–9.11). New
LockFile/LockFolder/UnlockFile/UnlockFolder/RefreshLockonIClient, returning a strongly-typedLockInfo(token, owner, type / scope, depth, timeout, lock-root). Tokens are normalized so callers can pass them either bare (opaquelocktoken:abc) or angle-bracket-wrapped — the form that comes back in theLock-Tokenheader just works. - PROPPATCH (RFC 4918 §9.2). New
SetProperty(path, name, namespace, value)andRemoveProperty(path, name, namespace)for managing custom (dead) properties. Property names are validated as XMLNCNames, the request body is built withXmlWriterso values are safely escaped, and theDAV:namespace is rejected client-side (those properties are protected). Per-property failures inside the207 Multi-Statusresponse surface asWebDAVException(orWebDAVConflictExceptionfor409). - OPTIONS (RFC 4918 §9.1, RFC 9110 §9.3.7). New
GetServerOptions(path, ct)returns a strongly-typedServerOptionsexposing the parsedDAVcompliance classes (IsClass1/IsClass2/IsClass3, plusHasComplianceTokenfor non-numeric extensions likeaccess-controlorcalendar-access) and theAllowmethods (SupportsMethod). Use it as a preflight to confirm a server is actually WebDAV before issuing PROPFIND/LOCK, or to discover which methods the endpoint supports. - Granular PROPFIND (RFC 4918 §9.1). The historical
<allprop/>body is unchanged, but new overloads onList/GetFolder/GetFileaccept anIEnumerable<PropertyName>(sends<prop>), and new sibling methodsListPropertyNames/GetFolderPropertyNames/GetFilePropertyNamessend<propname/>. ReturnedItemexposes three nullable collections:FoundProperties(status 200),NotFoundProperties(404 / 401 / 403 / 424) andAvailablePropertyNames(propname). Big bandwidth win on directories with hundreds of items. Iflock-token submission on PUT / DELETE / MOVE / COPY (RFC 4918 §10.4).Upload/UploadPartial/DeleteFile/DeleteFoldergot an optionallockToken;MoveFile/MoveFoldergotsourceLockTokenanddestinationLockToken;CopyFile/CopyFoldergotdestinationLockToken(COPY doesn’t modify the source per §7.5.1, so no source token). Without these, a server with locks rejects modifications with423 Locked— which the client also surfaces asWebDAVExceptionnow.
Spec-compliance bug fixes
Overwriteheader on COPY / MOVE (RFC 4918 §9.8.3 / §9.9.3). The client now always sendsOverwrite: Tso server behaviour is deterministic.MoveFile/MoveFolder/CopyFile/CopyFoldergained an optionaloverwriteparameter — passfalseto sendOverwrite: Fand protect an existing destination (the server returns412 Precondition Failedinstead of silently clobbering).204 No Contentis also accepted as success now (returned when the destination already existed).Depth: infinityon DELETE (RFC 4918 §9.6.1). Strict servers reject collection deletes without it. The header was just missing.
Authentication
- Bearer / OAuth 2.0. The constructor only accepted
ICredentials— fine for Basic / Windows / Digest, useless for Nextcloud, ownCloud, Box, SharePoint Online, or any Azure AD-fronted endpoint. There are now two newClientoverloads:Client(string bearerToken, ...)— static token.Client(Func<CancellationToken, Task<string>> bearerTokenProvider, ...)— async refreshable provider for rotation flows (Azure IdentityTokenCredential, MSAL, IdentityModel, custom OAuth 2.0 token stores).
Both wire a new publicWebDAVClient.Authentication.BearerTokenAuthenticationHandler(aDelegatingHandler) ahead of the existingHttpClientHandler, so the bearer header is injected on every outbound request — including the upload pipeline. Anull/ empty token from the provider intentionally omits the header rather than throwing, so transient “no token yet” states surface as a server401instead of a client exception.
Internal
- Extracted the static helpers in
Client.csinto focused helper classes underWebDAVClient.Helpers:LockTokenHeaderHelper(token normalisation +If/Lock-Tokenheader logic),PropPatchRequestBuilder(property-name / namespace validation + body emission), andXmlEscape. No public-API change — but the responsibilities are now clearer at each call site, and each helper is directly unit-testable.
Tests
The unit-test project that landed in 2.6 paid off this release: every one of the items above shipped with regression coverage — 237 tests on net8.0 + net10.0 by the end (was 36 at the start of the branch, since most of the deep behaviour wasn’t testable before the helpers extraction).
NuGet: WebDAVClient 2.7.0 · Source: github.com/saguiitay/WebDAVClient
Should be a drop-in upgrade from previous versions – every new API is additive and the only signature changes on existing methods are new optional parameters with backward-compatible defaults (lockToken = null, overwrite = true).
The one thing worth double-checking: COPY / MOVE now actually send Overwrite: T instead of relying on each server’s house default, and DELETE now sends Depth: infinity. Both are what the spec says should have been happening all along – but if your code was relying on a specific server’s default conflict behavior, give it a quick smoke test.