Tencent Cloud COS native XML-API client for MoonBit
Dependencies
fn refused(f : () -> Unit raise @coserr.CosError) -> String {
try {
f()
"no-error"
} catch {
@coserr.CosError::BadParam(_) => "BadParam"
_ => "other"
}
}
test "签名、URL 与边界都在本地算得出,这一块不打网络" {
let id = "AKIDtestid00000000000000000000"
let secret = "testSecretKey0123456789abcdef"
let bucket = "examplebucket-1250000000"
let host = "examplebucket-1250000000.cos.ap-guangzhou.myqcloud.com"
let credentials = @cosauth.Credentials::new(id, secret)
let config = @cosconfig.Config::new("ap-guangzhou", credentials)
// 桶名是长命名 `<Name>-<APPID>`:appid 就在桶名里,本 SDK 没有另一个 appid 配置项。
// 形状不对本地就响亮拒绝,不拿它换服务端一句照样应答的 NoSuchBucket。
assert_eq(config.host(bucket), host)
assert_eq(refused(() => @cosconfig.validate_bucket("no-appid-here")), "BadParam")
// 能力位:对象预设 ACL 的字面量。`default` 是对象的默认值(继承桶权限)
assert_eq(@sdk.CannedAcl::Default.text(), "default")
assert_eq(@sdk.CannedAcl::PublicRead.text(), "public-read")
// 签的那一串与发的那一串本来就该不同形:签名里是解码态 path,请求行里逐段编码、斜杠保留
let tricky = "moonfs/a b+c.txt"
assert_eq(@cosconfig.path_of(tricky), "/moonfs/a b+c.txt")
assert_eq(@cosconfig.encode_key(tricky), "moonfs/a%20b%2Bc.txt")
// Authorization 的七个字段:算法恒 `sha1`(`sha256` 那一档实测被端点 403 拒收),
// 空值参数写成 `acl=`(带等号),键名先编码再整体小写
let signed = @cosauth.authorization(
credentials,
@cosauth.RequestToSign::{
verb: "GET",
path: "/note.txt",
query: [("acl", "")],
headers: [("host", host)],
},
@cosauth.window_at(1760000000000L, 600L),
)
assert_true(signed.has_prefix("q-sign-algorithm=sha1&q-ak=" + id))
assert_true(signed.contains("&q-header-list=host&q-url-param-list=acl"))
assert_true(signed.find(secret) is None)
// `sign_host=false` 那一档(现在只在 auth 层):Host 退出签名,两个 list 为空仍要写出来
let no_host = @cosauth.authorization(
credentials,
@cosauth.RequestToSign::{
verb: "GET",
path: "/note.txt",
query: [],
headers: [("host", host)],
},
@cosauth.window_at(1760000000000L, 600L),
sign_host=false,
)
assert_true(no_host.contains("&q-header-list=&q-url-param-list=&q-signature="))
// 预签名=把同一批字段整体搬进 query:时间窗写在 `q-sign-time` 的 `start;end` 里,
// 没有 `X-Amz-Expires` 那样的独立参数。URL 由虚主机派生,签名里也带着那个 Host
let client = @sdk.Client::new(config, @transport.AsyncHttpTransport::new())
let url = client.presigned_get_url(bucket, "moonfs/note.txt", expires_sec=600L)
assert_true(url.has_prefix("https://" + host + "/"))
assert_true(url.contains("/moonfs/note.txt?q-sign-algorithm=sha1&"))
assert_true(url.contains("q-sign-time="))
// 窗口不成立就在本地拒掉,不必发出去换一句 403;上限不设(文档没数字、端点那档未测)
let gone = @cosauth.window_at(1760000000000L, -120L)
assert_eq(
refused(() => {
let _ = @cosauth.presign_query(
credentials,
@cosauth.RequestToSign::{
verb: "GET",
path: "/note.txt",
query: [],
headers: [],
},
gone,
)
}),
"BadParam",
)
// 604801 秒照签:起始时刻往前回拨 60 秒、结束时刻是 `now + expires`,本仓不自创一个顶
assert_eq(@cosauth.window_at(1760000000000L, 604801L).text(), "1759999940;1760604801")
}moon add mldong/moon-cosInstall
Download zipTencent Cloud COS native XML-API client for MoonBit
Dependencies