Files
markbase/docs/WEBDAV_LOCK_INTERCEPT.md
2026-05-18 17:02:30 +08:00

238 lines
6.0 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# WebDAV LOCK拦截机制详解
## 核心拦截点
dav-server框架在HTTP请求处理流程中内置锁检查机制无需手动拦截。
### 1. PUT写文件拦截点
**文件位置**: `~/.cargo/registry/src/*/dav-server-0.11.0/src/handle_put.rs:131-139`
```rust
// 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`
```rust
// 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`
```rust
// 创建新锁
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`
```rust
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() 参数详解
```rust
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>` - 单个token
- `If: (<urn:uuid:xxx>)` - 标准格式
- `If-Match: *` - 需要任意锁
- `If-None-Match: *` - 需要无锁
## MarkBase实现位置
**LockManager**: `src/webdav/lock_manager.rs`
```rust
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()返回Errtoken无效|
| 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
## 测试方法
```bash
# 手动测试锁机制
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(拦截点详解版)