[go: up one dir, main page]

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}