1use std::{
2 collections::HashMap,
3 fmt::{Debug, Display},
4 io::Write,
5 str::FromStr,
6 sync::Mutex,
7};
8static OWNER_ID: &str = "ffffffffffffffff";
9pub type DateTime = chrono::DateTime<chrono::Utc>;
10pub struct Error(String);
11impl From<String> for Error {
12 fn from(value: String) -> Self {
13 Self(value)
14 }
15}
16impl From<&str> for Error {
17 fn from(value: &str) -> Self {
18 Self(value.to_string())
19 }
20}
21
22impl Display for Error {
23 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
24 f.write_str(&self.0)
25 }
26}
27impl Debug for Error {
28 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
29 f.debug_tuple("Error").field(&self.0).finish()
30 }
31}
32impl std::error::Error for Error {}
33pub trait VRequest: crate::authorization::v4::VHeader {
34 fn method(&self) -> String;
35 fn url_path(&self) -> String;
36 fn get_query(&self, k: &str) -> Option<String>;
37 fn all_query(&self, cb: impl FnMut(&str, &str) -> bool);
38}
39pub trait BodyWriter {
40 type BodyWriter<'a>: crate::utils::io::PollWrite + Send + Unpin
41 where
42 Self: 'a;
43 fn get_body_writer<'b>(
44 &'b mut self,
45 ) -> std::pin::Pin<
46 Box<dyn 'b + Send + std::future::Future<Output = Result<Self::BodyWriter<'_>, String>>>,
47 >;
48}
49pub trait BodyReader {
50 type BodyReader: crate::utils::io::PollRead + Send;
51 fn get_body_reader<'b>(
52 self,
53 ) -> std::pin::Pin<
54 Box<dyn 'b + Send + std::future::Future<Output = Result<Self::BodyReader, String>>>,
55 >;
56}
57pub trait HeaderTaker {
58 type Head: crate::authorization::v4::VHeader;
59 fn take_header(&self) -> Self::Head;
60}
61pub trait VRequestPlus: VRequest {
62 fn body<'a>(
63 self,
64 ) -> std::pin::Pin<
65 Box<dyn 'a + Send + std::future::Future<Output = Result<Vec<u8>, std::io::Error>>>,
66 >;
67}
68pub trait VResponse: crate::authorization::v4::VHeader + BodyWriter {
69 fn set_status(&mut self, status: u16);
70 fn send_header(&mut self);
71}
72
73#[derive(Default, Debug, Serialize)]
74pub struct HeadObjectResult {
75 #[serde(rename = "AcceptRanges")]
76 pub accept_ranges: Option<String>,
77 #[serde(rename = "ArchiveStatus")]
78 pub archive_status: Option<String>, #[serde(rename = "BucketKeyEnabled")]
80 pub bucket_key_enabled: Option<bool>,
81 #[serde(rename = "CacheControl")]
82 pub cache_control: Option<String>,
83 #[serde(rename = "ChecksumCRC32")]
84 pub checksum_crc32: Option<String>,
85 #[serde(rename = "ChecksumCRC32C")]
86 pub checksum_crc32c: Option<String>,
87 #[serde(rename = "ChecksumCRC64")]
88 pub checksum_crc64: Option<String>,
89 #[serde(rename = "ChecksumSHA1")]
90 pub checksum_sha1: Option<String>,
91 #[serde(rename = "ChecksumSHA256")]
92 pub checksum_sha256: Option<String>,
93 #[serde(rename = "ChecksumType")]
94 pub checksum_type: Option<String>,
95 #[serde(rename = "ContentDisposition")]
96 pub content_disposition: Option<String>,
97 #[serde(rename = "ContentEncoding")]
98 pub content_encoding: Option<String>,
99 #[serde(rename = "ContentLanguage")]
100 pub content_language: Option<String>,
101 #[serde(rename = "ContentLength")]
102 pub content_length: Option<usize>,
103 #[serde(rename = "ContentRange")]
104 pub content_range: Option<String>,
105 #[serde(rename = "ContentType")]
106 pub content_type: Option<String>,
107 #[serde(rename = "DeleteMarker")]
108 pub delete_marker: Option<bool>,
109 #[serde(rename = "ETag")]
110 pub etag: Option<String>,
111 #[serde(rename = "Expiration")]
112 pub expiration: Option<String>,
113 #[serde(rename = "Expires")]
114 pub expires: Option<String>, #[serde(rename = "ExpiresString")]
116 pub expires_string: Option<String>,
117 #[serde(rename = "LastModified")]
118 pub last_modified: Option<String>, #[serde(rename = "Metadata")]
120 pub metadata: Option<HashMap<String, String>>,
121 #[serde(rename = "MissingMeta")]
122 pub missing_meta: Option<i32>,
123 #[serde(rename = "ObjectLockLegalHoldStatus")]
124 pub object_lock_legal_hold_status: Option<String>,
125 #[serde(rename = "ObjectLockMode")]
126 pub object_lock_mode: Option<String>,
127 #[serde(rename = "ObjectLockRetainUntilDate")]
128 pub object_lock_retain_until_date: Option<String>,
129 #[serde(rename = "PartsCount")]
130 pub parts_count: Option<i32>,
131 #[serde(rename = "ReplicationStatus")]
132 pub replication_status: Option<String>,
133 #[serde(rename = "RequestCharged")]
134 pub request_charged: Option<String>,
135 #[serde(rename = "Restore")]
136 pub restore: Option<String>,
137 #[serde(rename = "SSECustomerAlgorithm")]
138 pub sse_customer_algorithm: Option<String>,
139 #[serde(rename = "SSECustomerKeyMD5")]
140 pub sse_customer_key_md5: Option<String>,
141 #[serde(rename = "SSEKMSKeyId")]
142 pub sse_kms_key_id: Option<String>,
143 #[serde(rename = "ServerSideEncryption")]
144 pub server_side_encryption: Option<String>,
145 #[serde(rename = "StorageClass")]
146 pub storage_class: Option<String>,
147 #[serde(rename = "VersionId")]
148 pub version_id: Option<String>,
149 #[serde(rename = "WebsiteRedirectLocation")]
150 pub website_redirect_location: Option<String>,
151}
152
153pub trait HeadHandler {
154 fn lookup<'a>(
155 &self,
156 bucket: &str,
157 object: &str,
158 ) -> std::pin::Pin<
159 Box<
160 dyn 'a
161 + Send
162 + Sync
163 + std::future::Future<Output = Result<Option<HeadObjectResult>, Error>>,
164 >,
165 >;
166}
167#[derive(Default)]
168pub struct GetObjectOption {
169 }
172
173pub trait GetObjectHandler: HeadHandler {
174 fn handle<'a>(
175 &'a self,
176 bucket: &str,
177 object: &str,
178 opt: GetObjectOption,
179 out: tokio::sync::Mutex<
180 std::pin::Pin<Box<dyn 'a + Send + crate::utils::io::PollWrite + Unpin>>,
181 >,
182 ) -> std::pin::Pin<Box<dyn 'a + Send + std::future::Future<Output = Result<(), String>>>>;
183}
184extern crate serde;
185use serde::Serialize;
186use sha1::Digest;
187use tokio::io::AsyncSeekExt;
188
189use crate::utils::io::PollWrite;
190
191#[derive(Debug, Serialize)]
192#[serde(rename_all = "PascalCase")]
193#[serde(rename = "ListBucketResult")]
194pub struct ListObjectResult {
195 pub name: String,
196 pub prefix: Option<String>,
197 pub key_count: Option<u32>,
198 pub max_keys: Option<u32>,
199 pub delimiter: Option<String>,
200 pub is_truncated: bool,
201 #[serde(default)]
202 pub contents: Vec<ListObjectContent>,
203 #[serde(default)]
204 pub common_prefixes: Vec<CommonPrefix>,
205}
206
207#[derive(Debug, Serialize)]
208#[serde(rename_all = "PascalCase")]
209pub struct ListObjectContent {
210 pub key: String,
211 pub last_modified: Option<String>,
212 pub etag: Option<String>,
213 pub size: u64,
214 pub storage_class: Option<String>,
215 pub owner: Option<Owner>,
216}
217
218#[derive(Debug, Serialize)]
219#[serde(rename = "ListAllMyBucketsResult")]
220#[serde(rename_all = "PascalCase")]
221pub struct ListAllMyBucketsResult {
222 #[serde(
223 rename = "xmlns",
224 default = "s3_namespace",
225 skip_serializing_if = "String::is_empty"
226 )]
227 pub xmlns: String,
228
229 pub owner: Owner,
230
231 pub buckets: Buckets,
232}
233#[derive(Debug, Serialize)]
234pub struct Buckets {
235 #[serde(rename = "Bucket")]
236 pub bucket: Vec<Bucket>,
237}
238
239#[derive(Debug, Serialize)]
240#[serde(rename_all = "PascalCase")]
241pub struct Bucket {
242 pub name: String,
243 pub creation_date: String,
244 pub bucket_region: String,
245}
246
247#[derive(Debug, Serialize)]
248#[serde(rename_all = "PascalCase")]
249pub struct Owner {
250 pub id: String,
251 pub display_name: String,
252}
253
254#[derive(Debug, Serialize)]
255#[serde(rename_all = "PascalCase")]
256pub struct CommonPrefix {
257 pub prefix: String,
258}
259
260#[derive(Debug, Serialize)]
261#[serde(rename_all = "PascalCase")]
262pub struct ListObjectOption {
263 pub bucket: String,
264
265 #[serde(skip_serializing_if = "Option::is_none")]
266 pub continuation_token: Option<String>,
267
268 #[serde(skip_serializing_if = "Option::is_none")]
269 pub delimiter: Option<String>,
270
271 #[serde(skip_serializing_if = "Option::is_none")]
272 pub encoding_type: Option<String>, #[serde(skip_serializing_if = "Option::is_none")]
275 pub expected_bucket_owner: Option<String>,
276
277 #[serde(skip_serializing_if = "Option::is_none")]
278 pub fetch_owner: Option<bool>,
279
280 #[serde(skip_serializing_if = "Option::is_none")]
281 pub max_keys: Option<i32>,
282
283 #[serde(skip_serializing_if = "Option::is_none")]
284 pub optional_object_attributes: Option<Vec<String>>, #[serde(skip_serializing_if = "Option::is_none")]
287 pub prefix: Option<String>,
288
289 #[serde(skip_serializing_if = "Option::is_none")]
290 pub request_payer: Option<String>, #[serde(skip_serializing_if = "Option::is_none")]
293 pub start_after: Option<String>,
294}
295
296pub trait ListObjectHandler {
297 fn handle<'a>(
298 &'a self,
299 opt: &'a ListObjectOption,
300 bucket: &'a str,
301 ) -> std::pin::Pin<
302 Box<dyn 'a + Send + std::future::Future<Output = Result<Vec<ListObjectContent>, String>>>,
303 >;
304}
305pub fn handle_head_object<T: VRequest, F: VResponse, E: HeadHandler>(
306 req: &T,
307 resp: &mut F,
308 handler: &E,
309) {
310 todo!()
311}
312pub async fn handle_get_object<T: VRequest, F: VResponse>(
313 req: T,
314 resp: &mut F,
315 handler: &std::sync::Arc<dyn GetObjectHandler + Send + Sync>,
316) {
317 use tokio::io::AsyncWriteExt;
318 if req.method() != "GET" {
319 resp.set_status(405);
320 resp.send_header();
321 return;
322 }
323 let rpath = req.url_path();
324 let raw = rpath.trim_matches('/');
325 let r = raw.find('/');
326 if r.is_none() {
327 resp.set_status(400);
328 resp.send_header();
329 return;
330 }
331 let opt = GetObjectOption::default();
332 let next = r.unwrap();
333 let bucket = &raw[..next];
334 let object = &raw[next + 1..];
335
336 let head = handler.lookup(bucket, object).await;
337 if let Err(e) = head {
338 log::error!("lookup {bucket} {object} error: {e}");
339 resp.set_status(500);
340 resp.send_header();
341 return;
342 }
343 let head = head.unwrap();
344 if head.is_none() {
345 log::info!("not found {bucket} {object}");
346 resp.set_status(404);
347 resp.send_header();
348 return;
349 }
350 let head = head.unwrap();
352 if let Some(v) = head.content_length {
353 resp.set_header("content-length", v.to_string().as_str())
354 }
355 if let Some(v) = head.etag {
356 resp.set_header("etag", &v)
357 }
358 if let Some(v) = head.content_type {
359 resp.set_header("content-type", &v)
360 }
361 if let Some(v) = head.last_modified {
362 resp.set_header("last-modified", &v)
363 }
364 resp.set_status(200);
366 resp.send_header();
367 let ret = {
368 match resp.get_body_writer().await {
369 Ok(body) => {
370 let ret = handler
371 .handle(bucket, object, opt, tokio::sync::Mutex::new(Box::pin(body)))
372 .await;
373 if let Err(err) = ret {
374 Err(err)
375 } else {
376 Ok(())
377 }
378 }
379 Err(err) => Err(err),
380 }
381 };
382 if let Err(err) = ret {
383 log::error!("body handle error {err}");
384 resp.set_status(500);
385 }
386}
387
388pub async fn handle_get_list_object<T: VRequest, F: VResponse>(
390 req: T,
391 resp: &mut F,
392 handler: &std::sync::Arc<dyn ListObjectHandler + Send + Sync>,
393) {
394 if req.method() != "GET" {
395 resp.set_status(405);
396 resp.send_header();
397 return;
398 }
399 let rpath = req.url_path();
400 let bucket = rpath.trim_matches('/').to_string();
401 let opt = ListObjectOption {
402 bucket: bucket.clone(),
403 continuation_token: req.get_query("continuation-token"),
404 delimiter: req.get_query("delimiter"),
405 expected_bucket_owner: req.get_query("expected-bucket-owner"),
406 max_keys: req
407 .get_query("max-keys")
408 .and_then(|v| v.parse::<i32>().ok()),
409 optional_object_attributes: None, request_payer: req.get_header("x-amz-request-layer"),
411 start_after: req.get_query("start-after"),
412 encoding_type: req.get_query("encoding-type"),
413 fetch_owner: req.get_query("fetch-owner").and_then(|v| {
414 if v == "true" {
415 Some(true)
416 } else if v == "false" {
417 Some(false)
418 } else {
419 None
420 }
421 }),
422 prefix: req.get_query("prefix"),
423 };
424 let ret = handler.handle(&opt, rpath.trim_matches('/')).await;
425 match ret {
426 Ok(ans) => {
427 let result = ListObjectResult {
428 name: bucket,
429 prefix: opt.prefix,
430 key_count: Some(ans.len() as u32),
431 max_keys: opt.max_keys.map(|v| v as u32),
432 delimiter: opt.delimiter,
433 is_truncated: false,
434 contents: ans,
435 common_prefixes: vec![],
436 };
437 match quick_xml::se::to_string(&result) {
438 Ok(data) => {
439 resp.set_header("content-type", "application/xml");
440 resp.set_header("content-length", data.len().to_string().as_str());
441 resp.set_status(200);
442 resp.send_header();
443 let ret = match resp.get_body_writer().await {
444 Ok(mut body) => {
445 if let Err(err) = body.poll_write(data.as_bytes()).await {
446 log::info!("write to response body error {err}");
447 }
448 Ok(())
449 }
450 Err(err) => Err(err),
451 };
452 if let Err(err) = ret {
453 log::error!("write body error {err}");
454 resp.set_status(500);
455 resp.send_header();
456 return;
457 }
458 }
465 Err(err) => {
466 log::error!("xml marshal failed {err}");
467 }
468 }
469 }
470 Err(err) => log::error!("get_list_object error {err}"),
471 }
472}
473
474pub trait ListBucketHandler {
475 fn handle<'a>(
476 &'a self,
477 opt: &'a ListBucketsOption,
478 ) -> std::pin::Pin<Box<dyn 'a + Send + std::future::Future<Output = Result<Vec<Bucket>, String>>>>;
479}
480pub trait GetBucketLocationHandler {
481 fn handle<'a>(
482 &'a self,
483 loc: Option<&'a str>,
484 ) -> std::pin::Pin<
485 Box<dyn 'a + Send + std::future::Future<Output = Result<Option<&'static str>, ()>>>,
486 > {
487 Box::pin(async { Ok(Some("us-west-1")) })
488 }
489}
490#[derive(Debug)]
491pub struct ListBucketsOption {
492 pub bucket_region: Option<String>,
493 pub continuation_token: Option<String>,
494 pub max_buckets: Option<i32>,
495 pub prefix: Option<String>,
496}
497pub async fn handle_get_list_buckets<T: VRequest, F: VResponse>(
498 req: T,
499 resp: &mut F,
500 handler: &std::sync::Arc<dyn ListBucketHandler + Send + Sync>,
501) {
502 if req.method() != "GET" {
503 resp.set_status(405);
504 resp.send_header();
505 return;
506 }
507 let opt = ListBucketsOption {
508 bucket_region: req.get_query("bucket-region"),
509 continuation_token: req.get_query("continuation-token"),
510 max_buckets: req
511 .get_query("max-buckets")
512 .and_then(|v| v.parse::<i32>().ok()),
513 prefix: req.get_query("prefix"),
514 };
515 match handler.handle(&opt).await {
516 Ok(v) => {
517 let res = ListAllMyBucketsResult {
518 xmlns: r#"xmlns="http://s3.amazonaws.com/doc/2006-03-01/""#.to_string(),
519 owner: Owner {
520 id: OWNER_ID.to_string(),
521 display_name: "bws".to_string(),
522 },
523 buckets: Buckets { bucket: v },
524 };
525 match quick_xml::se::to_string(&res) {
526 Ok(v) => match resp.get_body_writer().await {
527 Ok(mut w) => {
528 if let Err(err) = w.poll_write(v.as_bytes()).await {
529 log::info!("write to client body error {err}");
530 }
531 }
532 Err(e) => log::error!("get_body_writer error: {e}"),
533 },
534 Err(e) => {
535 resp.set_status(500);
536 resp.send_header();
537 log::error!("xml serde error: {e}")
538 }
539 }
540 }
541 Err(e) => {
542 log::info!("listbucket handle error: {e}");
543 resp.set_status(500);
544 resp.send_header();
545 }
546 }
547}
548#[derive(Default)]
549pub struct PutObjectOption {
550 pub cache_control: Option<String>,
552 pub checksum_algorithm: Option<ChecksumAlgorithm>,
553 pub checksum_crc32: Option<String>,
554 pub checksum_crc32c: Option<String>,
555 pub checksum_crc64nvme: Option<String>,
556 pub checksum_sha1: Option<String>,
557 pub checksum_sha256: Option<String>,
558 pub content_disposition: Option<String>,
559 pub content_encoding: Option<String>,
560 pub content_language: Option<String>,
561 pub content_length: Option<i64>,
562 pub content_md5: Option<String>,
563 pub content_type: Option<String>,
564 pub expected_bucket_owner: Option<String>,
565 pub expires: Option<DateTime>,
566 pub grant_full_control: Option<String>,
567 pub grant_read: Option<String>,
568 pub if_match: Option<String>,
569 pub if_none_match: Option<String>,
570 pub object_lock_legal_hold_status: Option<ObjectLockLegalHoldStatus>,
572 pub object_lock_mode: Option<ObjectLockMode>,
573 pub object_lock_retain_until_date: Option<DateTime>,
574 pub request_payer: Option<RequestPayer>,
575 pub storage_class: Option<String>,
576 pub write_offset_bytes: Option<i64>,
579}
580impl PutObjectOption {
581 pub fn invalid(&self) -> bool {
582 if self.content_length.is_none() {
583 return false;
584 } else if self.content_md5.is_none() {
585 return false;
586 }
587 true
588 }
589}
590
591#[derive(Debug, PartialEq)]
592pub enum ChecksumAlgorithm {
593 Crc32,
594 Crc32c,
595 Sha1,
596 Sha256,
597 Crc64nvme,
598}
599
600impl std::str::FromStr for ChecksumAlgorithm {
601 type Err = String;
602
603 fn from_str(s: &str) -> Result<Self, Self::Err> {
604 match s {
605 "CRC32" => Ok(ChecksumAlgorithm::Crc32),
606 "CRC32C" => Ok(ChecksumAlgorithm::Crc32c),
607 "SHA1" => Ok(ChecksumAlgorithm::Sha1),
608 "SHA256" => Ok(ChecksumAlgorithm::Sha256),
609 "CRC64NVME" => Ok(ChecksumAlgorithm::Crc64nvme),
610 _ => Err(format!("Invalid checksum algorithm: {}", s)),
611 }
612 }
613}
614
615#[derive(Debug)]
616pub enum RequestPayer {
617 Requester,
618}
619impl std::str::FromStr for RequestPayer {
620 type Err = Error;
621 fn from_str(s: &str) -> Result<Self, Self::Err> {
622 match s {
623 "requester" => Ok(RequestPayer::Requester),
624 _ => Err(Error(format!("Invalid RequestPayer value: {}", s))),
625 }
626 }
627}
628#[derive(Debug)]
629pub enum ObjectLockMode {
630 Governance,
631 Compliance,
632}
633impl std::str::FromStr for ObjectLockMode {
634 type Err = Error;
635 fn from_str(s: &str) -> Result<Self, Self::Err> {
636 match s {
637 "GOVERNANCE" => Ok(ObjectLockMode::Governance),
638 "COMPLIANCE" => Ok(ObjectLockMode::Compliance),
639 _ => Err(Error(format!("Invalid ObjectLockMode value: {}", s))),
640 }
641 }
642}
643#[derive(Debug)]
644pub enum ObjectLockLegalHoldStatus {
645 On,
646 Off,
647}
648impl std::str::FromStr for ObjectLockLegalHoldStatus {
649 type Err = Error;
650 fn from_str(s: &str) -> Result<Self, Self::Err> {
651 match s {
652 "ON" => Ok(ObjectLockLegalHoldStatus::On),
653 "OFF" => Ok(ObjectLockLegalHoldStatus::Off),
654 _ => Err(Error(format!(
655 "Invalid ObjectLockLegalHoldStatus value: {}",
656 s
657 ))),
658 }
659 }
660}
661
662pub trait PutObjectHandler {
663 fn handle<'a>(
664 &'a self,
665 opt: &PutObjectOption,
666 bucket: &'a str,
667 object: &'a str,
668 body: &'a mut (dyn tokio::io::AsyncRead + Unpin + Send),
669 ) -> std::pin::Pin<Box<dyn 'a + Send + std::future::Future<Output = Result<(), String>>>>;
670}
671pub async fn handle_put_object<T: VRequest + BodyReader, F: VResponse>(
672 mut v4head: crate::authorization::v4::V4Head,
673 mut req: T,
674 resp: &mut F,
675 handler: &std::sync::Arc<dyn PutObjectHandler + Send + Sync>,
676) {
677 if req.method() != "PUT" {
678 resp.set_status(405);
679 resp.send_header();
680 return;
681 }
682 let url_path = req.url_path();
683 let url_path = url_path.trim_matches('/');
684 let ret = url_path.find('/');
685 if ret.is_none() {
686 resp.set_status(400);
687 resp.send_header();
688 return;
689 }
690 let next = ret.unwrap();
691 let bucket = &url_path[..next];
692 let object = &url_path[next + 1..];
693 let opt = PutObjectOption {
694 cache_control: req.get_header("cache-control"),
695 checksum_algorithm: req
696 .get_header("checksum-algorithm")
697 .and_then(|v| ChecksumAlgorithm::from_str(&v).ok()),
698 checksum_crc32: req.get_header("x-amz-checksum-crc32"),
699 checksum_crc32c: req.get_header("x-amz-checksum-crc32c"),
700 checksum_crc64nvme: req.get_header("x-amz-checksum-crc64vme"),
701 checksum_sha1: req.get_header("x-amz-checksum-sha1"),
702 checksum_sha256: req.get_header("x-amz-checksum-sha256"),
703 content_disposition: req.get_header("content-disposition"),
704 content_encoding: req.get_header("cotent-encoding"),
705 content_language: req.get_header("content-language"),
706 content_length: req
707 .get_header("content-length")
708 .and_then(|v| v.parse::<i64>().map_or(Some(-1), Some)),
709 content_md5: req.get_header("content-md5"),
710 content_type: req.get_header("content-type"),
711 expected_bucket_owner: req.get_header("x-amz-expected-bucket-owner"),
712 expires: req.get_header("expire").and_then(|v| {
713 chrono::NaiveDateTime::parse_from_str(&v, "%a, %d %b %Y %H:%M:%S GMT")
714 .map_or(None, |v| {
715 Some(chrono::DateTime::from_naive_utc_and_offset(v, chrono::Utc))
716 })
717 }),
718 grant_full_control: req.get_header("x-amz-grant-full-control"),
719 grant_read: req.get_header("x-amz-grant-read"),
720 if_match: req.get_header("if-match"),
721 if_none_match: req.get_header("if-none-match"),
722 object_lock_legal_hold_status: req
724 .get_header("x-amz-object-lock-legal-hold-status")
725 .and_then(|v| ObjectLockLegalHoldStatus::from_str(&v).ok()),
726 object_lock_mode: req
727 .get_header("x-amz-object-lock-mode")
728 .and_then(|v| ObjectLockMode::from_str(&v).ok()),
729 object_lock_retain_until_date: req
730 .get_header("x-amz-object-lock-retain_until_date")
731 .and_then(|v| {
732 chrono::NaiveDateTime::parse_from_str(&v, "%a, %d %b %Y %H:%M:%S GMT")
733 .map_or(None, |v| {
734 Some(chrono::DateTime::from_naive_utc_and_offset(v, chrono::Utc))
735 })
736 }),
737 request_payer: req
738 .get_header("x-amz-request-payer")
739 .and_then(|v| RequestPayer::from_str(&v).ok()),
740 storage_class: req.get_header("x-amz-storage-class"),
741 write_offset_bytes: req
744 .get_header("x-amz-write-offset-bytes")
745 .and_then(|v| v.parse::<i64>().ok()),
746 };
747
748 enum ContentSha256 {
750 Hash(String),
751 Streaming,
752 }
753 let content_sha256 = req.get_header("x-amz-content-sha256").map_or_else(
754 || None,
755 |content_sha256| {
756 if content_sha256.as_str() == "STREAMING-AWS4-HMAC-SHA256-PAYLOAD" {
757 Some(ContentSha256::Streaming)
758 } else {
759 Some(ContentSha256::Hash(content_sha256))
760 }
761 },
762 );
763 if content_sha256.is_none() {
764 resp.set_status(403);
765 return;
766 }
767 let content_sha256 = content_sha256.unwrap();
768 let ret = req.get_body_reader().await;
769 if let Err(err) = ret {
770 resp.set_status(500);
771 resp.send_header();
772 log::error!("get body reader error: {err}");
773 return;
774 }
775 let r = ret.unwrap();
776 let ret: Result<(), String> = match content_sha256 {
777 ContentSha256::Hash(cs) => {
778 if opt.content_length.is_none() {
779 resp.set_status(403);
780 resp.send_header();
781 return;
782 }
783 let content_length = opt.content_length.unwrap() as usize;
784 if content_length <= 10 << 20 {
785 let mut buff = vec![0u8; content_length];
786 match parse_body(r, &mut buff, &cs, content_length).await {
787 Ok(_) => {
788 let mut buff = tokio::io::BufReader::new(std::io::Cursor::new(buff));
789 handler.handle(&opt, bucket, object, &mut buff).await
790 }
791 Err(err) => match err {
792 ParseBodyError::HashNoMatch => {
793 log::warn!("put object hash not match");
794 resp.set_status(400);
795 resp.send_header();
796 return;
797 }
798 ParseBodyError::ContentLengthIncorrect => {
799 log::warn!("content length invalid");
800 resp.set_status(400);
801 resp.send_header();
802 return;
803 }
804 ParseBodyError::Io(err) => {
805 log::error!("parse body io error {err}");
806 resp.set_status(500);
807 resp.send_header();
808 return;
809 }
810 },
811 }
812 } else {
813 match tokio::fs::OpenOptions::new()
814 .create_new(true)
815 .write(true)
816 .read(true)
817 .mode(0o644)
818 .open(format!(".sys_bws/{}", cs))
819 .await
820 {
821 Ok(mut fd) => match parse_body(r, &mut fd, &cs, content_length).await {
822 Ok(_) => {
823 if let Err(err) = fd.seek(std::io::SeekFrom::Start(0)).await {
824 log::error!("fd seek failed {err}");
825 resp.set_status(500);
826 resp.send_header();
827 return;
828 }
829 handler.handle(&opt, bucket, object, &mut fd).await
830 }
831 Err(err) => match err {
832 ParseBodyError::HashNoMatch => {
833 log::warn!("put object hash not match");
834 resp.set_status(400);
835 resp.send_header();
836 return;
837 }
838 ParseBodyError::ContentLengthIncorrect => {
839 log::warn!("content length invalid");
840 resp.set_status(400);
841 resp.send_header();
842 return;
843 }
844 ParseBodyError::Io(err) => {
845 log::error!("parse body io error {err}");
846 resp.set_status(500);
847 resp.send_header();
848 return;
849 }
850 },
851 },
852 Err(err) => {
853 log::error!("open local path error {err}");
854 resp.set_status(500);
855 resp.send_header();
856 return;
857 }
858 }
859 }
860 }
861 ContentSha256::Streaming => {
862 let file_name = crate::random_str!(4);
863 let file_name = format!(".sys_bws/{}", file_name);
864 let ret = match tokio::fs::OpenOptions::new()
865 .create_new(true)
866 .write(true)
867 .read(true)
868 .mode(0o644)
869 .open(file_name.as_str())
870 .await
871 {
872 Ok(mut fd) => crate::utils::chunk_parse(r, &mut fd, v4head.hasher()).await,
873 Err(err) => {
874 log::error!("open local temp file error {err}");
875 resp.set_status(500);
876 resp.send_header();
877 return;
878 }
879 };
880 if let Err(err) = ret {
881 tokio::fs::remove_file(file_name.as_str())
882 .await
883 .unwrap_or_else(|err| log::error!("remove file {file_name} error {err}"));
884 match err {
885 crate::utils::ChunkParseError::HashNoMatch => {
886 log::warn!("accept hash no match request");
887 resp.set_status(400);
888 resp.send_header();
889 return;
890 }
891 crate::utils::ChunkParseError::IllegalContent => {
892 log::warn!("accept illegal content request");
893 resp.set_status(400);
894 resp.send_header();
895 return;
896 }
897 crate::utils::ChunkParseError::Io(err) => {
898 log::error!("local io error {err}");
899 resp.set_status(500);
900 resp.send_header();
901 return;
902 }
903 }
904 }
905 match tokio::fs::OpenOptions::new()
906 .read(true)
907 .open(file_name.as_str())
908 .await
909 {
910 Ok(mut fd) => {
911 let ret = handler.handle(&opt, bucket, object, &mut fd).await;
912 tokio::fs::remove_file(file_name.as_str())
913 .await
914 .unwrap_or_else(|err| log::error!("remove file {file_name} error {err}"));
915 ret
916 }
917 Err(err) => {
918 log::error!("open file {file_name} error {err}");
919 resp.set_status(500);
920 resp.send_header();
921 tokio::fs::remove_file(file_name.as_str())
922 .await
923 .unwrap_or_else(|err| log::error!("remove file {file_name} error {err}"));
924 return;
925 }
926 }
927 }
928 };
929 match ret {
931 Ok(_) => {
932 resp.set_status(200);
933 resp.send_header();
934 }
935 Err(err) => {
936 resp.set_status(500);
937 resp.send_header();
938 log::error!("put object handle error: {err}");
939 }
940 }
941}
942pub struct DeleteObjectOption {}
943pub trait DeleteObjectHandler {
944 fn handle<'a>(
945 &'a self,
946 opt: &'a DeleteObjectOption,
947 object: &'a str,
948 ) -> std::pin::Pin<Box<dyn 'a + Send + std::future::Future<Output = Result<(), String>>>>;
949}
950
951pub async fn handle_delete_object<T: VRequest, F: VResponse>(
952 req: T,
953 resp: &mut F,
954 handler: &std::sync::Arc<dyn DeleteObjectHandler + Send + Sync>,
955) {
956 let opt = DeleteObjectOption {};
957 let url_path = req.url_path();
958 if let Err(e) = handler.handle(&opt, url_path.trim_matches('/')).await {
959 resp.set_status(500);
960 log::info!("delete object handler error: {e}");
961 } else {
962 resp.set_status(204);
963 }
964}
965pub struct MultiUploadObjectCompleteOption {
966 pub if_match: Option<String>,
967 pub if_none_match: Option<String>,
968}
969pub trait MultiUploadObjectHandler {
970 fn handle_create_session<'a>(
971 &'a self,
972 bucket: &'a str,
973 key: &'a str,
974 ) -> std::pin::Pin<Box<dyn 'a + Send + std::future::Future<Output = Result<String, ()>>>>;
975 fn handle_upload_part<'a>(
977 &'a self,
978 bucket: &'a str,
979 key: &'a str,
980 upload_id: &'a str,
981 part_number: u32,
982 body: &'a mut (dyn tokio::io::AsyncRead + Unpin + Send),
983 ) -> std::pin::Pin<Box<dyn 'a + Send + std::future::Future<Output = Result<String, ()>>>>;
984 fn handle_complete<'a>(
985 &'a self,
986 bucket: &'a str,
987 key: &'a str,
988 upload_id: &'a str,
989 data: &'a [(&'a str, u32)],
991 opts: MultiUploadObjectCompleteOption,
992 ) -> std::pin::Pin<Box<dyn 'a + Send + std::future::Future<Output = Result<String, ()>>>>;
993 fn handle_abort<'a>(
994 &'a self,
995 bucket: &'a str,
996 key: &'a str,
997 upload_id: &'a str,
998 ) -> std::pin::Pin<Box<dyn 'a + Send + std::future::Future<Output = Result<(), ()>>>>;
999}
1000pub async fn handle_multipart_create_session<T: VRequest, F: VResponse>(
1001 req: T,
1002 resp: &mut F,
1003 handler: &std::sync::Arc<dyn MultiUploadObjectHandler + Send + Sync>,
1004) {
1005 let raw_path = req.url_path();
1006 let raw = raw_path
1007 .trim_start_matches('/')
1008 .splitn(2, '/')
1009 .collect::<Vec<&str>>();
1010 if raw.len() != 2 {
1011 resp.set_status(400);
1012 resp.send_header();
1013 return;
1014 }
1015 let bucket = raw[0];
1016 let key = raw[1];
1017 match handler.handle_create_session(bucket, key).await {
1018 Ok(upload_id) => {
1019 #[derive(Debug, serde::Serialize)]
1020 #[serde(rename_all = "PascalCase")]
1021 pub struct MultipartInitResponse<'a> {
1022 #[serde(rename = "Bucket")]
1023 pub bucket: &'a str,
1024 #[serde(rename = "Key")]
1025 pub key: &'a str,
1026 #[serde(rename = "UploadId")]
1027 pub upload_id: &'a str,
1028 }
1029 let r = MultipartInitResponse {
1030 bucket,
1031 key,
1032 upload_id: &upload_id,
1033 };
1034 let is_err = match quick_xml::se::to_string(&r) {
1035 Ok(content) => match resp.get_body_writer().await {
1036 Ok(mut w) => {
1037 let _ = w.poll_write(content.as_bytes()).await;
1038 None
1039 }
1040 Err(err) => {
1041 log::error!("get body writer error {err}");
1042 Some(())
1043 }
1044 },
1045 Err(err) => {
1046 log::error!("xml encode error {err}");
1047 Some(())
1048 }
1049 };
1050 if is_err.is_some() {
1051 resp.set_status(500);
1052 resp.send_header();
1053 }
1054 }
1055 Err(_) => {
1056 log::error!("handle create session error");
1057 resp.set_status(500);
1058 resp.send_header();
1059 }
1060 }
1061}
1062pub async fn handle_multipart_upload_part<T: VRequest + BodyReader + HeaderTaker, F: VResponse>(
1063 req: T,
1064 resp: &mut F,
1065 handler: &std::sync::Arc<dyn MultiUploadObjectHandler + Send + Sync>,
1066) {
1067 let upload_id = req.get_query("uploadId");
1068 let part_number = req.get_query("partNumber");
1069 if upload_id.is_none() || part_number.is_none() {
1070 resp.set_status(400);
1071 } else {
1072 let ret = u32::from_str_radix(part_number.unwrap().as_str(), 10);
1073 if ret.is_err() {
1074 resp.set_status(400);
1075 return;
1076 }
1077 let part_number = ret.unwrap();
1078 let raw_path = req.url_path();
1079 let raw = raw_path
1080 .trim_start_matches('/')
1081 .splitn(2, '/')
1082 .collect::<Vec<&str>>();
1083 if raw.len() != 2 {
1084 resp.set_status(400);
1085 return;
1086 }
1087 let header = req.take_header();
1088 let body_reader = match req.get_body_reader().await {
1089 Ok(body_reader) => body_reader,
1090 Err(err) => {
1091 log::error!("get body reader failed {err}");
1092 resp.set_status(500);
1093 resp.send_header();
1094 return;
1095 }
1096 };
1097 let (body, release) = match get_body_stream(body_reader, &header).await {
1098 Ok(data) => data,
1099 Err(err) => {
1100 log::error!("get body stream error {err}");
1101 resp.set_status(500);
1102 resp.send_header();
1103 return;
1104 }
1105 };
1106 let ret = match body {
1107 StreamType::File(mut file) => {
1108 handler
1109 .handle_upload_part(
1110 raw[0],
1111 raw[1],
1112 upload_id.unwrap().as_str(),
1113 part_number,
1114 &mut file,
1115 )
1116 .await
1117 }
1118 StreamType::Buff(mut buf_reader) => {
1119 handler
1120 .handle_upload_part(
1121 raw[0],
1122 raw[1],
1123 upload_id.unwrap().as_str(),
1124 part_number,
1125 &mut buf_reader,
1126 )
1127 .await
1128 }
1129 };
1130 if let Some(release) = release {
1131 release.await;
1132 }
1133 if let Ok(etag) = ret {
1134 resp.set_header("etag", &etag);
1135 } else {
1136 resp.set_status(500);
1137 resp.send_header();
1138 }
1139 }
1140}
1141pub async fn handle_multipart_complete_session<T: VRequestPlus, F: VResponse>(
1142 req: T,
1143 resp: &mut F,
1144 handler: &std::sync::Arc<dyn MultiUploadObjectHandler + Send + Sync>,
1145) {
1146 let raw_path = req.url_path();
1147 let raw = raw_path
1148 .trim_start_matches('/')
1149 .splitn(2, '/')
1150 .collect::<Vec<&str>>();
1151 if raw.len() != 2 {
1152 resp.set_status(400);
1153 resp.send_header();
1154 return;
1155 }
1156 let bucket = raw[0];
1157 let key = raw[1];
1158 let upload_id = req.get_query("uploadId");
1159 if let Some(upload_id) = upload_id {
1160 #[derive(Debug, serde::Deserialize)]
1161 #[serde(rename_all = "PascalCase")]
1162 pub struct CompleteMultiPartUploadRequest {
1163 #[serde(rename = "Part")]
1164 pub parts: Vec<CompletedPart>,
1165 }
1166 #[derive(Debug, serde::Deserialize)]
1167 #[serde(rename_all = "PascalCase")]
1168 pub struct CompletedPart {
1169 #[serde(rename = "ETag")]
1170 pub etag: String,
1171 #[serde(rename = "PartNumber")]
1172 pub part_number: u32,
1173 }
1174 match req.body().await {
1175 Ok(body) => {
1176 match quick_xml::de::from_str::<CompleteMultiPartUploadRequest>(unsafe {
1177 std::str::from_utf8_unchecked(&body)
1178 }) {
1179 Ok(upload_request) => {
1180 let data = upload_request
1181 .parts
1182 .iter()
1183 .map(|data| (data.etag.as_str(), data.part_number))
1184 .collect::<Vec<(&str, u32)>>();
1185 match handler
1186 .handle_complete(
1187 bucket,
1188 key,
1189 &upload_id,
1190 &data,
1191 MultiUploadObjectCompleteOption {
1192 if_match: None,
1193 if_none_match: None,
1194 },
1195 )
1196 .await
1197 {
1198 Ok(etag) => {
1199 use serde::{Deserialize, Serialize};
1200 #[derive(Debug, Serialize, Deserialize)]
1201 #[serde(rename_all = "PascalCase")]
1202 pub struct CompleteMultipartUploadResponse<'a> {
1203 #[serde(rename = "Location")]
1204 pub location: &'a str,
1205 #[serde(rename = "Bucket")]
1206 pub bucket: &'a str,
1207 #[serde(rename = "Key")]
1208 pub key: &'a str,
1209 #[serde(rename = "ETag")]
1210 pub etag: &'a str,
1211 }
1212 let r = CompleteMultipartUploadResponse {
1213 location: "",
1214 bucket,
1215 key,
1216 etag: &etag,
1217 };
1218 match quick_xml::se::to_string(&r) {
1219 Ok(content) => {
1220 let err = match resp.get_body_writer().await {
1221 Ok(mut w) => {
1222 let _ = w.poll_write(content.as_bytes()).await;
1223 None
1224 }
1225 Err(err) => Some(err),
1226 };
1227 if let Some(err) = err {
1228 log::error!("get body writer error {err}");
1229 resp.set_status(500);
1230 resp.send_header();
1231 }
1232 }
1233 Err(err) => {
1234 log::error!("quick xml encode error {err}");
1235 resp.set_status(500);
1236 resp.send_header();
1237 }
1238 }
1239 }
1240 Err(_) => {
1241 log::error!("handle_complete error");
1242 resp.set_status(500);
1243 resp.send_header();
1244 }
1245 }
1246 }
1247 Err(_) => {
1248 resp.set_status(400);
1249 resp.send_header();
1250 }
1251 }
1252 }
1253 Err(err) => {
1254 log::error!("read body error {err}");
1255 resp.set_status(500);
1256 resp.send_header();
1257 }
1258 }
1259 } else {
1260 resp.set_status(400);
1261 resp.send_header();
1262 }
1263}
1264pub async fn handle_multipart_abort_session<T: VRequest, F: VResponse>(
1265 req: T,
1266 resp: &mut F,
1267 handler: &std::sync::Arc<dyn MultiUploadObjectHandler + Send + Sync>,
1268) {
1269 todo!()
1270}
1271pub struct CreateBucketOption {
1272 pub grant_full_control: Option<String>,
1273 pub grant_read: Option<String>,
1274 pub grant_read_acp: Option<String>,
1275 pub grant_write: Option<String>,
1276 pub grant_write_acp: Option<String>,
1277 pub object_lock_enabled_for_bucket: Option<bool>,
1278 pub object_ownership: Option<ObjectOwnership>,
1279}
1280pub enum ObjectOwnership {
1281 BucketOwnerPreferred,
1282 ObjectWriter,
1283 BucketOwnerEnforced,
1284}
1285impl FromStr for ObjectOwnership {
1286 type Err = Error;
1287
1288 fn from_str(s: &str) -> Result<Self, Self::Err> {
1289 match s {
1290 "BucketOwnerPreferred" => Ok(ObjectOwnership::BucketOwnerPreferred),
1291 "ObjectWriter" => Ok(ObjectOwnership::ObjectWriter),
1292 "BucketOwnerEnforced" => Ok(ObjectOwnership::BucketOwnerEnforced),
1293 _ => Err(Error(s.to_string())),
1294 }
1295 }
1296}
1297pub struct CreateBucketConfiguration {
1298 pub bucket: Option<BucketInfo>,
1299 pub location: Option<LocationInfo>,
1300 pub location_constraint: Option<BucketLocationConstraint>,
1301}
1302pub struct BucketInfo {
1303 pub data_redundancy: DataRedundancy,
1304 pub bucket_type: BucketType,
1305}
1306#[derive(Debug, Clone, PartialEq, Eq)]
1307pub enum DataRedundancy {
1308 SingleAvailabilityZone,
1309 SingleLocalZone,
1310 Unknown(String),
1311}
1312
1313impl From<&str> for DataRedundancy {
1314 fn from(s: &str) -> Self {
1315 match s {
1316 "SingleAvailabilityZone" => Self::SingleAvailabilityZone,
1317 "SingleLocalZone" => Self::SingleLocalZone,
1318 other => Self::Unknown(other.to_string()),
1319 }
1320 }
1321}
1322impl Display for DataRedundancy {
1323 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1324 f.write_str(
1325 match self {
1326 DataRedundancy::SingleAvailabilityZone => "SingleAvailabilityZone".to_string(),
1327 DataRedundancy::SingleLocalZone => "SingleLocalZone".to_string(),
1328 DataRedundancy::Unknown(s) => s.clone(),
1329 }
1330 .as_str(),
1331 )
1332 }
1333}
1334#[derive(Debug, Clone, PartialEq, Eq)]
1335pub enum BucketType {
1336 Directory,
1337 Unknown(String),
1338}
1339
1340impl From<&str> for BucketType {
1341 fn from(s: &str) -> Self {
1342 match s {
1343 "Directory" => Self::Directory,
1344 other => Self::Unknown(other.to_string()),
1345 }
1346 }
1347}
1348
1349impl Display for BucketType {
1350 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1351 f.write_str(
1352 match self {
1353 Self::Directory => "Directory".to_string(),
1354 Self::Unknown(s) => s.clone(),
1355 }
1356 .as_str(),
1357 )
1358 }
1359}
1360
1361pub struct LocationInfo {
1362 pub name: Option<String>,
1363 pub location_type: LocationType,
1364}
1365
1366pub enum LocationType {
1367 AvailabilityZone,
1368 LocalZone,
1369}
1370#[derive(Debug, Clone, PartialEq, Eq)]
1371pub enum BucketLocationConstraint {
1372 AfSouth1,
1373 ApEast1,
1374 ApNortheast1,
1375 ApNortheast2,
1376 ApNortheast3,
1377 ApSouth1,
1378 ApSouth2,
1379 ApSoutheast1,
1380 ApSoutheast2,
1381 ApSoutheast3,
1382 ApSoutheast4,
1383 ApSoutheast5,
1384 CaCentral1,
1385 CnNorth1,
1386 CnNorthwest1,
1387 Eu,
1388 EuCentral1,
1389 EuCentral2,
1390 EuNorth1,
1391 EuSouth1,
1392 EuSouth2,
1393 EuWest1,
1394 EuWest2,
1395 EuWest3,
1396 IlCentral1,
1397 MeCentral1,
1398 MeSouth1,
1399 SaEast1,
1400 UsEast2,
1401 UsGovEast1,
1402 UsGovWest1,
1403 UsWest1,
1404 UsWest2,
1405 Unknown(String),
1406}
1407impl From<&str> for BucketLocationConstraint {
1408 fn from(s: &str) -> Self {
1409 match s {
1410 "af-south-1" => Self::AfSouth1,
1411 "ap-east-1" => Self::ApEast1,
1412 "ap-northeast-1" => Self::ApNortheast1,
1413 "ap-northeast-2" => Self::ApNortheast2,
1414 "ap-northeast-3" => Self::ApNortheast3,
1415 "ap-south-1" => Self::ApSouth1,
1416 "ap-south-2" => Self::ApSouth2,
1417 "ap-southeast-1" => Self::ApSoutheast1,
1418 "ap-southeast-2" => Self::ApSoutheast2,
1419 "ap-southeast-3" => Self::ApSoutheast3,
1420 "ap-southeast-4" => Self::ApSoutheast4,
1421 "ap-southeast-5" => Self::ApSoutheast5,
1422 "ca-central-1" => Self::CaCentral1,
1423 "cn-north-1" => Self::CnNorth1,
1424 "cn-northwest-1" => Self::CnNorthwest1,
1425 "EU" => Self::Eu,
1426 "eu-central-1" => Self::EuCentral1,
1427 "eu-central-2" => Self::EuCentral2,
1428 "eu-north-1" => Self::EuNorth1,
1429 "eu-south-1" => Self::EuSouth1,
1430 "eu-south-2" => Self::EuSouth2,
1431 "eu-west-1" => Self::EuWest1,
1432 "eu-west-2" => Self::EuWest2,
1433 "eu-west-3" => Self::EuWest3,
1434 "il-central-1" => Self::IlCentral1,
1435 "me-central-1" => Self::MeCentral1,
1436 "me-south-1" => Self::MeSouth1,
1437 "sa-east-1" => Self::SaEast1,
1438 "us-east-2" => Self::UsEast2,
1439 "us-gov-east-1" => Self::UsGovEast1,
1440 "us-gov-west-1" => Self::UsGovWest1,
1441 "us-west-1" => Self::UsWest1,
1442 "us-west-2" => Self::UsWest2,
1443 other => Self::Unknown(other.to_string()),
1444 }
1445 }
1446}
1447
1448impl Display for BucketLocationConstraint {
1449 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1450 f.write_str(match self {
1451 Self::AfSouth1 => "af-south-1",
1452 Self::ApEast1 => "ap-east-1",
1453 Self::ApNortheast1 => "ap-northeast-1",
1454 Self::ApNortheast2 => "ap-northeast-2",
1455 Self::ApNortheast3 => "ap-northeast-3",
1456 Self::ApSouth1 => "ap-south-1",
1457 Self::ApSouth2 => "ap-south-2",
1458 Self::ApSoutheast1 => "ap-southeast-1",
1459 Self::ApSoutheast2 => "ap-southeast-2",
1460 Self::ApSoutheast3 => "ap-southeast-3",
1461 Self::ApSoutheast4 => "ap-southeast-4",
1462 Self::ApSoutheast5 => "ap-southeast-5",
1463 Self::CaCentral1 => "ca-central-1",
1464 Self::CnNorth1 => "cn-north-1",
1465 Self::CnNorthwest1 => "cn-northwest-1",
1466 Self::Eu => "EU",
1467 Self::EuCentral1 => "eu-central-1",
1468 Self::EuCentral2 => "eu-central-2",
1469 Self::EuNorth1 => "eu-north-1",
1470 Self::EuSouth1 => "eu-south-1",
1471 Self::EuSouth2 => "eu-south-2",
1472 Self::EuWest1 => "eu-west-1",
1473 Self::EuWest2 => "eu-west-2",
1474 Self::EuWest3 => "eu-west-3",
1475 Self::IlCentral1 => "il-central-1",
1476 Self::MeCentral1 => "me-central-1",
1477 Self::MeSouth1 => "me-south-1",
1478 Self::SaEast1 => "sa-east-1",
1479 Self::UsEast2 => "us-east-2",
1480 Self::UsGovEast1 => "us-gov-east-1",
1481 Self::UsGovWest1 => "us-gov-west-1",
1482 Self::UsWest1 => "us-west-1",
1483 Self::UsWest2 => "us-west-2",
1484 Self::Unknown(s) => s,
1485 })
1486 }
1487}
1488
1489pub trait CreateBucketHandler {
1490 fn handle<'a>(
1491 &'a self,
1492 opt: &'a CreateBucketOption,
1493 bucket: &'a str,
1494 ) -> std::pin::Pin<Box<dyn 'a + Send + std::future::Future<Output = Result<(), String>>>>;
1495}
1496pub async fn handle_create_bucket<T: VRequest, F: VResponse>(
1497 req: T,
1498 resp: &mut F,
1499 handler: &std::sync::Arc<dyn CreateBucketHandler + Send + Sync>,
1500) {
1501 if req.method() != "PUT" {
1502 resp.set_status(405);
1503 resp.send_header();
1504 return;
1505 }
1506
1507 let opt = CreateBucketOption {
1508 grant_full_control: req.get_header("x-amz-grant-full-control"),
1509 grant_read: req.get_header("x-amz-grant-read"),
1510 grant_read_acp: req.get_header("x-amz-grant-read-acp"),
1511 grant_write: req.get_header("x-amz-grant-write"),
1512 grant_write_acp: req.get_header("x-amz-grant-write-acp"),
1513 object_lock_enabled_for_bucket: req
1514 .get_header("x-amz-bucket-object-lock-enabled")
1515 .and_then(|v| {
1516 if v == "true" {
1517 Some(true)
1518 } else if v == "false" {
1519 Some(false)
1520 } else {
1521 None
1522 }
1523 }),
1524 object_ownership: req
1525 .get_header("x-amz-object-ownership")
1526 .and_then(|v| v.parse().ok()),
1527 };
1528 let url_path = req.url_path();
1529 if let Err(e) = handler.handle(&opt, url_path.trim_matches('/')).await {
1530 resp.set_status(500);
1531 log::info!("delete object handler error: {e}")
1532 }
1533}
1534
1535pub struct DeleteBucketOption {
1536 pub expected_owner: Option<String>,
1537}
1538pub trait DeleteBucketHandler {
1539 fn handle<'a>(
1540 &'a self,
1541 opt: &'a DeleteBucketOption,
1542 bucket: &'a str,
1543 ) -> std::pin::Pin<Box<dyn 'a + Send + std::future::Future<Output = Result<(), String>>>>;
1544}
1545
1546pub async fn handle_delete_bucket<T: VRequest, F: VResponse>(
1547 req: T,
1548 resp: &mut F,
1549 handler: &std::sync::Arc<dyn DeleteBucketHandler + Send + Sync>,
1550) {
1551 if req.method() != "DELETE" {
1552 resp.set_status(405);
1553 resp.send_header();
1554 return;
1555 }
1556 let opt = DeleteBucketOption {
1557 expected_owner: req.get_header("x-amz-expected-bucket-owner"),
1558 };
1559 let url_path = req.url_path();
1560 match handler.handle(&opt, url_path.trim_matches('/')).await {
1561 Ok(_) => {
1562 resp.set_status(204);
1563 resp.send_header();
1564 }
1565 Err(e) => {
1566 resp.set_status(500);
1567 log::error!("delete object handler error: {e}")
1568 }
1569 }
1570}
1571
1572#[derive(Debug)]
1574enum ParseBodyError {
1575 HashNoMatch,
1576 ContentLengthIncorrect,
1577 Io(String),
1578}
1579impl Display for ParseBodyError {
1580 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1581 f.write_str(match self {
1582 ParseBodyError::HashNoMatch => "hash no match",
1583 ParseBodyError::ContentLengthIncorrect => "content length incorrect",
1584 ParseBodyError::Io(err) => err.as_str(),
1585 })
1586 }
1587}
1588impl std::error::Error for ParseBodyError {}
1589enum StreamType {
1590 File(tokio::fs::File),
1591 Buff(tokio::io::BufReader<std::io::Cursor<Vec<u8>>>),
1592}
1593async fn get_body_stream<
1594 T: crate::utils::io::PollRead + Send,
1595 H: crate::authorization::v4::VHeader,
1596>(
1597 src: T,
1598 header: &H,
1599) -> Result<
1600 (
1601 StreamType,
1602 Option<std::pin::Pin<Box<dyn Send + std::future::Future<Output = ()>>>>,
1603 ),
1604 ParseBodyError,
1605> {
1606 let cl = header.get_header("content-length");
1607 let acs = header
1608 .get_header("x-amz-content-sha256")
1609 .ok_or(ParseBodyError::HashNoMatch)?;
1610 if let Some(cl) = cl {
1611 let cl = cl
1612 .as_str()
1613 .parse::<usize>()
1614 .or(Err(ParseBodyError::ContentLengthIncorrect))?;
1615 if acs.as_str() != "STREAMING-AWS4-HMAC-SHA256-PAYLOAD" {
1616 if cl <= 10 << 20 {
1617 let mut buff = vec![0u8; cl];
1618 parse_body(src, &mut buff, &acs, cl).await?;
1619 return Ok((
1620 StreamType::Buff(tokio::io::BufReader::new(std::io::Cursor::new(buff))),
1621 None,
1622 ));
1623 } else {
1624 let file_name = format!(".sys_bws/{}", crate::random_str!(4));
1625 let mut fd = tokio::fs::OpenOptions::new()
1626 .create_new(true)
1627 .write(true)
1628 .mode(0o644)
1629 .open(file_name.as_str())
1630 .await
1631 .map_err(|err| ParseBodyError::Io(err.to_string()))?;
1632 parse_body(src, &mut fd, &acs, cl).await?;
1633 drop(fd);
1634 match tokio::fs::OpenOptions::new()
1635 .read(true)
1636 .open(file_name.as_str())
1637 .await
1638 {
1639 Ok(fd) => {
1640 return Ok((
1641 StreamType::File(fd),
1642 Some({
1643 Box::pin(async move {
1644 let _ = tokio::fs::remove_file(file_name.as_str()).await;
1645 })
1646 }),
1647 ))
1648 }
1649 Err(err) => {
1650 let _ = tokio::fs::remove_file(file_name.as_str()).await;
1651 return Err(ParseBodyError::Io(err.to_string()));
1652 }
1653 }
1654 }
1656 }
1657 }
1658 todo!()
1660}
1661
1662async fn parse_body<
1663 T: crate::utils::io::PollRead + Send,
1664 E: tokio::io::AsyncWrite + Send + Unpin,
1665>(
1666 mut src: T,
1667 dst: &mut E,
1668 content_sha256: &str,
1669 mut content_length: usize,
1670) -> Result<(), ParseBodyError> {
1671 use tokio::io::AsyncWriteExt;
1672 let mut hsh = sha2::Sha256::new();
1673 while let Some(buff) = src.poll_read().await.map_err(ParseBodyError::Io)? {
1675 let buff_len = buff.len();
1676 if content_length < buff_len {
1677 return Err(ParseBodyError::ContentLengthIncorrect);
1678 }
1679 content_length -= buff_len;
1680 let _ = hsh.write_all(&buff);
1681 dst.write_all(&buff)
1682 .await
1683 .map_err(|err| ParseBodyError::Io(format!("write error {err}")))?;
1684 }
1685 let ret = hsh.finalize();
1686 let real_sha256 = hex::encode(ret);
1687 if real_sha256.as_str() != content_sha256 {
1688 Err(ParseBodyError::HashNoMatch)
1689 } else {
1690 Ok(())
1691 }
1692}
1693
1694async fn parse_streaming_body<
1695 T: crate::utils::io::PollRead + Send,
1696 E: tokio::io::AsyncWrite + Send + Unpin,
1697>(
1698 mut src: T,
1699 dst: &mut E,
1700 content_sha256: &str,
1701) -> Result<(), ParseBodyError> {
1702 todo!()
1703}
1704