use crate::body::{s3s_body_into_sdk_body, sdk_body_into_s3s_body};
use s3s::service::S3Service;
use std::ops::Not;
use aws_sdk_s3::config::RuntimeComponents;
use aws_smithy_runtime_api::client::http::{HttpClient, HttpConnectorSettings, SharedHttpConnector};
use aws_smithy_runtime_api::client::http::{HttpConnector, HttpConnectorFuture};
use aws_smithy_runtime_api::client::orchestrator::HttpRequest as AwsHttpRequest;
use aws_smithy_runtime_api::client::orchestrator::HttpResponse as AwsHttpResponse;
use aws_smithy_runtime_api::client::result::ConnectorError;
use hyper::header::HOST;
use hyper::http;
#[derive(Debug)]
pub struct Client(S3Service);
impl HttpClient for Client {
fn http_connector(&self, _: &HttpConnectorSettings, _: &RuntimeComponents) -> SharedHttpConnector {
SharedHttpConnector::new(Connector(self.0.clone()))
}
}
impl From<S3Service> for Client {
fn from(val: S3Service) -> Self {
Self(val)
}
}
#[derive(Debug, Clone)]
pub struct Connector(S3Service);
impl From<S3Service> for Connector {
fn from(val: S3Service) -> Self {
Self(val)
}
}
fn on_err<E>(e: E) -> ConnectorError
where
E: std::error::Error + Send + Sync + 'static,
{
let kind = aws_smithy_runtime_api::client::retries::ErrorKind::ServerError;
ConnectorError::other(Box::new(e), Some(kind))
}
impl HttpConnector for Connector {
fn call(&self, req: AwsHttpRequest) -> HttpConnectorFuture {
let service = self.0.clone();
HttpConnectorFuture::new_boxed(Box::pin(async move { convert_output(service.call(convert_input(req)?).await) }))
}
}
fn convert_input(req: AwsHttpRequest) -> Result<s3s::HttpRequest, ConnectorError> {
let mut req = req.try_into_http1x().map_err(on_err)?;
if req.headers().contains_key(HOST).not() {
let host = auto_host_header(req.uri());
req.headers_mut().insert(HOST, host);
}
Ok(req.map(sdk_body_into_s3s_body))
}
fn convert_output(result: Result<s3s::HttpResponse, s3s::HttpError>) -> Result<AwsHttpResponse, ConnectorError> {
match result {
Ok(res) => res.map(s3s_body_into_sdk_body).try_into().map_err(on_err),
Err(e) => {
let kind = aws_smithy_runtime_api::client::retries::ErrorKind::ServerError;
Err(ConnectorError::other(e.into(), Some(kind)))
}
}
}
fn auto_host_header(uri: &http::Uri) -> http::HeaderValue {
let hostname = uri.host().expect("authority implies host");
match get_non_default_port(uri) {
Some(port) => http::HeaderValue::try_from(format!("{hostname}:{port}")),
None => http::HeaderValue::from_str(hostname),
}
.expect("uri host is valid header value")
}
fn get_non_default_port(uri: &http::Uri) -> Option<http::uri::Port<&str>> {
match (uri.port().map(|p| p.as_u16()), is_schema_secure(uri)) {
(Some(443), true) => None,
(Some(80), false) => None,
_ => uri.port(),
}
}
fn is_schema_secure(uri: &http::Uri) -> bool {
uri.scheme_str()
.is_some_and(|scheme_str| matches!(scheme_str, "wss" | "https"))
}