aliyun_oss_rust_sdk/
url.rs1use std::collections::HashMap;
2use reqwest::header::DATE;
3use crate::auth::AuthAPI;
4use crate::debug;
5use crate::oss::{API, OSS, OSSInfo};
6use crate::request::{RequestBuilder, RequestType};
7
8pub trait UrlApi: OSSInfo + API {
9 fn sign_download_url<S: AsRef<str>>(&self, key: S, build: &RequestBuilder) -> String;
28
29 fn sign_upload_url<S: AsRef<str>>(&self, key: S, build: &RequestBuilder) -> String;
49 fn sign_url<S: AsRef<str>>(&self, key: S, build: &RequestBuilder) -> String;
50}
51
52impl UrlApi for OSS {
53 fn sign_download_url<S: AsRef<str>>(&self, key: S, build: &RequestBuilder) -> String {
54 let sign = self.sign_url(key.as_ref(), build);
55 if let Some(cdn) = &build.cdn {
56 let download_url = format!("{}{}", cdn, sign);
57 debug!("download_url: {}", download_url);
58 download_url
59 } else {
60 let schema = if build.https {
61 "https://"
62 } else {
63 "http://"
64 };
65 let download_url = format!("{}{}.{}{}", schema, self.bucket(), self.endpoint(), sign);
66 debug!("download_url: {}", download_url);
67 download_url
68 }
69 }
70
71 fn sign_upload_url<S: AsRef<str>>(&self, key: S, build: &RequestBuilder) -> String {
72 let mut build = build.clone();
73 build.method = RequestType::Put;
74 let sign = self.sign_url(key.as_ref(), &build);
75 if let Some(cdn) = &build.cdn {
76 let download_url = format!("{}{}", cdn, sign);
77 debug!("upload_url: {}", download_url);
78 download_url
79 } else {
80 let schema = if build.https {
81 "https://"
82 } else {
83 "http://"
84 };
85 let download_url = format!("{}{}.{}{}", schema, self.bucket(), self.endpoint(), sign);
86 debug!("upload_url: {}", download_url);
87 download_url
88 }
89 }
90
91 fn sign_url<S: AsRef<str>>(&self, key: S, build: &RequestBuilder) -> String {
92 let mut build = build.clone();
93 let key = self.format_key(key);
94 let expiration = chrono::Local::now() + chrono::Duration::seconds(build.expire);
95 build.headers.insert(DATE.to_string(), expiration.timestamp().to_string());
96 let signature = self.sign(
97 key.as_str(),
98 &build,
99 );
100 debug!("signature: {}", signature);
101 let mut query_parameters = HashMap::new();
102 query_parameters.insert("Expires".to_string(), expiration.timestamp().to_string());
103 query_parameters.insert("OSSAccessKeyId".to_string(), self.key_id().to_string());
104 query_parameters.insert("Signature".to_string(), urlencoding::encode(&signature).into_owned());
105 build.parameters.iter().for_each(|(k, v)| {
106 query_parameters.insert(k.to_string(), urlencoding::encode(v).into_owned());
107 });
108
109 let mut params = query_parameters
110 .into_iter()
111 .filter(|(k, _)| k != "x-oss-ac-source-ip")
112 .collect::<Vec<_>>();
113
114 params.sort_by(|a, b| a.0.cmp(&b.0));
115
116 format!(
117 "{}?{}",
118 self.key_urlencode(key),
119 params.into_iter().map(|(k, v)| format!("{}={}", k, v)).collect::<Vec<String>>().join("&")
120 )
121 }
122}
123
124#[cfg(test)]
125mod tests {
126 use crate::oss::OSS;
127 use crate::request::RequestBuilder;
128 use crate::url::UrlApi;
129
130 #[inline]
131 fn init_log() {
132 tracing_subscriber::fmt()
133 .with_max_level(tracing::Level::DEBUG)
134 .with_line_number(true)
135 .init();
136 }
137
138 #[test]
139 fn sign_download_url_test() {
140 init_log();
141 let oss = OSS::from_env();
142 let build = RequestBuilder::new()
143 .with_cdn("https://cdn.ipadump.com")
144 .with_expire(60)
145 .oss_download_allow_ip("14.145.28.62", 32);
146 oss.sign_download_url(
148 "hello.txt",
149 &build,
151 );
152 }
153
154 #[test]
155 fn sign_upload_url_test() {
156 init_log();
157 let oss = OSS::from_env();
158 let build = RequestBuilder::new()
159 .with_cdn("http://cdn.ipadump.com")
160 .with_content_type("text/plain")
161 .with_expire(600);
162 oss.sign_upload_url(
163 "tmp.txt",
164 &build,
165 );
166 }
167}