1use std::collections::HashMap;
2
3use crate::serde_types::CompleteMultipartUploadData;
4
5use crate::EMPTY_PAYLOAD_SHA;
6use sha2::{Digest, Sha256};
7
8pub enum HttpMethod {
9 Delete,
10 Get,
11 Put,
12 Post,
13 Head,
14}
15
16use std::fmt;
17
18impl fmt::Display for HttpMethod {
19 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
20 match self {
21 HttpMethod::Delete => write!(f, "DELETE"),
22 HttpMethod::Get => write!(f, "GET"),
23 HttpMethod::Post => write!(f, "POST"),
24 HttpMethod::Put => write!(f, "PUT"),
25 HttpMethod::Head => write!(f, "HEAD"),
26 }
27 }
28}
29use crate::bucket_ops::BucketConfiguration;
30use http::HeaderMap;
31
32#[derive(Clone, Debug)]
33pub struct Multipart<'a> {
34 part_number: u32,
35 upload_id: &'a str,
36}
37
38impl<'a> Multipart<'a> {
39 pub fn query_string(&self) -> String {
40 format!(
41 "?partNumber={}&uploadId={}",
42 self.part_number, self.upload_id
43 )
44 }
45
46 pub fn new(part_number: u32, upload_id: &'a str) -> Self {
47 Multipart {
48 part_number,
49 upload_id,
50 }
51 }
52}
53
54#[derive(Clone, Debug)]
55pub enum Command<'a> {
56 HeadObject,
57 CopyObject {
58 from: &'a str,
59 },
60 DeleteObject,
61 DeleteObjectTagging,
62 GetObject,
63 GetObjectTorrent,
64 GetObjectRange {
65 start: u64,
66 end: Option<u64>,
67 },
68 GetObjectTagging,
69 PutObject {
70 content: &'a [u8],
71 content_type: &'a str,
72 multipart: Option<Multipart<'a>>,
73 },
74 PutObjectTagging {
75 tags: &'a str,
76 },
77 ListMultipartUploads {
78 prefix: Option<&'a str>,
79 delimiter: Option<&'a str>,
80 key_marker: Option<String>,
81 max_uploads: Option<usize>,
82 },
83 ListObjects {
84 prefix: String,
85 delimiter: Option<String>,
86 marker: Option<String>,
87 max_keys: Option<usize>,
88 },
89 ListObjectsV2 {
90 prefix: String,
91 delimiter: Option<String>,
92 continuation_token: Option<String>,
93 start_after: Option<String>,
94 max_keys: Option<usize>,
95 },
96 GetBucketLocation,
97 PresignGet {
98 expiry_secs: u32,
99 custom_queries: Option<HashMap<String, String>>,
100 },
101 PresignPut {
102 expiry_secs: u32,
103 custom_headers: Option<HeaderMap>,
104 },
105 PresignPost {
106 expiry_secs: u32,
107 post_policy: String,
108 },
109 PresignDelete {
110 expiry_secs: u32,
111 },
112 InitiateMultipartUpload {
113 content_type: &'a str,
114 },
115 UploadPart {
116 part_number: u32,
117 content: &'a [u8],
118 upload_id: &'a str,
119 },
120 AbortMultipartUpload {
121 upload_id: &'a str,
122 },
123 CompleteMultipartUpload {
124 upload_id: &'a str,
125 data: CompleteMultipartUploadData,
126 },
127 CreateBucket {
128 config: BucketConfiguration,
129 },
130 DeleteBucket,
131}
132
133impl<'a> Command<'a> {
134 pub fn http_verb(&self) -> HttpMethod {
135 match *self {
136 Command::GetObject
137 | Command::GetObjectTorrent
138 | Command::GetObjectRange { .. }
139 | Command::ListObjects { .. }
140 | Command::ListObjectsV2 { .. }
141 | Command::GetBucketLocation
142 | Command::GetObjectTagging
143 | Command::ListMultipartUploads { .. }
144 | Command::PresignGet { .. } => HttpMethod::Get,
145 Command::PutObject { .. }
146 | Command::CopyObject { from: _ }
147 | Command::PutObjectTagging { .. }
148 | Command::PresignPut { .. }
149 | Command::UploadPart { .. }
150 | Command::CreateBucket { .. } => HttpMethod::Put,
151 Command::DeleteObject
152 | Command::DeleteObjectTagging
153 | Command::AbortMultipartUpload { .. }
154 | Command::PresignDelete { .. }
155 | Command::DeleteBucket => HttpMethod::Delete,
156 Command::InitiateMultipartUpload { .. } | Command::CompleteMultipartUpload { .. } => {
157 HttpMethod::Post
158 }
159 Command::HeadObject => HttpMethod::Head,
160 Command::PresignPost { .. } => HttpMethod::Post,
161 }
162 }
163
164 pub fn content_length(&self) -> usize {
165 match &self {
166 Command::CopyObject { from: _ } => 0,
167 Command::PutObject { content, .. } => content.len(),
168 Command::PutObjectTagging { tags } => tags.len(),
169 Command::UploadPart { content, .. } => content.len(),
170 Command::CompleteMultipartUpload { data, .. } => data.len(),
171 Command::CreateBucket { config } => {
172 if let Some(payload) = config.location_constraint_payload() {
173 Vec::from(payload).len()
174 } else {
175 0
176 }
177 }
178 _ => 0,
179 }
180 }
181
182 pub fn content_type(&self) -> String {
183 match self {
184 Command::InitiateMultipartUpload { content_type } => content_type.to_string(),
185 Command::PutObject { content_type, .. } => content_type.to_string(),
186 Command::CompleteMultipartUpload { .. } => "application/xml".into(),
187 _ => "text/plain".into(),
188 }
189 }
190
191 pub fn sha256(&self) -> String {
192 match &self {
193 Command::PutObject { content, .. } => {
194 let mut sha = Sha256::default();
195 sha.update(content);
196 hex::encode(sha.finalize().as_slice())
197 }
198 Command::PutObjectTagging { tags } => {
199 let mut sha = Sha256::default();
200 sha.update(tags.as_bytes());
201 hex::encode(sha.finalize().as_slice())
202 }
203 Command::CompleteMultipartUpload { data, .. } => {
204 let mut sha = Sha256::default();
205 sha.update(data.to_string().as_bytes());
206 hex::encode(sha.finalize().as_slice())
207 }
208 Command::CreateBucket { config } => {
209 if let Some(payload) = config.location_constraint_payload() {
210 let mut sha = Sha256::default();
211 sha.update(payload.as_bytes());
212 hex::encode(sha.finalize().as_slice())
213 } else {
214 EMPTY_PAYLOAD_SHA.into()
215 }
216 }
217 _ => EMPTY_PAYLOAD_SHA.into(),
218 }
219 }
220}