generic_async_http_client/
lib.rs1#![doc = include_str!("../README.md")]
2#![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;
47mod request;
49mod response;
50
51pub use request::Request;
52pub use response::Response;
53pub 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 #[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 #[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.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"); 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}