1use aws_smithy_async::rt::sleep::{AsyncSleep, Sleep};
8use aws_smithy_runtime_api::client::connector_metadata::ConnectorMetadata;
9use aws_smithy_runtime_api::{
10 client::{
11 http::{
12 HttpClient, HttpConnector, HttpConnectorFuture, HttpConnectorSettings,
13 SharedHttpClient, SharedHttpConnector,
14 },
15 orchestrator::HttpRequest,
16 result::ConnectorError,
17 runtime_components::RuntimeComponents,
18 },
19 http::Response,
20 shared::IntoShared,
21};
22use aws_smithy_types::{body::SdkBody, retry::ErrorKind};
23use http_body_util::{BodyStream, StreamBody};
24use std::time::Duration;
25use wstd::http::{Body as WstdBody, BodyExt as _, Client, Error as WstdError};
26
27#[derive(Debug, Clone)]
29pub struct WasiSleep;
30impl AsyncSleep for WasiSleep {
31 fn sleep(&self, duration: Duration) -> Sleep {
32 Sleep::new(async move {
33 wstd::task::sleep(wstd::time::Duration::from(duration)).await;
34 })
35 }
36}
37
38#[derive(Default, Debug)]
41#[non_exhaustive]
42pub struct WasiHttpClientBuilder {}
43
44impl WasiHttpClientBuilder {
45 pub fn new() -> Self {
47 Default::default()
48 }
49
50 pub fn build(self) -> SharedHttpClient {
52 let client = WasiHttpClient {};
53 client.into_shared()
54 }
55}
56
57#[derive(Debug, Clone)]
61#[non_exhaustive]
62pub struct WasiHttpClient {}
63
64impl HttpClient for WasiHttpClient {
65 fn http_connector(
66 &self,
67 settings: &HttpConnectorSettings,
68 _components: &RuntimeComponents,
69 ) -> SharedHttpConnector {
70 let mut client = Client::new();
71 if let Some(timeout) = settings.connect_timeout() {
72 client.set_connect_timeout(timeout);
73 }
74 if let Some(timeout) = settings.read_timeout() {
75 client.set_first_byte_timeout(timeout);
76 }
77 SharedHttpConnector::new(WasiHttpConnector(client))
78 }
79
80 fn connector_metadata(&self) -> Option<ConnectorMetadata> {
81 Some(ConnectorMetadata::new("wasi-http-client", None))
82 }
83}
84
85#[derive(Debug, Clone)]
87struct WasiHttpConnector(Client);
88
89impl HttpConnector for WasiHttpConnector {
90 fn call(&self, request: HttpRequest) -> HttpConnectorFuture {
91 let client = self.0.clone();
92 HttpConnectorFuture::new(async move {
93 let request = request
94 .try_into_http1x()
95 .map_err(|e| ConnectorError::other(Box::new(e), None))?;
97 let request = request.map(|body| {
100 WstdBody::from_http_body(body.map_err(|e| WstdError::msg(format!("{e:?}"))))
101 });
102 let response = client
106 .send(request)
107 .await
108 .map_err(|e| ConnectorError::other(e.into(), Some(ErrorKind::ClientError)))?;
109
110 Response::try_from(response.map(|wstd_body| {
111 let nonsync_body = wstd_body
114 .into_boxed_body()
115 .map_err(|e| e.into_boxed_dyn_error());
116 let nonsync_stream = BodyStream::new(nonsync_body);
123 let sync_stream = sync_wrapper::SyncStream::new(nonsync_stream);
124 let sync_body = StreamBody::new(sync_stream);
125 SdkBody::from_body_1_x(sync_body)
126 }))
127 .map_err(|e| ConnectorError::other(Box::new(e), None))
129 })
130 }
131}