1use std::collections::HashMap;
2
3use crate::serde_types::{CompleteMultipartUploadData, CorsConfiguration};
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, strum_macros::Display)]
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 ListBuckets,
132 PutBucketCors {
133 configuration: CorsConfiguration,
134 },
135}
136
137impl<'a> Command<'a> {
138 pub fn http_verb(&self) -> HttpMethod {
139 match *self {
140 Command::GetObject
141 | Command::GetObjectTorrent
142 | Command::GetObjectRange { .. }
143 | Command::ListBuckets
144 | Command::ListObjects { .. }
145 | Command::ListObjectsV2 { .. }
146 | Command::GetBucketLocation
147 | Command::GetObjectTagging
148 | Command::ListMultipartUploads { .. }
149 | Command::PresignGet { .. } => HttpMethod::Get,
150 Command::PutObject { .. }
151 | Command::CopyObject { from: _ }
152 | Command::PutObjectTagging { .. }
153 | Command::PresignPut { .. }
154 | Command::UploadPart { .. }
155 | Command::PutBucketCors { .. }
156 | Command::CreateBucket { .. } => HttpMethod::Put,
157 Command::DeleteObject
158 | Command::DeleteObjectTagging
159 | Command::AbortMultipartUpload { .. }
160 | Command::PresignDelete { .. }
161 | Command::DeleteBucket => HttpMethod::Delete,
162 Command::InitiateMultipartUpload { .. } | Command::CompleteMultipartUpload { .. } => {
163 HttpMethod::Post
164 }
165 Command::HeadObject => HttpMethod::Head,
166 Command::PresignPost { .. } => HttpMethod::Post,
167 }
168 }
169
170 pub fn content_length(&self) -> usize {
171 match &self {
172 Command::CopyObject { from: _ } => 0,
173 Command::PutObject { content, .. } => content.len(),
174 Command::PutObjectTagging { tags } => tags.len(),
175 Command::UploadPart { content, .. } => content.len(),
176 Command::CompleteMultipartUpload { data, .. } => data.len(),
177 Command::CreateBucket { config } => {
178 if let Some(payload) = config.location_constraint_payload() {
179 Vec::from(payload).len()
180 } else {
181 0
182 }
183 }
184 _ => 0,
185 }
186 }
187
188 pub fn content_type(&self) -> String {
189 match self {
190 Command::InitiateMultipartUpload { content_type } => content_type.to_string(),
191 Command::PutObject { content_type, .. } => content_type.to_string(),
192 Command::CompleteMultipartUpload { .. } => "application/xml".into(),
193 _ => "text/plain".into(),
194 }
195 }
196
197 pub fn sha256(&self) -> String {
198 match &self {
199 Command::PutObject { content, .. } => {
200 let mut sha = Sha256::default();
201 sha.update(content);
202 hex::encode(sha.finalize().as_slice())
203 }
204 Command::PutObjectTagging { tags } => {
205 let mut sha = Sha256::default();
206 sha.update(tags.as_bytes());
207 hex::encode(sha.finalize().as_slice())
208 }
209 Command::CompleteMultipartUpload { data, .. } => {
210 let mut sha = Sha256::default();
211 sha.update(data.to_string().as_bytes());
212 hex::encode(sha.finalize().as_slice())
213 }
214 Command::CreateBucket { config } => {
215 if let Some(payload) = config.location_constraint_payload() {
216 let mut sha = Sha256::default();
217 sha.update(payload.as_bytes());
218 hex::encode(sha.finalize().as_slice())
219 } else {
220 EMPTY_PAYLOAD_SHA.into()
221 }
222 }
223 _ => EMPTY_PAYLOAD_SHA.into(),
224 }
225 }
226}