Skip to main content

aws_sdk_s3/
presigning.rs

1// Code generated by software.amazon.smithy.rust.codegen.smithy-rs. DO NOT EDIT.
2/*
3 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7// TODO(https://github.com/smithy-lang/smithy-rs/issues/2902): Code generate this documentation so that service-specific examples can be added.
8//! Presigned request types and configuration.
9//!
10//! The [`Client`](crate::Client) is used to create presigned requests. They are made
11//! by calling `.presigned()` instead of `.send()` on an operation, and require a
12//! [`PresigningConfig`](crate::presigning::PresigningConfig) to provide an expiration time.
13//!
14//! Only operations that support presigning have the `presigned()` method on them.
15
16use aws_smithy_runtime_api::box_error::BoxError;
17use aws_smithy_runtime_api::client::orchestrator::HttpRequest;
18use aws_smithy_types::body::SdkBody;
19use aws_smithy_types::config_bag::{Storable, StoreReplace};
20use std::fmt;
21use std::time::{Duration, SystemTime};
22
23const ONE_WEEK: Duration = Duration::from_secs(604800);
24
25/// Presigning config values required for creating a presigned request.
26#[non_exhaustive]
27#[derive(Debug, Clone)]
28pub struct PresigningConfig {
29    start_time: SystemTime,
30    expires_in: Duration,
31}
32
33impl PresigningConfig {
34    /// Creates a `PresigningConfig` with the given `expires_in` duration.
35    ///
36    /// The `expires_in` duration is the total amount of time the presigned request should
37    /// be valid for. Other config values are defaulted.
38    ///
39    /// Credential expiration time takes priority over the `expires_in` value.
40    /// If the credentials used to sign the request expire before the presigned request is
41    /// set to expire, then the presigned request will become invalid.
42    pub fn expires_in(expires_in: Duration) -> Result<PresigningConfig, PresigningConfigError> {
43        Self::builder().expires_in(expires_in).build()
44    }
45
46    /// Creates a new builder for creating a `PresigningConfig`.
47    pub fn builder() -> PresigningConfigBuilder {
48        PresigningConfigBuilder::default()
49    }
50
51    /// Returns the amount of time the presigned request should be valid for.
52    pub fn expires(&self) -> Duration {
53        self.expires_in
54    }
55
56    /// Returns the start time. The presigned request will be valid between this and the end
57    /// time produced by adding the `expires()` value to it.
58    pub fn start_time(&self) -> SystemTime {
59        self.start_time
60    }
61}
62
63#[derive(Debug)]
64enum ErrorKind {
65    /// Presigned requests cannot be valid for longer than one week.
66    ExpiresInDurationTooLong,
67
68    /// The `PresigningConfig` builder requires a value for `expires_in`.
69    ExpiresInRequired,
70}
71
72/// `PresigningConfig` build errors.
73#[derive(Debug)]
74pub struct PresigningConfigError {
75    kind: ErrorKind,
76}
77
78impl std::error::Error for PresigningConfigError {}
79
80impl fmt::Display for PresigningConfigError {
81    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
82        match self.kind {
83            ErrorKind::ExpiresInDurationTooLong => {
84                write!(f, "`expires_in` must be no longer than one week")
85            }
86            ErrorKind::ExpiresInRequired => write!(f, "`expires_in` is required"),
87        }
88    }
89}
90
91impl From<ErrorKind> for PresigningConfigError {
92    fn from(kind: ErrorKind) -> Self {
93        Self { kind }
94    }
95}
96
97/// Builder used to create `PresigningConfig`.
98#[non_exhaustive]
99#[derive(Default, Debug)]
100pub struct PresigningConfigBuilder {
101    start_time: Option<SystemTime>,
102    expires_in: Option<Duration>,
103}
104
105impl PresigningConfigBuilder {
106    /// Sets the start time for the presigned request.
107    ///
108    /// The request will start to be valid at this time, and will cease to be valid after
109    /// the end time, which can be determined by adding the `expires_in` duration to this
110    /// start time. If not specified, this will default to the current time.
111    ///
112    /// Optional.
113    pub fn start_time(mut self, start_time: SystemTime) -> Self {
114        self.set_start_time(Some(start_time));
115        self
116    }
117
118    /// Sets the start time for the presigned request.
119    ///
120    /// The request will start to be valid at this time, and will cease to be valid after
121    /// the end time, which can be determined by adding the `expires_in` duration to this
122    /// start time. If not specified, this will default to the current time.
123    ///
124    /// Optional.
125    pub fn set_start_time(&mut self, start_time: Option<SystemTime>) {
126        self.start_time = start_time;
127    }
128
129    /// Sets how long the request should be valid after the `start_time` (which defaults
130    /// to the current time).
131    ///
132    /// Credential expiration time takes priority over the `expires_in` value.
133    /// If the credentials used to sign the request expire before the presigned request is
134    /// set to expire, then the presigned request will become invalid.
135    ///
136    /// Required.
137    pub fn expires_in(mut self, expires_in: Duration) -> Self {
138        self.set_expires_in(Some(expires_in));
139        self
140    }
141
142    /// Sets how long the request should be valid after the `start_time` (which defaults
143    /// to the current time).
144    ///
145    /// Credential expiration time takes priority over the `expires_in` value.
146    /// If the credentials used to sign the request expire before the presigned request is
147    /// set to expire, then the presigned request will become invalid.
148    ///
149    /// Required.
150    pub fn set_expires_in(&mut self, expires_in: Option<Duration>) {
151        self.expires_in = expires_in;
152    }
153
154    /// Builds the `PresigningConfig`. This will error if `expires_in` is not
155    /// given, or if it's longer than one week.
156    pub fn build(self) -> Result<PresigningConfig, PresigningConfigError> {
157        let expires_in = self.expires_in.ok_or(ErrorKind::ExpiresInRequired)?;
158        if expires_in > ONE_WEEK {
159            return Err(ErrorKind::ExpiresInDurationTooLong.into());
160        }
161        Ok(PresigningConfig {
162            start_time: self.start_time.unwrap_or_else(
163                // This usage is OK—customers can easily override this.
164                #[allow(clippy::disallowed_methods)]
165                SystemTime::now,
166            ),
167            expires_in,
168        })
169    }
170}
171
172/// Represents a presigned request. This only includes the HTTP request method, URI, and headers.
173///
174/// **This struct has conversion convenience functions:**
175///
176/// - [`PresignedRequest::make_http_1x_request<B>`][Self::make_http_1x_request] returns an [`http_1x::Request<B>`](https://docs.rs/http/1/http/request/struct.Request.html)
177/// - [`PresignedRequest::into_http_1x_request<B>`][Self::into_http_1x_request] returns an [`http_1x::Request<B>`](https://docs.rs/http/1/http/request/struct.Request.html)
178///
179/// The deprecated `http` 0.2.x equivalents, `make_http_02x_request` and `into_http_02x_request`,
180/// are only available when the `http-02x` feature is enabled.
181#[non_exhaustive]
182pub struct PresignedRequest {
183    http_request: HttpRequest,
184}
185
186impl Clone for PresignedRequest {
187    fn clone(&self) -> Self {
188        Self {
189            http_request: match self.http_request.try_clone() {
190                Some(body) => body,
191                None => {
192                    unreachable!("during construction, we replaced the body with `SdkBody::empty()`")
193                }
194            },
195        }
196    }
197}
198
199impl PresignedRequest {
200    #[allow(dead_code)]
201    pub(crate) fn new(inner: HttpRequest) -> Result<Self, BoxError> {
202        // throw out the body so we're sure it's cloneable
203        let http_request = inner.map(|_body| SdkBody::empty());
204        // this should never fail, a presigned request should always be convertible, but better to
205        // protect against this potential panic
206        let _ = http_request.try_clone().expect("must be cloneable, body is empty").try_into_http1x()?;
207        Ok(Self { http_request })
208    }
209
210    /// Returns the HTTP request method.
211    pub fn method(&self) -> &str {
212        self.http_request.method()
213    }
214
215    /// Returns the HTTP request URI.
216    pub fn uri(&self) -> &str {
217        self.http_request.uri()
218    }
219
220    /// Returns any HTTP headers that need to go along with the request, except for `Host`,
221    /// which should be sent based on the endpoint in the URI by the HTTP client rather than
222    /// added directly.
223    pub fn headers(&self) -> impl Iterator<Item = (&str, &str)> {
224        self.http_request.headers().iter()
225    }
226
227    /// Given a body, produce an `http::Request` from this `PresignedRequest`
228    ///
229    /// _Note: This is only available with the `http-02x` feature enabled._
230    #[cfg(feature = "http-02x")]
231    #[deprecated = "Prefer the `make_http_1x_request()` instead by enabling the `http-1x` feature."]
232    #[allow(deprecated)]
233    pub fn make_http_02x_request<B>(&self, body: B) -> http::Request<B> {
234        self.clone().into_http_02x_request(body)
235    }
236
237    /// Converts this `PresignedRequest` directly into an `http` request.
238    ///
239    /// _Note: This is only available with the `http-02x` feature enabled._
240    #[cfg(feature = "http-02x")]
241    #[deprecated = "Prefer the `into_http_1x_request` instead by enabling the `http-1x` feature."]
242    pub fn into_http_02x_request<B>(self, body: B) -> http::Request<B> {
243        self.http_request
244            .try_into_http02x()
245            .expect("constructor validated convertibility")
246            .map(|_req| body)
247    }
248
249    /// Given a body, produce an `http_1x::Request` from this `PresignedRequest`
250    pub fn make_http_1x_request<B>(&self, body: B) -> http_1x::Request<B> {
251        self.clone().into_http_1x_request(body)
252    }
253
254    /// Converts this `PresignedRequest` directly into an `http_1x` request.
255    pub fn into_http_1x_request<B>(self, body: B) -> http_1x::Request<B> {
256        self.http_request
257            .try_into_http1x()
258            .expect("constructor validated convertibility")
259            .map(|_req| body)
260    }
261}
262
263impl fmt::Debug for PresignedRequest {
264    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
265        f.debug_struct("PresignedRequest")
266            .field("method", &self.method())
267            .field("uri", &self.uri())
268            .field("headers", self.http_request.headers())
269            .finish()
270    }
271}
272
273/// A marker struct to be stored in the ConfigBag allowing other interceptors to know that
274/// the current request is Presigned
275#[derive(Debug)]
276pub(crate) struct PresigningMarker;
277
278impl Storable for PresigningMarker {
279    type Storer = StoreReplace<Self>;
280}