Skip to content

Commit 3014f58

Browse files
authored
Add scheme to PoolKey and let port be None. (fortanix#84)
PoolKey calls unwrap() on an option that can be None. Specifically, the local variable `port` can be None when PoolKey is constructed with a Url whose scheme is unrecognized in url.port_or_known_default(). To fix that, make port an Option. Also, make scheme part of the PoolKey. This prevents, for instance, a stream opened for `https://example.com:9999` being reused on a request for `http://example.com:9999`. Remove the test-only pool.get() accessor. This was used in only one test, agent_pool in range.rs. This seemed like it was testing the agent more than it was testing ranges, so I moved it to agent.rs and edited to remove the range-testing parts. Also, reject unrecognized URLs earlier in connect_socket so they don't reach try_get_connection.
1 parent 00461fb commit 3014f58

File tree

4 files changed

+46
-61
lines changed

4 files changed

+46
-61
lines changed

src/agent.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,7 @@ pub(crate) fn basic_auth(user: &str, pass: &str) -> String {
262262
#[cfg(test)]
263263
mod tests {
264264
use super::*;
265+
use std::io::Read;
265266

266267
///////////////////// AGENT TESTS //////////////////////////////
267268

@@ -273,6 +274,35 @@ mod tests {
273274
});
274275
}
275276

277+
#[test]
278+
#[cfg(any(feature = "tls", feature = "native-tls"))]
279+
fn agent_pool() {
280+
let agent = crate::agent();
281+
let url = "https://ureq.s3.eu-central-1.amazonaws.com/sherlock.txt";
282+
// req 1
283+
let resp = agent.get(url).call();
284+
assert!(resp.ok());
285+
let mut reader = resp.into_reader();
286+
let mut buf = vec![];
287+
// reading the entire content will return the connection to the pool
288+
reader.read_to_end(&mut buf).unwrap();
289+
290+
fn poolsize(agent: &Agent) -> usize {
291+
let mut lock = agent.state.lock().unwrap();
292+
let state = lock.as_mut().unwrap();
293+
state.pool().len()
294+
}
295+
assert_eq!(poolsize(&agent), 1);
296+
297+
// req 2 should be done with a reused connection
298+
let resp = agent.get(url).call();
299+
assert!(resp.ok());
300+
assert_eq!(poolsize(&agent), 0);
301+
let mut reader = resp.into_reader();
302+
let mut buf = vec![];
303+
reader.read_to_end(&mut buf).unwrap();
304+
}
305+
276306
//////////////////// REQUEST TESTS /////////////////////////////
277307

278308
#[test]

src/pool.rs

Lines changed: 12 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -33,43 +33,32 @@ impl ConnectionPool {
3333
pub fn len(&self) -> usize {
3434
self.recycle.len()
3535
}
36-
37-
#[cfg(all(test, any(feature = "tls", feature = "native-tls")))]
38-
pub fn get(&self, hostname: &str, port: u16) -> Option<&Stream> {
39-
let key = PoolKey {
40-
hostname: hostname.into(),
41-
port,
42-
};
43-
self.recycle.get(&key)
44-
}
4536
}
4637

4738
#[derive(Debug, PartialEq, Clone, Eq, Hash)]
4839
struct PoolKey {
40+
scheme: String,
4941
hostname: String,
50-
port: u16,
42+
port: Option<u16>,
5143
}
5244

5345
impl PoolKey {
5446
fn new(url: &Url) -> Self {
55-
let port = if cfg!(test) {
56-
if let Some(p) = url.port_or_known_default() {
57-
Some(p)
58-
} else if url.scheme() == "test" {
59-
Some(42)
60-
} else {
61-
None
62-
}
63-
} else {
64-
url.port_or_known_default()
65-
};
47+
let port = url.port_or_known_default();
6648
PoolKey {
67-
hostname: url.host_str().unwrap_or(DEFAULT_HOST).into(),
68-
port: port.expect("Failed to get port for pool key"),
49+
scheme: url.scheme().to_string(),
50+
hostname: url.host_str().unwrap_or("").to_string(),
51+
port,
6952
}
7053
}
7154
}
7255

56+
#[test]
57+
fn poolkey_new() {
58+
// Test that PoolKey::new() does not panic on unrecognized schemes.
59+
PoolKey::new(&Url::parse("zzz:///example.com").unwrap());
60+
}
61+
7362
/// Read wrapper that returns the stream to the pool once the
7463
/// read is exhausted (reached a 0).
7564
///

src/test/range.rs

Lines changed: 0 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -20,41 +20,3 @@ fn read_range() {
2020
[83, 99, 111, 116, 116, 34, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32]
2121
)
2222
}
23-
24-
#[test]
25-
#[cfg(any(feature = "tls", feature = "native-tls"))]
26-
fn agent_pool() {
27-
let agent = agent();
28-
29-
// req 1
30-
let resp = agent
31-
.get("https://ureq.s3.eu-central-1.amazonaws.com/sherlock.txt")
32-
.set("Range", "bytes=1000-1999")
33-
.call();
34-
assert_eq!(resp.status(), 206);
35-
let mut reader = resp.into_reader();
36-
let mut buf = vec![];
37-
// reading the entire content will return the connection to the pool
38-
let len = reader.read_to_end(&mut buf).unwrap();
39-
assert_eq!(len, 1000);
40-
41-
{
42-
let mut lock = agent.state.lock().unwrap();
43-
let state = lock.as_mut().unwrap();
44-
let pool = state.pool();
45-
assert_eq!(pool.len(), 1);
46-
let f = format!("{:?}", pool.get("ureq.s3.eu-central-1.amazonaws.com", 443));
47-
assert_eq!(f, "Some(Stream[https])"); // not a great way of testing.
48-
}
49-
50-
// req 2 should be done with a reused connection
51-
let resp = agent
52-
.get("https://ureq.s3.eu-central-1.amazonaws.com/sherlock.txt")
53-
.set("Range", "bytes=5000-6999")
54-
.call();
55-
assert_eq!(resp.status(), 206);
56-
let mut reader = resp.into_reader();
57-
let mut buf = vec![];
58-
let len = reader.read_to_end(&mut buf).unwrap();
59-
assert_eq!(len, 2000);
60-
}

src/unit.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,10 @@ pub(crate) fn combine_query(url: &Url, query: &QString, mix_queries: bool) -> St
291291

292292
/// Connect the socket, either by using the pool or grab a new one.
293293
fn connect_socket(unit: &Unit, use_pooled: bool) -> Result<(Stream, bool), Error> {
294+
match unit.url.scheme() {
295+
"http" | "https" | "test" => (),
296+
_ => return Err(Error::UnknownScheme(unit.url.scheme().to_string())),
297+
};
294298
if use_pooled {
295299
let state = &mut unit.agent.lock().unwrap();
296300
if let Some(agent) = state.as_mut() {

0 commit comments

Comments
 (0)