use super::http;
use super::http::header::CONTENT_TYPE;
use super::DEFAULT_CONTENT_TYPE;
use base64::{engine::general_purpose, Engine as _};
#[derive(Debug, Clone)]
pub(super) struct SingerV1<'a> {
pub(super) access_key_id: &'a str,
pub(super) access_key_secret: &'a str,
#[allow(unused)]
pub(super) sts_token: Option<&'a str>,
pub(super) headers: &'a http::HeaderMap,
pub(super) method: &'a http::Method,
pub(super) date: &'a String,
pub(super) resourse: Option<&'a str>,
}
impl<'a> SingerV1<'a> {
pub(super) fn complute(&self) -> String {
format!("OSS {}:{}", self.access_key_id, self.signature())
}
fn headers_str(&self) -> String {
let mut oss_key_name: Vec<&str> = self
.headers
.keys()
.filter_map(|k| {
let key = k.as_str();
if key.starts_with("x-oss") {
Some(key)
} else {
None
}
})
.collect();
oss_key_name.sort();
let mut value: Vec<String> = Vec::new();
for key_name in oss_key_name {
if let Some(key_value) = self.headers.get(key_name).and_then(|v| v.to_str().ok()) {
value.push(format!("{}:{}\n", key_name, key_value));
}
}
value.join("")
}
fn signature(&self) -> String {
let header_str = self.headers_str();
let content_type = match self.headers.get(CONTENT_TYPE) {
Some(content_type) => content_type.to_str().unwrap(),
None => DEFAULT_CONTENT_TYPE,
};
let content_md5 = match self.headers.get("content-md5") {
Some(content_type) => content_type.to_str().unwrap().to_string(),
None => "".to_string(),
};
let resource = urlencoding::decode(self.resourse.unwrap_or("/"))
.unwrap()
.replace("+", " ");
let value = format!(
"{VERB}\n{ContentMD5}\n{ContentType}\n{Date}\n{Header}{Resource}",
VERB = self.method,
ContentMD5 = content_md5,
ContentType = content_type,
Date = self.date,
Header = header_str,
Resource = resource
);
let key = self.access_key_secret.as_bytes();
let message = value.as_bytes();
let value = hmac_sha1::hmac_sha1(key, message);
let encoded = general_purpose::STANDARD.encode(value.as_slice());
encoded
}
}