Skip to content

Commit

Permalink
Remove pre_resolve workaround
Browse files Browse the repository at this point in the history
  • Loading branch information
kpcyrd committed Dec 10, 2018
1 parent 6e0d94a commit e6a4c87
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 94 deletions.
64 changes: 36 additions & 28 deletions src/connector.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use ct_logs;
use dns::{DnsResolver, RecordType};
use futures::{future, Poll};
use hyper::client::connect::Destination;
use hyper::client::connect::HttpConnector;
Expand All @@ -9,51 +10,56 @@ use rustls::ClientConfig;
use webpki_roots;

use errors::Error;
use std::collections::HashMap;
use std::io;
use std::net::IpAddr;
use std::sync::{Arc, Mutex};
use std::sync::Arc;

pub struct Connector<T> {
pub struct Connector<T, R: DnsResolver> {
http: T,
records: Arc<Mutex<HashMap<String, IpAddr>>>,
resolver: Arc<R>,
}

impl<T> Connector<T> {
pub fn resolve_dest(&self, dest: Destination) -> Resolving {
let records = self.records.clone();
let resolved = future::lazy(move || {
let cache = records.lock().unwrap_or_else(|x| x.into_inner());
let ip = cache.get(dest.host()).map(|x| x.to_owned());
Ok((dest, ip))
});
impl<T, R: DnsResolver + 'static> Connector<T, R> {
pub fn resolve_dest(&self, mut dest: Destination) -> Resolving {
let resolver = self.resolver.clone();
let host = dest.host().to_string();

let dest = Box::new(resolved.and_then(|(mut dest, ip)| {
let ip = match ip {
Some(IpAddr::V4(ip)) => ip.to_string(),
Some(IpAddr::V6(ip)) => format!("[{}]", ip),
None => bail!("host wasn't pre-resolved"),
};
let resolve = future::lazy(move || {
resolver
.resolve(&host, RecordType::A)
});

dest.set_host(&ip)?;
Ok(dest)
let resolved = Box::new(resolve.and_then(move |record| {
// TODO: we might have more than one record available
match record.success()?.into_iter().next() {
Some(record) => {
let ip = match record {
IpAddr::V4(ip) => ip.to_string(),
IpAddr::V6(ip) => format!("[{}]", ip),
};

dest.set_host(&ip)?;
Ok(dest)
}
None => bail!("no record found"),
}
}));

Resolving(dest)
Resolving(resolved)
}
}

impl Connector<HttpConnector> {
pub fn new(records: Arc<Mutex<HashMap<String, IpAddr>>>) -> Connector<HttpConnector> {
impl<R: DnsResolver> Connector<HttpConnector, R> {
pub fn new(resolver: Arc<R>) -> Connector<HttpConnector, R> {
let mut http = HttpConnector::new(4);
http.enforce_http(false);
Connector { http, records }
Connector { http, resolver }
}

pub fn https(
records: Arc<Mutex<HashMap<String, IpAddr>>>,
) -> HttpsConnector<Connector<HttpConnector>> {
let http = Connector::new(records);
resolver: Arc<R>,
) -> HttpsConnector<Connector<HttpConnector, R>> {
let http = Connector::new(resolver);

let mut config = ClientConfig::new();
config
Expand All @@ -65,13 +71,15 @@ impl Connector<HttpConnector> {
}
}

impl<T> Connect for Connector<T>
impl<T, R> Connect for Connector<T, R>
where
T: Connect<Error = io::Error>,
T: Clone,
T: 'static,
T::Transport: 'static,
T::Future: 'static,
R: DnsResolver,
R: 'static,
{
type Transport = T::Transport;
type Error = io::Error;
Expand Down
70 changes: 4 additions & 66 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,7 @@ use tokio::runtime::Runtime;

pub use http::Uri;
use std::collections::HashMap;
use std::net::IpAddr;
use std::sync::{Arc, Mutex};
use std::sync::Arc;
use std::time::Duration;

mod connector;
Expand All @@ -74,9 +73,7 @@ pub use errors::*;
/// Uses an specific DNS resolver.
#[derive(Debug)]
pub struct Client<R: DnsResolver> {
client: Arc<hyper::Client<HttpsConnector<Connector<HttpConnector>>>>,
resolver: Arc<R>,
records: Arc<Mutex<HashMap<String, IpAddr>>>,
client: Arc<hyper::Client<HttpsConnector<Connector<HttpConnector, R>>>>,
timeout: Option<Duration>,
}

Expand All @@ -85,16 +82,13 @@ impl<R: DnsResolver + 'static> Client<R> {
///
/// This bypasses `/etc/resolv.conf`.
pub fn new(resolver: R) -> Client<R> {
let records = Arc::new(Mutex::new(HashMap::new()));
let https = Connector::https(records.clone());
let https = Connector::https(Arc::new(resolver));
let client = hyper::Client::builder()
.keep_alive(false)
.build::<_, hyper::Body>(https);

Client {
client: Arc::new(client),
resolver: Arc::new(resolver),
records,
timeout: None,
}
}
Expand All @@ -104,37 +98,6 @@ impl<R: DnsResolver + 'static> Client<R> {
self.timeout = Some(timeout);
}

/// Pre-populate the DNS cache. This function is usually called internally.
pub fn pre_resolve(&self, uri: Uri) -> PreResolving {
let resolver = self.resolver.clone();
let records = self.records.clone();

let host = future::lazy(move || match uri.host() {
Some(host) => Ok(host.to_string()),
None => bail!("url has no host"),
});

let resolve = host.and_then(move |host| {
resolver
.resolve(&host, RecordType::A)
.map(|record| (host, record))
});

let cache = resolve.and_then(move |(host, record)| {
match record.success()?.into_iter().next() {
Some(record) => {
// TODO: make sure we only add the records we want
let mut cache = records.lock().unwrap_or_else(|x| x.into_inner());
cache.insert(host.to_string(), record);
Ok(())
}
None => bail!("no record found"),
}
});

PreResolving::new(cache)
}

/// Shorthand function to do a GET request with [`HttpClient::request`].
///
/// [`HttpClient::request`]: trait.HttpClient.html#tymethod.request
Expand Down Expand Up @@ -173,9 +136,7 @@ impl<R: DnsResolver + 'static> HttpClient for Client<R> {
let timeout = self.timeout.clone();

info!("sending request to {:?}", request.uri());
let fut = self
.pre_resolve(request.uri().clone())
.and_then(move |_| client.request(request).map_err(Error::from))
let fut = client.request(request).map_err(Error::from)
.and_then(|res| {
debug!("http response: {:?}", res);
let (parts, body) = res.into_parts();
Expand All @@ -199,29 +160,6 @@ impl<R: DnsResolver + 'static> HttpClient for Client<R> {
}
}

/// A `Future` that represents a DNS query being pre-resolved and saved in the cache.
#[must_use = "futures do nothing unless polled"]
pub struct PreResolving(Box<Future<Item = (), Error = Error> + Send>);

impl PreResolving {
/// Creates a new `PreResolving` future.
pub(crate) fn new<F>(inner: F) -> Self
where
F: Future<Item = (), Error = Error> + Send + 'static,
{
PreResolving(Box::new(inner))
}
}

impl Future for PreResolving {
type Item = ();
type Error = Error;

fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
self.0.poll()
}
}

/// A `Future` that will resolve to an HTTP Response.
#[must_use = "futures do nothing unless polled"]
pub struct ResponseFuture(Box<Future<Item = Response, Error = Error> + Send>);
Expand Down

0 comments on commit e6a4c87

Please sign in to comment.