Push mutexes down into pool and cookie store. (#193)
Previously, Agent stored most of its state in one big Arc<Mutex<AgentState>>. This separates the Arc from the Mutexes. Now, Agent is a thin wrapper around an Arc<AgentState>. The individual components that need locking, ConnectionPool and CookieStore, now are responsible for their own locking. There were a couple of reasons for this. Internal components that needed an Agent were often instead carrying around an Arc<Mutex<AgentState>>. This felt like the components were too intertwined: those other components shouldn't have to care quite so much about how Agent is implemented. Also, this led to compromises of convenience: the Proxy on Agent wound up stored inside the `Arc<Mutex<AgentState>>` even though it didn't need locking. It was more convenient that way because that was what Request and Unit had access too. The other reason to push things down like this is that it can reduce lock contention. Mutations to the cookie store don't need to lock the connection pool, and vice versa. This was a secondary concern, since I haven't actually profiled these things and found them to be a problem, but it's a happy result of the refactoring. Now all the components outside of Agent take an Agent instead of AgentState. In the process I removed `Agent.cookie()`. Its API was hard to use correctly, since it didn't distinguish between cookies on different hosts. And it would have required updates as part of this refactoring. I'm open to reinstating some similar functionality with a refreshed API. I kept `Agent.set_cookie`, but updated its method signature to take a URL as well as a cookie. Many of ConnectionPool's methods went from `&mut self` to `&self`, because ConnectionPool is now using interior mutability.
This commit is contained in:
committed by
GitHub
parent
75bc803cf1
commit
703ca41960
34
src/unit.rs
34
src/unit.rs
@@ -8,12 +8,12 @@ use url::Url;
|
||||
#[cfg(feature = "cookie")]
|
||||
use cookie::Cookie;
|
||||
|
||||
#[cfg(feature = "cookie")]
|
||||
use crate::agent::AgentState;
|
||||
use crate::body::{self, BodySize, Payload, SizedReader};
|
||||
use crate::header;
|
||||
use crate::resolve::ArcResolver;
|
||||
use crate::stream::{self, connect_test, Stream};
|
||||
#[cfg(feature = "cookie")]
|
||||
use crate::Agent;
|
||||
use crate::{Error, Header, Request, Response};
|
||||
|
||||
/// It's a "unit of work". Maybe a bad name for it?
|
||||
@@ -117,7 +117,7 @@ impl Unit {
|
||||
}
|
||||
|
||||
pub fn resolver(&self) -> ArcResolver {
|
||||
self.req.agent.lock().unwrap().resolver.clone()
|
||||
self.req.agent.state.resolver.clone()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -256,12 +256,13 @@ pub(crate) fn connect(
|
||||
}
|
||||
|
||||
#[cfg(feature = "cookie")]
|
||||
fn extract_cookies(state: &std::sync::Mutex<AgentState>, url: &Url) -> Option<Header> {
|
||||
let state = state.lock().unwrap();
|
||||
let header_value = state
|
||||
fn extract_cookies(agent: &Agent, url: &Url) -> Option<Header> {
|
||||
let header_value = agent
|
||||
.state
|
||||
.jar
|
||||
.get_request_cookies(url)
|
||||
.map(|c| Cookie::new(c.name(), c.value()).encoded().to_string())
|
||||
.iter()
|
||||
.map(|c| c.encoded().to_string())
|
||||
.collect::<Vec<_>>()
|
||||
.join(";");
|
||||
match header_value.as_str() {
|
||||
@@ -287,11 +288,15 @@ fn connect_socket(unit: &Unit, hostname: &str, use_pooled: bool) -> Result<(Stre
|
||||
_ => return Err(Error::UnknownScheme(unit.url.scheme().to_string())),
|
||||
};
|
||||
if use_pooled {
|
||||
let state = &mut unit.req.agent.lock().unwrap();
|
||||
let agent = &unit.req.agent;
|
||||
// The connection may have been closed by the server
|
||||
// due to idle timeout while it was sitting in the pool.
|
||||
// Loop until we find one that is still good or run out of connections.
|
||||
while let Some(stream) = state.pool.try_get_connection(&unit.url, &unit.req.proxy) {
|
||||
while let Some(stream) = agent
|
||||
.state
|
||||
.pool
|
||||
.try_get_connection(&unit.url, &unit.req.proxy)
|
||||
{
|
||||
let server_closed = stream.server_closed()?;
|
||||
if !server_closed {
|
||||
return Ok((stream, true));
|
||||
@@ -389,8 +394,11 @@ fn save_cookies(unit: &Unit, resp: &Response) {
|
||||
Ok(c) => Some(c),
|
||||
}
|
||||
});
|
||||
let state = &mut unit.req.agent.lock().unwrap();
|
||||
state.jar.store_response_cookies(cookies, &unit.url.clone());
|
||||
unit.req
|
||||
.agent
|
||||
.state
|
||||
.jar
|
||||
.store_response_cookies(cookies, &unit.url.clone());
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -409,14 +417,12 @@ mod tests {
|
||||
let cookie2: Cookie = "cookie2=value2; Domain=crates.io; Path=/".parse().unwrap();
|
||||
agent
|
||||
.state
|
||||
.lock()
|
||||
.unwrap()
|
||||
.jar
|
||||
.store_response_cookies(vec![cookie1, cookie2].into_iter(), &url);
|
||||
|
||||
// There's no guarantee to the order in which cookies are defined.
|
||||
// Ensure that they're either in one order or the other.
|
||||
let result = extract_cookies(&agent.state, &url);
|
||||
let result = extract_cookies(&agent, &url);
|
||||
let order1 = "cookie1=value1;cookie2=value2";
|
||||
let order2 = "cookie2=value2;cookie1=value1";
|
||||
|
||||
|
||||
Reference in New Issue
Block a user