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(not(any(feature = "use_hyper", feature = "use_async_h1")))]
24#[path = "dummy/mod.rs"]
25mod imp;
26
27#[cfg(any(feature = "use_hyper", feature = "use_async_h1", feature = "proxies"))]
28mod tcp;
29#[cfg(all(
30    any(feature = "use_hyper", feature = "use_async_h1"),
31    feature = "proxies"
32))]
33#[cfg_attr(docsrs, doc(cfg(feature = "proxies")))]
34#[doc(inline)]
35pub use tcp::proxy;
36
37#[cfg(feature = "use_async_h1")]
38#[path = "a_h1/mod.rs"]
39mod imp;
40
41#[cfg(feature = "use_hyper")]
42#[path = "hyper/mod.rs"]
43mod imp;
44
45mod body;
46mod header;
47//mod session;
48mod request;
49mod response;
50
51pub use request::Request;
52pub use response::Response;
53//pub use session::Session;
54pub use body::Body;
55pub use header::{HeaderName, HeaderValue};
56pub use imp::Error;
57
58#[cfg(all(test,
59    any(feature = "use_hyper", feature = "use_async_h1")
60))]
61mod tests {
62    #[cfg(feature = "use_async_h1")]
63    pub(crate) use async_std::{
64        io::prelude::{ReadExt, WriteExt},
65        net::{TcpListener, TcpStream},
66        task::spawn,
67    };
68    #[cfg(feature = "use_async_h1")]
69    pub(crate) fn block_on(
70        fut: impl futures::Future<Output = Result<(), Box<dyn std::error::Error>>>,
71    ) -> Result<(), Box<dyn std::error::Error>> {
72        async_std::task::block_on(fut)
73    }
74    //use futures::{AsyncWriteExt};
75    #[cfg(feature = "use_hyper")]
76    pub(crate) use tokio::{
77        io::{AsyncReadExt as ReadExt, AsyncWriteExt as WriteExt},
78        net::{TcpListener, TcpStream},
79        runtime::Builder,
80    };
81    #[cfg(feature = "use_hyper")]
82    pub(crate) fn block_on(
83        fut: impl futures::Future<Output = Result<(), Box<dyn std::error::Error>>>,
84    ) -> Result<(), Box<dyn std::error::Error>> {
85        Builder::new_current_thread()
86            .enable_all()
87            .build()
88            .expect("rt")
89            .block_on(fut)
90    }
91    #[cfg(feature = "use_hyper")]
92    pub(crate) fn spawn<T>(fut: T) -> impl futures::Future<Output = T::Output>
93    where
94        T: futures::Future + Send + 'static,
95        T::Output: Send + 'static,
96    {
97        let jh = tokio::task::spawn(fut);
98        async { jh.await.expect("spawn failed") }
99    }
100
101    pub(crate) async fn assert_stream(
102        stream: &mut TcpStream,
103        should_be: &[u8],
104    ) -> std::io::Result<()> {
105        let l = should_be.len();
106        let mut req: Vec<u8> = vec![0; l];
107        let _r = stream.read(req.as_mut_slice()).await?;
108        assert_eq!(req, should_be);
109        Ok(())
110    }
111    pub(crate) async fn listen_somewhere() -> Result<(TcpListener, u16, String), std::io::Error> {
112        let listener = TcpListener::bind("127.0.0.1:0").await?;
113        let addr = listener.local_addr()?;
114        Ok((listener, addr.port(), addr.ip().to_string()))
115    }
116
117    use super::*;
118    #[test]
119    fn get() {
120        async fn server(listener: TcpListener, host: String, port: u16) -> std::io::Result<bool> {
121            let (mut stream, _) = listener.accept().await?;
122            let mut output = Vec::with_capacity(1);
123            assert_stream(
124                &mut stream,
125                format!(
126                    "GET / HTTP/1.1\r\nhost: {}:{}\r\ncontent-length: 0\r\n\r\n",
127                    host, port
128                )
129                .as_bytes(),
130            )
131            .await?;
132
133            stream
134                .write_all(b"HTTP/1.1 200 OK\r\ncontent-length: 3\r\n\r\nabc")
135                .await?;
136            let _ = stream.read(&mut output).await?;
137            Ok(true)
138        }
139        block_on(async {
140            let (listener, port, host) = listen_somewhere().await?;
141            let uri = format!("http://{}:{}", host, port);
142            let t = spawn(server(listener, host, port));
143            let r = Request::get(&uri);
144            let mut aw = r.exec().await?;
145
146            assert_eq!(aw.status_code(), 200, "wrong status");
147            assert_eq!(aw.text().await?, "abc", "wrong text");
148            assert!(t.await?, "not cool");
149            Ok(())
150        })
151        .unwrap();
152    }
153    #[test]
154    fn header() {
155        async fn server(listener: TcpListener, host: String, port: u16) -> std::io::Result<bool> {
156            let (mut stream, _) = listener.accept().await?;
157            //let mut output = Vec::with_capacity(2);
158
159            #[cfg(feature = "use_async_h1")]
160            assert_stream(
161                &mut stream,
162                format!(
163                    "PUT / HTTP/1.1\r\nhost: {}:{}\r\ncontent-length: 0\r\ncookies: jo\r\n\r\n",
164                    host, port
165                )
166                .as_bytes(),
167            )
168            .await?;
169            #[cfg(feature = "use_hyper")]
170            assert_stream(
171                &mut stream,
172                format!(
173                    "PUT / HTTP/1.1\r\ncookies: jo\r\nhost: {}:{}\r\ncontent-length: 0\r\n\r\n",
174                    host, port
175                )
176                .as_bytes(),
177            )
178            .await?;
179
180            stream
181                .write_all(b"HTTP/1.1 200 OK\r\ntest: a\r\ntest: 1\r\n\r\n")
182                .await?;
183            //stream.read(&mut output).await?;
184            stream.flush().await?;
185            Ok(true)
186        }
187        block_on(async {
188            let (listener, port, host) = listen_somewhere().await?;
189            let uri = format!("http://{}:{}", host, port);
190            let server = spawn(server(listener, host, port));
191            let r = Request::new("PUT", &uri)?;
192            let r = r.set_header("Cookies", "jo")?;
193            let resp = r.exec().await;
194            if resp.is_err() {
195                server.await.expect("sent data wrong");
196                resp.expect("request failed");
197                return Ok(());
198            }
199            let resp = resp.expect("request failed");
200
201            assert_eq!(
202                resp.header("test").expect("no test header"),
203                "a",
204                "wrong first header"
205            );
206
207            let mut h = resp.headers().filter(|(n, _v)| *n != "date"); //async h1 adds date
208            let (n, v) = h.next().expect("two header missing");
209            assert_eq!(
210                <header::HeaderName as AsRef<[u8]>>::as_ref(n),
211                &b"test"[..],
212                "wrong 1st header"
213            );
214            assert_eq!(v, "a", "wrong 1st header");
215            let (n, v) = h.next().expect("one header missing");
216            assert_eq!(
217                <header::HeaderName as AsRef<str>>::as_ref(n),
218                "test",
219                "wrong 2nd header"
220            );
221            assert_eq!(v, "1", "wrong 2nd header");
222
223            let fin = h.next();
224            assert!(fin.is_none(), "to much headers {:?}", fin);
225
226            assert!(server.await?, "not cool");
227            Ok(())
228        })
229        .unwrap();
230    }
231}