object_storage_lib/
lib.rs

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