1#![doc = include_str!("../readme.md")]
6#![forbid(unsafe_code)]
7
8mod body;
9mod connection;
10mod driver;
11mod error;
12mod outgoing;
13mod reuse;
14mod sink;
15mod writable;
16pub use writable::{PendingResponse, RequestBodyWriter, StreamingResponseBody, WritableRequest};
17
18pub use body::ResponseBody;
19pub use connection::Connection;
20pub use driver::Driver;
21pub use error::Error;
22
23use base_http::{Authority, InterimHandler, Method, Request, Response, Scheme, body::ReadableBody};
24use base_pool::MultiplexPool;
25use base_scheduler::{
26 Cancellation, SocketClose, SocketConnect, SocketOpen, SocketStreamReading, SocketStreamWriting,
27};
28use driver::{CancelOnDrop, Operation, Pending, Queue, checked};
29use std::{
30 convert::Infallible,
31 net::SocketAddr,
32 sync::{Arc, atomic::Ordering},
33};
34
35#[derive(Clone, Debug)]
38pub struct Endpoint {
39 pub address: SocketAddr,
40 pub authority: Authority,
41 #[cfg(feature = "rustls")]
42 pub(crate) tls: Option<Tls>,
43}
44#[cfg(feature = "rustls")]
45#[derive(Clone, Debug)]
46pub(crate) struct Tls {
47 config: Arc<rustls::ClientConfig>,
48 server_name: String,
49}
50impl Endpoint {
51 pub fn new(address: SocketAddr) -> Self {
52 Self {
53 address,
54 authority: address.to_string().into(),
55 #[cfg(feature = "rustls")]
56 tls: None,
57 }
58 }
59 #[cfg(feature = "rustls")]
62 pub fn with_tls(
63 mut self,
64 config: Arc<rustls::ClientConfig>,
65 server_name: impl Into<String>,
66 ) -> Self {
67 self.tls = Some(Tls {
68 config,
69 server_name: server_name.into(),
70 });
71 self
72 }
73 pub fn scheme(&self) -> &'static str {
74 #[cfg(feature = "rustls")]
75 if self.tls.is_some() {
76 return "https";
77 }
78 "http"
79 }
80 pub(crate) fn request_scheme(&self) -> Scheme {
82 #[cfg(feature = "rustls")]
83 if self.tls.is_some() {
84 return Scheme::HTTPS;
85 }
86 Scheme::HTTP
87 }
88}
89
90#[derive(Clone, Debug)]
91pub struct Options {
92 pub pool: base_pool::Options,
93 pub http1: base_http1::Options,
94 pub protocols: base_http_protocol::HttpProtocols,
96 pub max_requests: usize,
98 pub response_buffer_bytes: usize,
99 pub response_buffer_frames: usize,
100}
101impl Default for Options {
102 fn default() -> Self {
103 Self {
104 pool: base_pool::Options::default(),
105 http1: base_http1::Options::default(),
106 protocols: base_http_protocol::HttpProtocols::default(),
107 max_requests: 1024,
108 response_buffer_bytes: 64 * 1024,
109 response_buffer_frames: 16,
110 }
111 }
112}
113impl Options {
114 pub fn validate(&self) -> Result<(), Error> {
115 self.pool.validate()?;
116 self.http1.validate()?;
117 if self.max_requests == 0
118 || self.response_buffer_frames == 0
119 || self.response_buffer_bytes < self.http1.read_size.max(self.http1.max_head_bytes)
120 {
121 return Err(Error::InvalidOptions);
122 }
123 Ok(())
124 }
125}
126
127pub struct Client<'scope> {
131 endpoint: Endpoint,
132 queue: Arc<Queue<'scope>>,
133 pool: MultiplexPool<Connection<'scope>>,
134 context: Arc<driver::Context<'scope>>,
135}
136impl<'scope> Client<'scope> {
137 pub fn new<S>(
141 scheduler: &'scope S,
142 endpoint: Endpoint,
143 options: Options,
144 ) -> Result<(Self, Driver<'scope>), Error>
145 where
146 S: SocketOpen
147 + SocketConnect
148 + SocketClose
149 + SocketStreamReading
150 + SocketStreamWriting
151 + 'scope,
152 S::Socket: 'static,
153 {
154 options.validate()?;
155 let driver::ClientParts {
156 queue,
157 pool,
158 driver,
159 context,
160 } = driver::make(scheduler, endpoint.clone(), options);
161 Ok((
162 Self {
163 endpoint,
164 queue,
165 pool,
166 context,
167 },
168 driver,
169 ))
170 }
171 pub fn endpoint(&self) -> &Endpoint {
172 &self.endpoint
173 }
174 pub fn pool_stats(&self) -> base_pool::Stats {
175 self.pool.stats()
176 }
177 pub fn begin_close(&self) {
180 self.queue.begin_close();
181 }
182
183 pub async fn call<B>(
186 &self,
187 request: Request<B>,
188 cancellation: &Cancellation,
189 ) -> Result<Response<ResponseBody>, Error>
190 where
191 B: ReadableBody + 'scope,
192 B::Data: Into<Vec<u8>>,
193 {
194 self.call_with_interim(
195 request,
196 |_: base_http::ResponseMetadata| async { Ok::<_, Infallible>(()) },
197 cancellation,
198 )
199 .await
200 }
201 pub async fn call_with_interim<B, I>(
203 &self,
204 mut request: Request<B>,
205 interim: I,
206 cancellation: &Cancellation,
207 ) -> Result<Response<ResponseBody>, Error>
208 where
209 B: ReadableBody + 'scope,
210 B::Data: Into<Vec<u8>>,
211 I: InterimHandler + 'scope,
212 {
213 if cancellation.is_cancelled() {
214 request.body.close(None);
215 return Err(Error::Cancelled);
216 }
217 if request.method == Method::CONNECT
218 || request.protocol.is_some()
219 || request.fields.contains("upgrade")
220 {
221 request.body.close(None);
222 return Err(Error::Unsupported("managed client tunnels and upgrades"));
223 }
224 if request
225 .scheme
226 .as_deref()
227 .is_some_and(|scheme| scheme != self.endpoint.scheme())
228 {
229 request.body.close(None);
230 return Err(Error::Unsupported(
231 "request scheme does not match the endpoint",
232 ));
233 }
234 request
235 .scheme
236 .get_or_insert_with(|| self.endpoint.request_scheme());
237 request
238 .authority
239 .get_or_insert_with(|| self.endpoint.authority.clone());
240 let local = Cancellation::new();
241 let mut guard = CancelOnDrop(local.clone(), true);
242 let (reply, response) = futures_channel::oneshot::channel();
243 let pending = match Pending::new(self.queue.clone()) {
244 Ok(pending) => pending,
245 Err(error) => {
246 request.body.close(None);
247 return Err(error);
248 }
249 };
250 let operation = Operation {
251 request: request.map_body(|body| outgoing::OutgoingBody::new(body, local.clone())),
252 interim: Box::new(interim),
253 reply: Some(reply),
254 cancellation: local,
255 lifetime: cancellation.clone(),
256 _pending: Some(pending),
257 };
258 let _ = self.queue.push(operation);
260 let response = checked(
261 async { response.await.map_err(|_| Error::Closed)? },
262 cancellation,
263 )
264 .await?;
265 guard.1 = false;
266 Ok(response)
267 }
268}
269impl Clone for Client<'_> {
270 fn clone(&self) -> Self {
271 self.queue.handles.fetch_add(1, Ordering::Relaxed);
272 Self {
273 endpoint: self.endpoint.clone(),
274 queue: self.queue.clone(),
275 pool: self.pool.clone(),
276 context: self.context.clone(),
277 }
278 }
279}
280impl Drop for Client<'_> {
281 fn drop(&mut self) {
282 if self.queue.handles.fetch_sub(1, Ordering::AcqRel) == 1 {
283 self.queue.begin_close();
284 }
285 }
286}