any_object_storage/
oss.rs1use crate::auth::AuthAPI;
2use crate::request::RequestBuilder;
3use chrono::{DateTime, Utc};
4use reqwest::header::{HeaderMap, InvalidHeaderValue, AUTHORIZATION, CONTENT_TYPE, DATE};
5
6#[derive(Debug, Clone)]
8pub struct OSS {
9 key_id: String,
10 key_secret: String,
11 endpoint: String,
12 bucket: String,
13}
14
15unsafe impl Send for OSS {}
16
17unsafe impl Sync for OSS {}
18
19pub trait OSSInfo {
20 fn endpoint(&self) -> String;
21 fn bucket(&self) -> String;
22 fn key_id(&self) -> String;
23 fn key_secret(&self) -> String;
24}
25
26pub trait API {
27 fn key_urlencode<S: AsRef<str>>(&self, key: S) -> String {
28 key.as_ref()
29 .split("/")
30 .map(|x| urlencoding::encode(x))
31 .collect::<Vec<_>>()
32 .join("/")
33 }
34
35 fn format_key<S: AsRef<str>>(&self, key: S) -> String {
36 let key = key.as_ref();
37 if key.starts_with("/") {
38 key.to_string()
39 } else {
40 format!("/{}", key)
41 }
42 }
43
44 fn format_oss_resource_str<S: AsRef<str>>(&self, bucket: S, key: S) -> String;
45}
46
47impl OSSInfo for OSS {
48 fn endpoint(&self) -> String {
49 self.endpoint.clone()
50 }
51 fn bucket(&self) -> String {
52 self.bucket.clone()
53 }
54
55 fn key_id(&self) -> String {
56 self.key_id.clone()
57 }
58
59 fn key_secret(&self) -> String {
60 self.key_secret.clone()
61 }
62}
63
64impl API for OSS {
65 fn format_oss_resource_str<S: AsRef<str>>(&self, bucket: S, key: S) -> String {
66 let bucket = bucket.as_ref();
67 if bucket == "" {
68 format!("/{}", bucket)
69 } else {
70 format!("/{}{}", bucket, key.as_ref())
71 }
72 }
73}
74
75impl<'a> OSS {
76 pub fn from_env() -> Self {
77 let key_id = std::env::var("OSS_KEY_ID").expect("OSS_KEY_ID not found");
78 let key_secret = std::env::var("OSS_KEY_SECRET").expect("OSS_KEY_SECRET not found");
79 let endpoint = std::env::var("OSS_ENDPOINT").expect("OSS_ENDPOINT not found");
80 let bucket = std::env::var("OSS_BUCKET").expect("OSS_BUCKET not found");
81 OSS::new(key_id, key_secret, endpoint, bucket)
82 }
83
84 #[cfg(feature = "debug-print")]
85 pub fn open_debug(&self) {
86 std::env::set_var("RUST_LOG", "oss=debug");
87 tracing_subscriber::fmt()
88 .with_max_level(tracing::Level::DEBUG)
89 .with_line_number(true)
90 .init();
91 }
92 #[cfg(not(feature = "debug-print"))]
93 pub fn open_debug(&self) {}
94 pub fn new<S: Into<String>>(key_id: S, key_secret: S, endpoint: S, bucket: S) -> Self {
95 OSS {
96 key_id: key_id.into(),
97 key_secret: key_secret.into(),
98 endpoint: endpoint.into(),
99 bucket: bucket.into(),
100 }
101 }
102
103 pub fn format_host<S: AsRef<str>>(&self, bucket: S, key: S, build: &RequestBuilder) -> String {
104 let key = if key.as_ref().starts_with("/") {
105 key.as_ref().to_string()
106 } else {
107 format!("/{}", key.as_ref())
108 };
109 if let Some(cdn) = &build.cdn {
110 format!("{}{}", cdn, key,)
111 } else {
112 if self.endpoint().starts_with("https") {
113 format!(
114 "https://{}.{}{}",
115 bucket.as_ref(),
116 self.endpoint().replacen("https://", "", 1),
117 key,
118 )
119 } else {
120 format!(
121 "http://{}.{}{}",
122 bucket.as_ref(),
123 self.endpoint().replacen("http://", "", 1),
124 key,
125 )
126 }
127 }
128 }
129
130 pub fn build_request<S: AsRef<str>>(
131 &self,
132 key: S,
133 build: RequestBuilder,
134 ) -> Result<(String, HeaderMap), InvalidHeaderValue> {
135 let mut build = build.clone();
136 let host = self.format_host(self.bucket(), key.as_ref().to_string(), &build);
137 let mut header = HeaderMap::new();
138 let date = self.date();
139 header.insert(DATE, date.parse()?);
140 build.headers.insert(DATE.to_string(), date);
141 let key = key.as_ref();
142 let authorization = self.oss_sign(key, &build);
143 if let Some(content_type) = build.content_type {
144 header.insert(CONTENT_TYPE, content_type.parse()?);
145 }
146 header.insert(AUTHORIZATION, authorization.parse()?);
147 Ok((host, header))
148 }
149 pub fn date(&self) -> String {
150 let now: DateTime<Utc> = Utc::now();
151 now.format("%a, %d %b %Y %T GMT").to_string()
152 }
153}
154
155#[cfg(test)]
156mod tests {
157 use crate::error::OssError;
158 use std::io::Read;
159
160 fn open_file(file_name: &str) -> Result<String, OssError> {
161 let mut file = std::fs::File::open(file_name)?;
162 let mut contents = String::new();
163 file.read_to_string(&mut contents)?;
164 Ok(contents)
165 }
166
167 #[test]
168 fn test_read_file() {
169 open_file("a").unwrap();
170 }
171}