# 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()返回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

## 测试方法

```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（拦截点详解版）