s3s_aws/
connector.rs

1use crate::body::{s3s_body_into_sdk_body, sdk_body_into_s3s_body};
2
3use s3s::service::S3Service;
4
5use std::ops::Not;
6
7use aws_sdk_s3::config::RuntimeComponents;
8use aws_smithy_runtime_api::client::http::{HttpClient, HttpConnectorSettings, SharedHttpConnector};
9use aws_smithy_runtime_api::client::http::{HttpConnector, HttpConnectorFuture};
10use aws_smithy_runtime_api::client::orchestrator::HttpRequest as AwsHttpRequest;
11use aws_smithy_runtime_api::client::orchestrator::HttpResponse as AwsHttpResponse;
12use aws_smithy_runtime_api::client::result::ConnectorError;
13
14use hyper::header::HOST;
15use hyper::http;
16
17#[derive(Debug)]
18pub struct Client(S3Service);
19
20impl HttpClient for Client {
21    fn http_connector(&self, _: &HttpConnectorSettings, _: &RuntimeComponents) -> SharedHttpConnector {
22        SharedHttpConnector::new(Connector(self.0.clone()))
23    }
24}
25
26impl From<S3Service> for Client {
27    fn from(val: S3Service) -> Self {
28        Self(val)
29    }
30}
31
32#[derive(Debug, Clone)]
33pub struct Connector(S3Service);
34
35impl From<S3Service> for Connector {
36    fn from(val: S3Service) -> Self {
37        Self(val)
38    }
39}
40
41fn on_err<E>(e: E) -> ConnectorError
42where
43    E: std::error::Error + Send + Sync + 'static,
44{
45    let kind = aws_smithy_runtime_api::client::retries::ErrorKind::ServerError;
46    ConnectorError::other(Box::new(e), Some(kind))
47}
48
49impl HttpConnector for Connector {
50    fn call(&self, req: AwsHttpRequest) -> HttpConnectorFuture {
51        let service = self.0.clone();
52        HttpConnectorFuture::new_boxed(Box::pin(async move { convert_output(service.call(convert_input(req)?).await) }))
53    }
54}
55
56fn convert_input(req: AwsHttpRequest) -> Result<s3s::HttpRequest, ConnectorError> {
57    let mut req = req.try_into_http1x().map_err(on_err)?;
58
59    if req.headers().contains_key(HOST).not() {
60        let host = auto_host_header(req.uri());
61        req.headers_mut().insert(HOST, host);
62    }
63
64    Ok(req.map(sdk_body_into_s3s_body))
65}
66
67fn convert_output(result: Result<s3s::HttpResponse, s3s::HttpError>) -> Result<AwsHttpResponse, ConnectorError> {
68    match result {
69        Ok(res) => res.map(s3s_body_into_sdk_body).try_into().map_err(on_err),
70        Err(e) => {
71            let kind = aws_smithy_runtime_api::client::retries::ErrorKind::ServerError;
72            Err(ConnectorError::other(e.into(), Some(kind)))
73        }
74    }
75}
76
77// From <https://docs.rs/hyper/0.14.23/src/hyper/client/client.rs.html#253-260>
78fn auto_host_header(uri: &http::Uri) -> http::HeaderValue {
79    let hostname = uri.host().expect("authority implies host");
80    match get_non_default_port(uri) {
81        Some(port) => http::HeaderValue::try_from(format!("{hostname}:{port}")),
82        None => http::HeaderValue::from_str(hostname),
83    }
84    .expect("uri host is valid header value")
85}
86
87/// From <https://docs.rs/hyper/0.14.23/src/hyper/client/client.rs.html#860-872>
88fn get_non_default_port(uri: &http::Uri) -> Option<http::uri::Port<&str>> {
89    match (uri.port().map(|p| p.as_u16()), is_schema_secure(uri)) {
90        (Some(443), true) => None,
91        (Some(80), false) => None,
92        _ => uri.port(),
93    }
94}
95
96fn is_schema_secure(uri: &http::Uri) -> bool {
97    uri.scheme_str()
98        .is_some_and(|scheme_str| matches!(scheme_str, "wss" | "https"))
99}