generic_async_http_client/
lib.rs1#![doc = include_str!("../README.md")]
2#![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;
54mod request;
56mod response;
57
58pub use request::Request;
59pub use response::Response;
60pub 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 #[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 #[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.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"); 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}