1extern crate alloc;
19use crate::s3::utils::get_default_text;
20use bytes::{Buf, Bytes};
21use http::HeaderMap;
22use std::fmt;
23use xmltree::Element;
24
25#[derive(Clone, Debug, Default, PartialEq)]
26pub enum ErrorCode {
27 #[default]
28 NoError,
29
30 PermanentRedirect,
31 Redirect,
32 BadRequest,
33 RetryHead,
34 NoSuchBucket,
35 NoSuchBucketPolicy,
36 ReplicationConfigurationNotFoundError,
37 ServerSideEncryptionConfigurationNotFoundError,
38 NoSuchTagSet,
39 NoSuchObjectLockConfiguration,
40 NoSuchLifecycleConfiguration,
41 NoSuchKey,
42 ResourceNotFound,
43 MethodNotAllowed,
44 ResourceConflict,
45 AccessDenied,
46 NotSupported,
47 BucketNotEmpty,
48 BucketAlreadyOwnedByYou,
49 InvalidWriteOffset,
50
51 OtherError(String),
52}
53
54impl ErrorCode {
55 pub fn parse(s: &str) -> Self {
56 match s.to_lowercase().as_str() {
57 "permanentredirect" => ErrorCode::PermanentRedirect,
58 "redirect" => ErrorCode::Redirect,
59 "badrequest" => ErrorCode::BadRequest,
60 "retryhead" => ErrorCode::RetryHead,
61 "nosuchbucket" => ErrorCode::NoSuchBucket,
62 "nosuchbucketpolicy" => ErrorCode::NoSuchBucketPolicy,
63 "replicationconfigurationnotfounderror" => {
64 ErrorCode::ReplicationConfigurationNotFoundError
65 }
66 "serversideencryptionconfigurationnotfounderror" => {
67 ErrorCode::ServerSideEncryptionConfigurationNotFoundError
68 }
69 "nosuchtagset" => ErrorCode::NoSuchTagSet,
70 "nosuchobjectlockconfiguration" => ErrorCode::NoSuchObjectLockConfiguration,
71 "nosuchlifecycleconfiguration" => ErrorCode::NoSuchLifecycleConfiguration,
72 "nosuchkey" => ErrorCode::NoSuchKey,
73 "resourcenotfound" => ErrorCode::ResourceNotFound,
74 "methodnotallowed" => ErrorCode::MethodNotAllowed,
75 "resourceconflict" => ErrorCode::ResourceConflict,
76 "accessdenied" => ErrorCode::AccessDenied,
77 "notsupported" => ErrorCode::NotSupported,
78 "bucketnotempty" => ErrorCode::BucketNotEmpty,
79 "bucketalreadyownedbyyou" => ErrorCode::BucketAlreadyOwnedByYou,
80 "invalidwriteoffset" => ErrorCode::InvalidWriteOffset,
81
82 v => ErrorCode::OtherError(v.to_owned()),
83 }
84 }
85}
86
87#[derive(Clone, Debug, Default)]
88pub struct ErrorResponse {
90 pub headers: HeaderMap,
92 pub code: ErrorCode,
93 pub message: String,
94 pub resource: String,
95 pub request_id: String,
96 pub host_id: String,
97 pub bucket_name: String,
98 pub object_name: String,
99}
100
101impl ErrorResponse {
102 pub fn parse(body: Bytes, headers: HeaderMap) -> Result<Self, Error> {
103 let root = match Element::parse(body.reader()) {
104 Ok(v) => v,
105 Err(e) => return Err(Error::XmlParseError(e)),
106 };
107
108 Ok(Self {
109 headers,
110 code: ErrorCode::parse(&get_default_text(&root, "Code")),
111 message: get_default_text(&root, "Message"),
112 resource: get_default_text(&root, "Resource"),
113 request_id: get_default_text(&root, "RequestId"),
114 host_id: get_default_text(&root, "HostId"),
115 bucket_name: get_default_text(&root, "BucketName"),
116 object_name: get_default_text(&root, "Key"),
117 })
118 }
119}
120
121#[derive(Debug)]
123pub enum Error {
124 TimeParseError(chrono::ParseError),
125 InvalidUrl(http::uri::InvalidUri),
126 IOError(std::io::Error),
127 XmlParseError(xmltree::ParseError),
128 HttpError(reqwest::Error),
129 StrError(reqwest::header::ToStrError),
130 IntError(std::num::ParseIntError),
131 BoolError(std::str::ParseBoolError),
132 Utf8Error(alloc::string::FromUtf8Error),
133 JsonError(serde_json::Error),
134 XmlError(String),
135 InvalidBucketName(String),
136 InvalidBaseUrl(String),
137 UrlBuildError(String),
138 RegionMismatch(String, String),
139 S3Error(ErrorResponse),
140 InvalidResponse(u16, String),
141 ServerError(u16),
142 InvalidObjectName(String),
143 InvalidUploadId(String),
144 InvalidPartNumber(String),
145 InvalidUserMetadata(String),
146 EmptyParts(String),
147 InvalidRetentionMode(String),
148 InvalidRetentionConfig(String),
149 InvalidMinPartSize(u64),
150 InvalidMaxPartSize(u64),
151 InvalidObjectSize(u64),
152 MissingPartSize,
153 InvalidPartCount(u64, u64, u16),
154 TooManyParts,
155 SseTlsRequired(Option<String>),
156 TooMuchData(u64),
157 InsufficientData(u64, u64),
158 InvalidLegalHold(String),
159 InvalidSelectExpression(String),
160 InvalidHeaderValueType(u8),
161 CrcMismatch(String, u32, u32),
162 UnknownEventType(String),
163 SelectError(String, String),
164 UnsupportedApi(String),
165 InvalidComposeSource(String),
166 InvalidComposeSourceOffset(String, String, Option<String>, u64, u64),
167 InvalidComposeSourceLength(String, String, Option<String>, u64, u64),
168 InvalidComposeSourceSize(String, String, Option<String>, u64, u64),
169 InvalidComposeSourcePartSize(String, String, Option<String>, u64, u64),
170 InvalidComposeSourceMultipart(String, String, Option<String>, u64, u64),
171 InvalidDirective(String),
172 InvalidCopyDirective(String),
173 InvalidMultipartCount(u16),
174 MissingLifecycleAction,
175 InvalidExpiredObjectDeleteMarker,
176 InvalidDateAndDays(String),
177 InvalidLifecycleRuleId,
178 InvalidFilter,
179 InvalidVersioningStatus(String),
180 PostPolicyError(String),
181 InvalidObjectLockConfig(String),
182 NoClientProvided,
183 TagDecodingError(String, String),
184 ContentLengthUnknown,
185}
186
187impl std::error::Error for Error {}
188
189impl fmt::Display for Error {
190 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
191 match self {
192 Error::TimeParseError(e) => write!(f, "{e}"),
193 Error::InvalidUrl(e) => write!(f, "{e}"),
194 Error::IOError(e) => write!(f, "{e}"),
195 Error::XmlParseError(e) => write!(f, "{e}"),
196 Error::HttpError(e) => write!(f, "{e}"),
197 Error::StrError(e) => write!(f, "{e}"),
198 Error::IntError(e) => write!(f, "{e}"),
199 Error::BoolError(e) => write!(f, "{e}"),
200 Error::Utf8Error(e) => write!(f, "{e}"),
201 Error::JsonError(e) => write!(f, "{e}"),
202 Error::XmlError(m) => write!(f, "{m}"),
203 Error::InvalidBucketName(m) => write!(f, "{m}"),
204 Error::InvalidObjectName(m) => write!(f, "{m}"),
205 Error::InvalidUploadId(m) => write!(f, "{m}"),
206 Error::InvalidPartNumber(m) => write!(f, "{m}"),
207 Error::InvalidUserMetadata(m) => write!(f, "{m}"),
208 Error::EmptyParts(m) => write!(f, "{m}"),
209 Error::InvalidRetentionMode(m) => write!(f, "invalid retention mode {m}"),
210 Error::InvalidRetentionConfig(m) => write!(f, "invalid retention configuration; {m}"),
211 Error::InvalidMinPartSize(s) => {
212 write!(f, "part size {s} is not supported; minimum allowed 5MiB")
213 }
214 Error::InvalidMaxPartSize(s) => {
215 write!(f, "part size {s} is not supported; maximum allowed 5GiB")
216 }
217 Error::InvalidObjectSize(s) => {
218 write!(f, "object size {s} is not supported; maximum allowed 5TiB",)
219 }
220 Error::MissingPartSize => write!(
221 f,
222 "valid part size must be provided when object size is unknown"
223 ),
224 Error::InvalidPartCount(os, ps, pc) => write!(
225 f,
226 "object size {os} and part size {ps} make more than {pc} parts for upload"
227 ),
228 Error::TooManyParts => write!(f, "too many parts for upload"),
229 Error::SseTlsRequired(m) => write!(
230 f,
231 "{}SSE operation must be performed over a secure connection",
232 m.as_ref().map_or(String::new(), |v| v.clone())
233 ),
234 Error::TooMuchData(s) => write!(f, "too much data in the stream - exceeds {s} bytes"),
235 Error::InsufficientData(expected, got) => write!(
236 f,
237 "not enough data in the stream; expected: {expected}, got: {got} bytes",
238 ),
239 Error::InvalidBaseUrl(m) => write!(f, "{m}"),
240 Error::UrlBuildError(m) => write!(f, "{m}"),
241 Error::InvalidLegalHold(s) => write!(f, "invalid legal hold {s}"),
242 Error::RegionMismatch(br, r) => write!(f, "region must be {br}, but passed {r}"),
243 Error::S3Error(er) => write!(
244 f,
245 "s3 operation failed; code: {:?}, message: {}, resource: {}, request_id: {}, host_id: {}, bucket_name: {}, object_name: {}",
246 er.code,
247 er.message,
248 er.resource,
249 er.request_id,
250 er.host_id,
251 er.bucket_name,
252 er.object_name,
253 ),
254 Error::InvalidResponse(sc, ct) => write!(
255 f,
256 "invalid response received; status code: {sc}; content-type: {ct}"
257 ),
258 Error::ServerError(sc) => write!(f, "server failed with HTTP status code {sc}"),
259 Error::InvalidSelectExpression(m) => write!(f, "{m}"),
260 Error::InvalidHeaderValueType(v) => write!(f, "invalid header value type {v}"),
261 Error::CrcMismatch(t, e, g) => {
262 write!(f, "{t} CRC mismatch; expected: {e}, got: {g}")
263 }
264 Error::UnknownEventType(et) => write!(f, "unknown event type {et}"),
265 Error::SelectError(ec, em) => write!(f, "error code: {ec}, error message: {em}"),
266 Error::UnsupportedApi(a) => write!(f, "{a} API is not supported in Amazon AWS S3"),
267 Error::InvalidComposeSource(m) => write!(f, "{m}"),
268 Error::InvalidComposeSourceOffset(b, o, v, of, os) => write!(
269 f,
270 "source {}/{}{}: offset {} is beyond object size {}",
271 b,
272 o,
273 v.as_ref()
274 .map_or(String::new(), |v| String::from("?versionId=") + v),
275 of,
276 os
277 ),
278 Error::InvalidComposeSourceLength(b, o, v, l, os) => write!(
279 f,
280 "source {}/{}{}: length {} is beyond object size {}",
281 b,
282 o,
283 v.as_ref()
284 .map_or(String::new(), |v| String::from("?versionId=") + v),
285 l,
286 os
287 ),
288 Error::InvalidComposeSourceSize(b, o, v, cs, os) => write!(
289 f,
290 "source {}/{}{}: compose size {} is beyond object size {}",
291 b,
292 o,
293 v.as_ref()
294 .map_or(String::new(), |v| String::from("?versionId=") + v),
295 cs,
296 os
297 ),
298 Error::InvalidDirective(m) => write!(f, "{m}"),
299 Error::InvalidCopyDirective(m) => write!(f, "{m}"),
300 Error::InvalidComposeSourcePartSize(b, o, v, s, es) => write!(
301 f,
302 "source {}/{}{}: size {} must be greater than {}",
303 b,
304 o,
305 v.as_ref()
306 .map_or(String::new(), |v| String::from("?versionId=") + v),
307 s,
308 es
309 ),
310 Error::InvalidComposeSourceMultipart(b, o, v, s, es) => write!(
311 f,
312 "source {}/{}{}: size {} for multipart split upload of {}, last part size is less than {}",
313 b,
314 o,
315 v.as_ref()
316 .map_or(String::new(), |v| String::from("?versionId=") + v),
317 s,
318 s,
319 es
320 ),
321 Error::InvalidMultipartCount(c) => write!(
322 f,
323 "Compose sources create more than allowed multipart count {c}",
324 ),
325 Error::MissingLifecycleAction => write!(
326 f,
327 "at least one of action (AbortIncompleteMultipartUpload, Expiration, NoncurrentVersionExpiration, NoncurrentVersionTransition or Transition) must be specified in a rule"
328 ),
329 Error::InvalidExpiredObjectDeleteMarker => write!(
330 f,
331 "ExpiredObjectDeleteMarker must not be provided along with Date and Days"
332 ),
333 Error::InvalidDateAndDays(m) => {
334 write!(f, "Only one of date or days of {m} must be set")
335 }
336 Error::InvalidLifecycleRuleId => write!(f, "id must be exceed 255 characters"),
337 Error::InvalidFilter => write!(f, "only one of And, Prefix or Tag must be provided"),
338 Error::InvalidVersioningStatus(m) => write!(f, "{m}"),
339 Error::PostPolicyError(m) => write!(f, "{m}"),
340 Error::InvalidObjectLockConfig(m) => write!(f, "{m}"),
341 Error::NoClientProvided => write!(f, "no client provided"),
342 Error::TagDecodingError(input, error_message) => {
343 write!(f, "tag decoding failed: {error_message} on input '{input}'")
344 }
345 Error::ContentLengthUnknown => write!(f, "content length is unknown"),
346 }
347 }
348}
349
350impl From<chrono::ParseError> for Error {
351 fn from(err: chrono::ParseError) -> Self {
352 Error::TimeParseError(err)
353 }
354}
355
356impl From<http::uri::InvalidUri> for Error {
357 fn from(err: http::uri::InvalidUri) -> Self {
358 Error::InvalidUrl(err)
359 }
360}
361
362impl From<std::io::Error> for Error {
363 fn from(err: std::io::Error) -> Self {
364 Error::IOError(err)
365 }
366}
367
368impl From<xmltree::ParseError> for Error {
369 fn from(err: xmltree::ParseError) -> Self {
370 Error::XmlParseError(err)
371 }
372}
373
374impl From<reqwest::Error> for Error {
375 fn from(err: reqwest::Error) -> Self {
376 Error::HttpError(err)
377 }
378}
379
380impl From<reqwest::header::ToStrError> for Error {
381 fn from(err: reqwest::header::ToStrError) -> Self {
382 Error::StrError(err)
383 }
384}
385
386impl From<std::num::ParseIntError> for Error {
387 fn from(err: std::num::ParseIntError) -> Self {
388 Error::IntError(err)
389 }
390}
391
392impl From<std::str::ParseBoolError> for Error {
393 fn from(err: std::str::ParseBoolError) -> Self {
394 Error::BoolError(err)
395 }
396}
397
398impl From<alloc::string::FromUtf8Error> for Error {
399 fn from(err: alloc::string::FromUtf8Error) -> Self {
400 Error::Utf8Error(err)
401 }
402}
403
404impl From<serde_json::Error> for Error {
405 fn from(err: serde_json::Error) -> Self {
406 Error::JsonError(err)
407 }
408}