feat(lib): redesign API to use Futures and Tokio
There are many changes involved with this, but let's just talk about
user-facing changes.
- Creating a `Client` and `Server` now needs a Tokio `Core` event loop
to attach to.
- `Request` and `Response` both no longer implement the
`std::io::{Read,Write}` traits, but instead represent their bodies as a
`futures::Stream` of items, where each item is a `Chunk`.
- The `Client.request` method now takes a `Request`, instead of being
used as a builder, and returns a `Future` that resolves to `Response`.
- The `Handler` trait for servers is no more, and instead the Tokio
`Service` trait is used. This allows interoperability with generic
middleware.
BREAKING CHANGE: A big sweeping set of breaking changes.
This commit is contained in:
@@ -1,96 +1,53 @@
|
||||
use std::io;
|
||||
use std::net::{IpAddr, SocketAddr, ToSocketAddrs};
|
||||
use std::thread;
|
||||
use std::net::{SocketAddr, ToSocketAddrs};
|
||||
use std::vec;
|
||||
|
||||
use ::spmc;
|
||||
|
||||
use http::channel;
|
||||
use ::futures::{Future, Poll};
|
||||
use ::futures_cpupool::{CpuPool, CpuFuture};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Dns {
|
||||
tx: spmc::Sender<String>,
|
||||
rx: channel::Receiver<Answer>,
|
||||
pool: CpuPool,
|
||||
}
|
||||
|
||||
pub type Answer = (String, io::Result<IpAddrs>);
|
||||
impl Dns {
|
||||
pub fn new(threads: usize) -> Dns {
|
||||
Dns {
|
||||
pool: CpuPool::new(threads)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resolve(&self, host: String, port: u16) -> Query {
|
||||
Query(self.pool.spawn_fn(move || work(host, port)))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Query(CpuFuture<IpAddrs, io::Error>);
|
||||
|
||||
impl Future for Query {
|
||||
type Item = IpAddrs;
|
||||
type Error = io::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
||||
self.0.poll()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct IpAddrs {
|
||||
iter: vec::IntoIter<SocketAddr>,
|
||||
}
|
||||
|
||||
impl Iterator for IpAddrs {
|
||||
type Item = IpAddr;
|
||||
type Item = SocketAddr;
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<IpAddr> {
|
||||
self.iter.next().map(|addr| addr.ip())
|
||||
fn next(&mut self) -> Option<SocketAddr> {
|
||||
self.iter.next()
|
||||
}
|
||||
}
|
||||
|
||||
impl Dns {
|
||||
pub fn new(notify: (channel::Sender<Answer>, channel::Receiver<Answer>), threads: usize) -> Dns {
|
||||
let (tx, rx) = spmc::channel();
|
||||
for _ in 0..threads {
|
||||
work(rx.clone(), notify.0.clone());
|
||||
}
|
||||
Dns {
|
||||
tx: tx,
|
||||
rx: notify.1,
|
||||
}
|
||||
}
|
||||
pub type Answer = io::Result<IpAddrs>;
|
||||
|
||||
pub fn resolve<T: Into<String>>(&self, hostname: T) {
|
||||
self.tx.send(hostname.into()).expect("DNS workers all died unexpectedly");
|
||||
}
|
||||
|
||||
pub fn resolved(&self) -> Result<Answer, channel::TryRecvError> {
|
||||
self.rx.try_recv()
|
||||
}
|
||||
}
|
||||
|
||||
fn work(rx: spmc::Receiver<String>, notify: channel::Sender<Answer>) {
|
||||
thread::Builder::new().name(String::from("hyper-dns")).spawn(move || {
|
||||
let mut worker = Worker::new(rx, notify);
|
||||
let rx = worker.rx.as_ref().expect("Worker lost rx");
|
||||
let notify = worker.notify.as_ref().expect("Worker lost notify");
|
||||
while let Ok(host) = rx.recv() {
|
||||
debug!("resolve {:?}", host);
|
||||
let res = match (&*host, 80).to_socket_addrs().map(|i| IpAddrs{ iter: i }) {
|
||||
Ok(addrs) => (host, Ok(addrs)),
|
||||
Err(e) => (host, Err(e))
|
||||
};
|
||||
|
||||
if let Err(_) = notify.send(res) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
worker.shutdown = true;
|
||||
}).expect("spawn dns thread");
|
||||
}
|
||||
|
||||
struct Worker {
|
||||
rx: Option<spmc::Receiver<String>>,
|
||||
notify: Option<channel::Sender<Answer>>,
|
||||
shutdown: bool,
|
||||
}
|
||||
|
||||
impl Worker {
|
||||
fn new(rx: spmc::Receiver<String>, notify: channel::Sender<Answer>) -> Worker {
|
||||
Worker {
|
||||
rx: Some(rx),
|
||||
notify: Some(notify),
|
||||
shutdown: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Worker {
|
||||
fn drop(&mut self) {
|
||||
if !self.shutdown {
|
||||
trace!("Worker.drop panicked, restarting");
|
||||
work(self.rx.take().expect("Worker lost rx"),
|
||||
self.notify.take().expect("Worker lost notify"));
|
||||
} else {
|
||||
trace!("Worker.drop shutdown, closing");
|
||||
}
|
||||
}
|
||||
fn work(hostname: String, port: u16) -> Answer {
|
||||
debug!("resolve {:?}:{:?}", hostname, port);
|
||||
(&*hostname, port).to_socket_addrs().map(|i| IpAddrs { iter: i })
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user