surf/client.rs
1use std::convert::TryFrom;
2use std::fmt;
3use std::sync::Arc;
4
5use crate::http::{Method, Url};
6use crate::middleware::{Middleware, Next};
7use crate::{Config, HttpClient, Request, RequestBuilder, Response, Result};
8
9use cfg_if::cfg_if;
10
11cfg_if! {
12 if #[cfg(feature = "curl-client")] {
13 use http_client::isahc::IsahcClient as DefaultClient;
14 } else if #[cfg(feature = "wasm-client")] {
15 use http_client::wasm::WasmClient as DefaultClient;
16 } else if #[cfg(any(feature = "h1-client", feature = "h1-client-rustls", feature = "h1-client-no-tls"))] {
17 use http_client::h1::H1Client as DefaultClient;
18 } else if #[cfg(feature = "hyper-client")] {
19 use http_client::hyper::HyperClient as DefaultClient;
20 }
21}
22cfg_if! {
23 if #[cfg(any(feature = "curl-client", feature = "h1-client", feature = "h1-client-rustls", feature = "hyper-client"))] {
24 use once_cell::sync::Lazy;
25 static GLOBAL_CLIENT: Lazy<Arc<DefaultClient>> = Lazy::new(|| Arc::new(DefaultClient::new()));
26 }
27}
28
29/// An HTTP client, capable of sending `Request`s and running a middleware stack.
30///
31/// Can be optionally set with a base url.
32///
33/// # Examples
34///
35/// ```no_run
36/// # #[async_std::main]
37/// # async fn main() -> surf::Result<()> {
38/// let client = surf::Client::new();
39/// let res1 = client.recv_string(surf::get("https://httpbin.org/get"));
40/// let res2 = client.recv_string(surf::get("https://httpbin.org/get"));
41/// let (str1, str2) = futures_util::future::try_join(res1, res2).await?;
42/// # Ok(()) }
43/// ```
44pub struct Client {
45 config: Config,
46 http_client: Arc<dyn HttpClient>,
47 /// Holds the middleware stack.
48 ///
49 /// Note(Fishrock123): We do actually want this structure.
50 /// The outer Arc allows us to clone in .send() without cloning the array.
51 /// The Vec allows us to add middleware at runtime.
52 /// The inner Arc-s allow us to implement Clone without sharing the vector with the parent.
53 /// We don't use a Mutex around the Vec here because adding a middleware during execution should be an error.
54 #[allow(clippy::rc_buffer)]
55 middleware: Arc<Vec<Arc<dyn Middleware>>>,
56}
57
58impl Clone for Client {
59 /// Clones the Client.
60 ///
61 /// This copies the middleware stack from the original, but shares
62 /// the `HttpClient` and http client config of the original.
63 /// Note that individual middleware in the middleware stack are
64 /// still shared by reference.
65 fn clone(&self) -> Self {
66 Self {
67 config: self.config.clone(),
68 http_client: self.http_client.clone(),
69 middleware: Arc::new(self.middleware.iter().cloned().collect()),
70 }
71 }
72}
73
74impl fmt::Debug for Client {
75 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
76 write!(f, "Client {{}}")
77 }
78}
79
80#[cfg(feature = "default-client")]
81impl Default for Client {
82 fn default() -> Self {
83 Self::new()
84 }
85}
86
87impl Client {
88 /// Create a new `Client` instance.
89 ///
90 /// # Examples
91 ///
92 /// ```rust
93 /// # #[async_std::main]
94 /// # async fn main() -> surf::Result<()> {
95 /// let client = surf::Client::new();
96 ///
97 /// let req = surf::get("https://httpbin.org/get");
98 /// let res = client.send(req).await?;
99 /// # Ok(()) }
100 /// ```
101 #[cfg(feature = "default-client")]
102 pub fn new() -> Self {
103 Self::with_http_client(DefaultClient::new())
104 }
105
106 pub(crate) fn new_shared_or_panic() -> Self {
107 cfg_if! {
108 if #[cfg(feature = "default-client")] {
109 Self::new_shared()
110 } else {
111 panic!("default client not configured")
112 }
113 }
114 }
115
116 /// Create a new `Client` instance with an `http_client::HttpClient` backend.
117 ///
118 /// # Examples
119 ///
120 /// ```rust
121 /// # #[cfg(feature = "curl-client")] {
122 /// # use std::sync::Arc;
123 /// use http_client::isahc::IsahcClient;
124 /// let client = surf::Client::with_http_client(IsahcClient::new());
125 /// # }
126 /// ```
127 pub fn with_http_client<C: HttpClient>(http_client: C) -> Self {
128 Self::with_http_client_internal(Arc::new(http_client))
129 }
130
131 fn with_http_client_internal(http_client: Arc<dyn HttpClient>) -> Self {
132 let client = Self {
133 config: Config::default(),
134 http_client,
135 middleware: Arc::new(vec![]),
136 };
137
138 #[cfg(feature = "middleware-logger")]
139 let client = client.with(crate::middleware::Logger::new());
140
141 client
142 }
143
144 #[cfg(feature = "default-client")]
145 pub(crate) fn new_shared() -> Self {
146 cfg_if! {
147 if #[cfg(any(feature = "curl-client", feature = "h1-client", feature = "h1-client-rustls", feature = "hyper-client"))] {
148 Self::with_http_client_internal(GLOBAL_CLIENT.clone())
149 } else {
150 Self::new()
151 }
152 }
153 }
154
155 /// Push middleware onto the middleware stack.
156 ///
157 /// See the [middleware] submodule for more information on middleware.
158 ///
159 /// [middleware]: ../middleware/index.html
160 ///
161 /// # Examples
162 ///
163 /// ```no_run
164 /// # #[async_std::main]
165 /// # async fn main() -> surf::Result<()> {
166 /// let req = surf::get("https://httpbin.org/get");
167 /// let client = surf::client()
168 /// .with(surf::middleware::Redirect::default());
169 /// let res = client.send(req).await?;
170 /// # Ok(()) }
171 /// ```
172 pub fn with(mut self, middleware: impl Middleware) -> Self {
173 let m = Arc::get_mut(&mut self.middleware)
174 .expect("Registering middleware is not possible after the Client has been used");
175 m.push(Arc::new(middleware));
176 self
177 }
178
179 /// Send a `Request` using this client.
180 ///
181 /// Client middleware is run before per-request middleware.
182 ///
183 /// # Examples
184 ///
185 /// ```no_run
186 /// # #[async_std::main]
187 /// # async fn main() -> surf::Result<()> {
188 /// let req = surf::get("https://httpbin.org/get");
189 /// let client = surf::client();
190 /// let res = client.send(req).await?;
191 /// # Ok(()) }
192 /// ```
193 pub async fn send(&self, req: impl Into<Request>) -> Result<Response> {
194 let mut req: Request = req.into();
195 let http_client = self.http_client.clone();
196 let middleware = self.middleware.clone();
197
198 let mw_stack = match req.take_middleware() {
199 Some(req_mw) => {
200 let mut mw = Vec::with_capacity(middleware.len() + req_mw.len());
201 mw.extend(middleware.iter().cloned());
202 mw.extend(req_mw);
203 Arc::new(mw)
204 }
205 None => middleware,
206 };
207
208 let next = Next::new(&mw_stack, &|req, client| {
209 Box::pin(async move {
210 let req: http_types::Request = req.into();
211 client.http_client.send(req).await.map(Into::into)
212 })
213 });
214
215 let client = Self {
216 config: self.config.clone(),
217 http_client,
218 // Erase the middleware stack for the Client accessible from within middleware.
219 // This avoids gratuitous circular borrow & logic issues.
220 middleware: Arc::new(vec![]),
221 };
222
223 let res = next.run(req, client).await?;
224 Ok(Response::new(res.into()))
225 }
226
227 /// Submit a `Request` and get the response body as bytes.
228 ///
229 /// # Examples
230 ///
231 /// ```no_run
232 /// # #[async_std::main]
233 /// # async fn main() -> surf::Result<()> {
234 /// let req = surf::get("https://httpbin.org/get");
235 /// let bytes = surf::client().recv_bytes(req).await?;
236 /// assert!(bytes.len() > 0);
237 /// # Ok(()) }
238 /// ```
239 pub async fn recv_bytes(&self, req: impl Into<Request>) -> Result<Vec<u8>> {
240 let mut res = self.send(req.into()).await?;
241 Ok(res.body_bytes().await?)
242 }
243
244 /// Submit a `Request` and get the response body as a string.
245 ///
246 /// # Examples
247 ///
248 /// ```no_run
249 /// # #[async_std::main]
250 /// # async fn main() -> surf::Result<()> {
251 /// let req = surf::get("https://httpbin.org/get");
252 /// let string = surf::client().recv_string(req).await?;
253 /// assert!(string.len() > 0);
254 /// # Ok(()) }
255 /// ```
256 pub async fn recv_string(&self, req: impl Into<Request>) -> Result<String> {
257 let mut res = self.send(req.into()).await?;
258 Ok(res.body_string().await?)
259 }
260
261 /// Submit a `Request` and decode the response body from json into a struct.
262 ///
263 /// # Examples
264 ///
265 /// ```no_run
266 /// # use serde::{Deserialize, Serialize};
267 /// # #[async_std::main]
268 /// # async fn main() -> surf::Result<()> {
269 /// #[derive(Deserialize, Serialize)]
270 /// struct Ip {
271 /// ip: String
272 /// }
273 ///
274 /// let req = surf::get("https://api.ipify.org?format=json");
275 /// let Ip { ip } = surf::client().recv_json(req).await?;
276 /// assert!(ip.len() > 10);
277 /// # Ok(()) }
278 /// ```
279 pub async fn recv_json<T: serde::de::DeserializeOwned>(
280 &self,
281 req: impl Into<Request>,
282 ) -> Result<T> {
283 let mut res = self.send(req.into()).await?;
284 Ok(res.body_json::<T>().await?)
285 }
286
287 /// Submit a `Request` and decode the response body from form encoding into a struct.
288 ///
289 /// # Errors
290 ///
291 /// Any I/O error encountered while reading the body is immediately returned
292 /// as an `Err`.
293 ///
294 /// If the body cannot be interpreted as valid json for the target type `T`,
295 /// an `Err` is returned.
296 ///
297 /// # Examples
298 ///
299 /// ```no_run
300 /// # use serde::{Deserialize, Serialize};
301 /// # #[async_std::main]
302 /// # async fn main() -> surf::Result<()> {
303 /// #[derive(Deserialize, Serialize)]
304 /// struct Body {
305 /// apples: u32
306 /// }
307 ///
308 /// let req = surf::get("https://api.example.com/v1/response");
309 /// let Body { apples } = surf::client().recv_form(req).await?;
310 /// # Ok(()) }
311 /// ```
312 pub async fn recv_form<T: serde::de::DeserializeOwned>(
313 &self,
314 req: impl Into<Request>,
315 ) -> Result<T> {
316 let mut res = self.send(req.into()).await?;
317 Ok(res.body_form::<T>().await?)
318 }
319
320 /// Perform an HTTP `GET` request using the `Client` connection.
321 ///
322 /// # Panics
323 ///
324 /// This will panic if a malformed URL is passed.
325 ///
326 /// # Errors
327 ///
328 /// Returns errors from the middleware, http backend, and network sockets.
329 ///
330 /// # Examples
331 ///
332 /// ```no_run
333 /// # #[async_std::main]
334 /// # async fn main() -> surf::Result<()> {
335 /// let client = surf::client();
336 /// let string = client.get("https://httpbin.org/get").recv_string().await?;
337 /// # Ok(()) }
338 /// ```
339 pub fn get(&self, uri: impl AsRef<str>) -> RequestBuilder {
340 RequestBuilder::new(Method::Get, self.url(uri)).with_client(self.clone())
341 }
342
343 /// Perform an HTTP `HEAD` request using the `Client` connection.
344 ///
345 /// # Panics
346 ///
347 /// This will panic if a malformed URL is passed.
348 ///
349 /// # Errors
350 ///
351 /// Returns errors from the middleware, http backend, and network sockets.
352 ///
353 /// # Examples
354 ///
355 /// ```no_run
356 /// # #[async_std::main]
357 /// # async fn main() -> surf::Result<()> {
358 /// let client = surf::client();
359 /// let string = client.head("https://httpbin.org/head").recv_string().await?;
360 /// # Ok(()) }
361 /// ```
362 pub fn head(&self, uri: impl AsRef<str>) -> RequestBuilder {
363 RequestBuilder::new(Method::Head, self.url(uri)).with_client(self.clone())
364 }
365
366 /// Perform an HTTP `POST` request using the `Client` connection.
367 ///
368 /// # Panics
369 ///
370 /// This will panic if a malformed URL is passed.
371 ///
372 /// # Errors
373 ///
374 /// Returns errors from the middleware, http backend, and network sockets.
375 ///
376 /// # Examples
377 ///
378 /// ```no_run
379 /// # #[async_std::main]
380 /// # async fn main() -> surf::Result<()> {
381 /// let client = surf::client();
382 /// let string = client.post("https://httpbin.org/post").recv_string().await?;
383 /// # Ok(()) }
384 /// ```
385 pub fn post(&self, uri: impl AsRef<str>) -> RequestBuilder {
386 RequestBuilder::new(Method::Post, self.url(uri)).with_client(self.clone())
387 }
388
389 /// Perform an HTTP `PUT` request using the `Client` connection.
390 ///
391 /// # Panics
392 ///
393 /// This will panic if a malformed URL is passed.
394 ///
395 /// # Errors
396 ///
397 /// Returns errors from the middleware, http backend, and network sockets.
398 ///
399 /// # Examples
400 ///
401 /// ```no_run
402 /// # #[async_std::main]
403 /// # async fn main() -> surf::Result<()> {
404 /// let client = surf::client();
405 /// let string = client.put("https://httpbin.org/put").recv_string().await?;
406 /// # Ok(()) }
407 /// ```
408 pub fn put(&self, uri: impl AsRef<str>) -> RequestBuilder {
409 RequestBuilder::new(Method::Put, self.url(uri)).with_client(self.clone())
410 }
411
412 /// Perform an HTTP `DELETE` request using the `Client` connection.
413 ///
414 /// # Panics
415 ///
416 /// This will panic if a malformed URL is passed.
417 ///
418 /// # Errors
419 ///
420 /// Returns errors from the middleware, http backend, and network sockets.
421 ///
422 /// # Examples
423 ///
424 /// ```no_run
425 /// # #[async_std::main]
426 /// # async fn main() -> surf::Result<()> {
427 /// let client = surf::client();
428 /// let string = client.delete("https://httpbin.org/delete").recv_string().await?;
429 /// # Ok(()) }
430 /// ```
431 pub fn delete(&self, uri: impl AsRef<str>) -> RequestBuilder {
432 RequestBuilder::new(Method::Delete, self.url(uri)).with_client(self.clone())
433 }
434
435 /// Perform an HTTP `CONNECT` request using the `Client` connection.
436 ///
437 /// # Panics
438 ///
439 /// This will panic if a malformed URL is passed.
440 ///
441 /// # Errors
442 ///
443 /// Returns errors from the middleware, http backend, and network sockets.
444 ///
445 /// # Examples
446 ///
447 /// ```no_run
448 /// # #[async_std::main]
449 /// # async fn main() -> surf::Result<()> {
450 /// let client = surf::client();
451 /// let string = client.connect("https://httpbin.org/connect").recv_string().await?;
452 /// # Ok(()) }
453 /// ```
454 pub fn connect(&self, uri: impl AsRef<str>) -> RequestBuilder {
455 RequestBuilder::new(Method::Connect, self.url(uri)).with_client(self.clone())
456 }
457
458 /// Perform an HTTP `OPTIONS` request using the `Client` connection.
459 ///
460 /// # Panics
461 ///
462 /// This will panic if a malformed URL is passed.
463 ///
464 /// # Errors
465 ///
466 /// Returns errors from the middleware, http backend, and network sockets.
467 ///
468 /// # Examples
469 ///
470 /// ```no_run
471 /// # #[async_std::main]
472 /// # async fn main() -> surf::Result<()> {
473 /// let client = surf::client();
474 /// let string = client.options("https://httpbin.org/options").recv_string().await?;
475 /// # Ok(()) }
476 /// ```
477 pub fn options(&self, uri: impl AsRef<str>) -> RequestBuilder {
478 RequestBuilder::new(Method::Options, self.url(uri)).with_client(self.clone())
479 }
480
481 /// Perform an HTTP `TRACE` request using the `Client` connection.
482 ///
483 /// # Panics
484 ///
485 /// This will panic if a malformed URL is passed.
486 ///
487 /// # Errors
488 ///
489 /// Returns errors from the middleware, http backend, and network sockets.
490 ///
491 /// # Examples
492 ///
493 /// ```no_run
494 /// # #[async_std::main]
495 /// # async fn main() -> surf::Result<()> {
496 /// let client = surf::client();
497 /// let string = client.trace("https://httpbin.org/trace").recv_string().await?;
498 /// # Ok(()) }
499 /// ```
500 pub fn trace(&self, uri: impl AsRef<str>) -> RequestBuilder {
501 RequestBuilder::new(Method::Trace, self.url(uri)).with_client(self.clone())
502 }
503
504 /// Perform an HTTP `PATCH` request using the `Client` connection.
505 ///
506 /// # Panics
507 ///
508 /// This will panic if a malformed URL is passed.
509 ///
510 /// # Errors
511 ///
512 /// Returns errors from the middleware, http backend, and network sockets.
513 ///
514 /// # Examples
515 ///
516 /// ```no_run
517 /// # #[async_std::main]
518 /// # async fn main() -> surf::Result<()> {
519 /// let client = surf::client();
520 /// let string = client.patch("https://httpbin.org/patch").recv_string().await?;
521 /// # Ok(()) }
522 /// ```
523 pub fn patch(&self, uri: impl AsRef<str>) -> RequestBuilder {
524 RequestBuilder::new(Method::Patch, self.url(uri)).with_client(self.clone())
525 }
526
527 /// Perform a HTTP request with the given verb using the `Client` connection.
528 ///
529 /// # Panics
530 ///
531 /// This will panic if a malformed URL is passed.
532 ///
533 /// # Errors
534 ///
535 /// Returns errors from the middleware, http backend, and network sockets.
536 ///
537 /// # Examples
538 /// ```no_run
539 /// # #[async_std::main]
540 /// # async fn main() -> surf::Result<()> {
541 /// use http_types::Method;
542 /// let client = surf::client();
543 /// let req = client.request(Method::Get, "http://httpbin.org/get");
544 /// let res = client.send(req).await?;
545 /// # Ok(()) }
546 /// ```
547 pub fn request(&self, verb: Method, uri: impl AsRef<str>) -> RequestBuilder {
548 RequestBuilder::new(verb, self.url(uri)).with_client(self.clone())
549 }
550
551 /// Sets the base URL for this client. All request URLs will be relative to this URL.
552 ///
553 /// Note: a trailing slash is significant.
554 /// Without it, the last path component is considered to be a “file” name
555 /// to be removed to get at the “directory” that is used as the base.
556 ///
557 /// # Examples
558 /// ```no_run
559 /// # use http_types::Url;
560 /// # fn main() -> http_types::Result<()> { async_std::task::block_on(async {
561 /// let mut client = surf::client();
562 /// client.set_base_url(Url::parse("http://example.com/api/v1/")?);
563 /// client.get("posts.json").recv_json().await?; /// http://example.com/api/v1/posts.json
564 /// # Ok(()) }) }
565 /// ```
566 #[deprecated(since = "6.5.0", note = "Please use `Config` instead")]
567 pub fn set_base_url(&mut self, base: Url) {
568 self.config.base_url = Some(base);
569 }
570
571 /// Get the current configuration.
572 pub fn config(&self) -> &Config {
573 &self.config
574 }
575
576 // private function to generate a url based on the base_path
577 fn url(&self, uri: impl AsRef<str>) -> Url {
578 match &self.config.base_url {
579 None => uri.as_ref().parse().unwrap(),
580 Some(base) => base.join(uri.as_ref()).unwrap(),
581 }
582 }
583}
584
585impl TryFrom<Config> for Client {
586 #[cfg(feature = "default-client")]
587 type Error = <DefaultClient as TryFrom<http_client::Config>>::Error;
588 #[cfg(not(feature = "default-client"))]
589 type Error = std::convert::Infallible;
590
591 fn try_from(mut config: Config) -> std::result::Result<Self, Self::Error> {
592 let http_client = match config.http_client.take() {
593 Some(client) => client,
594 #[cfg(feature = "default-client")]
595 None => Arc::new(DefaultClient::try_from(config.http_config.clone())?),
596 #[cfg(not(feature = "default-client"))]
597 None => panic!("Config without an http client provided to Surf configured without a default client.")
598 };
599
600 Ok(Client {
601 config,
602 http_client,
603 middleware: Arc::new(vec![]),
604 })
605 }
606}
607
608#[cfg(test)]
609mod client_tests {
610 use std::convert::TryInto;
611
612 use super::Client;
613 use super::Config;
614 use crate::Url;
615
616 #[test]
617 fn base_url() {
618 let base_url = Url::parse("http://example.com/api/v1/").unwrap();
619
620 let client: Client = Config::new().set_base_url(base_url).try_into().unwrap();
621 let url = client.url("posts.json");
622 assert_eq!(url.as_str(), "http://example.com/api/v1/posts.json");
623 }
624}