1use axum::response::IntoResponse;
2use futures::StreamExt;
3use tokio::io::AsyncWriteExt;
4
5use crate::service::s3::VRequest;
6
7pub struct Request {
8 request: axum::http::Request<axum::body::Body>,
9 query: Option<std::collections::HashMap<String, String>>,
10}
11impl From<Request> for axum::extract::Request {
12 fn from(val: Request) -> Self {
13 val.request
14 }
15}
16impl From<axum::extract::Request> for Request {
17 fn from(value: axum::extract::Request) -> Self {
18 let query = value.uri().query().map(|query| {
19 query
20 .split("&")
21 .map(|item| {
22 item.find("=")
23 .map_or((item.to_string(), "".to_string()), |pos| {
24 (item[..pos].to_string(), item[pos + 1..].to_string())
25 })
26 })
27 .collect::<std::collections::HashMap<String, String>>()
28 });
29 Self {
30 request: value,
31 query,
32 }
33 }
34}
35impl crate::authorization::v4::VHeader for Request {
36 fn get_header(&self, key: &str) -> Option<String> {
37 self.request
38 .headers()
39 .get(key)
40 .and_then(|value| value.to_str().ok().map(|value| value.to_string()))
41 }
42
43 fn set_header(&mut self, key: &str, val: &str) {
44 let key: axum::http::HeaderName = key.to_string().parse().unwrap();
45 self.request.headers_mut().insert(key, val.parse().unwrap());
46 }
47
48 fn delete_header(&mut self, key: &str) {
49 self.request.headers_mut().remove(key);
50 }
51
52 fn rng_header(&self, mut cb: impl FnMut(&str, &str) -> bool) {
53 for (k, v) in self.request.headers().iter() {
54 if !cb(k.as_str(), unsafe {
55 std::str::from_utf8_unchecked(v.as_bytes())
56 }) {
57 return;
58 }
59 }
60 }
61}
62impl crate::service::s3::VRequest for Request {
63 fn method(&self) -> String {
64 self.request.method().as_str().to_string()
65 }
66
67 fn url_path(&self) -> String {
68 self.request.uri().path().to_string()
69 }
70
71 fn get_query(&self, k: &str) -> Option<String> {
72 self.query
73 .as_ref()
74 .and_then(|query| query.get(k))
75 .map(|v| v.to_string())
76 }
77
78 fn all_query(&self, mut cb: impl FnMut(&str, &str) -> bool) {
79 self.query
80 .as_ref()
81 .map(|query| query.iter().all(|(k, v)| cb(k, v)));
82 }
83}
84impl crate::service::s3::VRequestPlus for Request {
85 fn body<'a>(
86 self,
87 ) -> std::pin::Pin<
88 Box<dyn 'a + Send + std::future::Future<Output = Result<Vec<u8>, std::io::Error>>>,
89 > {
90 Box::pin(async move {
91 let mut bodystream = self.request.into_body().into_data_stream();
92 let mut ret = Vec::new();
93 while let Some(bodystream) = bodystream.next().await {
94 let bytes = bodystream.map_err(std::io::Error::other)?;
95 ret.extend_from_slice(bytes.iter().as_slice());
96 }
97 Ok(ret)
98 })
99 }
100}
101pub struct BodyReader(axum_core::body::BodyDataStream);
102impl crate::utils::io::PollRead for BodyReader {
103 fn poll_read<'a>(
104 &'a mut self,
105 ) -> std::pin::Pin<
106 Box<dyn 'a + Send + std::future::Future<Output = Result<Option<Vec<u8>>, String>>>,
107 > {
108 Box::pin(async move {
109 let data = self.0.next().await;
110 match data {
111 Some(ret) => match ret {
112 Ok(ret) => Ok(Some(ret.to_vec())),
113 Err(err) => Err(err.to_string()),
114 },
115 None => Ok(None),
116 }
117 })
118 }
119}
120impl crate::service::s3::BodyReader for Request {
121 type BodyReader = BodyReader;
122
123 fn get_body_reader<'b>(
124 self,
125 ) -> std::pin::Pin<
126 Box<dyn 'b + Send + std::future::Future<Output = Result<Self::BodyReader, String>>>,
127 > {
128 Box::pin(async move {
129 let ret: axum::body::Body = self.request.into_body();
130 Ok(BodyReader(ret.into_data_stream()))
131 })
132 }
133}
134pub struct HeaderWarp(axum::http::HeaderMap);
135impl crate::authorization::v4::VHeader for HeaderWarp {
136 fn get_header(&self, key: &str) -> Option<String> {
137 self.0
138 .get(key)
139 .and_then(|value| value.to_str().ok().map(|value| value.to_string()))
140 }
141
142 fn set_header(&mut self, key: &str, val: &str) {
143 let key: axum::http::HeaderName = key.to_string().parse().unwrap();
144 self.0.insert(key, val.parse().unwrap());
145 }
146
147 fn delete_header(&mut self, key: &str) {
148 self.0.remove(key);
149 }
150
151 fn rng_header(&self, mut cb: impl FnMut(&str, &str) -> bool) {
152 for (k, v) in self.0.iter() {
153 if !cb(k.as_str(), unsafe {
154 std::str::from_utf8_unchecked(v.as_bytes())
155 }) {
156 return;
157 }
158 }
159 }
160}
161impl crate::service::s3::HeaderTaker for Request {
162 type Head = HeaderWarp;
163
164 fn take_header(&self) -> Self::Head {
165 HeaderWarp(self.request.headers().clone())
166 }
167}
168pub struct Response {
169 status: u16,
170 headers: axum::http::HeaderMap,
171 body: tokio::io::BufWriter<Vec<u8>>,
172}
173impl Default for Response {
174 fn default() -> Self {
175 Self {
176 status: Default::default(),
177 headers: Default::default(),
178 body: tokio::io::BufWriter::new(Default::default()),
179 }
180 }
181}
182impl From<Response> for axum::response::Response {
183 fn from(val: Response) -> Self {
184 let mut respbuilder = axum::response::Response::builder().status(if val.status == 0 {
185 200
186 } else {
187 val.status
188 });
189 if !val.headers.is_empty() {
190 if let Some(header) = respbuilder.headers_mut() {
191 *header = val.headers;
192 }
193 }
194 let raw = val.body.into_inner();
195 log::info!("result length {}", raw.len());
196 respbuilder.body(raw.into()).unwrap()
197 }
204}
205impl crate::authorization::v4::VHeader for Response {
206 fn get_header(&self, key: &str) -> Option<String> {
207 self.headers
208 .get(key)
209 .and_then(|v| v.to_str().ok().map(|v| v.to_string()))
210 }
211
212 fn set_header(&mut self, key: &str, val: &str) {
213 log::info!("set header {key} {val}");
214 self.headers.insert(
215 key.to_string().parse::<axum::http::HeaderName>().unwrap(),
216 val.parse().unwrap(),
217 );
218 }
219
220 fn delete_header(&mut self, key: &str) {
221 self.headers.remove(key);
222 }
223
224 fn rng_header(&self, mut cb: impl FnMut(&str, &str) -> bool) {
225 self.headers.iter().all(|(k, v)| {
226 cb(k.as_str(), unsafe {
227 std::str::from_utf8_unchecked(v.as_bytes())
228 })
229 });
230 }
231}
232pub struct BodyWriter<'a>(&'a mut tokio::io::BufWriter<Vec<u8>>);
233impl<'b> crate::utils::io::PollWrite for BodyWriter<'b> {
234 fn poll_write<'a>(
235 &'a mut self,
236 buff: &'a [u8],
237 ) -> std::pin::Pin<
238 Box<dyn 'a + Send + std::future::Future<Output = Result<usize, std::io::Error>>>,
239 > {
240 Box::pin(async move {
241 log::info!("write buff {}", buff.len());
242 let _ = self.0.write_all(buff).await;
243 let _ = self.0.flush().await;
244 Ok(buff.len())
245 })
246 }
247}
248impl crate::service::s3::BodyWriter for Response {
249 type BodyWriter<'a>
250 = BodyWriter<'a> where Self: 'a;
251
252 fn get_body_writer<'b>(
253 &'b mut self,
254 ) -> std::pin::Pin<
255 Box<dyn 'b + Send + std::future::Future<Output = Result<Self::BodyWriter<'_>, String>>>,
256 > {
257 Box::pin(async move { Ok(BodyWriter(&mut self.body)) })
258 }
259}
260impl crate::service::s3::VResponse for Response {
261 fn set_status(&mut self, status: u16) {
262 self.status = status;
263 }
264
265 fn send_header(&mut self) {}
266}
267pub async fn handle_fn(
268 req: axum::extract::Request<axum::body::Body>,
269 _next: axum::middleware::Next,
270) -> axum::response::Response {
271 use crate::service::s3::*;
272 use axum::http::StatusCode;
273 use std::sync::Arc;
274 let multipart_obj = req
275 .extensions()
276 .get::<Arc<dyn MultiUploadObjectHandler + Send + Sync>>()
277 .cloned();
278 match *req.method() {
279 axum::http::Method::PUT => {
280 let put_obj = req
281 .extensions()
282 .get::<std::sync::Arc<dyn crate::service::s3::PutObjectHandler + Sync + Send>>()
283 .cloned();
284 let create_bkt_obj = req
285 .extensions()
286 .get::<std::sync::Arc<dyn crate::service::s3::CreateBucketHandler + Sync + Send>>()
287 .cloned();
288 let v4head = req
289 .extensions()
290 .get::<crate::authorization::v4::V4Head>()
291 .cloned();
292 let path = req.uri().path();
293 let rpath = path
294 .trim_start_matches('/')
295 .splitn(2, '/')
296 .collect::<Vec<&str>>();
297 let rpath_len = rpath.len();
298 if rpath_len == 0 {
299 log::info!("args length invalid");
300 (StatusCode::BAD_REQUEST, b"").into_response()
301 } else {
302 let is_create_bkt = rpath_len == 1 || (rpath_len == 2 && rpath[1].is_empty());
303 let req = Request::from(req);
304 let mut resp = Response::default();
305 if is_create_bkt {
306 match create_bkt_obj {
308 Some(create_bkt_obj) => {
309 crate::service::s3::handle_create_bucket(
310 req,
311 &mut resp,
312 &create_bkt_obj,
313 )
314 .await;
315 }
316 None => {
317 log::warn!("not open create bucket method");
318 return (StatusCode::FORBIDDEN, b"").into_response();
319 }
320 }
321 } else {
322 let xid = req.get_query("x-id");
323 if let Some(xid) = xid {
324 if xid.as_str() == "UploadPart" {
325 let mut resp = Response::default();
326
327 return match multipart_obj {
334 Some(multipart_obj) => {
335 handle_multipart_upload_part(req, &mut resp, &multipart_obj)
336 .await;
337 resp.into()
338 }
339 None => (axum::http::StatusCode::INTERNAL_SERVER_ERROR, b"")
340 .into_response(),
341 };
342 }
343 }
344
345 match put_obj {
347 Some(put_obj) => {
348 crate::service::s3::handle_put_object(
349 v4head.unwrap(),
350 req,
351 &mut resp,
352 &put_obj,
353 )
354 .await;
355 }
356 None => {
357 log::warn!("not open put object method");
358 return (StatusCode::FORBIDDEN, b"").into_response();
359 }
360 }
361 }
362 resp.into()
363 }
364 }
365 axum::http::Method::GET => {
366 if req.uri().path().starts_with("/probe-bsign") {
367 return (axum::http::StatusCode::OK, b"").into_response();
368 }
369 let get_obj = req
370 .extensions()
371 .get::<Arc<dyn crate::service::s3::GetObjectHandler + Send + Sync>>()
372 .cloned();
373 let listbkt_obj = req
374 .extensions()
375 .get::<Arc<dyn crate::service::s3::ListBucketHandler + Send + Sync>>()
376 .cloned();
377 let getbkt_loc_obj = req
378 .extensions()
379 .get::<Arc<dyn crate::service::s3::GetBucketLocationHandler + Send + Sync>>()
380 .cloned();
381 let req = Request::from(req);
382 let url_path = req.url_path();
383 log::info!("path is {}", url_path.trim_start_matches('/').is_empty());
384 if let Some(lt) = req.get_query("list-type") {
385 if lt == "2" || url_path.trim_start_matches('/').is_empty() {
386 log::info!("is list bucket");
387 match listbkt_obj {
389 Some(listbkt_obj) => {
390 let mut resp = Response::default();
391 crate::service::s3::handle_get_list_buckets(
392 req,
393 &mut resp,
394 &listbkt_obj,
395 )
396 .await;
397 return resp.into();
398 }
399 None => {
400 log::warn!("not open head method");
401 let ret = (StatusCode::FORBIDDEN, b"").into_response();
402 return ret;
403 }
404 }
405 }
406 } else if url_path.trim_start_matches('/').is_empty() {
407 log::info!("is list bucket");
408 match listbkt_obj {
410 Some(listbkt_obj) => {
411 let mut resp = Response::default();
412 crate::service::s3::handle_get_list_buckets(req, &mut resp, &listbkt_obj)
413 .await;
414 return resp.into();
415 }
416 None => {
417 log::warn!("not open head method");
418 let ret = (StatusCode::FORBIDDEN, b"").into_response();
419 return ret;
420 }
421 }
422 }
423 if let Some(loc) = req.get_query("location") {
424 return match getbkt_loc_obj {
426 Some(bkt) => {
427 match bkt
428 .handle(if loc.is_empty() { None } else { Some(&loc) })
429 .await
430 {
431 Ok(loc) => {
432 let lc = match loc {
433 Some(loc) => bucket::LocationConstraint::new(loc),
434 None => bucket::LocationConstraint::new(""),
435 };
436 match quick_xml::se::to_string(&lc) {
437 Ok(content) => (StatusCode::OK, content).into_response(),
438 Err(err) => {
439 log::error!("xml encode error {err}");
440 (StatusCode::INTERNAL_SERVER_ERROR, b"").into_response()
441 }
442 }
443 }
444 Err(_) => {
445 log::error!("get bucket location error");
446 (StatusCode::INTERNAL_SERVER_ERROR, b"").into_response()
447 }
448 }
449 }
450 None => {
451 log::warn!("not open get bucket location method");
452 (StatusCode::FORBIDDEN, b"").into_response()
453 }
454 };
455 }
456 match get_obj {
458 Some(obj) => {
459 let mut resp = Response::default();
460 crate::service::s3::handle_get_object(req, &mut resp, &obj).await;
461 resp.into()
462 }
463 None => {
464 log::warn!("not open get object method");
465 (StatusCode::FORBIDDEN, b"").into_response()
466 }
467 }
468 }
469 axum::http::Method::DELETE => {
470 let path = req.uri().path().trim_start_matches('/');
471 if path.is_empty() {
472 return (StatusCode::BAD_REQUEST, b"").into_response();
473 }
474 let rr = path.split("/").collect::<Vec<&str>>();
475 let rr_len = rr.len();
476 if rr_len == 1 || (rr_len == 2 && rr[1].is_empty()) {
477 match req
478 .extensions()
479 .get::<Arc<dyn DeleteBucketHandler + Send + Sync>>()
480 .cloned()
481 {
482 Some(delete_bkt_obj) => {
483 let mut resp = Response::default();
484 handle_delete_bucket(Request::from(req), &mut resp, &delete_bkt_obj).await;
485 resp.into()
486 }
487 None => {
488 log::warn!("not open get delete bucket method");
489 (StatusCode::FORBIDDEN, b"").into_response()
490 }
491 }
492 } else {
493 match req
494 .extensions()
495 .get::<Arc<dyn DeleteObjectHandler + Send + Sync>>()
496 .cloned()
497 {
498 Some(delete_obj_obj) => {
499 let mut resp = Response::default();
500 handle_delete_object(Request::from(req), &mut resp, &delete_obj_obj).await;
501 resp.into()
502 }
503 None => {
504 log::warn!("not open get delete bucket method");
505 (StatusCode::FORBIDDEN, b"").into_response()
506 }
507 }
508 }
509 }
510 axum::http::Method::HEAD => {
511 let head_obj = req
512 .extensions()
513 .get::<std::sync::Arc<dyn crate::service::s3::HeadHandler + Sync + Send>>()
514 .cloned();
515 if head_obj.is_none() {
516 log::warn!("not open head features");
517 return (StatusCode::INTERNAL_SERVER_ERROR, b"").into_response();
518 }
519 let head_obj = head_obj.unwrap();
520 let req = Request::from(req);
521 let raw_path = req.url_path();
522 let args = raw_path
523 .trim_start_matches('/')
524 .splitn(2, '/')
525 .collect::<Vec<&str>>();
526 if args.len() != 2 {
527 return (StatusCode::BAD_REQUEST, b"").into_response();
528 }
529 match head_obj.lookup(args[0], args[1]).await {
530 Ok(metadata) => match metadata {
531 Some(head) => {
532 use crate::authorization::v4::VHeader;
533 let mut resp = Response::default();
534 if let Some(v) = head.content_length {
535 resp.set_header("content-length", v.to_string().as_str())
536 }
537 if let Some(v) = head.etag {
538 resp.set_header("etag", &v);
539 }
540 if let Some(v) = head.content_type {
541 resp.set_header("content-type", &v);
542 }
543 if let Some(v) = head.last_modified {
544 resp.set_header("last-modified", &v);
545 }
546 (StatusCode::OK, b"").into_response()
547 }
548 None => (StatusCode::NOT_FOUND, b"").into_response(),
549 },
550 Err(err) => {
551 log::error!("lookup object metadata error {err}");
552 (StatusCode::INTERNAL_SERVER_ERROR, b"").into_response()
553 }
554 }
555 }
556 axum::http::Method::POST => match multipart_obj {
557 Some(multipart_obj) => {
558 let mut resp = Response::default();
559 let is_create_session = if let Some(query) = req.uri().query() {
560 query.contains("uploads=")
561 } else {
562 false
563 };
564 let req = Request::from(req);
565 if is_create_session {
566 handle_multipart_create_session(req, &mut resp, &multipart_obj).await;
567 } else if req.get_query("uploadId").is_some() {
568 handle_multipart_complete_session(req, &mut resp, &multipart_obj).await;
569 } else {
570 return (StatusCode::BAD_REQUEST, b"").into_response();
571 }
572 resp.into()
573 }
574 None => {
575 log::warn!("not open multipart object features");
576 (StatusCode::INTERNAL_SERVER_ERROR, b"").into_response()
577 }
578 },
579 _ => (StatusCode::METHOD_NOT_ALLOWED, b"").into_response(),
580 }
581}
582pub async fn handle_authorization_middleware(
583 req: axum::extract::Request<axum::body::Body>,
584 next: axum::middleware::Next,
585) -> impl axum::response::IntoResponse {
586 let ret = req
587 .extensions()
588 .get::<std::sync::Arc<dyn crate::authorization::AccesskeyStore + Send + Sync>>()
589 .cloned();
590 let ak_store = match ret {
591 Some(ret) => ret,
592 None => {
593 return (axum::http::StatusCode::INTERNAL_SERVER_ERROR, b"").into_response();
594 }
595 };
596 let req = Request::from(req);
597 let base_arg = match crate::authorization::v4::extract_args(&req) {
598 Ok(arg) => arg,
599 Err(_) => {
600 return (axum::http::StatusCode::BAD_REQUEST, b"").into_response();
601 }
602 };
603 let mut query = Vec::new();
604 req.all_query(|k, v| {
605 query.push(crate::utils::BaseKv {
606 key: k.to_string(),
607 val: v.to_string(),
608 });
609 true
610 });
611 let secretkey = match ak_store.get(&base_arg.access_key).await {
612 Ok(secretkey) => {
613 if secretkey.is_none() {
614 return (axum::http::StatusCode::FORBIDDEN, b"").into_response();
615 }
616 secretkey.unwrap()
617 }
618 Err(_) => {
619 return (axum::http::StatusCode::INTERNAL_SERVER_ERROR, b"").into_response();
620 }
621 };
622 let ret = crate::authorization::v4::get_v4_signature(
623 &req,
624 req.method().as_str(),
625 &base_arg.region,
626 &base_arg.service,
627 req.url_path().as_str(),
628 &secretkey,
629 &base_arg.content_hash,
630 &base_arg.signed_headers,
631 query,
632 );
633 let circle_hasher = match ret {
634 Ok((sig, circle_hasher)) => {
635 if sig != base_arg.signature {
636 log::info!(
637 "expect {sig} got {} args {:?} url_path {}",
638 base_arg.signature,
639 base_arg,
640 req.url_path().as_str()
641 );
642 return (axum::http::StatusCode::FORBIDDEN, b"").into_response();
643 }
644 circle_hasher
645 }
646 Err(err) => {
647 log::error!("signature failed {err}");
648 return (axum::http::StatusCode::FORBIDDEN, b"").into_response();
649 }
650 };
651 let v4head = crate::authorization::v4::V4Head::new(
652 base_arg.signature,
653 base_arg.region,
654 base_arg.access_key,
655 circle_hasher,
656 );
657 let mut req: axum::http::Request<axum::body::Body> = req.into();
658 req.extensions_mut().insert(v4head);
659 next.run(req).await
660}
661mod bucket {
662
663 #[derive(serde::Serialize, Debug)]
664 #[serde(rename = "LocationConstraint", rename_all = "PascalCase")]
665 pub struct LocationConstraint {
666 #[serde(rename = "$value")]
667 region: String,
668
669 #[serde(rename = "xmlns")]
670 _xmlns: &'static str,
671 }
672 impl LocationConstraint {
673 pub fn new<T: Into<String>>(region: T) -> Self {
674 Self {
675 region: region.into(),
676 _xmlns: "http://s3.amazonaws.com/doc/2006-03-01/",
677 }
678 }
679 }
680}
681#[cfg(test)]
682mod itest {
683 use std::sync::Arc;
684
685 use tokio::io::AsyncReadExt;
686
687 #[derive(Default)]
688 struct Target {}
689 use crate::service::s3::*;
690 impl CreateBucketHandler for Target {
691 fn handle<'a>(
692 &'a self,
693 _opt: &'a CreateBucketOption,
694 _bucket: &'a str,
695 ) -> std::pin::Pin<Box<dyn 'a + Send + std::future::Future<Output = Result<(), String>>>>
696 {
697 Box::pin(async move {
698 log::info!("create bucket {_bucket}");
699 Ok(())
700 })
701 }
702 }
703 impl ListBucketHandler for Target {
704 fn handle<'a>(
705 &'a self,
706 _opt: &'a ListBucketsOption,
707 ) -> std::pin::Pin<
708 Box<dyn 'a + Send + std::future::Future<Output = Result<Vec<Bucket>, String>>>,
709 > {
710 Box::pin(async move {
711 let datetime = chrono::Utc::now().to_rfc3339();
712 Ok(vec![Bucket {
713 name: "test1".to_string(),
714 creation_date: datetime,
715 bucket_region: "us-east-1".to_string(),
716 }])
717 })
718 }
719 }
720 impl HeadHandler for Target {
721 fn lookup<'a>(
722 &self,
723 _bucket: &str,
724 _object: &str,
725 ) -> std::pin::Pin<
726 Box<
727 dyn 'a
728 + Send
729 + Sync
730 + std::future::Future<Output = Result<Option<HeadObjectResult>, Error>>,
731 >,
732 > {
733 Box::pin(async move {
734 let mut ret: HeadObjectResult = Default::default();
735 ret.checksum_sha256 = Some(
736 "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824".to_string(),
737 );
738 ret.content_length = Some(5);
739 ret.etag = Some("5d41402abc4b2a76b9719d911017c592".to_string());
740 ret.last_modified = Some(
741 chrono::Utc::now()
742 .format("%a, %d %b %Y %H:%M:%S GMT")
743 .to_string(),
744 );
745 Ok(Some(ret))
746 })
747 }
748 }
749 impl PutObjectHandler for Target {
750 fn handle<'a>(
751 &'a self,
752 opt: &PutObjectOption,
753 bucket: &'a str,
754 object: &'a str,
755 body: &'a mut (dyn tokio::io::AsyncRead + Unpin + Send),
756 ) -> std::pin::Pin<Box<dyn 'a + Send + std::future::Future<Output = Result<(), String>>>>
757 {
758 Box::pin(async move {
759 log::info!("put bucket {bucket} object {object}");
760 let mut buff = vec![];
761 match body.read_to_end(&mut buff).await {
762 Ok(size) => {
763 log::info!("get {}", unsafe {
764 std::str::from_utf8_unchecked(&buff[..size])
765 });
766 }
767 Err(err) => {
768 log::error!("read error {err}");
769 }
770 }
771 Ok(())
772 })
773 }
774 }
775 impl DeleteBucketHandler for Target {
776 fn handle<'a>(
777 &'a self,
778 _opt: &'a DeleteBucketOption,
779 _bucket: &'a str,
780 ) -> std::pin::Pin<Box<dyn 'a + Send + std::future::Future<Output = Result<(), String>>>>
781 {
782 Box::pin(async move {
783 log::info!("delete bucket {_bucket}");
784 Ok(())
785 })
786 }
787 }
788 impl DeleteObjectHandler for Target {
789 fn handle<'a>(
790 &'a self,
791 _opt: &'a DeleteObjectOption,
792 _object: &'a str,
793 ) -> std::pin::Pin<Box<dyn 'a + Send + std::future::Future<Output = Result<(), String>>>>
794 {
795 Box::pin(async move {
796 log::info!("delete object {_object}");
797 Ok(())
798 })
799 }
800 }
801 impl crate::authorization::AccesskeyStore for Target {
802 fn get<'a>(
803 &'a self,
804 _accesskey: &'a str,
805 ) -> std::pin::Pin<
806 Box<
807 dyn 'a + Send + Sync + std::future::Future<Output = Result<Option<String>, String>>,
808 >,
809 > {
810 Box::pin(async move { Ok(Some(format!("{_accesskey}12345"))) })
811 }
812 }
813 impl crate::service::s3::GetObjectHandler for Target {
814 fn handle<'a>(
815 &'a self,
816 bucket: &str,
817 object: &str,
818 opt: crate::service::s3::GetObjectOption,
819 mut out: tokio::sync::Mutex<
820 std::pin::Pin<
821 std::boxed::Box<(dyn crate::utils::io::PollWrite + Send + Unpin + 'a)>,
822 >,
823 >,
824 ) -> std::pin::Pin<Box<dyn 'a + Send + std::future::Future<Output = Result<(), String>>>>
825 {
826 Box::pin(async move {
827 let mut l = out.lock().await;
828 let _ = l.poll_write(b"hello").await.map_err(|err| {
829 log::error!("write error {err}");
830 });
831 Ok(())
832 })
833 }
834 }
835 impl crate::service::s3::GetBucketLocationHandler for Target {}
836 impl MultiUploadObjectHandler for Target {
837 fn handle_create_session<'a>(
838 &'a self,
839 bucket: &'a str,
840 key: &'a str,
841 ) -> std::pin::Pin<Box<dyn 'a + Send + std::future::Future<Output = Result<String, ()>>>>
842 {
843 Box::pin(async move { Ok("ffffff".to_string()) })
844 }
845
846 fn handle_upload_part<'a>(
847 &'a self,
848 bucket: &'a str,
849 key: &'a str,
850 upload_id: &'a str,
851 part_number: u32,
852 body: &'a mut (dyn tokio::io::AsyncRead + Unpin + Send),
853 ) -> std::pin::Pin<Box<dyn 'a + Send + std::future::Future<Output = Result<String, ()>>>>
854 {
855 Box::pin(async move {
856 let mut buff = Vec::new();
857 let size = body
858 .read_to_end(&mut buff)
859 .await
860 .map_err(|err| log::error!("read body error {err}"))?;
861 println!(
862 "upload part upload_id={upload_id} part_number={part_number} bucket={bucket} key={key}\n{}",
863 unsafe { std::str::from_boxed_utf8_unchecked((&buff[..size]).into()) }
864 );
865 Ok("5d41402abc4b2a76b9719d911017c592".to_string())
866 })
867 }
868
869 fn handle_complete<'a>(
870 &'a self,
871 bucket: &'a str,
872 key: &'a str,
873 upload_id: &'a str,
874 data: &'a [(&'a str, u32)],
876 opts: MultiUploadObjectCompleteOption,
877 ) -> std::pin::Pin<Box<dyn 'a + Send + std::future::Future<Output = Result<String, ()>>>>
878 {
879 Box::pin(async move { Ok("69a329523ce1ec88bf63061863d9cb14".to_string()) })
880 }
881
882 fn handle_abort<'a>(
883 &'a self,
884 bucket: &'a str,
885 key: &'a str,
886 upload_id: &'a str,
887 ) -> std::pin::Pin<Box<dyn 'a + Send + std::future::Future<Output = Result<(), ()>>>>
888 {
889 todo!()
890 }
891 }
892 #[tokio::test]
893 async fn test_server() -> Result<(), Box<dyn std::error::Error>> {
894 let _ = tokio::fs::create_dir_all(".sys_bws").await;
895 env_logger::builder()
896 .filter_level(log::LevelFilter::Info)
897 .init();
898 let target = Arc::new(Target::default());
899 let r = axum::Router::new()
900 .layer(axum::middleware::from_fn(super::handle_fn))
901 .layer(axum::middleware::from_fn(
902 super::handle_authorization_middleware,
903 ))
904 .layer(axum::Extension(
905 target.clone() as Arc<dyn PutObjectHandler + Send + Sync>
906 ))
907 .layer(axum::Extension(
908 target.clone() as Arc<dyn HeadHandler + Send + Sync>
909 ))
910 .layer(axum::Extension(
911 target.clone() as Arc<dyn ListBucketHandler + Send + Sync>
912 ))
913 .layer(axum::Extension(
914 target.clone() as Arc<dyn CreateBucketHandler + Send + Sync>
915 ))
916 .layer(axum::Extension(
917 target.clone() as Arc<dyn DeleteBucketHandler + Send + Sync>
918 ))
919 .layer(axum::Extension(
920 target.clone() as Arc<dyn DeleteObjectHandler + Send + Sync>
921 ))
922 .layer(axum::Extension(
923 target.clone() as Arc<dyn crate::authorization::AccesskeyStore + Send + Sync>
924 ))
925 .layer(axum::Extension(
926 target.clone() as Arc<dyn GetObjectHandler + Send + Sync>
927 ))
928 .layer(axum::Extension(
929 target.clone() as Arc<dyn GetBucketLocationHandler + Send + Sync>
930 )).layer(axum::Extension(
931 target.clone() as Arc<dyn MultiUploadObjectHandler + Send + Sync>
932 ));
933 let l = tokio::net::TcpListener::bind("0.0.0.0:9900").await?;
934 axum::serve(l, r).await?;
935 Ok(())
936 }
937}