s3_signer/multipart_upload/
part_upload_url.rs

1use serde::{Deserialize, Serialize};
2
3#[derive(Debug, Deserialize, Serialize)]
4pub struct PartUploadQueryParameters {
5  pub bucket: String,
6  pub path: String,
7}
8
9#[derive(Debug, Deserialize, Serialize)]
10#[cfg_attr(feature = "server", derive(utoipa::ToSchema))]
11pub struct PartUploadResponse {
12  pub presigned_url: String,
13}
14
15#[cfg(feature = "server")]
16pub(crate) mod server {
17  use super::{PartUploadQueryParameters, PartUploadResponse};
18  use crate::{to_ok_json_response, S3Configuration};
19  use rusoto_credential::AwsCredentials;
20  use rusoto_s3::{
21    util::{PreSignedRequest, PreSignedRequestOption},
22    UploadPartRequest,
23  };
24  use warp::{
25    hyper::{Body, Response},
26    Filter, Rejection, Reply,
27  };
28
29  /// Pre-sign part upload URL
30  #[utoipa::path(
31    get,
32    context_path = "/multipart-upload",
33    path = "/{upload_id}/part/{part_number}",
34    tag = "Multipart upload",
35    responses(
36      (
37        status = 200,
38        description = "Returns the pre-signed URL for getting an object",
39        content_type = "application/json",
40        body = PartUploadResponse
41      ),
42    ),
43    params(
44      ("upload_id" = String, Path, description = "ID of the upload"),
45      ("part_number" = i64, Path, description = "Index number of the part to upload"),
46      ("bucket" = String, Query, description = "Name of the bucket"),
47      ("path" = String, Query, description = "Key of the object to get")
48    ),
49  )]
50  pub(crate) fn route(
51    s3_configuration: &S3Configuration,
52  ) -> impl Filter<Extract = (impl Reply,), Error = Rejection> + Clone {
53    let s3_configuration = s3_configuration.clone();
54    warp::path!(String / "part" / i64)
55      .and(warp::get())
56      .and(warp::query::<PartUploadQueryParameters>())
57      .and(warp::any().map(move || s3_configuration.clone()))
58      .and_then(
59        |upload_id: String,
60         part_number: i64,
61         parameters: PartUploadQueryParameters,
62         s3_configuration: S3Configuration| async move {
63          handle_part_upload_presigned_url(
64            &s3_configuration,
65            parameters.bucket,
66            parameters.path,
67            upload_id,
68            part_number,
69          )
70          .await
71        },
72      )
73  }
74
75  async fn handle_part_upload_presigned_url(
76    s3_configuration: &S3Configuration,
77    bucket: String,
78    key: String,
79    upload_id: String,
80    part_number: i64,
81  ) -> Result<Response<Body>, Rejection> {
82    log::info!(
83      "Upload part: upload_id={}, part_number={}",
84      upload_id,
85      part_number,
86    );
87    let request = UploadPartRequest {
88      bucket,
89      key,
90      upload_id,
91      part_number,
92      ..Default::default()
93    };
94
95    let credentials = AwsCredentials::from(s3_configuration);
96
97    let presigned_url = request.get_presigned_url(
98      s3_configuration.region(),
99      &credentials,
100      &PreSignedRequestOption::default(),
101    );
102
103    let response = PartUploadResponse { presigned_url };
104    to_ok_json_response(&response)
105  }
106}