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::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, //请求路径前缀
25    key_prefix: LightString,  //资源对象前缀
26}
27
28pub struct UploadMapping {
29    path: LightString,       //上传请求路径
30    key_prefix: LightString, //资源对象前缀
31}
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}