generic_async_http_client/
lib.rs

1#![doc = include_str!("../README.md")]
2/*!
3 * # Example
4 * ```
5 * # use generic_async_http_client::{Request, Response, Error};
6 * # use serde::Serialize;
7 * #[derive(Serialize)]
8 * struct ContactForm {
9 *     email: String,
10 *     text: String,
11 * }
12 * async fn post_form(form: &ContactForm) -> Result<(), Error> {
13 *    let req = Request::post("http://example.com/").form(form)?;
14 *    assert_eq!(req.exec().await?.text().await?, "ok");
15 *    Ok(())
16 * }
17 * ```
18 *
19 * If performing more than one HTTP Request you should favor the use of [`Session`] over [`Request`].
20 */
21#![cfg_attr(docsrs, feature(doc_cfg))]
22
23#[cfg(all(feature = "mock_tests", any(test, docsrs)))]
24mod mock;
25#[cfg(all(feature = "mock_tests", any(test, docsrs)))]
26#[cfg_attr(docsrs, doc(cfg(all(test, feature = "mock_tests"))))]
27#[doc(inline)]
28pub use mock::{Mock, MockErr, MockedEndpoint};
29
30#[cfg(not(any(feature = "use_hyper", feature = "use_async_h1")))]
31#[path = "dummy/mod.rs"]
32mod imp;
33
34#[cfg(any(feature = "use_hyper", feature = "use_async_h1", feature = "proxies"))]
35mod tcp;
36#[cfg(all(
37    any(feature = "use_hyper", feature = "use_async_h1"),
38    feature = "proxies"
39))]
40#[cfg_attr(docsrs, doc(cfg(feature = "proxies")))]
41#[doc(inline)]
42pub use tcp::proxy;
43
44#[cfg(feature = "use_async_h1")]
45#[path = "a_h1/mod.rs"]
46mod imp;
47
48#[cfg(feature = "use_hyper")]
49#[path = "hyper/mod.rs"]
50mod imp;
51
52mod body;
53mod header;
54//mod session;
55mod request;
56mod response;
57
58pub use request::Request;
59pub use response::Response;
60//pub use session::Session;
61pub use body::Body;
62pub use header::{HeaderName, HeaderValue};
63
64#[derive(Debug)]
65pub enum Error {
66    Io(std::io::Error),
67    HTTPServerErr(u16, Response),
68    HTTPClientErr(u16, Response),
69    Other(imp::Error),
70    #[cfg(all(feature = "mock_tests", any(test, docsrs)))]
71    #[cfg_attr(docsrs, doc(cfg(all(test, feature = "mock_tests"))))]
72    Mock(mock::MockErr),
73}
74
75impl std::error::Error for Error {}
76use std::fmt;
77impl fmt::Display for Error {
78    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
79        match self {
80            Error::Other(i) => write!(f, "{}", i),
81            Error::HTTPClientErr(i, r) => write!(f, "{} {}", i, r.status()),
82            Error::HTTPServerErr(i, r) => write!(f, "{} {}", i, r.status()),
83            Error::Io(i) => write!(f, "{}", i),
84            #[cfg(all(feature = "mock_tests", test))]
85            Error::Mock(m) => write!(f, "{}", m),
86        }
87    }
88}
89impl From<std::convert::Infallible> for Error {
90    fn from(_e: std::convert::Infallible) -> Self {
91        unreachable!();
92    }
93}
94#[cfg(all(feature = "mock_tests", test))]
95impl From<mock::MockErr> for Error {
96    fn from(e: mock::MockErr) -> Self {
97        Self::Mock(e)
98    }
99}
100
101#[cfg(all(test, any(feature = "use_hyper", feature = "use_async_h1")))]
102mod tests {
103    #[cfg(feature = "use_async_h1")]
104    pub(crate) use async_std::{
105        io::prelude::{ReadExt, WriteExt},
106        net::{TcpListener, TcpStream},
107        task::spawn,
108    };
109    #[cfg(feature = "use_async_h1")]
110    pub(crate) fn block_on(
111        fut: impl futures::Future<Output = Result<(), Box<dyn std::error::Error>>>,
112    ) -> Result<(), Box<dyn std::error::Error>> {
113        async_std::task::block_on(fut)
114    }
115    //use futures::{AsyncWriteExt};
116    #[cfg(feature = "use_hyper")]
117    pub(crate) use tokio::{
118        io::{AsyncReadExt as ReadExt, AsyncWriteExt as WriteExt},
119        net::{TcpListener, TcpStream},
120        runtime::Builder,
121    };
122    #[cfg(feature = "use_hyper")]
123    pub(crate) fn block_on(
124        fut: impl futures::Future<Output = Result<(), Box<dyn std::error::Error>>>,
125    ) -> Result<(), Box<dyn std::error::Error>> {
126        Builder::new_current_thread()
127            .enable_all()
128            .build()
129            .expect("rt")
130            .block_on(fut)
131    }
132    #[cfg(feature = "use_hyper")]
133    pub(crate) fn spawn<T>(fut: T) -> impl futures::Future<Output = T::Output>
134    where
135        T: futures::Future + Send + 'static,
136        T::Output: Send + 'static,
137    {
138        let jh = tokio::task::spawn(fut);
139        async { jh.await.expect("spawn failed") }
140    }
141
142    pub(crate) async fn assert_stream(
143        stream: &mut TcpStream,
144        should_be: &[u8],
145    ) -> std::io::Result<()> {
146        let l = should_be.len();
147        let mut req: Vec<u8> = vec![0; l];
148        let _r = stream.read(req.as_mut_slice()).await?;
149        assert_eq!(req, should_be);
150        Ok(())
151    }
152    pub(crate) async fn listen_somewhere() -> Result<(TcpListener, u16, String), std::io::Error> {
153        let listener = TcpListener::bind("127.0.0.1:0").await?;
154        let addr = listener.local_addr()?;
155        Ok((listener, addr.port(), addr.ip().to_string()))
156    }
157
158    use super::*;
159    #[test]
160    fn get() {
161        async fn server(listener: TcpListener, host: String, port: u16) -> std::io::Result<bool> {
162            let (mut stream, _) = listener.accept().await?;
163            let mut output = Vec::with_capacity(1);
164            assert_stream(
165                &mut stream,
166                format!(
167                    "GET / HTTP/1.1\r\nhost: {}:{}\r\ncontent-length: 0\r\n\r\n",
168                    host, port
169                )
170                .as_bytes(),
171            )
172            .await?;
173
174            stream
175                .write_all(b"HTTP/1.1 200 OK\r\ncontent-length: 3\r\n\r\nabc")
176                .await?;
177            let _ = stream.read(&mut output).await?;
178            Ok(true)
179        }
180        block_on(async {
181            let (listener, port, host) = listen_somewhere().await?;
182            let uri = format!("http://{}:{}", host, port);
183            let t = spawn(server(listener, host, port));
184            let r = Request::get(&uri);
185            let mut aw = r.exec().await?;
186
187            assert_eq!(aw.status_code(), 200, "wrong status");
188            assert_eq!(aw.text().await?, "abc", "wrong text");
189            assert!(t.await?, "not cool");
190            Ok(())
191        })
192        .unwrap();
193    }
194    #[test]
195    fn header() {
196        async fn server(listener: TcpListener, host: String, port: u16) -> std::io::Result<bool> {
197            let (mut stream, _) = listener.accept().await?;
198            //let mut output = Vec::with_capacity(2);
199
200            #[cfg(feature = "use_async_h1")]
201            assert_stream(
202                &mut stream,
203                format!(
204                    "PUT / HTTP/1.1\r\nhost: {}:{}\r\ncontent-length: 0\r\ncookies: jo\r\n\r\n",
205                    host, port
206                )
207                .as_bytes(),
208            )
209            .await?;
210            #[cfg(feature = "use_hyper")]
211            assert_stream(
212                &mut stream,
213                format!(
214                    "PUT / HTTP/1.1\r\ncookies: jo\r\nhost: {}:{}\r\ncontent-length: 0\r\n\r\n",
215                    host, port
216                )
217                .as_bytes(),
218            )
219            .await?;
220
221            stream
222                .write_all(b"HTTP/1.1 200 OK\r\ntest: a\r\ntest: 1\r\n\r\n")
223                .await?;
224            //stream.read(&mut output).await?;
225            stream.flush().await?;
226            Ok(true)
227        }
228        block_on(async {
229            let (listener, port, host) = listen_somewhere().await?;
230            let uri = format!("http://{}:{}", host, port);
231            let server = spawn(server(listener, host, port));
232            let r = Request::new("PUT", &uri)?;
233            let r = r.set_header("Cookies", "jo")?;
234            let resp = r.exec().await;
235            if resp.is_err() {
236                server.await.expect("sent data wrong");
237                resp.expect("request failed");
238                return Ok(());
239            }
240            let resp = resp.expect("request failed");
241
242            assert_eq!(
243                resp.header("test").expect("no test header"),
244                "a",
245                "wrong first header"
246            );
247
248            let mut h = resp.headers().filter(|(n, _v)| *n != "date"); //async h1 adds date
249            let (n, v) = h.next().expect("two header missing");
250            assert_eq!(
251                <header::HeaderName as AsRef<[u8]>>::as_ref(n),
252                &b"test"[..],
253                "wrong 1st header"
254            );
255            assert_eq!(v, "a", "wrong 1st header");
256            let (n, v) = h.next().expect("one header missing");
257            assert_eq!(
258                <header::HeaderName as AsRef<str>>::as_ref(n),
259                "test",
260                "wrong 2nd header"
261            );
262            assert_eq!(v, "1", "wrong 2nd header");
263
264            let fin = h.next();
265            assert!(fin.is_none(), "to much headers {:?}", fin);
266
267            assert!(server.await?, "not cool");
268            Ok(())
269        })
270        .unwrap();
271    }
272}