object_storage_lib/
lib.rs1mod action;
2mod config;
3mod context;
4
5use async_trait::async_trait;
6pub use config::Config;
7pub use config::Oss;
8use context::Context;
9use headers::{ContentType, HeaderMapExt};
10use hyper;
11use hyper::body::Incoming;
12use hyper::{Request, Response, StatusCode};
13use object_storage_sdk as sdk;
14use sdk::storage::GetReq;
15use std::net::SocketAddr;
16use std::sync::Arc;
17use tihu::LightString;
18use tihu_native::http::Body;
19use tihu_native::http::BoxBody;
20use tihu_native::http::HttpHandler;
21use tihu_native::http::RequestData;
22
23pub struct GetMapping {
24 path_prefix: LightString, key_prefix: LightString, }
27
28pub struct UploadMapping {
29 path: LightString, key_prefix: LightString, }
32
33pub struct OssHandler {
34 context: Arc<Context>,
35 get_mapping: Vec<GetMapping>,
36 upload_mapping: Vec<UploadMapping>,
37 delete_path: Option<LightString>,
38 namespaces: Vec<LightString>,
39}
40
41impl OssHandler {
42 pub async fn try_init_from_config(
43 config: Config,
44 adjust_error_code: impl Fn(i32) -> i32 + Send + Sync + 'static,
45 ) -> Result<Self, anyhow::Error> {
46 let context = Context::try_init_from_config(config, adjust_error_code)?;
47 let context = Arc::new(context);
48 return Ok(Self {
49 context: context,
50 get_mapping: Vec::new(),
51 upload_mapping: Vec::new(),
52 delete_path: None,
53 namespaces: Vec::new(),
54 });
55 }
56
57 pub fn add_get_mapping(
58 &mut self,
59 path_prefix: LightString,
60 key_prefix: LightString,
61 ) -> &mut Self {
62 self.get_mapping.push(GetMapping {
63 path_prefix: path_prefix.clone(),
64 key_prefix: key_prefix,
65 });
66 self.namespaces.push(path_prefix);
67 return self;
68 }
69
70 pub fn add_upload_mapping(&mut self, path: LightString, key_prefix: LightString) -> &mut Self {
71 self.upload_mapping.push(UploadMapping {
72 path: path.clone(),
73 key_prefix: key_prefix,
74 });
75 self.namespaces.push(path);
76 return self;
77 }
78
79 pub fn set_delete_path(&mut self, delete_path: LightString) -> &mut Self {
80 self.delete_path.replace(delete_path);
81 return self;
82 }
83}
84
85#[async_trait]
86impl HttpHandler for OssHandler {
87 fn namespace(&self) -> &[LightString] {
88 return &self.namespaces;
89 }
90 async fn handle(
91 &self,
92 request: Request<Incoming>,
93 _remote_addr: SocketAddr,
94 _request_data: &mut RequestData,
95 prefix: Option<&str>,
96 ) -> Result<Response<BoxBody>, anyhow::Error> {
97 let prefix = prefix.unwrap_or("");
98 let (_, route) = request.uri().path().split_at(prefix.len());
99 for get_mapping in &self.get_mapping {
100 let path_prefix = &get_mapping.path_prefix;
101 let key_prefix = &get_mapping.key_prefix;
102 if route.starts_with(path_prefix.as_ref()) {
103 let key = format!(
104 "{}{}",
105 key_prefix,
106 String::from_utf8_lossy(&route.as_bytes()[path_prefix.len()..])
107 );
108 if key.is_empty() {
109 let mut response = text_response("file not found!");
110 *response.status_mut() = StatusCode::NOT_FOUND;
111 return Ok(response.map(From::from));
112 } else {
113 let response =
114 action::storage::get(self.context.clone(), GetReq { key: key }).await?;
115 return Ok(response.map(From::from));
116 }
117 }
118 }
119
120 for upload_mapping in &self.upload_mapping {
121 let path = &upload_mapping.path;
122 let key_prefix = &upload_mapping.key_prefix;
123 if route == path.as_ref() {
124 let hash = request.headers().get("X-Hash");
125 let hash = hash
126 .map(|hash| hash.to_str().map(|hash| hash.to_string()).ok())
127 .flatten();
128 if let Some(hash) = hash {
129 let response =
130 action::storage::put(self.context.clone(), request, key_prefix, hash)
131 .await?;
132 return Ok(response.map(From::from));
133 } else {
134 let response = Response::builder()
135 .status(StatusCode::BAD_REQUEST)
136 .body(Body::from("BAD REQUEST"))
137 .unwrap();
138 return Ok(response.map(From::from));
139 }
140 }
141 }
142
143 if let Some(delete_path) = self.delete_path.as_ref() {
144 if route == delete_path.as_ref() {
145 let hash = request.headers().get("X-Hash");
146 let hash = hash
147 .map(|hash| hash.to_str().map(|hash| hash.to_string()).ok())
148 .flatten();
149 if let Some(hash) = hash {
150 let response =
151 action::storage::delete(self.context.clone(), request, hash).await?;
152 return Ok(response.map(From::from));
153 } else {
154 let response = Response::builder()
155 .status(StatusCode::BAD_REQUEST)
156 .body(Body::from("BAD REQUEST"))
157 .unwrap();
158 return Ok(response.map(From::from));
159 }
160 }
161 }
162 let mut response = text_response("file not found!");
163 *response.status_mut() = StatusCode::NOT_FOUND;
164 return Ok(response.map(From::from));
165 }
166}
167
168fn text_response<T: Into<Body>>(body: T) -> Response<Body> {
169 let mut response = Response::new(body.into());
170 response
171 .headers_mut()
172 .typed_insert(ContentType::text_utf8());
173 return response;
174}