blob: 3206a834f5e0c48d90b755bccfafb4b5afc0a02f [file] [log] [blame]
use std::env;
use std::str::FromStr;
use std::time::Duration;
use anyhow::Result;
use http::header::AUTHORIZATION;
use http::header::CONTENT_LENGTH;
use http::Request;
use http::StatusCode;
use log::debug;
use log::warn;
use percent_encoding::utf8_percent_encode;
use percent_encoding::NON_ALPHANUMERIC;
use reqsign::TencentCosConfig;
use reqsign::TencentCosCredentialLoader;
use reqsign::TencentCosSigner;
use reqwest::Client;
fn init_signer() -> Option<(TencentCosCredentialLoader, TencentCosSigner)> {
let _ = env_logger::builder().is_test(true).try_init();
dotenv::from_filename(".env").ok();
if env::var("REQSIGN_TENCENT_COS_TEST").is_err()
|| env::var("REQSIGN_TENCENT_COS_TEST").unwrap() != "on"
{
return None;
}
let config = TencentCosConfig {
secret_id: Some(
env::var("REQSIGN_TENCENT_COS_ACCESS_KEY")
.expect("env REQSIGN_TENCENT_COS_ACCESS_KEY must set"),
),
secret_key: Some(
env::var("REQSIGN_TENCENT_COS_SECRET_KEY")
.expect("env REQSIGN_TENCENT_COS_SECRET_KEY must set"),
),
..Default::default()
};
let loader = TencentCosCredentialLoader::new(reqwest::Client::new(), config);
Some((loader, TencentCosSigner::new()))
}
#[tokio::test]
async fn test_get_object() -> Result<()> {
let signer = init_signer();
if signer.is_none() {
warn!("REQSIGN_TENCENT_COS_TEST is not set, skipped");
return Ok(());
}
let (loader, signer) = signer.unwrap();
let cred = loader.load().await?.unwrap();
let url = &env::var("REQSIGN_TENCENT_COS_URL").expect("env REQSIGN_TENCENT_COS_URL must set");
let mut req = Request::new("");
*req.method_mut() = http::Method::GET;
*req.uri_mut() = http::Uri::from_str(&format!("{}/{}", url, "not_exist_file"))?;
signer
.sign(&mut req, &cred)
.expect("sign request must success");
debug!("signed request: {:?}", req.headers().get(AUTHORIZATION));
let client = Client::new();
let resp = client
.execute(req.try_into()?)
.await
.expect("request must succeed");
let status = resp.status();
debug!("got response: {:?}", resp);
debug!("got response content: {}", resp.text().await?);
assert_eq!(StatusCode::NOT_FOUND, status);
Ok(())
}
#[tokio::test]
async fn test_delete_objects() -> Result<()> {
let signer = init_signer();
if signer.is_none() {
warn!("REQSIGN_TENCENT_COS_TEST is not set, skipped");
return Ok(());
}
let (loader, signer) = signer.unwrap();
let cred = loader.load().await?.unwrap();
let url = &env::var("REQSIGN_TENCENT_COS_URL").expect("env REQSIGN_TENCENT_COS_URL must set");
let content = r#"<Delete>
<Object>
<Key>sample1.txt</Key>
</Object>
<Object>
<Key>sample2.txt</Key>
</Object>
</Delete>"#;
let mut req = Request::new(content);
*req.method_mut() = http::Method::POST;
*req.uri_mut() = http::Uri::from_str(&format!("{}/?delete", url))?;
req.headers_mut()
.insert(CONTENT_LENGTH, content.len().to_string().parse().unwrap());
req.headers_mut()
.insert("CONTENT-MD5", "WOctCY1SS662e7ziElh4cw==".parse().unwrap());
signer
.sign(&mut req, &cred)
.expect("sign request must success");
debug!("signed request: {:?}", req);
let client = Client::new();
let resp = client
.execute(req.try_into()?)
.await
.expect("request must succeed");
let status = resp.status();
debug!("got response: {:?}", resp);
debug!("got response content: {}", resp.text().await?);
assert_eq!(StatusCode::OK, status);
Ok(())
}
#[tokio::test]
async fn test_get_object_with_query_sign() -> Result<()> {
let signer = init_signer();
if signer.is_none() {
warn!("REQSIGN_TENCENT_COS_TEST is not set, skipped");
return Ok(());
}
let (loader, signer) = signer.unwrap();
let cred = loader.load().await?.unwrap();
let url = &env::var("REQSIGN_TENCENT_COS_URL").expect("env REQSIGN_TENCENT_COS_URL must set");
let mut req = Request::new("");
*req.method_mut() = http::Method::GET;
*req.uri_mut() = http::Uri::from_str(&format!("{}/{}", url, "not_exist_file"))?;
signer
.sign_query(&mut req, Duration::from_secs(3600), &cred)
.expect("sign request must success");
debug!("signed request: {:?}", req);
let client = Client::new();
let resp = client
.execute(req.try_into()?)
.await
.expect("request must succeed");
let status = resp.status();
debug!("got response: {:?}", resp);
debug!("got response content: {}", resp.text().await?);
assert_eq!(StatusCode::NOT_FOUND, status);
Ok(())
}
#[tokio::test]
async fn test_head_object_with_special_characters() -> Result<()> {
let signer = init_signer();
if signer.is_none() {
warn!("REQSIGN_TENCENT_COS_TEST is not set, skipped");
return Ok(());
}
let (loader, signer) = signer.unwrap();
let cred = loader.load().await?.unwrap();
let url = &env::var("REQSIGN_TENCENT_COS_URL").expect("env REQSIGN_TENCENT_COS_URL must set");
let mut req = Request::new("");
*req.method_mut() = http::Method::HEAD;
*req.uri_mut() = http::Uri::from_str(&format!(
"{}/{}",
url,
utf8_percent_encode("not-exist-!@#$%^&*()_+-=;:'><,/?.txt", NON_ALPHANUMERIC)
))?;
signer
.sign(&mut req, &cred)
.expect("sign request must success");
debug!("signed request: {:?}", req);
let client = Client::new();
let resp = client
.execute(req.try_into()?)
.await
.expect("request must success");
debug!("got response: {:?}", resp);
assert_eq!(StatusCode::NOT_FOUND, resp.status());
Ok(())
}
#[tokio::test]
async fn test_put_object_with_special_characters() -> Result<()> {
let signer = init_signer();
if signer.is_none() {
warn!("REQSIGN_TENCENT_COS_TEST is not set, skipped");
return Ok(());
}
let (loader, signer) = signer.unwrap();
let cred = loader.load().await?.unwrap();
let url = &env::var("REQSIGN_TENCENT_COS_URL").expect("env REQSIGN_TENCENT_COS_URL must set");
let mut req = Request::new("");
*req.method_mut() = http::Method::PUT;
*req.uri_mut() = http::Uri::from_str(&format!(
"{}/{}",
url,
utf8_percent_encode("put-!@#$%^&*()_+-=;:'><,/?.txt", NON_ALPHANUMERIC)
))?;
req.headers_mut()
.insert(CONTENT_LENGTH, "0".parse().unwrap());
signer
.sign(&mut req, &cred)
.expect("sign request must success");
debug!("signed request: {:?}", req);
let client = Client::new();
let resp = client
.execute(req.try_into()?)
.await
.expect("request must success");
let status = resp.status();
debug!("got response: {:?}", resp);
debug!("got response content: {:?}", resp.text().await?);
assert_eq!(StatusCode::OK, status);
Ok(())
}
#[tokio::test]
async fn test_list_bucket() -> Result<()> {
let signer = init_signer();
if signer.is_none() {
warn!("REQSIGN_TENCENT_COS_TEST is not set, skipped");
return Ok(());
}
let (loader, signer) = signer.unwrap();
let cred = loader.load().await?.unwrap();
let url = &env::var("REQSIGN_TENCENT_COS_URL").expect("env REQSIGN_TENCENT_COS_URL must set");
let mut req = Request::new("");
*req.method_mut() = http::Method::GET;
*req.uri_mut() =
http::Uri::from_str(&format!("{url}?list-type=2&delimiter=/&encoding-type=url"))?;
signer
.sign(&mut req, &cred)
.expect("sign request must success");
debug!("signed request: {:?}", req);
let client = Client::new();
let resp = client
.execute(req.try_into()?)
.await
.expect("request must success");
let status = resp.status();
debug!("got response: {:?}", resp);
debug!("got response content: {}", resp.text().await?);
assert_eq!(StatusCode::OK, status);
Ok(())
}