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