diff --git a/src/agent.rs b/src/agent.rs index 154b8a1..f104050 100644 --- a/src/agent.rs +++ b/src/agent.rs @@ -387,7 +387,7 @@ mod tests { use std::io::Read; let agent = crate::agent(); - let url = "https://ureq.s3.eu-central-1.amazonaws.com/sherlock.txt"; + let url = "http://example.com"; // req 1 let resp = agent.get(url).call().unwrap(); let mut reader = resp.into_reader(); diff --git a/src/lib.rs b/src/lib.rs index 80a854d..2c4eab2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -136,7 +136,10 @@ pub use serde_json::{to_value as serde_to_value, Map as SerdeMap, Value as Serde /// Agents are used to keep state between requests. pub fn agent() -> Agent { - Agent::default() + #[cfg(not(test))] + return Agent::default(); + #[cfg(test)] + return test::test_agent(); } /// Make a request setting the HTTP method via a string. diff --git a/src/test/mod.rs b/src/test/mod.rs index d6f9306..98f507f 100644 --- a/src/test/mod.rs +++ b/src/test/mod.rs @@ -1,10 +1,10 @@ -use crate::error::Error; -use crate::stream::Stream; use crate::unit::Unit; +use crate::{error::Error, Agent}; +use crate::{stream::Stream, AgentBuilder}; use once_cell::sync::Lazy; -use std::collections::HashMap; use std::io::{Cursor, Write}; use std::sync::{Arc, Mutex}; +use std::{collections::HashMap, net::ToSocketAddrs}; mod agent_test; mod auth; @@ -14,9 +14,44 @@ mod query_string; mod range; mod redirect; mod simple; -mod testserver; +pub(crate) mod testserver; mod timeout; +// An agent to be installed by default for tests and doctests, such +// that all hostnames resolve to a TestServer on localhost. +pub(crate) fn test_agent() -> Agent { + use std::io; + use std::net::{SocketAddr, TcpStream}; + let testserver = testserver::TestServer::new(|mut stream: TcpStream| -> io::Result<()> { + testserver::read_headers(&stream); + stream.write_all(b"HTTP/1.1 200 OK\r\n")?; + stream.write_all(b"Transfer-Encoding: chunked\r\n")?; + stream.write_all(b"Content-Type: text/html; charset=ISO-8859-1\r\n")?; + stream.write_all(b"\r\n")?; + stream.write_all(b"7\r\n")?; + stream.write_all(b"success\r\n")?; + stream.write_all(b"0\r\n")?; + stream.write_all(b"\r\n")?; + Ok(()) + }); + // Slightly tricky thing here: we want to make sure the TestServer lives + // as long as the agent. This is accomplished by `move`ing it into the + // closure, which becomes owned by the agent. + AgentBuilder::new() + .resolver(move |h: &str| -> io::Result> { + // Don't override resolution for HTTPS requests yet, since we + // don't have a setup for an HTTPS testserver. Also, skip localhost + // resolutions since those may come from a unittest that set up + // its own, specific testserver. + if h.ends_with(":443") || h.starts_with("localhost:") { + return Ok(h.to_socket_addrs()?.collect::>()); + } + let addr: SocketAddr = format!("127.0.0.1:{}", testserver.port).parse().unwrap(); + Ok(vec![addr]) + }) + .build() +} + type RequestHandler = dyn Fn(&Unit) -> Result + Send + 'static; pub(crate) static TEST_HANDLERS: Lazy>>>> = diff --git a/src/test/redirect.rs b/src/test/redirect.rs index 9be9b4f..649625c 100644 --- a/src/test/redirect.rs +++ b/src/test/redirect.rs @@ -85,6 +85,9 @@ fn redirect_get() { #[test] fn redirect_host() { // Set up a redirect to a host that doesn't exist; it should fail. + // TODO: This actually relies on the network for the DNS lookup + // of example.invalid. We can probably do better by, e.g. + // overriding the resolver. let srv = TestServer::new(|mut stream: TcpStream| -> io::Result<()> { testserver::read_headers(&stream); write!(stream, "HTTP/1.1 302 Found\r\n")?; @@ -93,8 +96,13 @@ fn redirect_host() { Ok(()) }); let url = format!("http://localhost:{}/", srv.port); - let resp = crate::get(&url).call(); - assert!(matches!(resp.err(), Some(Error::DnsFailed(_)))); + let resp = crate::Agent::default().get(&url).call(); + let err = resp.err(); + assert!( + matches!(err, Some(Error::DnsFailed(_))), + "expected DnsFailed, got: {:?}", + err + ); } #[test] diff --git a/src/test/testserver.rs b/src/test/testserver.rs index de17ca4..2b764d6 100644 --- a/src/test/testserver.rs +++ b/src/test/testserver.rs @@ -34,7 +34,10 @@ pub fn read_headers(stream: &TcpStream) -> TestHeaders { let mut results = vec![]; for line in BufReader::new(stream).lines() { match line { - Err(e) => panic!(e), + Err(e) => { + eprintln!("testserver: in read_headers: {}", e); + break; + } Ok(line) if line == "" => break, Ok(line) => results.push(line), }; @@ -50,6 +53,10 @@ impl TestServer { let done_clone = done.clone(); thread::spawn(move || { for stream in listener.incoming() { + if let Err(e) = stream { + eprintln!("testserver: handling just-accepted stream: {}", e); + break; + } thread::spawn(move || handler(stream.unwrap())); if done.load(Ordering::Relaxed) { break;