1use std::{future::Future, io::Result};
2
3use futures::{AsyncRead, AsyncWrite};
4use http::{Request, Response};
5
6use crate::body::BodyReader;
7use crate::reader::HttpReader;
8use crate::writer::HttpWriter;
9
10pub trait HttpSend {
12 fn send<S>(self, stream: S) -> impl Future<Output = Result<Response<BodyReader>>>
14 where
15 S: AsyncRead + AsyncWrite + Unpin + Send + 'static;
16}
17
18impl HttpSend for Request<BodyReader> {
19 fn send<S>(self, mut stream: S) -> impl Future<Output = Result<Response<BodyReader>>>
20 where
21 S: AsyncRead + AsyncWrite + Unpin + Send + 'static,
22 {
23 async move {
24 stream.write_request(self).await?;
25
26 stream.read_response().await
27 }
28 }
29}
30
31#[cfg(feature = "with_rasi")]
32pub mod rasio {
33 use std::{
34 future::Future,
35 io::{Error, ErrorKind, Result},
36 net::{SocketAddr, ToSocketAddrs},
37 path::{Path, PathBuf},
38 };
39
40 use futures_boring::{
41 connect,
42 ssl::{SslConnector, SslMethod},
43 };
44 use http::{uri::Scheme, Request, Response};
45 use rasi::net::TcpStream;
46
47 use crate::body::BodyReader;
48
49 #[derive(Default, Debug, Clone)]
51 pub struct HttpClientOptions {
52 raddrs: Option<Vec<SocketAddr>>,
53 server_name: Option<String>,
54 ca_file: Option<PathBuf>,
55 use_server_name_indication: bool,
56 }
57
58 impl HttpClientOptions {
59 pub fn new() -> HttpClientOptionsBuilder {
61 HttpClientOptionsBuilder {
62 ops: Ok(HttpClientOptions {
63 use_server_name_indication: true,
64 ..Default::default()
65 }),
66 }
67 }
68
69 async fn send(self, request: Request<BodyReader>) -> Result<Response<BodyReader>> {
70 let scheme = request.uri().scheme().ok_or(Error::new(
71 ErrorKind::InvalidInput,
72 "Unspecified request scheme",
73 ))?;
74
75 let host = request.uri().host().ok_or(Error::new(
76 ErrorKind::InvalidInput,
77 "Unspecified request uri",
78 ))?;
79
80 let port = request.uri().port_u16().unwrap_or_else(|| {
81 if scheme == &Scheme::HTTP {
82 80
83 } else {
84 443
85 }
86 });
87
88 let raddrs = if let Some(raddrs) = &self.raddrs {
89 raddrs.to_owned()
90 } else {
91 format!("{}:{}", host, port)
92 .to_socket_addrs()?
93 .collect::<Vec<_>>()
94 };
95
96 if scheme == &Scheme::HTTP {
97 let transport = TcpStream::connect(raddrs.as_slice()).await?;
98
99 return super::HttpSend::send(request, transport).await;
100 } else {
101 let stream = TcpStream::connect(raddrs.as_slice()).await?;
102
103 let mut config = SslConnector::builder(SslMethod::tls_client())
104 .map_err(|err| Error::new(ErrorKind::InvalidInput, err))?;
105
106 if let Some(ca_file) = self.ca_file.to_owned() {
107 log::trace!("load trust root ca: {:?}", ca_file);
108
109 config
110 .set_ca_file(ca_file)
111 .map_err(|err| Error::new(ErrorKind::InvalidInput, err))?;
112 }
113
114 let mut config = config.build().configure().unwrap();
115
116 config.set_use_server_name_indication(self.use_server_name_indication);
117
118 let transport = connect(config, host, stream)
119 .await
120 .map_err(|err| Error::new(ErrorKind::ConnectionRefused, err))?;
121
122 return super::HttpSend::send(request, transport).await;
123 }
124 }
125 }
126
127 impl TryInto<HttpClientOptions> for &HttpClientOptions {
128 type Error = std::io::Error;
129
130 fn try_into(self) -> std::result::Result<HttpClientOptions, Self::Error> {
131 Ok(self.clone())
132 }
133 }
134
135 pub struct HttpClientOptionsBuilder {
137 ops: Result<HttpClientOptions>,
138 }
139
140 impl HttpClientOptionsBuilder {
141 pub fn redirect<R: ToSocketAddrs>(self, raddrs: R) -> Self {
142 self.and_then(|mut ops| {
143 ops.raddrs = Some(
144 raddrs
145 .to_socket_addrs()
146 .map(|iter| iter.collect::<Vec<_>>())?,
147 );
148 Ok(ops)
149 })
150 }
151
152 pub fn with_server_name(self, server_name: &str) -> Self {
154 self.and_then(|mut ops| {
155 ops.server_name = Some(server_name.to_string());
156
157 Ok(ops)
158 })
159 }
160
161 pub fn with_ca_file<P: AsRef<Path>>(self, ca_file: P) -> Self {
163 self.and_then(|mut ops| {
164 ops.ca_file = Some(ca_file.as_ref().to_path_buf());
165
166 Ok(ops)
167 })
168 }
169
170 pub fn set_use_server_name_indication(self, value: bool) -> Self {
173 self.and_then(|mut ops| {
174 ops.use_server_name_indication = value;
175
176 Ok(ops)
177 })
178 }
179
180 fn and_then<F>(self, func: F) -> Self
181 where
182 F: FnOnce(HttpClientOptions) -> Result<HttpClientOptions>,
183 {
184 HttpClientOptionsBuilder {
185 ops: self.ops.and_then(func),
186 }
187 }
188 }
189
190 impl TryInto<HttpClientOptions> for HttpClientOptionsBuilder {
191 type Error = std::io::Error;
192 fn try_into(self) -> std::result::Result<HttpClientOptions, Self::Error> {
193 self.ops
194 }
195 }
196
197 pub trait HttpClient {
199 fn send<Op>(self, ops: Op) -> impl Future<Output = Result<Response<BodyReader>>>
203 where
204 Op: TryInto<HttpClientOptions, Error = std::io::Error>;
205 }
206
207 impl HttpClient for Request<BodyReader> {
208 fn send<Op>(self, ops: Op) -> impl Future<Output = Result<Response<BodyReader>>>
209 where
210 Op: TryInto<HttpClientOptions, Error = std::io::Error>,
211 {
212 async move {
213 let ops: HttpClientOptions = ops.try_into()?;
214
215 ops.send(self).await
216 }
217 }
218 }
219}