Skip to main content

s3_wire/client/multipart/
operations.rs

1use http::header::{CONTENT_TYPE, ETAG};
2use http::{HeaderMap, HeaderValue, Method};
3
4use super::errors::embedded_complete_error;
5use super::headers::{
6    checksum_headers, create_headers, optional_header, request_ids, required_header,
7    response_checksums,
8};
9use super::query::{create_query, list_query, upload_part_query, upload_query};
10use crate::client::S3Client;
11use crate::error::S3Error;
12use crate::operation::{
13    AbortMultipartUploadRequest, CompleteMultipartUploadOutput, CompleteMultipartUploadRequest,
14    CreateMultipartUploadOutput, CreateMultipartUploadRequest, ListMultipartUploadsOutput,
15    ListMultipartUploadsRequest, RequestIds, UploadPartOutput, UploadPartRequest,
16};
17use crate::protocol::{
18    CompleteMultipartResponse, parse_complete_multipart_upload, parse_create_multipart_upload,
19    parse_list_multipart_uploads, serialize_complete_multipart_upload,
20};
21use crate::stream::ByteStream;
22
23impl S3Client {
24    /// Initiates a multipart upload for an object.
25    ///
26    /// # Errors
27    ///
28    /// Returns an error for invalid headers, transport or service failures, or
29    /// a malformed or oversized service response.
30    pub async fn create_multipart_upload(
31        &self,
32        request: CreateMultipartUploadRequest,
33    ) -> Result<CreateMultipartUploadOutput, S3Error> {
34        let target = self.multipart_object_target(Some(request.key.as_str()))?;
35        let headers = create_headers(&request)?;
36        let deadline = self.deadline();
37        let response = self
38            .send_signed(
39                Method::POST,
40                target,
41                &create_query(),
42                headers,
43                None,
44                &deadline,
45            )
46            .await?;
47        let request_ids = request_ids(response.headers());
48        let body = self
49            .collect_response(response, self.config().max_xml_response_size(), &deadline)
50            .await?;
51        let mut output =
52            parse_create_multipart_upload(&body, self.config().max_xml_response_size())
53                .map_err(crate::client::request::protocol_error)?;
54        output.request_ids = request_ids;
55        Ok(output)
56    }
57
58    /// Uploads one part of an in-progress multipart upload.
59    ///
60    /// # Errors
61    ///
62    /// Returns an error for an invalid checksum, body preparation failure,
63    /// transport or service failure, or malformed response headers.
64    pub async fn upload_part(
65        &self,
66        request: UploadPartRequest<ByteStream>,
67    ) -> Result<UploadPartOutput, S3Error> {
68        let target = self.multipart_object_target(Some(request.key().as_str()))?;
69        let query = upload_part_query(request.part_number().get(), request.upload_id());
70        let headers = checksum_headers(request.checksum())?;
71        let part_number = request.part_number();
72        let body = request.into_body().prepare().await?;
73        let deadline = self.deadline();
74        let response = self
75            .send_signed(Method::PUT, target, &query, headers, Some(&body), &deadline)
76            .await?;
77        let headers = response.headers();
78        let e_tag = required_header(
79            headers,
80            ETAG.as_str(),
81            "upload-part response has no usable ETag",
82        )?;
83        let checksum = response_checksums(headers)?;
84        let request_ids = request_ids(headers);
85        Ok(UploadPartOutput {
86            part_number,
87            e_tag,
88            checksum,
89            request_ids,
90        })
91    }
92
93    /// Completes an in-progress multipart upload.
94    ///
95    /// # Errors
96    ///
97    /// Returns an error when the completion document cannot be serialized, the
98    /// request fails, or S3 returns a malformed, oversized, or embedded error.
99    pub async fn complete_multipart_upload(
100        &self,
101        request: CompleteMultipartUploadRequest,
102    ) -> Result<CompleteMultipartUploadOutput, S3Error> {
103        let target = self.multipart_object_target(Some(request.key.as_str()))?;
104        let query = upload_query(request.upload_id());
105        let document =
106            serialize_complete_multipart_upload(&request, self.config().max_xml_response_size())
107                .map_err(crate::client::request::protocol_error)?;
108        let body = ByteStream::from(document).prepare().await?;
109        let mut headers = HeaderMap::new();
110        headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/xml"));
111        let deadline = self.deadline();
112        let response = self
113            .send_signed(
114                Method::POST,
115                target,
116                &query,
117                headers,
118                Some(&body),
119                &deadline,
120            )
121            .await?;
122        let response_headers = response.headers().clone();
123        let request_ids = request_ids(&response_headers);
124        let version_id = optional_header(&response_headers, "x-amz-version-id")?;
125        let body = self
126            .collect_response(response, self.config().max_xml_response_size(), &deadline)
127            .await?;
128        match parse_complete_multipart_upload(&body, self.config().max_xml_response_size())
129            .map_err(crate::client::request::protocol_error)?
130        {
131            CompleteMultipartResponse::Complete(mut output) => {
132                output.version_id = version_id;
133                output.request_ids = request_ids;
134                Ok(output)
135            }
136            CompleteMultipartResponse::EmbeddedError(parsed) => {
137                Err(embedded_complete_error(&response_headers, parsed))
138            }
139        }
140    }
141
142    /// Aborts an in-progress multipart upload.
143    ///
144    /// # Errors
145    ///
146    /// Returns an error when target construction, signing, transport, or the
147    /// S3 service rejects the abort request.
148    pub async fn abort_multipart_upload(
149        &self,
150        request: AbortMultipartUploadRequest,
151    ) -> Result<RequestIds, S3Error> {
152        let target = self.multipart_object_target(Some(request.key().as_str()))?;
153        let query = upload_query(request.upload_id());
154        let deadline = self.deadline();
155        let response = self
156            .send_signed(
157                Method::DELETE,
158                target,
159                &query,
160                HeaderMap::new(),
161                None,
162                &deadline,
163            )
164            .await?;
165        Ok(request_ids(response.headers()))
166    }
167
168    /// Retrieves one bounded page of in-progress multipart uploads.
169    ///
170    /// # Errors
171    ///
172    /// Returns an error for inconsistent pagination markers, a failed request,
173    /// or a malformed or oversized listing response.
174    pub async fn list_multipart_uploads(
175        &self,
176        request: ListMultipartUploadsRequest,
177    ) -> Result<ListMultipartUploadsOutput, S3Error> {
178        let target = self.multipart_object_target(None)?;
179        let query = list_query(&request)?;
180        let deadline = self.deadline();
181        let response = self
182            .send_signed(
183                Method::GET,
184                target,
185                &query,
186                HeaderMap::new(),
187                None,
188                &deadline,
189            )
190            .await?;
191        let request_ids = request_ids(response.headers());
192        let body = self
193            .collect_response(response, self.config().max_xml_response_size(), &deadline)
194            .await?;
195        let mut output = parse_list_multipart_uploads(&body, self.config().max_xml_response_size())
196            .map_err(crate::client::request::protocol_error)?;
197        output.request_ids = request_ids;
198        Ok(output)
199    }
200
201    fn multipart_object_target(
202        &self,
203        key: Option<&str>,
204    ) -> Result<crate::endpoint::EndpointUrl, S3Error> {
205        self.config().endpoint().object_url(
206            self.config().bucket(),
207            key,
208            self.config().addressing_style(),
209        )
210    }
211}