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
77fn 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
87fn 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}