// 锁操作:LOCK(申请 / 刷新)、UNLOCK、锁发现(PROPFIND lockdiscovery)。
// 请求体生成与 lockdiscovery 解析见 xmlutil/lockinfo.mbt;
// 锁令牌落到 `If` / `Lock-Token` 请求头见 client.mbt。
///|
/// 申请独占写锁(LOCK,RFC 4918 §9.10)。
///
/// - `path`:要锁定的资源路径;
/// - `owner`:持有者标识,写进请求体 `` 的文本,省略则不写该元素;
/// - `timeout`:`Timeout` 头原文,如 `"Second-300"` / `"Infinite"`,
/// 省略则用服务端默认值;
/// - `depth`:只支持 `Depth::Zero` 与 `Depth::Infinity`;`Depth::One`
/// 对 LOCK 非法,发请求前抛 `Api` 400。省略时不发 `Depth` 头;
/// - `scope`:锁作用域,省略时按独占写锁(`LockScope::Exclusive`);
/// 共享锁传 `LockScope::Shared`,可与别的共享锁共存;
/// - `signal`:取消信号。
///
/// 返回服务端确认的锁:令牌优先取 `Lock-Token` 响应头,其次取响应体的
/// 首个 `activelock`;两者都没有时抛 `Api`。响应体 XML 非法但带
/// `Lock-Token` 头时仍算成功,元数据退化为默认值。成功后这把锁会记进
/// 客户端本地注册表,供写请求被 423 拒绝时自动补令牌重试。
pub async fn WebdavClient::lock(
self : WebdavClient,
path~ : String,
owner? : String,
timeout? : String,
depth? : @types.Depth,
scope? : @types.LockScope,
signal? : @moonhttp.AbortSignal,
) -> @types.LockResult raise @types.WebdavError {
check_lock_depth(depth)
let body = @xmlutil.lockinfo_body(owner?, scope?) catch {
error => raise invalid_request_body(error, operation="LOCK")
}
let headers : Array[(String, String)] = [
("Content-Type", "application/xml; charset=utf-8"),
]
match timeout {
Some(value) => headers.push(("Timeout", value))
None => ()
}
match depth {
Some(value) => headers.push(("Depth", value.to_header()))
None => ()
}
let response = self.send(
operation="LOCK",
http_method=@config.Method::Other("LOCK"),
path~,
headers~,
body~,
signal?,
ok=[200, 201],
)
let result : @types.LockResult = {
status: response.status,
created: response.status == 201,
lock: lock_from_response(
response,
fallback="",
operation="LOCK",
default_scope=scope.unwrap_or(@types.LockScope::Exclusive),
),
}
// 记账:拿到锁之后,写请求被 423 拒绝才有令牌可自动补;
// 响应没回 depth 时用请求时声明的 Depth 兜底(祖先锁的覆盖范围靠它)
let declared_depth = match depth {
Some(value) => Some(value.to_header())
None => None
}
self.remember_lock(path, result.lock, declared_depth?)
result
}
///|
/// 刷新锁(LOCK 无请求体 + `If` 头,RFC 4918 §9.10.2)。
///
/// 刷新不换令牌:响应里没有令牌时沿用传入的 `token`。
/// `timeout` 与 `signal` 含义同 `lock`;`Depth` 不参与刷新,一律不发。
pub async fn WebdavClient::refresh_lock(
self : WebdavClient,
path~ : String,
token~ : String,
timeout? : String,
signal? : @moonhttp.AbortSignal,
) -> @types.LockResult raise @types.WebdavError {
let normalized = require_lock_token(token, operation="LOCK")
let headers : Array[(String, String)] = [
("If", lock_token_if_value(normalized)),
]
match timeout {
Some(value) => headers.push(("Timeout", value))
None => ()
}
let response = self.send(
operation="LOCK",
http_method=@config.Method::Other("LOCK"),
path~,
headers~,
signal?,
ok=[200],
)
let result : @types.LockResult = {
status: response.status,
created: false,
lock: lock_from_response(response, fallback=normalized, operation="LOCK"),
}
// 刷新可能换新令牌(服务端有权这么做),按最新令牌重新记账
self.remember_lock(path, result.lock)
result
}
///|
/// 释放锁(UNLOCK,RFC 4918 §9.11)。`Lock-Token` 头按 `<令牌>` 形态发送。
///
/// 200 / 204 表示已释放;409 通常表示令牌与资源上的锁不匹配。
/// 释放成功后这把锁从本地注册表里去掉,之后写请求不会再自动带它。
pub async fn WebdavClient::unlock(
self : WebdavClient,
path~ : String,
token~ : String,
signal? : @moonhttp.AbortSignal,
) -> Unit raise @types.WebdavError {
let normalized = require_lock_token(token, operation="UNLOCK")
ignore(
self.send(
operation="UNLOCK",
http_method=@config.Method::Other("UNLOCK"),
path~,
headers=[("Lock-Token", lock_token_uri(normalized))],
signal?,
ok=[200, 204],
),
)
self.forget_lock(path, normalized)
}
///|
/// 读资源上的活动锁(PROPFIND `lockdiscovery`,Depth 0)。
///
/// 返回活动锁列表;没有锁时是空数组。部分服务端(如 hacdias/webdav)
/// 不回 `lockdiscovery` 的值,此时同样得到空数组。
pub async fn WebdavClient::get_locks(
self : WebdavClient,
path~ : String,
signal? : @moonhttp.AbortSignal,
) -> Array[@types.ActiveLock] raise @types.WebdavError {
let body = @xmlutil.propfind_named_body(["lockdiscovery"], ns_uri="DAV:") catch {
error => raise invalid_request_body(error, operation="PROPFIND")
}
let response = self.send(
operation="PROPFIND",
http_method=@config.Method::Other("PROPFIND"),
path~,
headers=[("Depth", "0"), ("Content-Type", "application/xml; charset=utf-8")],
body~,
signal?,
ok=[207],
)
@xmlutil.parse_lockdiscovery(response.text()) catch {
error => raise @types.WebdavError::xml(error)
}
}
///|
/// LOCK 的 `Depth` 只允许 0 与 infinity(RFC 4918 §9.10.3)。
fn check_lock_depth(depth : @types.Depth?) -> Unit raise @types.WebdavError {
match depth {
Some(@types.Depth::One) =>
raise @types.WebdavError::api(
status=400,
operation="LOCK",
message="LOCK 的 Depth 只能是 0 或 infinity",
)
_ => ()
}
}
///|
/// 校验并归一化锁令牌:空令牌在发请求前就拦掉,避免发出 `If: ()` /
/// `Lock-Token: <>` 这种必然被服务端拒绝的请求。
fn require_lock_token(
token : String,
operation~ : String,
) -> String raise @types.WebdavError {
let normalized = @types.normalize_lock_token(token)
if normalized == "" {
raise @types.WebdavError::api(
status=400,
operation~,
message="锁令牌不能为空",
)
}
normalized
}