use aws_smithy_runtime_api::box_error::BoxError;
use aws_smithy_runtime_api::client::orchestrator::HttpRequest;
use aws_smithy_types::body::SdkBody;
use aws_smithy_types::config_bag::{Storable, StoreReplace};
use std::fmt;
use std::time::{Duration, SystemTime};
const ONE_WEEK: Duration = Duration::from_secs(604800);
#[non_exhaustive]
#[derive(Debug, Clone)]
pub struct PresigningConfig {
start_time: SystemTime,
expires_in: Duration,
}
impl PresigningConfig {
pub fn expires_in(expires_in: Duration) -> Result<PresigningConfig, PresigningConfigError> {
Self::builder().expires_in(expires_in).build()
}
pub fn builder() -> PresigningConfigBuilder {
PresigningConfigBuilder::default()
}
pub fn expires(&self) -> Duration {
self.expires_in
}
pub fn start_time(&self) -> SystemTime {
self.start_time
}
}
#[derive(Debug)]
enum ErrorKind {
ExpiresInDurationTooLong,
ExpiresInRequired,
}
#[derive(Debug)]
pub struct PresigningConfigError {
kind: ErrorKind,
}
impl std::error::Error for PresigningConfigError {}
impl fmt::Display for PresigningConfigError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.kind {
ErrorKind::ExpiresInDurationTooLong => {
write!(f, "`expires_in` must be no longer than one week")
}
ErrorKind::ExpiresInRequired => write!(f, "`expires_in` is required"),
}
}
}
impl From<ErrorKind> for PresigningConfigError {
fn from(kind: ErrorKind) -> Self {
Self { kind }
}
}
#[non_exhaustive]
#[derive(Default, Debug)]
pub struct PresigningConfigBuilder {
start_time: Option<SystemTime>,
expires_in: Option<Duration>,
}
impl PresigningConfigBuilder {
pub fn start_time(mut self, start_time: SystemTime) -> Self {
self.set_start_time(Some(start_time));
self
}
pub fn set_start_time(&mut self, start_time: Option<SystemTime>) {
self.start_time = start_time;
}
pub fn expires_in(mut self, expires_in: Duration) -> Self {
self.set_expires_in(Some(expires_in));
self
}
pub fn set_expires_in(&mut self, expires_in: Option<Duration>) {
self.expires_in = expires_in;
}
pub fn build(self) -> Result<PresigningConfig, PresigningConfigError> {
let expires_in = self.expires_in.ok_or(ErrorKind::ExpiresInRequired)?;
if expires_in > ONE_WEEK {
return Err(ErrorKind::ExpiresInDurationTooLong.into());
}
Ok(PresigningConfig {
start_time: self.start_time.unwrap_or_else(
#[allow(clippy::disallowed_methods)]
SystemTime::now,
),
expires_in,
})
}
}
#[non_exhaustive]
pub struct PresignedRequest {
http_request: HttpRequest,
}
impl Clone for PresignedRequest {
fn clone(&self) -> Self {
Self {
http_request: match self.http_request.try_clone() {
Some(body) => body,
None => {
unreachable!("during construction, we replaced the body with `SdkBody::empty()`")
}
},
}
}
}
impl PresignedRequest {
#[allow(dead_code)]
pub(crate) fn new(inner: HttpRequest) -> Result<Self, BoxError> {
let http_request = inner.map(|_body| SdkBody::empty());
let _ = http_request.try_clone().expect("must be cloneable, body is empty").try_into_http1x()?;
Ok(Self { http_request })
}
pub fn method(&self) -> &str {
self.http_request.method()
}
pub fn uri(&self) -> &str {
self.http_request.uri()
}
pub fn headers(&self) -> impl Iterator<Item = (&str, &str)> {
self.http_request.headers().iter()
}
#[deprecated = "Prefer the `make_http_1x_request()` instead by enabling the `http-1x` feature."]
#[allow(deprecated)]
pub fn make_http_02x_request<B>(&self, body: B) -> http::Request<B> {
self.clone().into_http_02x_request(body)
}
#[deprecated = "Prefer the `into_http_1x_request` instead by enabling the `http-1x` feature."]
pub fn into_http_02x_request<B>(self, body: B) -> http::Request<B> {
self.http_request
.try_into_http02x()
.expect("constructor validated convertibility")
.map(|_req| body)
}
pub fn make_http_1x_request<B>(&self, body: B) -> http_1x::Request<B> {
self.clone().into_http_1x_request(body)
}
pub fn into_http_1x_request<B>(self, body: B) -> http_1x::Request<B> {
self.http_request
.try_into_http1x()
.expect("constructor validated convertibility")
.map(|_req| body)
}
}
impl fmt::Debug for PresignedRequest {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("PresignedRequest")
.field("method", &self.method())
.field("uri", &self.uri())
.field("headers", self.http_request.headers())
.finish()
}
}
#[derive(Debug)]
pub(crate) struct PresigningMarker;
impl Storable for PresigningMarker {
type Storer = StoreReplace<Self>;
}