6.0 KiB
6.0 KiB
WebDAV LOCK拦截机制详解
核心拦截点
dav-server框架在HTTP请求处理流程中内置锁检查机制,无需手动拦截。
1. PUT(写文件)拦截点
文件位置: ~/.cargo/registry/src/*/dav-server-0.11.0/src/handle_put.rs:131-139
// if locked check if we hold that lock.
if let Some(ref locksystem) = self.ls {
let principal = self.principal.as_deref();
if let Err(_l) = locksystem
.check(&path, principal, false, false, &tokens)
.await
{
return Err(DavError::StatusClose(SC::LOCKED)); // 423 LOCKED
}
}
拦截流程:
客户端 PUT /webdav/file.txt
↓
DavHandler.handle_put()
↓
if_match_get_tokens() - 解析If头中的lock token
↓
locksystem.check() - 检查锁冲突
↓ (失败)
返回 423 LOCKED (客户端无权限)
↓ (成功)
LocalFs.write() - 执行实际写入
2. DELETE(删除)拦截点
文件位置: ~/.cargo/registry/src/*/dav-server-0.11.0/src/handle_delete.rs:123-132
// check locks. since we cancel the entire operation if there is
// a conflicting lock, we do not return a 207 multistatus, but
// just a simple status.
if let Some(ref locksystem) = self.ls {
let principal = self.principal.as_deref();
if let Err(_l) = locksystem
.check(&path, principal, false, true, &tokens) // deep=true
.await
{
return Err(DavError::Status(StatusCode::LOCKED));
}
}
关键参数: deep=true 表示检查整个路径树(包括子目录)
拦截流程:
客户端 DELETE /webdav/folder/
↓
DavHandler.handle_delete()
↓
locksystem.check(&path, principal, false, true, &tokens)
↓ (冲突)
返回 423 LOCKED
↓ (成功)
locksystem.delete(&path) - 删除所有锁记录
↓
LocalFs.remove_dir() - 执行删除
3. LOCK(加锁)拦截点
文件位置: ~/.cargo/registry/src/*/dav-server-0.11.0/src/handle_lock.rs
// 创建新锁
let lock = locksystem.lock(
&path,
principal,
owner,
timeout,
shared,
deep,
).await;
// 刷新锁
let lock = locksystem.refresh(&path, &tokens[0], timeout).await;
拦截流程:
客户端 LOCK /webdav/file.txt
↓
DavHandler.handle_lock()
↓
检查If头中的token
↓ (有token)
locksystem.refresh() - 刷新现有锁
↓ (无token)
locksystem.lock() - 创建新锁
↓
返回 200 OK + lock token
4. UNLOCK(解锁)拦截点
文件位置: ~/.cargo/registry/src/*/dav-server-0.11.0/src/handle_lock.rs
locksystem.unlock(&path, &lock_token).await
拦截流程:
客户端 UNLOCK /webdav/file.txt
Header: Lock-Token: <urn:uuid:xxx>
↓
DavHandler.handle_unlock()
↓
解析Lock-Token头
↓
locksystem.unlock(&path, &token)
↓ (成功)
返回 204 No Content
↓ (失败)
返回 403 Forbidden (token无效)
DavLockSystem.check() 参数详解
fn check(
path: &DavPath, // 文件路径
principal: Option<&str>, // 用户身份(来自Authorization头)
ignore_principal: bool, // true=忽略用户身份检查
deep: bool, // true=检查子路径锁
submitted_tokens: &[String], // If头中的lock tokens
) -> LsFuture<'_, Result<(), DavLock>>
返回值:
Ok(())- 有权限(锁匹配或无锁)Err(DavLock)- 冲突锁(返回423 LOCKED)
if_match_get_tokens() 作用
文件位置: ~/.cargo/registry/src/*/dav-server-0.11.0/src/conditional.rs
解析HTTP请求中的lock tokens:
If: <urn:uuid:xxx>- 单个tokenIf: (<urn:uuid:xxx>)- 标准格式If-Match: *- 需要任意锁If-None-Match: *- 需要无锁
MarkBase实现位置
LockManager: src/webdav/lock_manager.rs
impl DavLockSystem for LockManager {
fn check(
&'_ self,
path: &DavPath,
principal: Option<&str>,
ignore_principal: bool,
deep: bool,
submitted_tokens: &[String],
) -> LsFuture<'_, Result<(), DavLock>> {
// 1. 查询SQLite数据库中的锁
// 2. 清理过期锁(cleanup_expired_locks)
// 3. 比对submitted_tokens(匹配则允许)
// 4. 比对principal(同用户则允许)
// 5. 检查deep锁(子路径冲突)
// 6. 返回冲突锁(Err)或允许(Ok)
}
}
HTTP状态码对照
| 状态码 | 含义 | 触发条件 |
|---|---|---|
| 200 OK | LOCK成功 | lock()返回Ok |
| 204 No Content | UNLOCK/PUT成功 | unlock()或write()成功 |
| 403 Forbidden | UNLOCK失败 | unlock()返回Err(token无效) |
| 423 Locked | 操作被锁阻止 | check()返回Err |
| 409 Conflict | 目标不存在 | 文件不存在且无法创建 |
| 412 Precondition Failed | If条件不满足 | If-Match/If-None-Match失败 |
macOS Finder行为
典型请求序列:
1. PROPFIND /webdav/ - 获取文件列表
2. LOCK /webdav/file.txt - 加锁( exclusive)
Header: If: (<urn:uuid:xxx>)
3. PUT /webdav/file.txt - 写入(带If头)
Header: If: (<urn:uuid:xxx>)
4. UNLOCK /webdav/file.txt - 解锁
Header: Lock-Token: <urn:uuid:xxx>
锁有效期: macOS Finder默认60秒超时,需定期refresh
测试方法
# 手动测试锁机制
curl -X LOCK http://localhost:4919/webdav/test.txt \
-H "Content-Type: application/xml" \
-d '<D:lockinfo><D:locktype><D:write/></D:locktype><D:lockscope><D:exclusive/></D:lockscope></D:lockinfo>'
# 查看锁token
curl -X PROPFIND http://localhost:4919/webdav/test.txt \
-H "Depth: 0" \
-H "Content-Type: application/xml" \
-d '<D:propfind><D:prop><D:lockdiscovery/></D:prop></D:propfind>'
# 尝试写入(无锁token)
curl -X PUT http://localhost:4919/webdav/test.txt \
-d "test content"
# 预期:423 Locked
# 写入(带正确token)
curl -X PUT http://localhost:4919/webdav/test.txt \
-H "If: (<urn:uuid:YOUR_TOKEN>)" \
-d "test content"
# 预期:204 No Content
创建时间: 2026-05-17 03:30 版本: 1.0(拦截点详解版)