refactor(lib): convert to futures 0.2.0-beta (#1470)
This commit is contained in:
16
Cargo.toml
16
Cargo.toml
@@ -22,20 +22,17 @@ include = [
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bytes = "0.4.4"
|
bytes = "0.4.4"
|
||||||
futures = "0.1.17"
|
futures = "0.2.0-beta"
|
||||||
futures-cpupool = "0.1.6"
|
futures-timer = { git = "https://github.com/alexcrichton/futures-timer.git" }
|
||||||
futures-timer = "0.1.0"
|
|
||||||
http = "0.1.5"
|
http = "0.1.5"
|
||||||
httparse = "1.0"
|
httparse = "1.0"
|
||||||
iovec = "0.1"
|
iovec = "0.1"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
net2 = "0.2.32"
|
net2 = "0.2.32"
|
||||||
time = "0.1"
|
time = "0.1"
|
||||||
tokio = "0.1.3"
|
tokio = { git = "https://github.com/seanmonstar/tokio.git", branch = "futures2-use-after-free", features = ["unstable-futures"] }
|
||||||
tokio-executor = "0.1.0"
|
tokio-executor = { git = "https://github.com/seanmonstar/tokio.git", branch = "futures2-use-after-free", features = ["unstable-futures"] }
|
||||||
tokio-service = "0.1"
|
want = { git = "https://github.com/srijs/want.git", branch = "futures-0.2" }
|
||||||
tokio-io = "0.1"
|
|
||||||
want = "0.0.2"
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
num_cpus = "1.0"
|
num_cpus = "1.0"
|
||||||
@@ -45,3 +42,6 @@ url = "1.0"
|
|||||||
|
|
||||||
[features]
|
[features]
|
||||||
nightly = []
|
nightly = []
|
||||||
|
|
||||||
|
[replace]
|
||||||
|
"futures:0.2.0-beta" = { git = "https://github.com/srijs/futures-rs.git", branch = "with-executor" }
|
||||||
|
|||||||
@@ -8,7 +8,8 @@ extern crate tokio;
|
|||||||
|
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
|
|
||||||
use futures::{Future, Stream};
|
use futures::{FutureExt, StreamExt};
|
||||||
|
use futures::executor::block_on;
|
||||||
use tokio::runtime::Runtime;
|
use tokio::runtime::Runtime;
|
||||||
use tokio::net::TcpListener;
|
use tokio::net::TcpListener;
|
||||||
|
|
||||||
@@ -22,19 +23,20 @@ fn get_one_at_a_time(b: &mut test::Bencher) {
|
|||||||
let addr = spawn_hello(&mut rt);
|
let addr = spawn_hello(&mut rt);
|
||||||
|
|
||||||
let client = hyper::Client::configure()
|
let client = hyper::Client::configure()
|
||||||
.build_with_executor(&rt.handle(), rt.executor());
|
.build(&rt.handle());
|
||||||
|
|
||||||
let url: hyper::Uri = format!("http://{}/get", addr).parse().unwrap();
|
let url: hyper::Uri = format!("http://{}/get", addr).parse().unwrap();
|
||||||
|
|
||||||
b.bytes = 160 * 2 + PHRASE.len() as u64;
|
b.bytes = 160 * 2 + PHRASE.len() as u64;
|
||||||
b.iter(move || {
|
b.iter(move || {
|
||||||
client.get(url.clone())
|
block_on(client.get(url.clone())
|
||||||
|
.with_executor(rt.executor())
|
||||||
.and_then(|res| {
|
.and_then(|res| {
|
||||||
res.into_body().into_stream().for_each(|_chunk| {
|
res.into_body().into_stream().for_each(|_chunk| {
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
}).map(|_| ())
|
||||||
})
|
})
|
||||||
.wait().expect("client wait");
|
).expect("client wait");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,7 +46,7 @@ fn post_one_at_a_time(b: &mut test::Bencher) {
|
|||||||
let addr = spawn_hello(&mut rt);
|
let addr = spawn_hello(&mut rt);
|
||||||
|
|
||||||
let client = hyper::Client::configure()
|
let client = hyper::Client::configure()
|
||||||
.build_with_executor(&rt.handle(), rt.executor());
|
.build(&rt.handle());
|
||||||
|
|
||||||
let url: hyper::Uri = format!("http://{}/post", addr).parse().unwrap();
|
let url: hyper::Uri = format!("http://{}/post", addr).parse().unwrap();
|
||||||
|
|
||||||
@@ -54,11 +56,14 @@ fn post_one_at_a_time(b: &mut test::Bencher) {
|
|||||||
let mut req = Request::new(post.into());
|
let mut req = Request::new(post.into());
|
||||||
*req.method_mut() = Method::POST;
|
*req.method_mut() = Method::POST;
|
||||||
*req.uri_mut() = url.clone();
|
*req.uri_mut() = url.clone();
|
||||||
client.request(req).and_then(|res| {
|
block_on(client.request(req)
|
||||||
res.into_body().into_stream().for_each(|_chunk| {
|
.with_executor(rt.executor())
|
||||||
Ok(())
|
.and_then(|res| {
|
||||||
|
res.into_body().into_stream().for_each(|_chunk| {
|
||||||
|
Ok(())
|
||||||
|
}).map(|_| ())
|
||||||
})
|
})
|
||||||
}).wait().expect("client wait");
|
).expect("client wait");
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -76,21 +81,22 @@ fn spawn_hello(rt: &mut Runtime) -> SocketAddr {
|
|||||||
let service = const_service(service_fn(|req: Request<Body>| {
|
let service = const_service(service_fn(|req: Request<Body>| {
|
||||||
req.into_body()
|
req.into_body()
|
||||||
.into_stream()
|
.into_stream()
|
||||||
.concat2()
|
.concat()
|
||||||
.map(|_| {
|
.map(|_| {
|
||||||
Response::new(Body::from(PHRASE))
|
Response::new(Body::from(PHRASE))
|
||||||
})
|
})
|
||||||
}));
|
}));
|
||||||
|
|
||||||
let srv = listener.incoming()
|
let srv = listener.incoming()
|
||||||
.into_future()
|
.next()
|
||||||
.map_err(|(e, _inc)| panic!("accept error: {}", e))
|
.map_err(|(e, _inc)| panic!("accept error: {}", e))
|
||||||
.and_then(move |(accepted, _inc)| {
|
.and_then(move |(accepted, _inc)| {
|
||||||
let socket = accepted.expect("accepted socket");
|
let socket = accepted.expect("accepted socket");
|
||||||
http.serve_connection(socket, service.new_service().expect("new_service"))
|
http.serve_connection(socket, service.new_service().expect("new_service"))
|
||||||
.map(|_| ())
|
.map(|_| ())
|
||||||
.map_err(|_| ())
|
.map_err(|_| ())
|
||||||
});
|
})
|
||||||
rt.spawn(srv);
|
.map_err(|_| panic!("server error"));
|
||||||
|
rt.spawn2(srv);
|
||||||
return addr
|
return addr
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ use std::io::{Read, Write};
|
|||||||
use std::net::{TcpListener, TcpStream};
|
use std::net::{TcpListener, TcpStream};
|
||||||
use std::sync::mpsc;
|
use std::sync::mpsc;
|
||||||
|
|
||||||
use futures::{future, stream, Future, Stream};
|
use futures::{future, stream, FutureExt, StreamExt};
|
||||||
use futures::sync::oneshot;
|
use futures::channel::oneshot;
|
||||||
|
|
||||||
use hyper::{Body, Request, Response};
|
use hyper::{Body, Request, Response};
|
||||||
use hyper::server::Service;
|
use hyper::server::Service;
|
||||||
@@ -31,7 +31,7 @@ macro_rules! bench_server {
|
|||||||
})).unwrap();
|
})).unwrap();
|
||||||
let addr = srv.local_addr().unwrap();
|
let addr = srv.local_addr().unwrap();
|
||||||
addr_tx.send(addr).unwrap();
|
addr_tx.send(addr).unwrap();
|
||||||
tokio::run(srv.run_until(until_rx.map_err(|_| ())).map_err(|e| panic!("server error: {}", e)));
|
tokio::runtime::run2(srv.run_until(until_rx.map_err(|_| ())).map_err(|e| panic!("server error: {}", e)));
|
||||||
});
|
});
|
||||||
|
|
||||||
addr_rx.recv().unwrap()
|
addr_rx.recv().unwrap()
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ extern crate pretty_env_logger;
|
|||||||
use std::env;
|
use std::env;
|
||||||
use std::io::{self, Write};
|
use std::io::{self, Write};
|
||||||
|
|
||||||
use futures::{Future, Stream};
|
use futures::{FutureExt, StreamExt};
|
||||||
use futures::future::lazy;
|
use futures::future::lazy;
|
||||||
|
|
||||||
use hyper::{Body, Client, Request};
|
use hyper::{Body, Client, Request};
|
||||||
@@ -30,7 +30,7 @@ fn main() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
tokio::run(lazy(move || {
|
tokio::runtime::run2(lazy(move |_| {
|
||||||
let client = Client::default();
|
let client = Client::default();
|
||||||
|
|
||||||
let mut req = Request::new(Body::empty());
|
let mut req = Request::new(Body::empty());
|
||||||
@@ -43,10 +43,13 @@ fn main() {
|
|||||||
res.into_parts().1.into_stream().for_each(|chunk| {
|
res.into_parts().1.into_stream().for_each(|chunk| {
|
||||||
io::stdout().write_all(&chunk).map_err(From::from)
|
io::stdout().write_all(&chunk).map_err(From::from)
|
||||||
})
|
})
|
||||||
}).map(|_| {
|
}).then(|result| {
|
||||||
println!("\n\nDone.");
|
if let Some(err) = result.err() {
|
||||||
}).map_err(|err| {
|
eprintln!("Error {}", err);
|
||||||
eprintln!("Error {}", err);
|
} else {
|
||||||
|
println!("\n\nDone.");
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
})
|
})
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ extern crate futures;
|
|||||||
extern crate pretty_env_logger;
|
extern crate pretty_env_logger;
|
||||||
extern crate tokio;
|
extern crate tokio;
|
||||||
|
|
||||||
use futures::Future;
|
use futures::FutureExt;
|
||||||
use futures::future::lazy;
|
use futures::future::lazy;
|
||||||
|
|
||||||
use hyper::{Body, Response};
|
use hyper::{Body, Response};
|
||||||
@@ -20,13 +20,13 @@ fn main() {
|
|||||||
Ok(Response::new(Body::from(PHRASE)))
|
Ok(Response::new(Body::from(PHRASE)))
|
||||||
}));
|
}));
|
||||||
|
|
||||||
tokio::run(lazy(move || {
|
tokio::runtime::run2(lazy(move |_| {
|
||||||
let server = Http::new()
|
let server = Http::new()
|
||||||
.sleep_on_errors(true)
|
.sleep_on_errors(true)
|
||||||
.bind(&addr, new_service)
|
.bind(&addr, new_service)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
println!("Listening on http://{} with 1 thread.", server.local_addr().unwrap());
|
println!("Listening on http://{}", server.local_addr().unwrap());
|
||||||
server.run().map_err(|err| eprintln!("Server error {}", err))
|
server.run().map_err(|err| panic!("Server error {}", err))
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,8 +4,9 @@ extern crate futures;
|
|||||||
extern crate pretty_env_logger;
|
extern crate pretty_env_logger;
|
||||||
extern crate tokio;
|
extern crate tokio;
|
||||||
|
|
||||||
use futures::{Future, Stream};
|
use futures::{FutureExt, StreamExt};
|
||||||
use futures::future::{FutureResult, lazy};
|
use futures::future::{FutureResult, lazy};
|
||||||
|
use futures::executor::spawn;
|
||||||
|
|
||||||
use hyper::{Body, Method, Request, Response, StatusCode};
|
use hyper::{Body, Method, Request, Response, StatusCode};
|
||||||
use hyper::server::{Http, Service};
|
use hyper::server::{Http, Service};
|
||||||
@@ -43,22 +44,20 @@ fn main() {
|
|||||||
let addr1 = "127.0.0.1:1337".parse().unwrap();
|
let addr1 = "127.0.0.1:1337".parse().unwrap();
|
||||||
let addr2 = "127.0.0.1:1338".parse().unwrap();
|
let addr2 = "127.0.0.1:1338".parse().unwrap();
|
||||||
|
|
||||||
tokio::run(lazy(move || {
|
tokio::runtime::run2(lazy(move |_| {
|
||||||
let srv1 = Http::new().serve_addr(&addr1, || Ok(Srv(INDEX1))).unwrap();
|
let srv1 = Http::new().serve_addr(&addr1, || Ok(Srv(INDEX1))).unwrap();
|
||||||
let srv2 = Http::new().serve_addr(&addr2, || Ok(Srv(INDEX2))).unwrap();
|
let srv2 = Http::new().serve_addr(&addr2, || Ok(Srv(INDEX2))).unwrap();
|
||||||
|
|
||||||
println!("Listening on http://{}", srv1.incoming_ref().local_addr());
|
println!("Listening on http://{}", srv1.incoming_ref().local_addr());
|
||||||
println!("Listening on http://{}", srv2.incoming_ref().local_addr());
|
println!("Listening on http://{}", srv2.incoming_ref().local_addr());
|
||||||
|
|
||||||
tokio::spawn(srv1.for_each(move |conn| {
|
spawn(srv1.map_err(|err| panic!("srv1 error: {:?}", err)).for_each(move |conn| {
|
||||||
tokio::spawn(conn.map(|_| ()).map_err(|err| println!("srv1 error: {:?}", err)));
|
spawn(conn.map(|_| ()).map_err(|err| panic!("srv1 error: {:?}", err)))
|
||||||
Ok(())
|
}).map(|_| ()));
|
||||||
}).map_err(|_| ()));
|
|
||||||
|
|
||||||
tokio::spawn(srv2.for_each(move |conn| {
|
spawn(srv2.map_err(|err| panic!("srv2 error: {:?}", err)).for_each(move |conn| {
|
||||||
tokio::spawn(conn.map(|_| ()).map_err(|err| println!("srv2 error: {:?}", err)));
|
spawn(conn.map(|_| ()).map_err(|err| panic!("srv2 error: {:?}", err)))
|
||||||
Ok(())
|
}).map(|_| ()));
|
||||||
}).map_err(|_| ()));
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}));
|
}));
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ extern crate pretty_env_logger;
|
|||||||
extern crate tokio;
|
extern crate tokio;
|
||||||
extern crate url;
|
extern crate url;
|
||||||
|
|
||||||
use futures::{Future, Stream};
|
use futures::{Future, FutureExt, StreamExt};
|
||||||
use futures::future::lazy;
|
use futures::future::lazy;
|
||||||
|
|
||||||
use hyper::{Body, Method, Request, Response, StatusCode};
|
use hyper::{Body, Method, Request, Response, StatusCode};
|
||||||
@@ -32,7 +32,7 @@ impl Service for ParamExample {
|
|||||||
Box::new(futures::future::ok(Response::new(INDEX.into())))
|
Box::new(futures::future::ok(Response::new(INDEX.into())))
|
||||||
},
|
},
|
||||||
(&Method::POST, "/post") => {
|
(&Method::POST, "/post") => {
|
||||||
Box::new(req.into_parts().1.into_stream().concat2().map(|b| {
|
Box::new(req.into_parts().1.into_stream().concat().map(|b| {
|
||||||
// Parse the request body. form_urlencoded::parse
|
// Parse the request body. form_urlencoded::parse
|
||||||
// always succeeds, but in general parsing may
|
// always succeeds, but in general parsing may
|
||||||
// fail (for example, an invalid post of json), so
|
// fail (for example, an invalid post of json), so
|
||||||
@@ -98,9 +98,11 @@ fn main() {
|
|||||||
pretty_env_logger::init();
|
pretty_env_logger::init();
|
||||||
let addr = "127.0.0.1:1337".parse().unwrap();
|
let addr = "127.0.0.1:1337".parse().unwrap();
|
||||||
|
|
||||||
tokio::run(lazy(move || {
|
tokio::runtime::run2(lazy(move |_| {
|
||||||
let server = Http::new().bind(&addr, || Ok(ParamExample)).unwrap();
|
let server = Http::new().bind(&addr, || Ok(ParamExample)).unwrap();
|
||||||
println!("Listening on http://{} with 1 thread.", server.local_addr().unwrap());
|
println!("Listening on http://{}", server.local_addr().unwrap());
|
||||||
server.run().map_err(|err| eprintln!("Server error {}", err))
|
server.run().recover(|err| {
|
||||||
|
eprintln!("Server error {}", err)
|
||||||
|
})
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ extern crate hyper;
|
|||||||
extern crate pretty_env_logger;
|
extern crate pretty_env_logger;
|
||||||
extern crate tokio;
|
extern crate tokio;
|
||||||
|
|
||||||
use futures::{Future/*, Sink*/};
|
use futures::{Future, FutureExt};
|
||||||
use futures::future::lazy;
|
use futures::future::lazy;
|
||||||
use futures::sync::oneshot;
|
use futures::channel::oneshot;
|
||||||
|
|
||||||
use hyper::{Body, /*Chunk,*/ Method, Request, Response, StatusCode};
|
use hyper::{Body, /*Chunk,*/ Method, Request, Response, StatusCode};
|
||||||
use hyper::error::Error;
|
use hyper::error::Error;
|
||||||
@@ -141,9 +141,9 @@ fn main() {
|
|||||||
pretty_env_logger::init();
|
pretty_env_logger::init();
|
||||||
let addr = "127.0.0.1:1337".parse().unwrap();
|
let addr = "127.0.0.1:1337".parse().unwrap();
|
||||||
|
|
||||||
tokio::run(lazy(move || {
|
tokio::runtime::run2(lazy(move |_| {
|
||||||
let server = Http::new().bind(&addr, || Ok(ResponseExamples)).unwrap();
|
let server = Http::new().bind(&addr, || Ok(ResponseExamples)).unwrap();
|
||||||
println!("Listening on http://{} with 1 thread.", server.local_addr().unwrap());
|
println!("Listening on http://{}", server.local_addr().unwrap());
|
||||||
server.run().map_err(|err| eprintln!("Server error {}", err))
|
server.run().map_err(|err| panic!("Server error {}", err))
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ extern crate hyper;
|
|||||||
extern crate pretty_env_logger;
|
extern crate pretty_env_logger;
|
||||||
extern crate tokio;
|
extern crate tokio;
|
||||||
|
|
||||||
use futures::Future;
|
use futures::FutureExt;
|
||||||
use futures::future::{FutureResult, lazy};
|
use futures::future::{FutureResult, lazy};
|
||||||
|
|
||||||
use hyper::{Body, Method, Request, Response, StatusCode};
|
use hyper::{Body, Method, Request, Response, StatusCode};
|
||||||
@@ -43,9 +43,11 @@ fn main() {
|
|||||||
pretty_env_logger::init();
|
pretty_env_logger::init();
|
||||||
let addr = "127.0.0.1:1337".parse().unwrap();
|
let addr = "127.0.0.1:1337".parse().unwrap();
|
||||||
|
|
||||||
tokio::run(lazy(move || {
|
tokio::runtime::run2(lazy(move |_| {
|
||||||
let server = Http::new().bind(&addr, || Ok(Echo)).unwrap();
|
let server = Http::new().bind(&addr, || Ok(Echo)).unwrap();
|
||||||
println!("Listening on http://{} with 1 thread.", server.local_addr().unwrap());
|
println!("Listening on http://{}", server.local_addr().unwrap());
|
||||||
server.run().map_err(|err| eprintln!("Server error {}", err))
|
server.run().recover(|err| {
|
||||||
|
eprintln!("Server error {}", err)
|
||||||
|
})
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,14 +4,15 @@ extern crate hyper;
|
|||||||
extern crate pretty_env_logger;
|
extern crate pretty_env_logger;
|
||||||
extern crate tokio;
|
extern crate tokio;
|
||||||
|
|
||||||
use futures::{Future, Stream};
|
use futures::{Future, FutureExt, StreamExt};
|
||||||
|
use futures::executor::spawn;
|
||||||
use futures::future::lazy;
|
use futures::future::lazy;
|
||||||
use tokio::reactor::Handle;
|
use tokio::reactor::Handle;
|
||||||
|
|
||||||
use hyper::{Body, Chunk, Client, Method, Request, Response, StatusCode};
|
use hyper::{Body, Chunk, Client, Method, Request, Response, StatusCode};
|
||||||
use hyper::server::{Http, Service};
|
use hyper::server::{Http, Service};
|
||||||
|
|
||||||
#[allow(unused)]
|
#[allow(unused, deprecated)]
|
||||||
use std::ascii::AsciiExt;
|
use std::ascii::AsciiExt;
|
||||||
|
|
||||||
static NOTFOUND: &[u8] = b"Not Found";
|
static NOTFOUND: &[u8] = b"Not Found";
|
||||||
@@ -78,13 +79,15 @@ fn main() {
|
|||||||
pretty_env_logger::init();
|
pretty_env_logger::init();
|
||||||
let addr = "127.0.0.1:1337".parse().unwrap();
|
let addr = "127.0.0.1:1337".parse().unwrap();
|
||||||
|
|
||||||
tokio::run(lazy(move || {
|
tokio::runtime::run2(lazy(move |_| {
|
||||||
let handle = Handle::current();
|
let handle = Handle::current();
|
||||||
let serve = Http::new().serve_addr(&addr, move || Ok(ResponseExamples(handle.clone()))).unwrap();
|
let serve = Http::new().serve_addr(&addr, move || Ok(ResponseExamples(handle.clone()))).unwrap();
|
||||||
println!("Listening on http://{} with 1 thread.", serve.incoming_ref().local_addr());
|
println!("Listening on http://{}", serve.incoming_ref().local_addr());
|
||||||
|
|
||||||
serve.map_err(|_| ()).for_each(move |conn| {
|
serve.map_err(|err| panic!("server error {:?}", err)).for_each(move |conn| {
|
||||||
tokio::spawn(conn.map(|_| ()).map_err(|err| println!("serve error: {:?}", err)))
|
spawn(conn.recover(|err| {
|
||||||
})
|
println!("connection error: {:?}", err);
|
||||||
|
}))
|
||||||
|
}).map(|_| ())
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,9 +11,10 @@ use std::fmt;
|
|||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use futures::{Async, Future, Poll};
|
use futures::{Async, Future, FutureExt, Poll};
|
||||||
use futures::future::{self, Either};
|
use futures::future::{self, Either};
|
||||||
use tokio_io::{AsyncRead, AsyncWrite};
|
use futures::task;
|
||||||
|
use futures::io::{AsyncRead, AsyncWrite};
|
||||||
|
|
||||||
use proto;
|
use proto;
|
||||||
use proto::body::Entity;
|
use proto::body::Entity;
|
||||||
@@ -123,8 +124,8 @@ impl<B> SendRequest<B>
|
|||||||
/// Polls to determine whether this sender can be used yet for a request.
|
/// Polls to determine whether this sender can be used yet for a request.
|
||||||
///
|
///
|
||||||
/// If the associated connection is closed, this returns an Error.
|
/// If the associated connection is closed, this returns an Error.
|
||||||
pub fn poll_ready(&mut self) -> Poll<(), ::Error> {
|
pub fn poll_ready(&mut self, cx: &mut task::Context) -> Poll<(), ::Error> {
|
||||||
self.dispatch.poll_ready()
|
self.dispatch.poll_ready(cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn is_closed(&self) -> bool {
|
pub(super) fn is_closed(&self) -> bool {
|
||||||
@@ -162,7 +163,7 @@ where
|
|||||||
/// # use http::header::HOST;
|
/// # use http::header::HOST;
|
||||||
/// # use hyper::client::conn::SendRequest;
|
/// # use hyper::client::conn::SendRequest;
|
||||||
/// # use hyper::Body;
|
/// # use hyper::Body;
|
||||||
/// use futures::Future;
|
/// use futures::FutureExt;
|
||||||
/// use hyper::Request;
|
/// use hyper::Request;
|
||||||
///
|
///
|
||||||
/// # fn doc(mut tx: SendRequest<Body>) {
|
/// # fn doc(mut tx: SendRequest<Body>) {
|
||||||
@@ -186,19 +187,19 @@ where
|
|||||||
pub fn send_request(&mut self, req: Request<B>) -> ResponseFuture {
|
pub fn send_request(&mut self, req: Request<B>) -> ResponseFuture {
|
||||||
let inner = match self.dispatch.send(req) {
|
let inner = match self.dispatch.send(req) {
|
||||||
Ok(rx) => {
|
Ok(rx) => {
|
||||||
Either::A(rx.then(move |res| {
|
rx.then(move |res| {
|
||||||
match res {
|
match res {
|
||||||
Ok(Ok(res)) => Ok(res),
|
Ok(Ok(res)) => Ok(res),
|
||||||
Ok(Err(err)) => Err(err),
|
Ok(Err(err)) => Err(err),
|
||||||
// this is definite bug if it happens, but it shouldn't happen!
|
// this is definite bug if it happens, but it shouldn't happen!
|
||||||
Err(_) => panic!("dispatch dropped without returning error"),
|
Err(_) => panic!("dispatch dropped without returning error"),
|
||||||
}
|
}
|
||||||
}))
|
}).left()
|
||||||
},
|
},
|
||||||
Err(_req) => {
|
Err(_req) => {
|
||||||
debug!("connection was not ready");
|
debug!("connection was not ready");
|
||||||
let err = ::Error::new_canceled(Some("connection was not ready"));
|
let err = ::Error::new_canceled(Some("connection was not ready"));
|
||||||
Either::B(future::err(err))
|
future::err(err).right()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -214,7 +215,7 @@ where
|
|||||||
{
|
{
|
||||||
let inner = match self.dispatch.try_send(req) {
|
let inner = match self.dispatch.try_send(req) {
|
||||||
Ok(rx) => {
|
Ok(rx) => {
|
||||||
Either::A(rx.then(move |res| {
|
Either::Left(rx.then(move |res| {
|
||||||
match res {
|
match res {
|
||||||
Ok(Ok(res)) => Ok(res),
|
Ok(Ok(res)) => Ok(res),
|
||||||
Ok(Err(err)) => Err(err),
|
Ok(Err(err)) => Err(err),
|
||||||
@@ -226,7 +227,7 @@ where
|
|||||||
Err(req) => {
|
Err(req) => {
|
||||||
debug!("connection was not ready");
|
debug!("connection was not ready");
|
||||||
let err = ::Error::new_canceled(Some("connection was not ready"));
|
let err = ::Error::new_canceled(Some("connection was not ready"));
|
||||||
Either::B(future::err((err, Some(req))))
|
Either::Right(future::err((err, Some(req))))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Box::new(inner)
|
Box::new(inner)
|
||||||
@@ -277,8 +278,8 @@ where
|
|||||||
/// upgrade. Once the upgrade is completed, the connection would be "done",
|
/// upgrade. Once the upgrade is completed, the connection would be "done",
|
||||||
/// but it is not desired to actally shutdown the IO object. Instead you
|
/// but it is not desired to actally shutdown the IO object. Instead you
|
||||||
/// would take it back using `into_parts`.
|
/// would take it back using `into_parts`.
|
||||||
pub fn poll_without_shutdown(&mut self) -> Poll<(), ::Error> {
|
pub fn poll_without_shutdown(&mut self, cx: &mut task::Context) -> Poll<(), ::Error> {
|
||||||
self.inner.poll_without_shutdown()
|
self.inner.poll_without_shutdown(cx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -290,8 +291,8 @@ where
|
|||||||
type Item = ();
|
type Item = ();
|
||||||
type Error = ::Error;
|
type Error = ::Error;
|
||||||
|
|
||||||
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
fn poll(&mut self, cx: &mut task::Context) -> Poll<Self::Item, Self::Error> {
|
||||||
self.inner.poll()
|
self.inner.poll(cx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -363,8 +364,8 @@ where
|
|||||||
type Item = (SendRequest<B>, Connection<T, B>);
|
type Item = (SendRequest<B>, Connection<T, B>);
|
||||||
type Error = ::Error;
|
type Error = ::Error;
|
||||||
|
|
||||||
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
fn poll(&mut self, cx: &mut task::Context) -> Poll<Self::Item, Self::Error> {
|
||||||
self.inner.poll()
|
self.inner.poll(cx)
|
||||||
.map(|async| {
|
.map(|async| {
|
||||||
async.map(|(tx, dispatch)| {
|
async.map(|(tx, dispatch)| {
|
||||||
(tx, Connection { inner: dispatch })
|
(tx, Connection { inner: dispatch })
|
||||||
@@ -394,8 +395,8 @@ where
|
|||||||
>);
|
>);
|
||||||
type Error = ::Error;
|
type Error = ::Error;
|
||||||
|
|
||||||
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
fn poll(&mut self, cx: &mut task::Context) -> Poll<Self::Item, Self::Error> {
|
||||||
self.inner.poll()
|
self.inner.poll(cx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -417,7 +418,7 @@ where
|
|||||||
>);
|
>);
|
||||||
type Error = ::Error;
|
type Error = ::Error;
|
||||||
|
|
||||||
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
fn poll(&mut self, _cx: &mut task::Context) -> Poll<Self::Item, Self::Error> {
|
||||||
let io = self.io.take().expect("polled more than once");
|
let io = self.io.take().expect("polled more than once");
|
||||||
let (tx, rx) = dispatch::channel();
|
let (tx, rx) = dispatch::channel();
|
||||||
let mut conn = proto::Conn::new(io);
|
let mut conn = proto::Conn::new(io);
|
||||||
@@ -441,8 +442,8 @@ impl Future for ResponseFuture {
|
|||||||
type Error = ::Error;
|
type Error = ::Error;
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
fn poll(&mut self, cx: &mut task::Context) -> Poll<Self::Item, Self::Error> {
|
||||||
self.inner.poll()
|
self.inner.poll(cx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,24 +8,21 @@
|
|||||||
use std::error::Error as StdError;
|
use std::error::Error as StdError;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::mem;
|
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
use std::sync::Arc;
|
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use futures::{Future, Poll, Async};
|
use futures::{Future, Never, Poll, Async};
|
||||||
use futures::future::{Executor, ExecuteError};
|
use futures::executor::{Executor, SpawnError, ThreadPoolBuilder};
|
||||||
use futures::sync::oneshot;
|
use futures::task;
|
||||||
use futures_cpupool::{Builder as CpuPoolBuilder};
|
use futures::io::{AsyncRead, AsyncWrite};
|
||||||
use http::Uri;
|
use http::Uri;
|
||||||
use http::uri::Scheme;
|
use http::uri::Scheme;
|
||||||
use net2::TcpBuilder;
|
use net2::TcpBuilder;
|
||||||
use tokio_io::{AsyncRead, AsyncWrite};
|
|
||||||
use tokio::reactor::Handle;
|
use tokio::reactor::Handle;
|
||||||
use tokio::net::{TcpStream, ConnectFuture};
|
use tokio::net::{TcpStream, ConnectFuture};
|
||||||
|
|
||||||
|
use executor::CloneBoxedExecutor;
|
||||||
use super::dns;
|
use super::dns;
|
||||||
use self::http_connector::HttpConnectorBlockingTask;
|
|
||||||
|
|
||||||
/// Connect to a destination, returning an IO transport.
|
/// Connect to a destination, returning an IO transport.
|
||||||
///
|
///
|
||||||
@@ -174,7 +171,7 @@ impl HttpConnector {
|
|||||||
/// Takes number of DNS worker threads.
|
/// Takes number of DNS worker threads.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new(threads: usize, handle: &Handle) -> HttpConnector {
|
pub fn new(threads: usize, handle: &Handle) -> HttpConnector {
|
||||||
let pool = CpuPoolBuilder::new()
|
let pool = ThreadPoolBuilder::new()
|
||||||
.name_prefix("hyper-dns")
|
.name_prefix("hyper-dns")
|
||||||
.pool_size(threads)
|
.pool_size(threads)
|
||||||
.create();
|
.create();
|
||||||
@@ -186,10 +183,10 @@ impl HttpConnector {
|
|||||||
/// Takes an executor to run blocking tasks on.
|
/// Takes an executor to run blocking tasks on.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new_with_executor<E: 'static>(executor: E, handle: &Handle) -> HttpConnector
|
pub fn new_with_executor<E: 'static>(executor: E, handle: &Handle) -> HttpConnector
|
||||||
where E: Executor<HttpConnectorBlockingTask> + Send + Sync
|
where E: Executor + Clone + Send + Sync
|
||||||
{
|
{
|
||||||
HttpConnector {
|
HttpConnector {
|
||||||
executor: HttpConnectExecutor(Arc::new(executor)),
|
executor: HttpConnectExecutor(Box::new(executor)),
|
||||||
enforce_http: true,
|
enforce_http: true,
|
||||||
handle: handle.clone(),
|
handle: handle.clone(),
|
||||||
keep_alive_timeout: None,
|
keep_alive_timeout: None,
|
||||||
@@ -298,7 +295,7 @@ pub struct HttpConnecting {
|
|||||||
|
|
||||||
enum State {
|
enum State {
|
||||||
Lazy(HttpConnectExecutor, String, u16),
|
Lazy(HttpConnectExecutor, String, u16),
|
||||||
Resolving(oneshot::SpawnHandle<dns::IpAddrs, io::Error>),
|
Resolving(dns::Resolving),
|
||||||
Connecting(ConnectingTcp),
|
Connecting(ConnectingTcp),
|
||||||
Error(Option<io::Error>),
|
Error(Option<io::Error>),
|
||||||
}
|
}
|
||||||
@@ -307,11 +304,11 @@ impl Future for HttpConnecting {
|
|||||||
type Item = (TcpStream, Connected);
|
type Item = (TcpStream, Connected);
|
||||||
type Error = io::Error;
|
type Error = io::Error;
|
||||||
|
|
||||||
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
fn poll(&mut self, cx: &mut task::Context) -> Poll<Self::Item, Self::Error> {
|
||||||
loop {
|
loop {
|
||||||
let state;
|
let state;
|
||||||
match self.state {
|
match self.state {
|
||||||
State::Lazy(ref executor, ref mut host, port) => {
|
State::Lazy(ref mut executor, ref mut host, port) => {
|
||||||
// If the host is already an IP addr (v4 or v6),
|
// If the host is already an IP addr (v4 or v6),
|
||||||
// skip resolving the dns and start connecting right away.
|
// skip resolving the dns and start connecting right away.
|
||||||
if let Some(addrs) = dns::IpAddrs::try_parse(host, port) {
|
if let Some(addrs) = dns::IpAddrs::try_parse(host, port) {
|
||||||
@@ -320,24 +317,19 @@ impl Future for HttpConnecting {
|
|||||||
current: None
|
current: None
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
let host = mem::replace(host, String::new());
|
let host = ::std::mem::replace(host, String::new());
|
||||||
let work = dns::Work::new(host, port);
|
state = State::Resolving(dns::Resolving::spawn(host, port, executor));
|
||||||
state = State::Resolving(oneshot::spawn(work, executor));
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
State::Resolving(ref mut future) => {
|
State::Resolving(ref mut future) => {
|
||||||
match try!(future.poll()) {
|
let addrs = try_ready!(future.poll(cx));
|
||||||
Async::NotReady => return Ok(Async::NotReady),
|
state = State::Connecting(ConnectingTcp {
|
||||||
Async::Ready(addrs) => {
|
addrs: addrs,
|
||||||
state = State::Connecting(ConnectingTcp {
|
current: None,
|
||||||
addrs: addrs,
|
});
|
||||||
current: None,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
State::Connecting(ref mut c) => {
|
State::Connecting(ref mut c) => {
|
||||||
let sock = try_ready!(c.poll(&self.handle));
|
let sock = try_ready!(c.poll(cx, &self.handle));
|
||||||
|
|
||||||
if let Some(dur) = self.keep_alive_timeout {
|
if let Some(dur) = self.keep_alive_timeout {
|
||||||
sock.set_keepalive(Some(dur))?;
|
sock.set_keepalive(Some(dur))?;
|
||||||
@@ -365,11 +357,11 @@ struct ConnectingTcp {
|
|||||||
|
|
||||||
impl ConnectingTcp {
|
impl ConnectingTcp {
|
||||||
// not a Future, since passing a &Handle to poll
|
// not a Future, since passing a &Handle to poll
|
||||||
fn poll(&mut self, handle: &Handle) -> Poll<TcpStream, io::Error> {
|
fn poll(&mut self, cx: &mut task::Context, handle: &Handle) -> Poll<TcpStream, io::Error> {
|
||||||
let mut err = None;
|
let mut err = None;
|
||||||
loop {
|
loop {
|
||||||
if let Some(ref mut current) = self.current {
|
if let Some(ref mut current) = self.current {
|
||||||
match current.poll() {
|
match current.poll(cx) {
|
||||||
Ok(ok) => return Ok(ok),
|
Ok(ok) => return Ok(ok),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
trace!("connect error {:?}", e);
|
trace!("connect error {:?}", e);
|
||||||
@@ -392,37 +384,19 @@ impl ConnectingTcp {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make this Future unnameable outside of this crate.
|
|
||||||
mod http_connector {
|
|
||||||
use super::*;
|
|
||||||
// Blocking task to be executed on a thread pool.
|
|
||||||
pub struct HttpConnectorBlockingTask {
|
|
||||||
pub(super) work: oneshot::Execute<dns::Work>
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Debug for HttpConnectorBlockingTask {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
f.pad("HttpConnectorBlockingTask")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Future for HttpConnectorBlockingTask {
|
|
||||||
type Item = ();
|
|
||||||
type Error = ();
|
|
||||||
|
|
||||||
fn poll(&mut self) -> Poll<(), ()> {
|
|
||||||
self.work.poll()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct HttpConnectExecutor(Arc<Executor<HttpConnectorBlockingTask> + Send + Sync>);
|
struct HttpConnectExecutor(Box<CloneBoxedExecutor>);
|
||||||
|
|
||||||
impl Executor<oneshot::Execute<dns::Work>> for HttpConnectExecutor {
|
impl Executor for HttpConnectExecutor {
|
||||||
fn execute(&self, future: oneshot::Execute<dns::Work>) -> Result<(), ExecuteError<oneshot::Execute<dns::Work>>> {
|
fn spawn(
|
||||||
self.0.execute(HttpConnectorBlockingTask { work: future })
|
&mut self,
|
||||||
.map_err(|err| ExecuteError::new(err.kind(), err.into_future().work))
|
f: Box<Future<Item = (), Error = Never> + 'static + Send>
|
||||||
|
) -> Result<(), SpawnError> {
|
||||||
|
self.0.spawn(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn status(&self) -> Result<(), SpawnError> {
|
||||||
|
self.0.status()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -430,7 +404,7 @@ impl Executor<oneshot::Execute<dns::Work>> for HttpConnectExecutor {
|
|||||||
mod tests {
|
mod tests {
|
||||||
#![allow(deprecated)]
|
#![allow(deprecated)]
|
||||||
use std::io;
|
use std::io;
|
||||||
use futures::Future;
|
use futures::executor::block_on;
|
||||||
use tokio::runtime::Runtime;
|
use tokio::runtime::Runtime;
|
||||||
use super::{Connect, Destination, HttpConnector};
|
use super::{Connect, Destination, HttpConnector};
|
||||||
|
|
||||||
@@ -443,7 +417,7 @@ mod tests {
|
|||||||
};
|
};
|
||||||
let connector = HttpConnector::new(1, runtime.handle());
|
let connector = HttpConnector::new(1, runtime.handle());
|
||||||
|
|
||||||
assert_eq!(connector.connect(dst).wait().unwrap_err().kind(), io::ErrorKind::InvalidInput);
|
assert_eq!(block_on(connector.connect(dst)).unwrap_err().kind(), io::ErrorKind::InvalidInput);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -455,7 +429,7 @@ mod tests {
|
|||||||
};
|
};
|
||||||
let connector = HttpConnector::new(1, runtime.handle());
|
let connector = HttpConnector::new(1, runtime.handle());
|
||||||
|
|
||||||
assert_eq!(connector.connect(dst).wait().unwrap_err().kind(), io::ErrorKind::InvalidInput);
|
assert_eq!(block_on(connector.connect(dst)).unwrap_err().kind(), io::ErrorKind::InvalidInput);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -468,6 +442,6 @@ mod tests {
|
|||||||
};
|
};
|
||||||
let connector = HttpConnector::new(1, runtime.handle());
|
let connector = HttpConnector::new(1, runtime.handle());
|
||||||
|
|
||||||
assert_eq!(connector.connect(dst).wait().unwrap_err().kind(), io::ErrorKind::InvalidInput);
|
assert_eq!(block_on(connector.connect(dst)).unwrap_err().kind(), io::ErrorKind::InvalidInput);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
use futures::{Async, Poll, Stream};
|
use futures::{Async, Never, Poll, Stream};
|
||||||
use futures::sync::{mpsc, oneshot};
|
use futures::channel::{mpsc, oneshot};
|
||||||
|
use futures::task;
|
||||||
use want;
|
use want;
|
||||||
|
|
||||||
use common::Never;
|
|
||||||
|
|
||||||
//pub type Callback<T, U> = oneshot::Sender<Result<U, (::Error, Option<T>)>>;
|
//pub type Callback<T, U> = oneshot::Sender<Result<U, (::Error, Option<T>)>>;
|
||||||
pub type RetryPromise<T, U> = oneshot::Receiver<Result<U, (::Error, Option<T>)>>;
|
pub type RetryPromise<T, U> = oneshot::Receiver<Result<U, (::Error, Option<T>)>>;
|
||||||
pub type Promise<T> = oneshot::Receiver<Result<T, ::Error>>;
|
pub type Promise<T> = oneshot::Receiver<Result<T, ::Error>>;
|
||||||
@@ -33,15 +32,15 @@ pub struct Sender<T, U> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<T, U> Sender<T, U> {
|
impl<T, U> Sender<T, U> {
|
||||||
pub fn poll_ready(&mut self) -> Poll<(), ::Error> {
|
pub fn poll_ready(&mut self, cx: &mut task::Context) -> Poll<(), ::Error> {
|
||||||
match self.inner.poll_ready() {
|
match self.inner.poll_ready(cx) {
|
||||||
Ok(Async::Ready(())) => {
|
Ok(Async::Ready(())) => {
|
||||||
// there's room in the queue, but does the Connection
|
// there's room in the queue, but does the Connection
|
||||||
// want a message yet?
|
// want a message yet?
|
||||||
self.giver.poll_want()
|
self.giver.poll_want(cx)
|
||||||
.map_err(|_| ::Error::Closed)
|
.map_err(|_| ::Error::Closed)
|
||||||
},
|
},
|
||||||
Ok(Async::NotReady) => Ok(Async::NotReady),
|
Ok(Async::Pending) => Ok(Async::Pending),
|
||||||
Err(_) => Err(::Error::Closed),
|
Err(_) => Err(::Error::Closed),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -75,16 +74,15 @@ impl<T, U> Stream for Receiver<T, U> {
|
|||||||
type Item = (T, Callback<T, U>);
|
type Item = (T, Callback<T, U>);
|
||||||
type Error = Never;
|
type Error = Never;
|
||||||
|
|
||||||
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
|
fn poll_next(&mut self, cx: &mut task::Context) -> Poll<Option<Self::Item>, Self::Error> {
|
||||||
match self.inner.poll() {
|
match self.inner.poll_next(cx)? {
|
||||||
Ok(Async::Ready(item)) => Ok(Async::Ready(item.map(|mut env| {
|
Async::Ready(item) => Ok(Async::Ready(item.map(|mut env| {
|
||||||
env.0.take().expect("envelope not dropped")
|
env.0.take().expect("envelope not dropped")
|
||||||
}))),
|
}))),
|
||||||
Ok(Async::NotReady) => {
|
Async::Pending => {
|
||||||
self.taker.want();
|
self.taker.want();
|
||||||
Ok(Async::NotReady)
|
Ok(Async::Pending)
|
||||||
}
|
}
|
||||||
Err(()) => unreachable!("mpsc never errors"),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -107,11 +105,11 @@ impl<T, U> Drop for Receiver<T, U> {
|
|||||||
// This poll() is safe to call in `Drop`, because we've
|
// This poll() is safe to call in `Drop`, because we've
|
||||||
// called, `close`, which promises that no new messages
|
// called, `close`, which promises that no new messages
|
||||||
// will arrive, and thus, once we reach the end, we won't
|
// will arrive, and thus, once we reach the end, we won't
|
||||||
// see a `NotReady` (and try to park), but a Ready(None).
|
// see a `Pending` (and try to park), but a Ready(None).
|
||||||
//
|
//
|
||||||
// All other variants:
|
// All other variants:
|
||||||
// - Ready(None): the end. we want to stop looping
|
// - Ready(None): the end. we want to stop looping
|
||||||
// - NotReady: unreachable
|
// - Pending: unreachable
|
||||||
// - Err: unreachable
|
// - Err: unreachable
|
||||||
while let Ok(Async::Ready(Some((val, cb)))) = self.inner.poll() {
|
while let Ok(Async::Ready(Some((val, cb)))) = self.inner.poll() {
|
||||||
let _ = cb.send(Err((::Error::new_canceled(None::<::Error>), Some(val))));
|
let _ = cb.send(Err((::Error::new_canceled(None::<::Error>), Some(val))));
|
||||||
@@ -137,10 +135,10 @@ pub enum Callback<T, U> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<T, U> Callback<T, U> {
|
impl<T, U> Callback<T, U> {
|
||||||
pub fn poll_cancel(&mut self) -> Poll<(), ()> {
|
pub fn poll_cancel(&mut self, cx: &mut task::Context) -> Poll<(), Never> {
|
||||||
match *self {
|
match *self {
|
||||||
Callback::Retry(ref mut tx) => tx.poll_cancel(),
|
Callback::Retry(ref mut tx) => tx.poll_cancel(cx),
|
||||||
Callback::NoRetry(ref mut tx) => tx.poll_cancel(),
|
Callback::NoRetry(ref mut tx) => tx.poll_cancel(cx),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,7 +160,8 @@ mod tests {
|
|||||||
#[cfg(feature = "nightly")]
|
#[cfg(feature = "nightly")]
|
||||||
extern crate test;
|
extern crate test;
|
||||||
|
|
||||||
use futures::{future, Future};
|
use futures::{future, FutureExt};
|
||||||
|
use futures::executor::block_on;
|
||||||
|
|
||||||
#[cfg(feature = "nightly")]
|
#[cfg(feature = "nightly")]
|
||||||
use futures::{Stream};
|
use futures::{Stream};
|
||||||
@@ -171,7 +170,7 @@ mod tests {
|
|||||||
fn drop_receiver_sends_cancel_errors() {
|
fn drop_receiver_sends_cancel_errors() {
|
||||||
let _ = pretty_env_logger::try_init();
|
let _ = pretty_env_logger::try_init();
|
||||||
|
|
||||||
future::lazy(|| {
|
block_on(future::lazy(|_| {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct Custom(i32);
|
struct Custom(i32);
|
||||||
let (mut tx, rx) = super::channel::<Custom, ()>();
|
let (mut tx, rx) = super::channel::<Custom, ()>();
|
||||||
@@ -188,7 +187,7 @@ mod tests {
|
|||||||
|
|
||||||
Ok::<(), ()>(())
|
Ok::<(), ()>(())
|
||||||
})
|
})
|
||||||
}).wait().unwrap();
|
})).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "nightly")]
|
#[cfg(feature = "nightly")]
|
||||||
@@ -197,18 +196,18 @@ mod tests {
|
|||||||
let (mut tx, mut rx) = super::channel::<i32, ()>();
|
let (mut tx, mut rx) = super::channel::<i32, ()>();
|
||||||
|
|
||||||
b.iter(move || {
|
b.iter(move || {
|
||||||
::futures::future::lazy(|| {
|
block_on(future::lazy(|cx| {
|
||||||
let _ = tx.send(1).unwrap();
|
let _ = tx.send(1).unwrap();
|
||||||
loop {
|
loop {
|
||||||
let async = rx.poll().unwrap();
|
let async = rx.poll_next(cx).unwrap();
|
||||||
if async.is_not_ready() {
|
if async.is_pending() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Ok::<(), ()>(())
|
Ok::<(), ()>(())
|
||||||
}).wait().unwrap();
|
})).unwrap();
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -218,11 +217,11 @@ mod tests {
|
|||||||
let (_tx, mut rx) = super::channel::<i32, ()>();
|
let (_tx, mut rx) = super::channel::<i32, ()>();
|
||||||
|
|
||||||
b.iter(move || {
|
b.iter(move || {
|
||||||
::futures::future::lazy(|| {
|
block_on(future::lazy(|cx| {
|
||||||
assert!(rx.poll().unwrap().is_not_ready());
|
assert!(rx.poll_next(cx).unwrap().is_pending());
|
||||||
|
|
||||||
Ok::<(), ()>(())
|
Ok::<(), ()>(())
|
||||||
}).wait().unwrap();
|
})).unwrap();
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,27 +6,44 @@ use std::net::{
|
|||||||
};
|
};
|
||||||
use std::vec;
|
use std::vec;
|
||||||
|
|
||||||
use ::futures::{Async, Future, Poll};
|
use futures::{Async, Future, Poll};
|
||||||
|
use futures::task;
|
||||||
|
use futures::future::lazy;
|
||||||
|
use futures::executor::Executor;
|
||||||
|
use futures::channel::oneshot;
|
||||||
|
|
||||||
pub struct Work {
|
pub struct Resolving {
|
||||||
host: String,
|
receiver: oneshot::Receiver<Result<IpAddrs, io::Error>>
|
||||||
port: u16
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Work {
|
impl Resolving {
|
||||||
pub fn new(host: String, port: u16) -> Work {
|
pub fn spawn(host: String, port: u16, executor: &mut Executor) -> Resolving {
|
||||||
Work { host: host, port: port }
|
let (sender, receiver) = oneshot::channel();
|
||||||
|
// The `Resolving` future will return an error when the sender is dropped,
|
||||||
|
// so we can just ignore the spawn error here
|
||||||
|
executor.spawn(Box::new(lazy(move |_| {
|
||||||
|
debug!("resolving host={:?}, port={:?}", host, port);
|
||||||
|
let result = (host.as_ref(), port).to_socket_addrs()
|
||||||
|
.map(|i| IpAddrs { iter: i });
|
||||||
|
sender.send(result).ok();
|
||||||
|
Ok(())
|
||||||
|
}))).ok();
|
||||||
|
Resolving { receiver }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Future for Work {
|
impl Future for Resolving {
|
||||||
type Item = IpAddrs;
|
type Item = IpAddrs;
|
||||||
type Error = io::Error;
|
type Error = io::Error;
|
||||||
|
|
||||||
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
fn poll(&mut self, cx: &mut task::Context) -> Poll<IpAddrs, io::Error> {
|
||||||
debug!("resolving host={:?}, port={:?}", self.host, self.port);
|
match self.receiver.poll(cx) {
|
||||||
(&*self.host, self.port).to_socket_addrs()
|
Ok(Async::Pending) => Ok(Async::Pending),
|
||||||
.map(|i| Async::Ready(IpAddrs { iter: i }))
|
Ok(Async::Ready(Ok(ips))) => Ok(Async::Ready(ips)),
|
||||||
|
Ok(Async::Ready(Err(err))) => Err(err),
|
||||||
|
Err(_) =>
|
||||||
|
Err(io::Error::new(io::ErrorKind::Other, "dns task was cancelled"))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,22 +6,21 @@ use std::marker::PhantomData;
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use futures::{Async, Future, Poll};
|
use futures::{Async, Future, FutureExt, Never, Poll};
|
||||||
use futures::future::{self, Executor};
|
use futures::future;
|
||||||
|
use futures::task;
|
||||||
use http::{Method, Request, Response, Uri, Version};
|
use http::{Method, Request, Response, Uri, Version};
|
||||||
use http::header::{Entry, HeaderValue, HOST};
|
use http::header::{Entry, HeaderValue, HOST};
|
||||||
use http::uri::Scheme;
|
use http::uri::Scheme;
|
||||||
use tokio::reactor::Handle;
|
use tokio::reactor::Handle;
|
||||||
use tokio_executor::spawn;
|
|
||||||
pub use tokio_service::Service;
|
|
||||||
|
|
||||||
|
pub use service::Service;
|
||||||
use proto::body::{Body, Entity};
|
use proto::body::{Body, Entity};
|
||||||
use proto;
|
use proto;
|
||||||
use self::pool::Pool;
|
use self::pool::Pool;
|
||||||
|
|
||||||
pub use self::connect::{Connect, HttpConnector};
|
pub use self::connect::{Connect, HttpConnector};
|
||||||
|
|
||||||
use self::background::{bg, Background};
|
|
||||||
use self::connect::Destination;
|
use self::connect::Destination;
|
||||||
|
|
||||||
pub mod conn;
|
pub mod conn;
|
||||||
@@ -35,7 +34,6 @@ mod tests;
|
|||||||
/// A Client to make outgoing HTTP requests.
|
/// A Client to make outgoing HTTP requests.
|
||||||
pub struct Client<C, B = proto::Body> {
|
pub struct Client<C, B = proto::Body> {
|
||||||
connector: Arc<C>,
|
connector: Arc<C>,
|
||||||
executor: Exec,
|
|
||||||
h1_writev: bool,
|
h1_writev: bool,
|
||||||
pool: Pool<PoolClient<B>>,
|
pool: Pool<PoolClient<B>>,
|
||||||
retry_canceled_requests: bool,
|
retry_canceled_requests: bool,
|
||||||
@@ -82,10 +80,9 @@ impl Client<HttpConnector, proto::Body> {
|
|||||||
|
|
||||||
impl<C, B> Client<C, B> {
|
impl<C, B> Client<C, B> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn configured(config: Config<C, B>, exec: Exec) -> Client<C, B> {
|
fn configured(config: Config<C, B>) -> Client<C, B> {
|
||||||
Client {
|
Client {
|
||||||
connector: Arc::new(config.connector),
|
connector: Arc::new(config.connector),
|
||||||
executor: exec,
|
|
||||||
h1_writev: config.h1_writev,
|
h1_writev: config.h1_writev,
|
||||||
pool: Pool::new(config.keep_alive, config.keep_alive_timeout),
|
pool: Pool::new(config.keep_alive, config.keep_alive_timeout),
|
||||||
retry_canceled_requests: config.retry_canceled_requests,
|
retry_canceled_requests: config.retry_canceled_requests,
|
||||||
@@ -125,14 +122,6 @@ where C: Connect<Error=io::Error> + Sync + 'static,
|
|||||||
|
|
||||||
/// Send a constructed Request using this Client.
|
/// Send a constructed Request using this Client.
|
||||||
pub fn request(&self, mut req: Request<B>) -> FutureResponse {
|
pub fn request(&self, mut req: Request<B>) -> FutureResponse {
|
||||||
// TODO(0.12): do this at construction time.
|
|
||||||
//
|
|
||||||
// It cannot be done in the constructor because the Client::configured
|
|
||||||
// does not have `B: 'static` bounds, which are required to spawn
|
|
||||||
// the interval. In 0.12, add a static bounds to the constructor,
|
|
||||||
// and move this.
|
|
||||||
self.schedule_pool_timer();
|
|
||||||
|
|
||||||
match req.version() {
|
match req.version() {
|
||||||
Version::HTTP_10 |
|
Version::HTTP_10 |
|
||||||
Version::HTTP_11 => (),
|
Version::HTTP_11 => (),
|
||||||
@@ -175,7 +164,6 @@ where C: Connect<Error=io::Error> + Sync + 'static,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
let client = self.clone();
|
let client = self.clone();
|
||||||
let uri = req.uri().clone();
|
let uri = req.uri().clone();
|
||||||
let fut = RetryableSendRequest {
|
let fut = RetryableSendRequest {
|
||||||
@@ -192,7 +180,6 @@ where C: Connect<Error=io::Error> + Sync + 'static,
|
|||||||
let url = req.uri().clone();
|
let url = req.uri().clone();
|
||||||
let checkout = self.pool.checkout(domain);
|
let checkout = self.pool.checkout(domain);
|
||||||
let connect = {
|
let connect = {
|
||||||
let executor = self.executor.clone();
|
|
||||||
let pool = self.pool.clone();
|
let pool = self.pool.clone();
|
||||||
let pool_key = Arc::new(domain.to_string());
|
let pool_key = Arc::new(domain.to_string());
|
||||||
let h1_writev = self.h1_writev;
|
let h1_writev = self.h1_writev;
|
||||||
@@ -200,36 +187,39 @@ where C: Connect<Error=io::Error> + Sync + 'static,
|
|||||||
let dst = Destination {
|
let dst = Destination {
|
||||||
uri: url,
|
uri: url,
|
||||||
};
|
};
|
||||||
future::lazy(move || {
|
future::lazy(move |_| {
|
||||||
connector.connect(dst)
|
connector.connect(dst)
|
||||||
.from_err()
|
.err_into()
|
||||||
.and_then(move |(io, connected)| {
|
.and_then(move |(io, connected)| {
|
||||||
conn::Builder::new()
|
conn::Builder::new()
|
||||||
.h1_writev(h1_writev)
|
.h1_writev(h1_writev)
|
||||||
.handshake_no_upgrades(io)
|
.handshake_no_upgrades(io)
|
||||||
.and_then(move |(tx, conn)| {
|
.and_then(move |(tx, conn)| {
|
||||||
executor.execute(conn.map_err(|e| debug!("client connection error: {}", e)))?;
|
future::lazy(move |cx| {
|
||||||
Ok(pool.pooled(pool_key, PoolClient {
|
execute(conn.recover(|e| {
|
||||||
is_proxied: connected.is_proxied,
|
debug!("client connection error: {}", e);
|
||||||
tx: tx,
|
}), cx)?;
|
||||||
}))
|
Ok(pool.pooled(pool_key, PoolClient {
|
||||||
|
is_proxied: connected.is_proxied,
|
||||||
|
tx: tx,
|
||||||
|
}))
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
let race = checkout.select(connect)
|
let race = checkout.select(connect).map(|either| {
|
||||||
.map(|(pooled, _work)| pooled)
|
either.either(|(pooled, _)| pooled, |(pooled, _)| pooled)
|
||||||
.map_err(|(e, _checkout)| {
|
}).map_err(|either| {
|
||||||
// the Pool Checkout cannot error, so the only error
|
// the Pool Checkout cannot error, so the only error
|
||||||
// is from the Connector
|
// is from the Connector
|
||||||
// XXX: should wait on the Checkout? Problem is
|
// XXX: should wait on the Checkout? Problem is
|
||||||
// that if the connector is failing, it may be that we
|
// that if the connector is failing, it may be that we
|
||||||
// never had a pooled stream at all
|
// never had a pooled stream at all
|
||||||
ClientError::Normal(e)
|
ClientError::Normal(either.either(|(e, _)| e, |(e, _)| e))
|
||||||
});
|
});
|
||||||
|
|
||||||
let executor = self.executor.clone();
|
|
||||||
let resp = race.and_then(move |mut pooled| {
|
let resp = race.and_then(move |mut pooled| {
|
||||||
let conn_reused = pooled.is_reused();
|
let conn_reused = pooled.is_reused();
|
||||||
set_relative_uri(req.uri_mut(), pooled.is_proxied);
|
set_relative_uri(req.uri_mut(), pooled.is_proxied);
|
||||||
@@ -245,24 +235,26 @@ where C: Connect<Error=io::Error> + Sync + 'static,
|
|||||||
ClientError::Normal(err)
|
ClientError::Normal(err)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.map(move |res| {
|
.and_then(move |res| {
|
||||||
// when pooled is dropped, it will try to insert back into the
|
future::lazy(move |cx| {
|
||||||
// pool. To delay that, spawn a future that completes once the
|
// when pooled is dropped, it will try to insert back into the
|
||||||
// sender is ready again.
|
// pool. To delay that, spawn a future that completes once the
|
||||||
//
|
// sender is ready again.
|
||||||
// This *should* only be once the related `Connection` has polled
|
//
|
||||||
// for a new request to start.
|
// This *should* only be once the related `Connection` has polled
|
||||||
//
|
// for a new request to start.
|
||||||
// It won't be ready if there is a body to stream.
|
//
|
||||||
if let Ok(Async::NotReady) = pooled.tx.poll_ready() {
|
// It won't be ready if there is a body to stream.
|
||||||
// If the executor doesn't have room, oh well. Things will likely
|
if let Ok(Async::Pending) = pooled.tx.poll_ready(cx) {
|
||||||
// be blowing up soon, but this specific task isn't required.
|
// If the executor doesn't have room, oh well. Things will likely
|
||||||
let _ = executor.execute(future::poll_fn(move || {
|
// be blowing up soon, but this specific task isn't required.
|
||||||
pooled.tx.poll_ready().map_err(|_| ())
|
execute(future::poll_fn(move |cx| {
|
||||||
}));
|
pooled.tx.poll_ready(cx).or(Ok(Async::Ready(())))
|
||||||
}
|
}), cx).ok();
|
||||||
|
}
|
||||||
|
|
||||||
res
|
Ok(res)
|
||||||
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@@ -271,10 +263,6 @@ where C: Connect<Error=io::Error> + Sync + 'static,
|
|||||||
|
|
||||||
Box::new(resp)
|
Box::new(resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn schedule_pool_timer(&self) {
|
|
||||||
self.pool.spawn_expired_interval(&self.executor);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C, B> Service for Client<C, B>
|
impl<C, B> Service for Client<C, B>
|
||||||
@@ -297,7 +285,6 @@ impl<C, B> Clone for Client<C, B> {
|
|||||||
fn clone(&self) -> Client<C, B> {
|
fn clone(&self) -> Client<C, B> {
|
||||||
Client {
|
Client {
|
||||||
connector: self.connector.clone(),
|
connector: self.connector.clone(),
|
||||||
executor: self.executor.clone(),
|
|
||||||
h1_writev: self.h1_writev,
|
h1_writev: self.h1_writev,
|
||||||
pool: self.pool.clone(),
|
pool: self.pool.clone(),
|
||||||
retry_canceled_requests: self.retry_canceled_requests,
|
retry_canceled_requests: self.retry_canceled_requests,
|
||||||
@@ -327,8 +314,8 @@ impl Future for FutureResponse {
|
|||||||
type Item = Response<Body>;
|
type Item = Response<Body>;
|
||||||
type Error = ::Error;
|
type Error = ::Error;
|
||||||
|
|
||||||
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
fn poll(&mut self, cx: &mut task::Context) -> Poll<Self::Item, Self::Error> {
|
||||||
self.0.poll()
|
self.0.poll(cx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -349,11 +336,11 @@ where
|
|||||||
type Item = Response<Body>;
|
type Item = Response<Body>;
|
||||||
type Error = ::Error;
|
type Error = ::Error;
|
||||||
|
|
||||||
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
fn poll(&mut self, cx: &mut task::Context) -> Poll<Self::Item, Self::Error> {
|
||||||
loop {
|
loop {
|
||||||
match self.future.poll() {
|
match self.future.poll(cx) {
|
||||||
Ok(Async::Ready(resp)) => return Ok(Async::Ready(resp)),
|
Ok(Async::Ready(resp)) => return Ok(Async::Ready(resp)),
|
||||||
Ok(Async::NotReady) => return Ok(Async::NotReady),
|
Ok(Async::Pending) => return Ok(Async::Pending),
|
||||||
Err(ClientError::Normal(err)) => return Err(err),
|
Err(ClientError::Normal(err)) => return Err(err),
|
||||||
Err(ClientError::Canceled {
|
Err(ClientError::Canceled {
|
||||||
connection_reused,
|
connection_reused,
|
||||||
@@ -561,18 +548,7 @@ where C: Connect<Error=io::Error>,
|
|||||||
/// Construct the Client with this configuration.
|
/// Construct the Client with this configuration.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn build(self) -> Client<C, B> {
|
pub fn build(self) -> Client<C, B> {
|
||||||
Client::configured(self, Exec::Default)
|
Client::configured(self)
|
||||||
}
|
|
||||||
|
|
||||||
/// Construct a Client with this configuration and an executor.
|
|
||||||
///
|
|
||||||
/// The executor will be used to spawn "background" connection tasks
|
|
||||||
/// to drive requests and responses.
|
|
||||||
pub fn executor<E>(self, executor: E) -> Client<C, B>
|
|
||||||
where
|
|
||||||
E: Executor<Background> + Send + Sync + 'static,
|
|
||||||
{
|
|
||||||
Client::configured(self, Exec::new(executor))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -590,21 +566,6 @@ where
|
|||||||
}
|
}
|
||||||
self.connector(connector).build()
|
self.connector(connector).build()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Construct a Client with this configuration and an executor.
|
|
||||||
///
|
|
||||||
/// The executor will be used to spawn "background" connection tasks
|
|
||||||
/// to drive requests and responses.
|
|
||||||
pub fn build_with_executor<E>(self, handle: &Handle, executor: E) -> Client<HttpConnector, B>
|
|
||||||
where
|
|
||||||
E: Executor<Background> + Send + Sync + 'static,
|
|
||||||
{
|
|
||||||
let mut connector = HttpConnector::new(4, handle);
|
|
||||||
if self.keep_alive {
|
|
||||||
connector.set_keepalive(self.keep_alive_timeout);
|
|
||||||
}
|
|
||||||
self.connector(connector).executor(executor)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C, B> fmt::Debug for Config<C, B> {
|
impl<C, B> fmt::Debug for Config<C, B> {
|
||||||
@@ -629,68 +590,15 @@ impl<C: Clone, B> Clone for Config<C, B> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// ===== impl Exec =====
|
fn execute<F>(fut: F, cx: &mut task::Context) -> Result<(), ::Error>
|
||||||
|
where F: Future<Item=(), Error=Never> + Send + 'static,
|
||||||
#[derive(Clone)]
|
{
|
||||||
enum Exec {
|
if let Some(executor) = cx.executor() {
|
||||||
Default,
|
executor.spawn(Box::new(fut)).map_err(|err| {
|
||||||
Executor(Arc<Executor<Background> + Send + Sync>),
|
debug!("executor error: {:?}", err);
|
||||||
}
|
::Error::Executor
|
||||||
|
})
|
||||||
|
} else {
|
||||||
impl Exec {
|
Err(::Error::Executor)
|
||||||
pub(crate) fn new<E: Executor<Background> + Send + Sync + 'static>(executor: E) -> Exec {
|
|
||||||
Exec::Executor(Arc::new(executor))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn execute<F>(&self, fut: F) -> io::Result<()>
|
|
||||||
where
|
|
||||||
F: Future<Item=(), Error=()> + Send + 'static,
|
|
||||||
{
|
|
||||||
match *self {
|
|
||||||
Exec::Default => spawn(fut),
|
|
||||||
Exec::Executor(ref e) => {
|
|
||||||
e.execute(bg(Box::new(fut)))
|
|
||||||
.map_err(|err| {
|
|
||||||
debug!("executor error: {:?}", err.kind());
|
|
||||||
io::Error::new(
|
|
||||||
io::ErrorKind::Other,
|
|
||||||
"executor error",
|
|
||||||
)
|
|
||||||
})?
|
|
||||||
},
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== impl Background =====
|
|
||||||
|
|
||||||
// The types inside this module are not exported out of the crate,
|
|
||||||
// so they are in essence un-nameable.
|
|
||||||
mod background {
|
|
||||||
use futures::{Future, Poll};
|
|
||||||
|
|
||||||
// This is basically `impl Future`, since the type is un-nameable,
|
|
||||||
// and only implementeds `Future`.
|
|
||||||
#[allow(missing_debug_implementations)]
|
|
||||||
pub struct Background {
|
|
||||||
inner: Box<Future<Item=(), Error=()> + Send>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn bg(fut: Box<Future<Item=(), Error=()> + Send>) -> Background {
|
|
||||||
Background {
|
|
||||||
inner: fut,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Future for Background {
|
|
||||||
type Item = ();
|
|
||||||
type Error = ();
|
|
||||||
|
|
||||||
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
|
||||||
self.inner.poll()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|||||||
@@ -4,14 +4,13 @@ use std::ops::{Deref, DerefMut};
|
|||||||
use std::sync::{Arc, Mutex, Weak};
|
use std::sync::{Arc, Mutex, Weak};
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
use futures::{Future, Async, Poll, Stream};
|
use futures::{Future, Async, Never, Poll, Stream};
|
||||||
use futures::sync::oneshot;
|
use futures::channel::oneshot;
|
||||||
|
use futures::task;
|
||||||
use futures_timer::Interval;
|
use futures_timer::Interval;
|
||||||
|
|
||||||
use super::Exec;
|
pub(super) struct Pool<T> {
|
||||||
|
inner: Arc<Mutex<PoolInner<T>>>
|
||||||
pub struct Pool<T> {
|
|
||||||
inner: Arc<Mutex<PoolInner<T>>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Before using a pooled connection, make sure the sender is not dead.
|
// Before using a pooled connection, make sure the sender is not dead.
|
||||||
@@ -48,7 +47,7 @@ struct PoolInner<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Pool<T> {
|
impl<T> Pool<T> {
|
||||||
pub fn new(enabled: bool, timeout: Option<Duration>) -> Pool<T> {
|
pub(super) fn new(enabled: bool, timeout: Option<Duration>) -> Pool<T> {
|
||||||
Pool {
|
Pool {
|
||||||
inner: Arc::new(Mutex::new(PoolInner {
|
inner: Arc::new(Mutex::new(PoolInner {
|
||||||
enabled: enabled,
|
enabled: enabled,
|
||||||
@@ -56,7 +55,7 @@ impl<T> Pool<T> {
|
|||||||
parked: HashMap::new(),
|
parked: HashMap::new(),
|
||||||
timeout: timeout,
|
timeout: timeout,
|
||||||
expired_timer_spawned: false,
|
expired_timer_spawned: false,
|
||||||
})),
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -67,6 +66,7 @@ impl<T: Closed> Pool<T> {
|
|||||||
key: Arc::new(key.to_owned()),
|
key: Arc::new(key.to_owned()),
|
||||||
pool: self.clone(),
|
pool: self.clone(),
|
||||||
parked: None,
|
parked: None,
|
||||||
|
spawned_expired_interval: false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -221,38 +221,38 @@ impl<T: Closed> PoolInner<T> {
|
|||||||
|
|
||||||
|
|
||||||
impl<T: Closed + Send + 'static> Pool<T> {
|
impl<T: Closed + Send + 'static> Pool<T> {
|
||||||
pub(super) fn spawn_expired_interval(&self, exec: &Exec) {
|
fn spawn_expired_interval(&mut self, cx: &mut task::Context) -> Result<(), ::Error> {
|
||||||
let dur = {
|
let dur = {
|
||||||
let mut inner = self.inner.lock().unwrap();
|
let mut inner = self.inner.lock().unwrap();
|
||||||
|
|
||||||
if !inner.enabled {
|
if !inner.enabled {
|
||||||
return;
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
if inner.expired_timer_spawned {
|
if inner.expired_timer_spawned {
|
||||||
return;
|
return Ok(());
|
||||||
}
|
}
|
||||||
inner.expired_timer_spawned = true;
|
inner.expired_timer_spawned = true;
|
||||||
|
|
||||||
if let Some(dur) = inner.timeout {
|
if let Some(dur) = inner.timeout {
|
||||||
dur
|
dur
|
||||||
} else {
|
} else {
|
||||||
return
|
return Ok(());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let interval = Interval::new(dur);
|
let interval = Interval::new(dur);
|
||||||
exec.execute(IdleInterval {
|
super::execute(IdleInterval {
|
||||||
interval: interval,
|
interval: interval,
|
||||||
pool: Arc::downgrade(&self.inner),
|
pool: Arc::downgrade(&self.inner),
|
||||||
}).unwrap();
|
}, cx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Clone for Pool<T> {
|
impl<T> Clone for Pool<T> {
|
||||||
fn clone(&self) -> Pool<T> {
|
fn clone(&self) -> Pool<T> {
|
||||||
Pool {
|
Pool {
|
||||||
inner: self.inner.clone(),
|
inner: self.inner.clone()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -322,22 +322,23 @@ pub struct Checkout<T> {
|
|||||||
key: Arc<String>,
|
key: Arc<String>,
|
||||||
pool: Pool<T>,
|
pool: Pool<T>,
|
||||||
parked: Option<oneshot::Receiver<T>>,
|
parked: Option<oneshot::Receiver<T>>,
|
||||||
|
spawned_expired_interval: bool
|
||||||
}
|
}
|
||||||
|
|
||||||
struct NotParked;
|
struct NotParked;
|
||||||
|
|
||||||
impl<T: Closed> Checkout<T> {
|
impl<T: Closed> Checkout<T> {
|
||||||
fn poll_parked(&mut self) -> Poll<Pooled<T>, NotParked> {
|
fn poll_parked(&mut self, cx: &mut task::Context) -> Poll<Pooled<T>, NotParked> {
|
||||||
let mut drop_parked = false;
|
let mut drop_parked = false;
|
||||||
if let Some(ref mut rx) = self.parked {
|
if let Some(ref mut rx) = self.parked {
|
||||||
match rx.poll() {
|
match rx.poll(cx) {
|
||||||
Ok(Async::Ready(value)) => {
|
Ok(Async::Ready(value)) => {
|
||||||
if !value.is_closed() {
|
if !value.is_closed() {
|
||||||
return Ok(Async::Ready(self.pool.reuse(&self.key, value)));
|
return Ok(Async::Ready(self.pool.reuse(&self.key, value)));
|
||||||
}
|
}
|
||||||
drop_parked = true;
|
drop_parked = true;
|
||||||
},
|
},
|
||||||
Ok(Async::NotReady) => return Ok(Async::NotReady),
|
Ok(Async::Pending) => return Ok(Async::Pending),
|
||||||
Err(_canceled) => drop_parked = true,
|
Err(_canceled) => drop_parked = true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -347,22 +348,27 @@ impl<T: Closed> Checkout<T> {
|
|||||||
Err(NotParked)
|
Err(NotParked)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn park(&mut self) {
|
fn park(&mut self, cx: &mut task::Context) {
|
||||||
if self.parked.is_none() {
|
if self.parked.is_none() {
|
||||||
let (tx, mut rx) = oneshot::channel();
|
let (tx, mut rx) = oneshot::channel();
|
||||||
let _ = rx.poll(); // park this task
|
let _ = rx.poll(cx); // park this task
|
||||||
self.pool.park(self.key.clone(), tx);
|
self.pool.park(self.key.clone(), tx);
|
||||||
self.parked = Some(rx);
|
self.parked = Some(rx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Closed> Future for Checkout<T> {
|
impl<T: Closed + Send + 'static> Future for Checkout<T> {
|
||||||
type Item = Pooled<T>;
|
type Item = Pooled<T>;
|
||||||
type Error = ::Error;
|
type Error = ::Error;
|
||||||
|
|
||||||
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
fn poll(&mut self, cx: &mut task::Context) -> Poll<Self::Item, Self::Error> {
|
||||||
match self.poll_parked() {
|
if !self.spawned_expired_interval {
|
||||||
|
self.pool.spawn_expired_interval(cx)?;
|
||||||
|
self.spawned_expired_interval = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
match self.poll_parked(cx) {
|
||||||
Ok(async) => return Ok(async),
|
Ok(async) => return Ok(async),
|
||||||
Err(_not_parked) => (),
|
Err(_not_parked) => (),
|
||||||
}
|
}
|
||||||
@@ -372,8 +378,8 @@ impl<T: Closed> Future for Checkout<T> {
|
|||||||
if let Some(pooled) = entry {
|
if let Some(pooled) = entry {
|
||||||
Ok(Async::Ready(pooled))
|
Ok(Async::Ready(pooled))
|
||||||
} else {
|
} else {
|
||||||
self.park();
|
self.park(cx);
|
||||||
Ok(Async::NotReady)
|
Ok(Async::Pending)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -409,11 +415,11 @@ struct IdleInterval<T> {
|
|||||||
|
|
||||||
impl<T: Closed + 'static> Future for IdleInterval<T> {
|
impl<T: Closed + 'static> Future for IdleInterval<T> {
|
||||||
type Item = ();
|
type Item = ();
|
||||||
type Error = ();
|
type Error = Never;
|
||||||
|
|
||||||
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
fn poll(&mut self, cx: &mut task::Context) -> Poll<Self::Item, Self::Error> {
|
||||||
loop {
|
loop {
|
||||||
try_ready!(self.interval.poll().map_err(|_| unreachable!("interval cannot error")));
|
try_ready!(self.interval.poll_next(cx).map_err(|_| unreachable!("interval cannot error")));
|
||||||
|
|
||||||
if let Some(inner) = self.pool.upgrade() {
|
if let Some(inner) = self.pool.upgrade() {
|
||||||
if let Ok(mut inner) = inner.lock() {
|
if let Ok(mut inner) = inner.lock() {
|
||||||
@@ -430,9 +436,10 @@ impl<T: Closed + 'static> Future for IdleInterval<T> {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use futures::{Async, Future};
|
use futures::{Async, Future, FutureExt};
|
||||||
use futures::future;
|
use futures::future;
|
||||||
use super::{Closed, Pool, Exec};
|
use futures::executor::block_on;
|
||||||
|
use super::{Closed, Pool};
|
||||||
|
|
||||||
impl Closed for i32 {
|
impl Closed for i32 {
|
||||||
fn is_closed(&self) -> bool {
|
fn is_closed(&self) -> bool {
|
||||||
@@ -442,34 +449,37 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_pool_checkout_smoke() {
|
fn test_pool_checkout_smoke() {
|
||||||
let pool = Pool::new(true, Some(Duration::from_secs(5)));
|
block_on(future::lazy(|cx| {
|
||||||
let key = Arc::new("foo".to_string());
|
let pool = Pool::new(true, Some(Duration::from_secs(5)));
|
||||||
let pooled = pool.pooled(key.clone(), 41);
|
let key = Arc::new("foo".to_string());
|
||||||
|
let pooled = pool.pooled(key.clone(), 41);
|
||||||
|
|
||||||
drop(pooled);
|
drop(pooled);
|
||||||
|
|
||||||
match pool.checkout(&key).poll().unwrap() {
|
match pool.checkout(&key).poll(cx).unwrap() {
|
||||||
Async::Ready(pooled) => assert_eq!(*pooled, 41),
|
Async::Ready(pooled) => assert_eq!(*pooled, 41),
|
||||||
_ => panic!("not ready"),
|
_ => panic!("not ready"),
|
||||||
}
|
}
|
||||||
|
Ok::<_, ()>(())
|
||||||
|
})).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_pool_checkout_returns_none_if_expired() {
|
fn test_pool_checkout_returns_none_if_expired() {
|
||||||
future::lazy(|| {
|
block_on(future::lazy(|cx| {
|
||||||
let pool = Pool::new(true, Some(Duration::from_millis(100)));
|
let pool = Pool::new(true, Some(Duration::from_millis(100)));
|
||||||
let key = Arc::new("foo".to_string());
|
let key = Arc::new("foo".to_string());
|
||||||
let pooled = pool.pooled(key.clone(), 41);
|
let pooled = pool.pooled(key.clone(), 41);
|
||||||
drop(pooled);
|
drop(pooled);
|
||||||
::std::thread::sleep(pool.inner.lock().unwrap().timeout.unwrap());
|
::std::thread::sleep(pool.inner.lock().unwrap().timeout.unwrap());
|
||||||
assert!(pool.checkout(&key).poll().unwrap().is_not_ready());
|
assert!(pool.checkout(&key).poll(cx).unwrap().is_pending());
|
||||||
::futures::future::ok::<(), ()>(())
|
Ok::<_, ()>(())
|
||||||
}).wait().unwrap();
|
})).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_pool_checkout_removes_expired() {
|
fn test_pool_checkout_removes_expired() {
|
||||||
future::lazy(|| {
|
block_on(future::lazy(|cx| {
|
||||||
let pool = Pool::new(true, Some(Duration::from_millis(100)));
|
let pool = Pool::new(true, Some(Duration::from_millis(100)));
|
||||||
let key = Arc::new("foo".to_string());
|
let key = Arc::new("foo".to_string());
|
||||||
|
|
||||||
@@ -481,20 +491,20 @@ mod tests {
|
|||||||
::std::thread::sleep(pool.inner.lock().unwrap().timeout.unwrap());
|
::std::thread::sleep(pool.inner.lock().unwrap().timeout.unwrap());
|
||||||
|
|
||||||
// checkout.poll() should clean out the expired
|
// checkout.poll() should clean out the expired
|
||||||
pool.checkout(&key).poll().unwrap();
|
pool.checkout(&key).poll(cx).unwrap();
|
||||||
assert!(pool.inner.lock().unwrap().idle.get(&key).is_none());
|
assert!(pool.inner.lock().unwrap().idle.get(&key).is_none());
|
||||||
|
|
||||||
Ok::<(), ()>(())
|
Ok::<_, ()>(())
|
||||||
}).wait().unwrap();
|
})).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_pool_timer_removes_expired() {
|
fn test_pool_timer_removes_expired() {
|
||||||
let runtime = ::tokio::runtime::Runtime::new().unwrap();
|
let mut pool = Pool::new(true, Some(Duration::from_millis(100)));
|
||||||
let pool = Pool::new(true, Some(Duration::from_millis(100)));
|
|
||||||
|
|
||||||
let executor = runtime.executor();
|
block_on(future::lazy(|cx| {
|
||||||
pool.spawn_expired_interval(&Exec::new(executor));
|
pool.spawn_expired_interval(cx)
|
||||||
|
})).unwrap();
|
||||||
let key = Arc::new("foo".to_string());
|
let key = Arc::new("foo".to_string());
|
||||||
|
|
||||||
pool.pooled(key.clone(), 41);
|
pool.pooled(key.clone(), 41);
|
||||||
@@ -503,9 +513,9 @@ mod tests {
|
|||||||
|
|
||||||
assert_eq!(pool.inner.lock().unwrap().idle.get(&key).map(|entries| entries.len()), Some(3));
|
assert_eq!(pool.inner.lock().unwrap().idle.get(&key).map(|entries| entries.len()), Some(3));
|
||||||
|
|
||||||
::futures_timer::Delay::new(
|
block_on(::futures_timer::Delay::new(
|
||||||
Duration::from_millis(400) // allow for too-good resolution
|
Duration::from_millis(400) // allow for too-good resolution
|
||||||
).wait().unwrap();
|
)).unwrap();
|
||||||
|
|
||||||
assert!(pool.inner.lock().unwrap().idle.get(&key).is_none());
|
assert!(pool.inner.lock().unwrap().idle.get(&key).is_none());
|
||||||
}
|
}
|
||||||
@@ -516,7 +526,7 @@ mod tests {
|
|||||||
let key = Arc::new("foo".to_string());
|
let key = Arc::new("foo".to_string());
|
||||||
let pooled = pool.pooled(key.clone(), 41);
|
let pooled = pool.pooled(key.clone(), 41);
|
||||||
|
|
||||||
let checkout = pool.checkout(&key).join(future::lazy(move || {
|
let checkout = pool.checkout(&key).join(future::lazy(move |_| {
|
||||||
// the checkout future will park first,
|
// the checkout future will park first,
|
||||||
// and then this lazy future will be polled, which will insert
|
// and then this lazy future will be polled, which will insert
|
||||||
// the pooled back into the pool
|
// the pooled back into the pool
|
||||||
@@ -525,12 +535,12 @@ mod tests {
|
|||||||
drop(pooled);
|
drop(pooled);
|
||||||
Ok(())
|
Ok(())
|
||||||
})).map(|(entry, _)| entry);
|
})).map(|(entry, _)| entry);
|
||||||
assert_eq!(*checkout.wait().unwrap(), 41);
|
assert_eq!(*block_on(checkout).unwrap(), 41);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_pool_checkout_drop_cleans_up_parked() {
|
fn test_pool_checkout_drop_cleans_up_parked() {
|
||||||
future::lazy(|| {
|
block_on(future::lazy(|cx| {
|
||||||
let pool = Pool::<i32>::new(true, Some(Duration::from_secs(10)));
|
let pool = Pool::<i32>::new(true, Some(Duration::from_secs(10)));
|
||||||
let key = Arc::new("localhost:12345".to_string());
|
let key = Arc::new("localhost:12345".to_string());
|
||||||
|
|
||||||
@@ -538,9 +548,9 @@ mod tests {
|
|||||||
let mut checkout2 = pool.checkout(&key);
|
let mut checkout2 = pool.checkout(&key);
|
||||||
|
|
||||||
// first poll needed to get into Pool's parked
|
// first poll needed to get into Pool's parked
|
||||||
checkout1.poll().unwrap();
|
checkout1.poll(cx).unwrap();
|
||||||
assert_eq!(pool.inner.lock().unwrap().parked.get(&key).unwrap().len(), 1);
|
assert_eq!(pool.inner.lock().unwrap().parked.get(&key).unwrap().len(), 1);
|
||||||
checkout2.poll().unwrap();
|
checkout2.poll(cx).unwrap();
|
||||||
assert_eq!(pool.inner.lock().unwrap().parked.get(&key).unwrap().len(), 2);
|
assert_eq!(pool.inner.lock().unwrap().parked.get(&key).unwrap().len(), 2);
|
||||||
|
|
||||||
// on drop, clean up Pool
|
// on drop, clean up Pool
|
||||||
@@ -550,7 +560,7 @@ mod tests {
|
|||||||
drop(checkout2);
|
drop(checkout2);
|
||||||
assert!(pool.inner.lock().unwrap().parked.get(&key).is_none());
|
assert!(pool.inner.lock().unwrap().parked.get(&key).is_none());
|
||||||
|
|
||||||
::futures::future::ok::<(), ()>(())
|
Ok::<_, ()>(())
|
||||||
}).wait().unwrap();
|
})).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ use std::time::Duration;
|
|||||||
|
|
||||||
use futures::Async;
|
use futures::Async;
|
||||||
use futures::future::poll_fn;
|
use futures::future::poll_fn;
|
||||||
|
use futures::executor::block_on;
|
||||||
use tokio::executor::thread_pool::{Builder as ThreadPoolBuilder};
|
use tokio::executor::thread_pool::{Builder as ThreadPoolBuilder};
|
||||||
|
|
||||||
use mock::MockConnector;
|
use mock::MockConnector;
|
||||||
@@ -22,7 +23,7 @@ fn retryable_request() {
|
|||||||
|
|
||||||
let client = Client::configure()
|
let client = Client::configure()
|
||||||
.connector(connector)
|
.connector(connector)
|
||||||
.executor(executor.sender().clone());
|
.build();
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -30,13 +31,13 @@ fn retryable_request() {
|
|||||||
.uri("http://mock.local/a")
|
.uri("http://mock.local/a")
|
||||||
.body(Default::default())
|
.body(Default::default())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let res1 = client.request(req);
|
let res1 = client.request(req).with_executor(executor.sender().clone());
|
||||||
let srv1 = poll_fn(|| {
|
let srv1 = poll_fn(|cx| {
|
||||||
try_ready!(sock1.read(&mut [0u8; 512]));
|
try_ready!(sock1.read(cx, &mut [0u8; 512]));
|
||||||
try_ready!(sock1.write(b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"));
|
try_ready!(sock1.write(b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"));
|
||||||
Ok(Async::Ready(()))
|
Ok(Async::Ready(()))
|
||||||
});
|
});
|
||||||
res1.join(srv1).wait().expect("res1");
|
block_on(res1.join(srv1)).expect("res1");
|
||||||
}
|
}
|
||||||
drop(sock1);
|
drop(sock1);
|
||||||
|
|
||||||
@@ -44,17 +45,17 @@ fn retryable_request() {
|
|||||||
.uri("http://mock.local/b")
|
.uri("http://mock.local/b")
|
||||||
.body(Default::default())
|
.body(Default::default())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let res2 = client.request(req)
|
let res2 = client.request(req).with_executor(executor.sender().clone())
|
||||||
.map(|res| {
|
.map(|res| {
|
||||||
assert_eq!(res.status().as_u16(), 222);
|
assert_eq!(res.status().as_u16(), 222);
|
||||||
});
|
});
|
||||||
let srv2 = poll_fn(|| {
|
let srv2 = poll_fn(|cx| {
|
||||||
try_ready!(sock2.read(&mut [0u8; 512]));
|
try_ready!(sock2.read(cx, &mut [0u8; 512]));
|
||||||
try_ready!(sock2.write(b"HTTP/1.1 222 OK\r\nContent-Length: 0\r\n\r\n"));
|
try_ready!(sock2.write(b"HTTP/1.1 222 OK\r\nContent-Length: 0\r\n\r\n"));
|
||||||
Ok(Async::Ready(()))
|
Ok(Async::Ready(()))
|
||||||
});
|
});
|
||||||
|
|
||||||
res2.join(srv2).wait().expect("res2");
|
block_on(res2.join(srv2)).expect("res2");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -68,7 +69,7 @@ fn conn_reset_after_write() {
|
|||||||
|
|
||||||
let client = Client::configure()
|
let client = Client::configure()
|
||||||
.connector(connector)
|
.connector(connector)
|
||||||
.executor(executor.sender().clone());
|
.build();
|
||||||
|
|
||||||
{
|
{
|
||||||
let req = Request::builder()
|
let req = Request::builder()
|
||||||
@@ -77,13 +78,13 @@ fn conn_reset_after_write() {
|
|||||||
.header("content-length", "0")
|
.header("content-length", "0")
|
||||||
.body(Default::default())
|
.body(Default::default())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let res1 = client.request(req);
|
let res1 = client.request(req).with_executor(executor.sender().clone());
|
||||||
let srv1 = poll_fn(|| {
|
let srv1 = poll_fn(|cx| {
|
||||||
try_ready!(sock1.read(&mut [0u8; 512]));
|
try_ready!(sock1.read(cx, &mut [0u8; 512]));
|
||||||
try_ready!(sock1.write(b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"));
|
try_ready!(sock1.write(b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"));
|
||||||
Ok(Async::Ready(()))
|
Ok(Async::Ready(()))
|
||||||
});
|
});
|
||||||
res1.join(srv1).wait().expect("res1");
|
block_on(res1.join(srv1)).expect("res1");
|
||||||
}
|
}
|
||||||
|
|
||||||
// sleep to allow some time for the connection to return to the pool
|
// sleep to allow some time for the connection to return to the pool
|
||||||
@@ -93,20 +94,20 @@ fn conn_reset_after_write() {
|
|||||||
.uri("http://mock.local/a")
|
.uri("http://mock.local/a")
|
||||||
.body(Default::default())
|
.body(Default::default())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let res2 = client.request(req);
|
let res2 = client.request(req).with_executor(executor.sender().clone());
|
||||||
let mut sock1 = Some(sock1);
|
let mut sock1 = Some(sock1);
|
||||||
let srv2 = poll_fn(|| {
|
let srv2 = poll_fn(|cx| {
|
||||||
// We purposefully keep the socket open until the client
|
// We purposefully keep the socket open until the client
|
||||||
// has written the second request, and THEN disconnect.
|
// has written the second request, and THEN disconnect.
|
||||||
//
|
//
|
||||||
// Not because we expect servers to be jerks, but to trigger
|
// Not because we expect servers to be jerks, but to trigger
|
||||||
// state where we write on an assumedly good connetion, and
|
// state where we write on an assumedly good connetion, and
|
||||||
// only reset the close AFTER we wrote bytes.
|
// only reset the close AFTER we wrote bytes.
|
||||||
try_ready!(sock1.as_mut().unwrap().read(&mut [0u8; 512]));
|
try_ready!(sock1.as_mut().unwrap().read(cx, &mut [0u8; 512]));
|
||||||
sock1.take();
|
sock1.take();
|
||||||
Ok(Async::Ready(()))
|
Ok(Async::Ready(()))
|
||||||
});
|
});
|
||||||
let err = res2.join(srv2).wait().expect_err("res2");
|
let err = block_on(res2.join(srv2)).expect_err("res2");
|
||||||
match err {
|
match err {
|
||||||
::Error::Incomplete => (),
|
::Error::Incomplete => (),
|
||||||
other => panic!("expected Incomplete, found {:?}", other)
|
other => panic!("expected Incomplete, found {:?}", other)
|
||||||
|
|||||||
@@ -1,2 +0,0 @@
|
|||||||
#[derive(Debug)]
|
|
||||||
pub enum Never {}
|
|
||||||
@@ -21,7 +21,8 @@ use self::Error::{
|
|||||||
Io,
|
Io,
|
||||||
TooLarge,
|
TooLarge,
|
||||||
Incomplete,
|
Incomplete,
|
||||||
Utf8
|
Utf8,
|
||||||
|
Executor
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Result type often returned from methods that can have hyper `Error`s.
|
/// Result type often returned from methods that can have hyper `Error`s.
|
||||||
@@ -56,6 +57,8 @@ pub enum Error {
|
|||||||
Io(IoError),
|
Io(IoError),
|
||||||
/// Parsing a field as string failed
|
/// Parsing a field as string failed
|
||||||
Utf8(Utf8Error),
|
Utf8(Utf8Error),
|
||||||
|
/// Executing a future failed
|
||||||
|
Executor,
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
__Nonexhaustive(Void)
|
__Nonexhaustive(Void)
|
||||||
@@ -127,6 +130,7 @@ impl StdError for Error {
|
|||||||
Cancel(ref e) => e.description(),
|
Cancel(ref e) => e.description(),
|
||||||
Io(ref e) => e.description(),
|
Io(ref e) => e.description(),
|
||||||
Utf8(ref e) => e.description(),
|
Utf8(ref e) => e.description(),
|
||||||
|
Executor => "executor is missing or failed to spawn",
|
||||||
Error::__Nonexhaustive(..) => unreachable!(),
|
Error::__Nonexhaustive(..) => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
17
src/executor.rs
Normal file
17
src/executor.rs
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
use futures::executor::Executor;
|
||||||
|
|
||||||
|
pub(crate) trait CloneBoxedExecutor: Executor + Send + Sync {
|
||||||
|
fn clone_boxed(&self) -> Box<CloneBoxedExecutor + Send + Sync>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: Executor + Clone + Send + Sync + 'static> CloneBoxedExecutor for E {
|
||||||
|
fn clone_boxed(&self) -> Box<CloneBoxedExecutor + Send + Sync> {
|
||||||
|
Box::new(self.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clone for Box<CloneBoxedExecutor> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
self.clone_boxed()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -129,7 +129,7 @@ fn eq_ascii(left: &str, right: &str) -> bool {
|
|||||||
// compiler says this trait is unused.
|
// compiler says this trait is unused.
|
||||||
//
|
//
|
||||||
// Once our minimum Rust compiler version is >=1.23, this can be removed.
|
// Once our minimum Rust compiler version is >=1.23, this can be removed.
|
||||||
#[allow(unused)]
|
#[allow(unused, deprecated)]
|
||||||
use std::ascii::AsciiExt;
|
use std::ascii::AsciiExt;
|
||||||
|
|
||||||
left.eq_ignore_ascii_case(right)
|
left.eq_ignore_ascii_case(right)
|
||||||
|
|||||||
@@ -18,7 +18,6 @@
|
|||||||
|
|
||||||
extern crate bytes;
|
extern crate bytes;
|
||||||
#[macro_use] extern crate futures;
|
#[macro_use] extern crate futures;
|
||||||
extern crate futures_cpupool;
|
|
||||||
extern crate futures_timer;
|
extern crate futures_timer;
|
||||||
extern crate http;
|
extern crate http;
|
||||||
extern crate httparse;
|
extern crate httparse;
|
||||||
@@ -28,8 +27,6 @@ extern crate net2;
|
|||||||
extern crate time;
|
extern crate time;
|
||||||
extern crate tokio;
|
extern crate tokio;
|
||||||
extern crate tokio_executor;
|
extern crate tokio_executor;
|
||||||
#[macro_use] extern crate tokio_io;
|
|
||||||
extern crate tokio_service;
|
|
||||||
extern crate want;
|
extern crate want;
|
||||||
|
|
||||||
#[cfg(all(test, feature = "nightly"))]
|
#[cfg(all(test, feature = "nightly"))]
|
||||||
@@ -49,7 +46,8 @@ pub use error::{Result, Error};
|
|||||||
pub use proto::{body, Body, Chunk};
|
pub use proto::{body, Body, Chunk};
|
||||||
pub use server::Server;
|
pub use server::Server;
|
||||||
|
|
||||||
mod common;
|
mod executor;
|
||||||
|
mod service;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod mock;
|
mod mock;
|
||||||
pub mod client;
|
pub mod client;
|
||||||
|
|||||||
191
src/mock.rs
191
src/mock.rs
@@ -3,10 +3,10 @@ use std::cmp;
|
|||||||
use std::io::{self, Read, Write};
|
use std::io::{self, Read, Write};
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
use bytes::Buf;
|
|
||||||
use futures::{Async, Poll};
|
use futures::{Async, Poll};
|
||||||
use futures::task::{self, Task};
|
use futures::task;
|
||||||
use tokio_io::{AsyncRead, AsyncWrite};
|
use futures::io::{AsyncRead, AsyncWrite};
|
||||||
|
use iovec::IoVec;
|
||||||
|
|
||||||
use ::client::connect::{Connect, Connected, Destination};
|
use ::client::connect::{Connect, Connected, Destination};
|
||||||
|
|
||||||
@@ -77,7 +77,7 @@ pub struct AsyncIo<T> {
|
|||||||
max_read_vecs: usize,
|
max_read_vecs: usize,
|
||||||
num_writes: usize,
|
num_writes: usize,
|
||||||
park_tasks: bool,
|
park_tasks: bool,
|
||||||
task: Option<Task>,
|
task: Option<task::Waker>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> AsyncIo<T> {
|
impl<T> AsyncIo<T> {
|
||||||
@@ -99,7 +99,7 @@ impl<T> AsyncIo<T> {
|
|||||||
self.bytes_until_block = bytes;
|
self.bytes_until_block = bytes;
|
||||||
|
|
||||||
if let Some(task) = self.task.take() {
|
if let Some(task) = self.task.take() {
|
||||||
task.notify();
|
task.wake();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,12 +130,12 @@ impl<T> AsyncIo<T> {
|
|||||||
self.num_writes
|
self.num_writes
|
||||||
}
|
}
|
||||||
|
|
||||||
fn would_block(&mut self) -> io::Error {
|
fn would_block<X, E>(&mut self, cx: &mut task::Context) -> Poll<X, E> {
|
||||||
self.blocked = true;
|
self.blocked = true;
|
||||||
if self.park_tasks {
|
if self.park_tasks {
|
||||||
self.task = Some(task::current());
|
self.task = Some(cx.waker().clone());
|
||||||
}
|
}
|
||||||
io::ErrorKind::WouldBlock.into()
|
Ok(Async::Pending)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -159,118 +159,101 @@ impl AsyncIo<MockCursor> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Read + Write> AsyncIo<T> {
|
|
||||||
fn write_no_vecs<B: Buf>(&mut self, buf: &mut B) -> Poll<usize, io::Error> {
|
|
||||||
if !buf.has_remaining() {
|
|
||||||
return Ok(Async::Ready(0));
|
|
||||||
}
|
|
||||||
|
|
||||||
let n = try_nb!(self.write(buf.bytes()));
|
|
||||||
buf.advance(n);
|
|
||||||
Ok(Async::Ready(n))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<S: AsRef<[u8]>, T: AsRef<[u8]>> PartialEq<S> for AsyncIo<T> {
|
impl<S: AsRef<[u8]>, T: AsRef<[u8]>> PartialEq<S> for AsyncIo<T> {
|
||||||
fn eq(&self, other: &S) -> bool {
|
fn eq(&self, other: &S) -> bool {
|
||||||
self.inner.as_ref() == other.as_ref()
|
self.inner.as_ref() == other.as_ref()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T: Read> AsyncRead for AsyncIo<T> {
|
||||||
impl<T: Read> Read for AsyncIo<T> {
|
fn poll_read(&mut self, cx: &mut task::Context, buf: &mut [u8]) -> Poll<usize, io::Error> {
|
||||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
|
||||||
self.blocked = false;
|
self.blocked = false;
|
||||||
if let Some(err) = self.error.take() {
|
if let Some(err) = self.error.take() {
|
||||||
Err(err)
|
Err(err)
|
||||||
} else if self.bytes_until_block == 0 {
|
} else if self.bytes_until_block == 0 {
|
||||||
Err(self.would_block())
|
self.would_block(cx)
|
||||||
} else {
|
} else {
|
||||||
let n = cmp::min(self.bytes_until_block, buf.len());
|
let n = cmp::min(self.bytes_until_block, buf.len());
|
||||||
let n = try!(self.inner.read(&mut buf[..n]));
|
let n = try!(self.inner.read(&mut buf[..n]));
|
||||||
self.bytes_until_block -= n;
|
self.bytes_until_block -= n;
|
||||||
Ok(n)
|
Ok(Async::Ready(n))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Write> Write for AsyncIo<T> {
|
impl<T: Read + Write> AsyncIo<T> {
|
||||||
fn write(&mut self, data: &[u8]) -> io::Result<usize> {
|
fn write_no_vecs(&mut self, cx: &mut task::Context, buf: &[u8]) -> Poll<usize, io::Error> {
|
||||||
|
if buf.len() == 0 {
|
||||||
|
return Ok(Async::Ready(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
self.poll_write(cx, buf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Read + Write> AsyncWrite for AsyncIo<T> {
|
||||||
|
fn poll_write(&mut self, cx: &mut task::Context, buf: &[u8]) -> Poll<usize, io::Error> {
|
||||||
self.num_writes += 1;
|
self.num_writes += 1;
|
||||||
if let Some(err) = self.error.take() {
|
if let Some(err) = self.error.take() {
|
||||||
trace!("AsyncIo::write error");
|
trace!("AsyncIo::write error");
|
||||||
Err(err)
|
Err(err)
|
||||||
} else if self.bytes_until_block == 0 {
|
} else if self.bytes_until_block == 0 {
|
||||||
trace!("AsyncIo::write would block");
|
trace!("AsyncIo::write would block");
|
||||||
Err(self.would_block())
|
self.would_block(cx)
|
||||||
} else {
|
} else {
|
||||||
trace!("AsyncIo::write; {} bytes", data.len());
|
trace!("AsyncIo::write; {} bytes", buf.len());
|
||||||
self.flushed = false;
|
self.flushed = false;
|
||||||
let n = cmp::min(self.bytes_until_block, data.len());
|
let n = cmp::min(self.bytes_until_block, buf.len());
|
||||||
let n = try!(self.inner.write(&data[..n]));
|
let n = try!(self.inner.write(&buf[..n]));
|
||||||
self.bytes_until_block -= n;
|
self.bytes_until_block -= n;
|
||||||
Ok(n)
|
Ok(Async::Ready(n))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn flush(&mut self) -> io::Result<()> {
|
fn poll_flush(&mut self, _cx: &mut task::Context) -> Poll<(), io::Error> {
|
||||||
self.flushed = true;
|
self.flushed = true;
|
||||||
self.inner.flush()
|
try!(self.inner.flush());
|
||||||
|
Ok(Async::Ready(()))
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Read + Write> AsyncRead for AsyncIo<T> {
|
fn poll_close(&mut self, _cx: &mut task::Context) -> Poll<(), io::Error> {
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Read + Write> AsyncWrite for AsyncIo<T> {
|
|
||||||
fn shutdown(&mut self) -> Poll<(), io::Error> {
|
|
||||||
Ok(().into())
|
Ok(().into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_buf<B: Buf>(&mut self, buf: &mut B) -> Poll<usize, io::Error> {
|
fn poll_vectored_write(&mut self, cx: &mut task::Context, vec: &[&IoVec]) -> Poll<usize, io::Error> {
|
||||||
if self.max_read_vecs == 0 {
|
if self.max_read_vecs == 0 {
|
||||||
return self.write_no_vecs(buf);
|
if let Some(ref first_iovec) = vec.get(0) {
|
||||||
|
return self.write_no_vecs(cx, &*first_iovec)
|
||||||
|
} else {
|
||||||
|
return Ok(Async::Ready(0));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
let r = {
|
|
||||||
static DUMMY: &[u8] = &[0];
|
let mut n = 0;
|
||||||
let mut bufs = [From::from(DUMMY); READ_VECS_CNT];
|
let mut ret = Ok(Async::Ready(0));
|
||||||
let i = Buf::bytes_vec(&buf, &mut bufs[..self.max_read_vecs]);
|
// each call to poll_write() will increase our count, but we assume
|
||||||
let mut n = 0;
|
// that if iovecs are used, its really only 1 write call.
|
||||||
let mut ret = Ok(0);
|
let num_writes = self.num_writes;
|
||||||
// each call to write() will increase our count, but we assume
|
for buf in vec {
|
||||||
// that if iovecs are used, its really only 1 write call.
|
match self.poll_write(cx, &buf) {
|
||||||
let num_writes = self.num_writes;
|
Ok(Async::Ready(num)) => {
|
||||||
for iovec in &bufs[..i] {
|
n += num;
|
||||||
match self.write(iovec) {
|
ret = Ok(Async::Ready(n));
|
||||||
Ok(num) => {
|
},
|
||||||
n += num;
|
Ok(Async::Pending) => {
|
||||||
ret = Ok(n);
|
if let Ok(Async::Ready(0)) = ret {
|
||||||
},
|
ret = Ok(Async::Pending);
|
||||||
Err(e) => {
|
|
||||||
if e.kind() == io::ErrorKind::WouldBlock {
|
|
||||||
if let Ok(0) = ret {
|
|
||||||
ret = Err(e);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ret = Err(e);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
},
|
||||||
|
Err(err) => {
|
||||||
|
ret = Err(err);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.num_writes = num_writes + 1;
|
|
||||||
ret
|
|
||||||
};
|
|
||||||
match r {
|
|
||||||
Ok(n) => {
|
|
||||||
Buf::advance(buf, n);
|
|
||||||
Ok(Async::Ready(n))
|
|
||||||
}
|
|
||||||
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
|
|
||||||
Ok(Async::NotReady)
|
|
||||||
}
|
|
||||||
Err(e) => Err(e),
|
|
||||||
}
|
}
|
||||||
|
self.num_writes = num_writes + 1;
|
||||||
|
ret
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -287,47 +270,33 @@ pub struct Duplex {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct DuplexInner {
|
struct DuplexInner {
|
||||||
handle_read_task: Option<Task>,
|
handle_read_task: Option<task::Waker>,
|
||||||
read: AsyncIo<MockCursor>,
|
read: AsyncIo<MockCursor>,
|
||||||
write: AsyncIo<MockCursor>,
|
write: AsyncIo<MockCursor>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Read for Duplex {
|
|
||||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
|
||||||
self.inner.lock().unwrap().read.read(buf)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Write for Duplex {
|
|
||||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
|
||||||
let mut inner = self.inner.lock().unwrap();
|
|
||||||
if let Some(task) = inner.handle_read_task.take() {
|
|
||||||
trace!("waking DuplexHandle read");
|
|
||||||
task.notify();
|
|
||||||
}
|
|
||||||
inner.write.write(buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn flush(&mut self) -> io::Result<()> {
|
|
||||||
self.inner.lock().unwrap().write.flush()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsyncRead for Duplex {
|
impl AsyncRead for Duplex {
|
||||||
|
fn poll_read(&mut self, cx: &mut task::Context, buf: &mut [u8]) -> Poll<usize, io::Error> {
|
||||||
|
self.inner.lock().unwrap().read.poll_read(cx, buf)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsyncWrite for Duplex {
|
impl AsyncWrite for Duplex {
|
||||||
fn shutdown(&mut self) -> Poll<(), io::Error> {
|
fn poll_write(&mut self, cx: &mut task::Context, buf: &[u8]) -> Poll<usize, io::Error> {
|
||||||
Ok(().into())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_buf<B: Buf>(&mut self, buf: &mut B) -> Poll<usize, io::Error> {
|
|
||||||
let mut inner = self.inner.lock().unwrap();
|
let mut inner = self.inner.lock().unwrap();
|
||||||
if let Some(task) = inner.handle_read_task.take() {
|
if let Some(task) = inner.handle_read_task.take() {
|
||||||
task.notify();
|
trace!("waking DuplexHandle read");
|
||||||
|
task.wake();
|
||||||
}
|
}
|
||||||
inner.write.write_buf(buf)
|
inner.write.poll_write(cx, buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn poll_flush(&mut self, cx: &mut task::Context) -> Poll<(), io::Error> {
|
||||||
|
self.inner.lock().unwrap().write.poll_flush(cx)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn poll_close(&mut self, _cx: &mut task::Context) -> Poll<(), io::Error> {
|
||||||
|
Ok(().into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -336,13 +305,13 @@ pub struct DuplexHandle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl DuplexHandle {
|
impl DuplexHandle {
|
||||||
pub fn read(&self, buf: &mut [u8]) -> Poll<usize, io::Error> {
|
pub fn read(&self, cx: &mut task::Context, buf: &mut [u8]) -> Poll<usize, io::Error> {
|
||||||
let mut inner = self.inner.lock().unwrap();
|
let mut inner = self.inner.lock().unwrap();
|
||||||
assert!(buf.len() >= inner.write.inner.len());
|
assert!(buf.len() >= inner.write.inner.len());
|
||||||
if inner.write.inner.is_empty() {
|
if inner.write.inner.is_empty() {
|
||||||
trace!("DuplexHandle read parking");
|
trace!("DuplexHandle read parking");
|
||||||
inner.handle_read_task = Some(task::current());
|
inner.handle_read_task = Some(cx.waker().clone());
|
||||||
return Ok(Async::NotReady);
|
return Ok(Async::Pending);
|
||||||
}
|
}
|
||||||
inner.write.inner.vec.truncate(0);
|
inner.write.inner.vec.truncate(0);
|
||||||
Ok(Async::Ready(inner.write.inner.len()))
|
Ok(Async::Ready(inner.write.inner.len()))
|
||||||
|
|||||||
@@ -3,8 +3,9 @@ use std::borrow::Cow;
|
|||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use futures::{Async, Future, Poll, Stream};
|
use futures::{Async, Future, Poll, Stream, StreamExt};
|
||||||
use futures::sync::{mpsc, oneshot};
|
use futures::task;
|
||||||
|
use futures::channel::{mpsc, oneshot};
|
||||||
use http::HeaderMap;
|
use http::HeaderMap;
|
||||||
|
|
||||||
use super::Chunk;
|
use super::Chunk;
|
||||||
@@ -24,14 +25,14 @@ pub trait Entity {
|
|||||||
///
|
///
|
||||||
/// Similar to `Stream::poll_next`, this yields `Some(Data)` until
|
/// Similar to `Stream::poll_next`, this yields `Some(Data)` until
|
||||||
/// the body ends, when it yields `None`.
|
/// the body ends, when it yields `None`.
|
||||||
fn poll_data(&mut self) -> Poll<Option<Self::Data>, Self::Error>;
|
fn poll_data(&mut self, cx: &mut task::Context) -> Poll<Option<Self::Data>, Self::Error>;
|
||||||
|
|
||||||
/// Poll for an optional **single** `HeaderMap` of trailers.
|
/// Poll for an optional **single** `HeaderMap` of trailers.
|
||||||
///
|
///
|
||||||
/// This should **only** be called after `poll_data` has ended.
|
/// This should **only** be called after `poll_data` has ended.
|
||||||
///
|
///
|
||||||
/// Note: Trailers aren't currently used for HTTP/1, only for HTTP/2.
|
/// Note: Trailers aren't currently used for HTTP/1, only for HTTP/2.
|
||||||
fn poll_trailers(&mut self) -> Poll<Option<HeaderMap>, Self::Error> {
|
fn poll_trailers(&mut self, _cx: &mut task::Context) -> Poll<Option<HeaderMap>, Self::Error> {
|
||||||
Ok(Async::Ready(None))
|
Ok(Async::Ready(None))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,12 +68,12 @@ impl<E: Entity> Entity for Box<E> {
|
|||||||
type Data = E::Data;
|
type Data = E::Data;
|
||||||
type Error = E::Error;
|
type Error = E::Error;
|
||||||
|
|
||||||
fn poll_data(&mut self) -> Poll<Option<Self::Data>, Self::Error> {
|
fn poll_data(&mut self, cx: &mut task::Context) -> Poll<Option<Self::Data>, Self::Error> {
|
||||||
(**self).poll_data()
|
(**self).poll_data(cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn poll_trailers(&mut self) -> Poll<Option<HeaderMap>, Self::Error> {
|
fn poll_trailers(&mut self, cx: &mut task::Context) -> Poll<Option<HeaderMap>, Self::Error> {
|
||||||
(**self).poll_trailers()
|
(**self).poll_trailers(cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_end_stream(&self) -> bool {
|
fn is_end_stream(&self) -> bool {
|
||||||
@@ -96,10 +97,10 @@ impl<E: Entity> Stream for EntityStream<E> {
|
|||||||
type Item = E::Data;
|
type Item = E::Data;
|
||||||
type Error = E::Error;
|
type Error = E::Error;
|
||||||
|
|
||||||
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
|
fn poll_next(&mut self, cx: &mut task::Context) -> Poll<Option<Self::Item>, Self::Error> {
|
||||||
loop {
|
loop {
|
||||||
if self.is_data_eof {
|
if self.is_data_eof {
|
||||||
return self.entity.poll_trailers()
|
return self.entity.poll_trailers(cx)
|
||||||
.map(|async| {
|
.map(|async| {
|
||||||
async.map(|_opt| {
|
async.map(|_opt| {
|
||||||
// drop the trailers and return that Stream is done
|
// drop the trailers and return that Stream is done
|
||||||
@@ -108,7 +109,7 @@ impl<E: Entity> Stream for EntityStream<E> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let opt = try_ready!(self.entity.poll_data());
|
let opt = try_ready!(self.entity.poll_data(cx));
|
||||||
if let Some(data) = opt {
|
if let Some(data) = opt {
|
||||||
return Ok(Async::Ready(Some(data)));
|
return Ok(Async::Ready(Some(data)));
|
||||||
} else {
|
} else {
|
||||||
@@ -211,14 +212,14 @@ impl Body {
|
|||||||
/// ```
|
/// ```
|
||||||
/// # extern crate futures;
|
/// # extern crate futures;
|
||||||
/// # extern crate hyper;
|
/// # extern crate hyper;
|
||||||
/// # use futures::{Future, Stream};
|
/// # use futures::{FutureExt, StreamExt};
|
||||||
/// # use hyper::{Body, Request};
|
/// # use hyper::{Body, Request};
|
||||||
/// # fn request_concat(some_req: Request<Body>) {
|
/// # fn request_concat(some_req: Request<Body>) {
|
||||||
/// let req: Request<Body> = some_req;
|
/// let req: Request<Body> = some_req;
|
||||||
/// let body = req.into_body();
|
/// let body = req.into_body();
|
||||||
///
|
///
|
||||||
/// let stream = body.into_stream();
|
/// let stream = body.into_stream();
|
||||||
/// stream.concat2()
|
/// stream.concat()
|
||||||
/// .map(|buf| {
|
/// .map(|buf| {
|
||||||
/// println!("body length: {}", buf.len());
|
/// println!("body length: {}", buf.len());
|
||||||
/// });
|
/// });
|
||||||
@@ -267,15 +268,15 @@ impl Entity for Body {
|
|||||||
type Data = Chunk;
|
type Data = Chunk;
|
||||||
type Error = ::Error;
|
type Error = ::Error;
|
||||||
|
|
||||||
fn poll_data(&mut self) -> Poll<Option<Self::Data>, Self::Error> {
|
fn poll_data(&mut self, cx: &mut task::Context) -> Poll<Option<Self::Data>, Self::Error> {
|
||||||
match self.kind {
|
match self.kind {
|
||||||
Kind::Chan { ref mut rx, .. } => match rx.poll().expect("mpsc cannot error") {
|
Kind::Chan { ref mut rx, .. } => match rx.poll_next(cx).expect("mpsc cannot error") {
|
||||||
Async::Ready(Some(Ok(chunk))) => Ok(Async::Ready(Some(chunk))),
|
Async::Ready(Some(Ok(chunk))) => Ok(Async::Ready(Some(chunk))),
|
||||||
Async::Ready(Some(Err(err))) => Err(err),
|
Async::Ready(Some(Err(err))) => Err(err),
|
||||||
Async::Ready(None) => Ok(Async::Ready(None)),
|
Async::Ready(None) => Ok(Async::Ready(None)),
|
||||||
Async::NotReady => Ok(Async::NotReady),
|
Async::Pending => Ok(Async::Pending),
|
||||||
},
|
},
|
||||||
Kind::Wrapped(ref mut s) => s.poll(),
|
Kind::Wrapped(ref mut s) => s.poll_next(cx),
|
||||||
Kind::Once(ref mut val) => Ok(Async::Ready(val.take())),
|
Kind::Once(ref mut val) => Ok(Async::Ready(val.take())),
|
||||||
Kind::Empty => Ok(Async::Ready(None)),
|
Kind::Empty => Ok(Async::Ready(None)),
|
||||||
}
|
}
|
||||||
@@ -310,13 +311,13 @@ impl fmt::Debug for Body {
|
|||||||
|
|
||||||
impl Sender {
|
impl Sender {
|
||||||
/// Check to see if this `Sender` can send more data.
|
/// Check to see if this `Sender` can send more data.
|
||||||
pub fn poll_ready(&mut self) -> Poll<(), ()> {
|
pub fn poll_ready(&mut self, cx: &mut task::Context) -> Poll<(), ()> {
|
||||||
match self.close_rx.poll() {
|
match self.close_rx.poll(cx) {
|
||||||
Ok(Async::Ready(())) | Err(_) => return Err(()),
|
Ok(Async::Ready(())) | Err(_) => return Err(()),
|
||||||
Ok(Async::NotReady) => (),
|
Ok(Async::Pending) => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
self.tx.poll_ready().map_err(|_| ())
|
self.tx.poll_ready(cx).map_err(|_| ())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sends data on this channel.
|
/// Sends data on this channel.
|
||||||
@@ -413,13 +414,11 @@ fn _assert_send_sync() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_body_stream_concat() {
|
fn test_body_stream_concat() {
|
||||||
use futures::{Stream, Future};
|
use futures::{StreamExt};
|
||||||
|
|
||||||
let body = Body::from("hello world");
|
let body = Body::from("hello world");
|
||||||
|
|
||||||
let total = body.into_stream()
|
let total = ::futures::executor::block_on(body.into_stream().concat())
|
||||||
.concat2()
|
|
||||||
.wait()
|
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(total.as_ref(), b"hello world");
|
assert_eq!(total.as_ref(), b"hello world");
|
||||||
|
|
||||||
|
|||||||
@@ -3,10 +3,10 @@ use std::io::{self};
|
|||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use futures::{Async, AsyncSink, Poll, StartSend};
|
use futures::{Async, Poll};
|
||||||
use futures::task::Task;
|
use futures::task;
|
||||||
|
use futures::io::{AsyncRead, AsyncWrite};
|
||||||
use http::{Method, Version};
|
use http::{Method, Version};
|
||||||
use tokio_io::{AsyncRead, AsyncWrite};
|
|
||||||
|
|
||||||
use proto::{BodyLength, Chunk, Decode, Http1Transaction, MessageHead};
|
use proto::{BodyLength, Chunk, Decode, Http1Transaction, MessageHead};
|
||||||
use super::io::{Cursor, Buffered};
|
use super::io::{Cursor, Buffered};
|
||||||
@@ -113,14 +113,14 @@ where I: AsyncRead + AsyncWrite,
|
|||||||
T::should_error_on_parse_eof() && !self.state.is_idle()
|
T::should_error_on_parse_eof() && !self.state.is_idle()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_head(&mut self) -> Poll<Option<(MessageHead<T::Incoming>, bool)>, ::Error> {
|
pub fn read_head(&mut self, cx: &mut task::Context) -> Poll<Option<(MessageHead<T::Incoming>, bool)>, ::Error> {
|
||||||
debug_assert!(self.can_read_head());
|
debug_assert!(self.can_read_head());
|
||||||
trace!("Conn::read_head");
|
trace!("Conn::read_head");
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let (version, head) = match self.io.parse::<T>() {
|
let (version, head) = match self.io.parse::<T>(cx) {
|
||||||
Ok(Async::Ready(head)) => (head.version, head),
|
Ok(Async::Ready(head)) => (head.version, head),
|
||||||
Ok(Async::NotReady) => return Ok(Async::NotReady),
|
Ok(Async::Pending) => return Ok(Async::Pending),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
// If we are currently waiting on a message, then an empty
|
// If we are currently waiting on a message, then an empty
|
||||||
// message should be reported as an error. If not, it is just
|
// message should be reported as an error. If not, it is just
|
||||||
@@ -132,7 +132,7 @@ where I: AsyncRead + AsyncWrite,
|
|||||||
return if was_mid_parse || must_error {
|
return if was_mid_parse || must_error {
|
||||||
debug!("parse error ({}) with {} bytes", e, self.io.read_buf().len());
|
debug!("parse error ({}) with {} bytes", e, self.io.read_buf().len());
|
||||||
self.on_parse_error(e)
|
self.on_parse_error(e)
|
||||||
.map(|()| Async::NotReady)
|
.map(|()| Async::Pending)
|
||||||
} else {
|
} else {
|
||||||
debug!("read eof");
|
debug!("read eof");
|
||||||
Ok(Async::Ready(None))
|
Ok(Async::Ready(None))
|
||||||
@@ -169,7 +169,7 @@ where I: AsyncRead + AsyncWrite,
|
|||||||
debug!("decoder error = {:?}", e);
|
debug!("decoder error = {:?}", e);
|
||||||
self.state.close_read();
|
self.state.close_read();
|
||||||
return self.on_parse_error(e)
|
return self.on_parse_error(e)
|
||||||
.map(|()| Async::NotReady);
|
.map(|()| Async::Pending);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -193,20 +193,20 @@ where I: AsyncRead + AsyncWrite,
|
|||||||
self.state.reading = reading;
|
self.state.reading = reading;
|
||||||
}
|
}
|
||||||
if !body {
|
if !body {
|
||||||
self.try_keep_alive();
|
self.try_keep_alive(cx);
|
||||||
}
|
}
|
||||||
return Ok(Async::Ready(Some((head, body))));
|
return Ok(Async::Ready(Some((head, body))));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_body(&mut self) -> Poll<Option<Chunk>, io::Error> {
|
pub fn read_body(&mut self, cx: &mut task::Context) -> Poll<Option<Chunk>, io::Error> {
|
||||||
debug_assert!(self.can_read_body());
|
debug_assert!(self.can_read_body());
|
||||||
|
|
||||||
trace!("Conn::read_body");
|
trace!("Conn::read_body");
|
||||||
|
|
||||||
let (reading, ret) = match self.state.reading {
|
let (reading, ret) = match self.state.reading {
|
||||||
Reading::Body(ref mut decoder) => {
|
Reading::Body(ref mut decoder) => {
|
||||||
match decoder.decode(&mut self.io) {
|
match decoder.decode(&mut self.io, cx) {
|
||||||
Ok(Async::Ready(slice)) => {
|
Ok(Async::Ready(slice)) => {
|
||||||
let (reading, chunk) = if !slice.is_empty() {
|
let (reading, chunk) = if !slice.is_empty() {
|
||||||
return Ok(Async::Ready(Some(Chunk::from(slice))));
|
return Ok(Async::Ready(Some(Chunk::from(slice))));
|
||||||
@@ -222,7 +222,7 @@ where I: AsyncRead + AsyncWrite,
|
|||||||
};
|
};
|
||||||
(reading, Ok(Async::Ready(chunk)))
|
(reading, Ok(Async::Ready(chunk)))
|
||||||
},
|
},
|
||||||
Ok(Async::NotReady) => return Ok(Async::NotReady),
|
Ok(Async::Pending) => return Ok(Async::Pending),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
trace!("decode stream error: {}", e);
|
trace!("decode stream error: {}", e);
|
||||||
(Reading::Closed, Err(e))
|
(Reading::Closed, Err(e))
|
||||||
@@ -233,19 +233,19 @@ where I: AsyncRead + AsyncWrite,
|
|||||||
};
|
};
|
||||||
|
|
||||||
self.state.reading = reading;
|
self.state.reading = reading;
|
||||||
self.try_keep_alive();
|
self.try_keep_alive(cx);
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_keep_alive(&mut self) -> Result<(), ::Error> {
|
pub fn read_keep_alive(&mut self, cx: &mut task::Context) -> Result<(), ::Error> {
|
||||||
debug_assert!(!self.can_read_head() && !self.can_read_body());
|
debug_assert!(!self.can_read_head() && !self.can_read_body());
|
||||||
|
|
||||||
trace!("read_keep_alive; is_mid_message={}", self.is_mid_message());
|
trace!("read_keep_alive; is_mid_message={}", self.is_mid_message());
|
||||||
|
|
||||||
if self.is_mid_message() {
|
if self.is_mid_message() {
|
||||||
self.maybe_park_read();
|
self.maybe_park_read(cx);
|
||||||
} else {
|
} else {
|
||||||
self.require_empty_read()?;
|
self.require_empty_read(cx)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -257,18 +257,19 @@ where I: AsyncRead + AsyncWrite,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn maybe_park_read(&mut self) {
|
fn maybe_park_read(&mut self, cx: &mut task::Context) {
|
||||||
if !self.io.is_read_blocked() {
|
if !self.io.is_read_blocked() {
|
||||||
// the Io object is ready to read, which means it will never alert
|
// the Io object is ready to read, which means it will never alert
|
||||||
// us that it is ready until we drain it. However, we're currently
|
// us that it is ready until we drain it. However, we're currently
|
||||||
// finished reading, so we need to park the task to be able to
|
// finished reading, so we need to park the task to be able to
|
||||||
// wake back up later when more reading should happen.
|
// wake back up later when more reading should happen.
|
||||||
|
let current_waker = cx.waker();
|
||||||
let park = self.state.read_task.as_ref()
|
let park = self.state.read_task.as_ref()
|
||||||
.map(|t| !t.will_notify_current())
|
.map(|t| !t.will_wake(current_waker))
|
||||||
.unwrap_or(true);
|
.unwrap_or(true);
|
||||||
if park {
|
if park {
|
||||||
trace!("parking current task");
|
trace!("parking current task");
|
||||||
self.state.read_task = Some(::futures::task::current());
|
self.state.read_task = Some(current_waker.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -277,14 +278,14 @@ where I: AsyncRead + AsyncWrite,
|
|||||||
//
|
//
|
||||||
// This should only be called for Clients wanting to enter the idle
|
// This should only be called for Clients wanting to enter the idle
|
||||||
// state.
|
// state.
|
||||||
fn require_empty_read(&mut self) -> io::Result<()> {
|
fn require_empty_read(&mut self, cx: &mut task::Context) -> io::Result<()> {
|
||||||
assert!(!self.can_read_head() && !self.can_read_body());
|
assert!(!self.can_read_head() && !self.can_read_body());
|
||||||
|
|
||||||
if !self.io.read_buf().is_empty() {
|
if !self.io.read_buf().is_empty() {
|
||||||
debug!("received an unexpected {} bytes", self.io.read_buf().len());
|
debug!("received an unexpected {} bytes", self.io.read_buf().len());
|
||||||
Err(io::Error::new(io::ErrorKind::InvalidData, "unexpected bytes after message ended"))
|
Err(io::Error::new(io::ErrorKind::InvalidData, "unexpected bytes after message ended"))
|
||||||
} else {
|
} else {
|
||||||
match self.try_io_read()? {
|
match self.try_io_read(cx)? {
|
||||||
Async::Ready(0) => {
|
Async::Ready(0) => {
|
||||||
// case handled in try_io_read
|
// case handled in try_io_read
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -298,15 +299,15 @@ where I: AsyncRead + AsyncWrite,
|
|||||||
};
|
};
|
||||||
Err(io::Error::new(io::ErrorKind::InvalidData, desc))
|
Err(io::Error::new(io::ErrorKind::InvalidData, desc))
|
||||||
},
|
},
|
||||||
Async::NotReady => {
|
Async::Pending => {
|
||||||
Ok(())
|
Ok(())
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn try_io_read(&mut self) -> Poll<usize, io::Error> {
|
fn try_io_read(&mut self, cx: &mut task::Context) -> Poll<usize, io::Error> {
|
||||||
match self.io.read_from_io() {
|
match self.io.read_from_io(cx) {
|
||||||
Ok(Async::Ready(0)) => {
|
Ok(Async::Ready(0)) => {
|
||||||
trace!("try_io_read; found EOF on connection: {:?}", self.state);
|
trace!("try_io_read; found EOF on connection: {:?}", self.state);
|
||||||
let must_error = self.should_error_on_eof();
|
let must_error = self.should_error_on_eof();
|
||||||
@@ -328,8 +329,8 @@ where I: AsyncRead + AsyncWrite,
|
|||||||
Ok(Async::Ready(n)) => {
|
Ok(Async::Ready(n)) => {
|
||||||
Ok(Async::Ready(n))
|
Ok(Async::Ready(n))
|
||||||
},
|
},
|
||||||
Ok(Async::NotReady) => {
|
Ok(Async::Pending) => {
|
||||||
Ok(Async::NotReady)
|
Ok(Async::Pending)
|
||||||
},
|
},
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
self.state.close();
|
self.state.close();
|
||||||
@@ -339,8 +340,8 @@ where I: AsyncRead + AsyncWrite,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn maybe_notify(&mut self) {
|
fn maybe_notify(&mut self, cx: &mut task::Context) {
|
||||||
// its possible that we returned NotReady from poll() without having
|
// its possible that we returned Pending from poll() without having
|
||||||
// exhausted the underlying Io. We would have done this when we
|
// exhausted the underlying Io. We would have done this when we
|
||||||
// determined we couldn't keep reading until we knew how writing
|
// determined we couldn't keep reading until we knew how writing
|
||||||
// would finish.
|
// would finish.
|
||||||
@@ -366,9 +367,9 @@ where I: AsyncRead + AsyncWrite,
|
|||||||
|
|
||||||
if !self.io.is_read_blocked() {
|
if !self.io.is_read_blocked() {
|
||||||
if wants_read && self.io.read_buf().is_empty() {
|
if wants_read && self.io.read_buf().is_empty() {
|
||||||
match self.io.read_from_io() {
|
match self.io.read_from_io(cx) {
|
||||||
Ok(Async::Ready(_)) => (),
|
Ok(Async::Ready(_)) => (),
|
||||||
Ok(Async::NotReady) => {
|
Ok(Async::Pending) => {
|
||||||
trace!("maybe_notify; read_from_io blocked");
|
trace!("maybe_notify; read_from_io blocked");
|
||||||
return
|
return
|
||||||
},
|
},
|
||||||
@@ -380,16 +381,16 @@ where I: AsyncRead + AsyncWrite,
|
|||||||
}
|
}
|
||||||
if let Some(ref task) = self.state.read_task {
|
if let Some(ref task) = self.state.read_task {
|
||||||
trace!("maybe_notify; notifying task");
|
trace!("maybe_notify; notifying task");
|
||||||
task.notify();
|
task.wake();
|
||||||
} else {
|
} else {
|
||||||
trace!("maybe_notify; no task to notify");
|
trace!("maybe_notify; no task to notify");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn try_keep_alive(&mut self) {
|
fn try_keep_alive(&mut self, cx: &mut task::Context) {
|
||||||
self.state.try_keep_alive();
|
self.state.try_keep_alive();
|
||||||
self.maybe_notify();
|
self.maybe_notify(cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn can_write_head(&self) -> bool {
|
pub fn can_write_head(&self) -> bool {
|
||||||
@@ -475,17 +476,15 @@ where I: AsyncRead + AsyncWrite,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_body(&mut self, chunk: Option<B>) -> StartSend<Option<B>, io::Error> {
|
pub fn write_body(&mut self, _cx: &mut task::Context, chunk: Option<B>) -> Poll<(), io::Error> {
|
||||||
debug_assert!(self.can_write_body());
|
debug_assert!(self.can_write_body());
|
||||||
|
|
||||||
if !self.can_buffer_body() {
|
if !self.can_buffer_body() {
|
||||||
if let Async::NotReady = self.flush()? {
|
// if chunk is Some(&[]), aka empty, whatever, just skip it
|
||||||
// if chunk is Some(&[]), aka empty, whatever, just skip it
|
if chunk.as_ref().map(|c| c.as_ref().is_empty()).unwrap_or(true) {
|
||||||
if chunk.as_ref().map(|c| c.as_ref().is_empty()).unwrap_or(false) {
|
return Ok(Async::Ready(()));
|
||||||
return Ok(AsyncSink::Ready);
|
} else {
|
||||||
} else {
|
return Err(io::Error::new(io::ErrorKind::Other, "tried to write chunk when body can't buffer"));
|
||||||
return Ok(AsyncSink::NotReady(chunk));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -493,7 +492,7 @@ where I: AsyncRead + AsyncWrite,
|
|||||||
Writing::Body(ref mut encoder) => {
|
Writing::Body(ref mut encoder) => {
|
||||||
if let Some(chunk) = chunk {
|
if let Some(chunk) = chunk {
|
||||||
if chunk.as_ref().is_empty() {
|
if chunk.as_ref().is_empty() {
|
||||||
return Ok(AsyncSink::Ready);
|
return Ok(Async::Ready(()));
|
||||||
}
|
}
|
||||||
|
|
||||||
let encoded = encoder.encode(Cursor::new(chunk));
|
let encoded = encoder.encode(Cursor::new(chunk));
|
||||||
@@ -506,7 +505,7 @@ where I: AsyncRead + AsyncWrite,
|
|||||||
Writing::KeepAlive
|
Writing::KeepAlive
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return Ok(AsyncSink::Ready);
|
return Ok(Async::Ready(()));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// end of stream, that means we should try to eof
|
// end of stream, that means we should try to eof
|
||||||
@@ -529,7 +528,7 @@ where I: AsyncRead + AsyncWrite,
|
|||||||
};
|
};
|
||||||
|
|
||||||
self.state.writing = state;
|
self.state.writing = state;
|
||||||
Ok(AsyncSink::Ready)
|
Ok(Async::Ready(()))
|
||||||
}
|
}
|
||||||
|
|
||||||
// When we get a parse error, depending on what side we are, we might be able
|
// When we get a parse error, depending on what side we are, we might be able
|
||||||
@@ -553,16 +552,16 @@ where I: AsyncRead + AsyncWrite,
|
|||||||
Err(err)
|
Err(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn flush(&mut self) -> Poll<(), io::Error> {
|
pub fn flush(&mut self, cx: &mut task::Context) -> Poll<(), io::Error> {
|
||||||
try_ready!(self.io.flush());
|
try_ready!(self.io.flush(cx));
|
||||||
self.try_keep_alive();
|
self.try_keep_alive(cx);
|
||||||
trace!("flushed {:?}", self.state);
|
trace!("flushed {:?}", self.state);
|
||||||
Ok(Async::Ready(()))
|
Ok(Async::Ready(()))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn shutdown(&mut self) -> Poll<(), io::Error> {
|
pub fn shutdown(&mut self, cx: &mut task::Context) -> Poll<(), io::Error> {
|
||||||
match self.io.io_mut().shutdown() {
|
match self.io.io_mut().poll_close(cx) {
|
||||||
Ok(Async::NotReady) => Ok(Async::NotReady),
|
Ok(Async::Pending) => Ok(Async::Pending),
|
||||||
Ok(Async::Ready(())) => {
|
Ok(Async::Ready(())) => {
|
||||||
trace!("shut down IO");
|
trace!("shut down IO");
|
||||||
Ok(Async::Ready(()))
|
Ok(Async::Ready(()))
|
||||||
@@ -612,7 +611,7 @@ struct State {
|
|||||||
error: Option<::Error>,
|
error: Option<::Error>,
|
||||||
keep_alive: KA,
|
keep_alive: KA,
|
||||||
method: Option<Method>,
|
method: Option<Method>,
|
||||||
read_task: Option<Task>,
|
read_task: Option<task::Waker>,
|
||||||
reading: Reading,
|
reading: Reading,
|
||||||
writing: Writing,
|
writing: Writing,
|
||||||
version: Version,
|
version: Version,
|
||||||
@@ -964,7 +963,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
match conn.poll() {
|
match conn.poll() {
|
||||||
Ok(Async::NotReady) => (),
|
Ok(Async::Pending) => (),
|
||||||
other => panic!("unexpected frame: {:?}", other)
|
other => panic!("unexpected frame: {:?}", other)
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ use std::usize;
|
|||||||
use std::io;
|
use std::io;
|
||||||
|
|
||||||
use futures::{Async, Poll};
|
use futures::{Async, Poll};
|
||||||
|
use futures::task;
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
|
|
||||||
use super::io::MemRead;
|
use super::io::MemRead;
|
||||||
@@ -84,7 +85,7 @@ impl Decoder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn decode<R: MemRead>(&mut self, body: &mut R) -> Poll<Bytes, io::Error> {
|
pub fn decode<R: MemRead>(&mut self, body: &mut R, cx: &mut task::Context) -> Poll<Bytes, io::Error> {
|
||||||
trace!("decode; state={:?}", self.kind);
|
trace!("decode; state={:?}", self.kind);
|
||||||
match self.kind {
|
match self.kind {
|
||||||
Length(ref mut remaining) => {
|
Length(ref mut remaining) => {
|
||||||
@@ -92,7 +93,7 @@ impl Decoder {
|
|||||||
Ok(Async::Ready(Bytes::new()))
|
Ok(Async::Ready(Bytes::new()))
|
||||||
} else {
|
} else {
|
||||||
let to_read = *remaining as usize;
|
let to_read = *remaining as usize;
|
||||||
let buf = try_ready!(body.read_mem(to_read));
|
let buf = try_ready!(body.read_mem(cx, to_read));
|
||||||
let num = buf.as_ref().len() as u64;
|
let num = buf.as_ref().len() as u64;
|
||||||
if num > *remaining {
|
if num > *remaining {
|
||||||
*remaining = 0;
|
*remaining = 0;
|
||||||
@@ -108,7 +109,7 @@ impl Decoder {
|
|||||||
loop {
|
loop {
|
||||||
let mut buf = None;
|
let mut buf = None;
|
||||||
// advances the chunked state
|
// advances the chunked state
|
||||||
*state = try_ready!(state.step(body, size, &mut buf));
|
*state = try_ready!(state.step(body, cx, size, &mut buf));
|
||||||
if *state == ChunkedState::End {
|
if *state == ChunkedState::End {
|
||||||
trace!("end of chunked");
|
trace!("end of chunked");
|
||||||
return Ok(Async::Ready(Bytes::new()));
|
return Ok(Async::Ready(Bytes::new()));
|
||||||
@@ -125,7 +126,7 @@ impl Decoder {
|
|||||||
// 8192 chosen because its about 2 packets, there probably
|
// 8192 chosen because its about 2 packets, there probably
|
||||||
// won't be that much available, so don't have MemReaders
|
// won't be that much available, so don't have MemReaders
|
||||||
// allocate buffers to big
|
// allocate buffers to big
|
||||||
let slice = try_ready!(body.read_mem(8192));
|
let slice = try_ready!(body.read_mem(cx, 8192));
|
||||||
*is_eof = slice.is_empty();
|
*is_eof = slice.is_empty();
|
||||||
Ok(Async::Ready(slice))
|
Ok(Async::Ready(slice))
|
||||||
}
|
}
|
||||||
@@ -152,8 +153,8 @@ impl fmt::Display for Decoder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! byte (
|
macro_rules! byte (
|
||||||
($rdr:ident) => ({
|
($rdr:ident, $cx:ident) => ({
|
||||||
let buf = try_ready!($rdr.read_mem(1));
|
let buf = try_ready!($rdr.read_mem($cx, 1));
|
||||||
if !buf.is_empty() {
|
if !buf.is_empty() {
|
||||||
buf[0]
|
buf[0]
|
||||||
} else {
|
} else {
|
||||||
@@ -166,27 +167,28 @@ macro_rules! byte (
|
|||||||
impl ChunkedState {
|
impl ChunkedState {
|
||||||
fn step<R: MemRead>(&self,
|
fn step<R: MemRead>(&self,
|
||||||
body: &mut R,
|
body: &mut R,
|
||||||
|
cx: &mut task::Context,
|
||||||
size: &mut u64,
|
size: &mut u64,
|
||||||
buf: &mut Option<Bytes>)
|
buf: &mut Option<Bytes>)
|
||||||
-> Poll<ChunkedState, io::Error> {
|
-> Poll<ChunkedState, io::Error> {
|
||||||
use self::ChunkedState::*;
|
use self::ChunkedState::*;
|
||||||
match *self {
|
match *self {
|
||||||
Size => ChunkedState::read_size(body, size),
|
Size => ChunkedState::read_size(body, cx, size),
|
||||||
SizeLws => ChunkedState::read_size_lws(body),
|
SizeLws => ChunkedState::read_size_lws(body, cx),
|
||||||
Extension => ChunkedState::read_extension(body),
|
Extension => ChunkedState::read_extension(body, cx),
|
||||||
SizeLf => ChunkedState::read_size_lf(body, *size),
|
SizeLf => ChunkedState::read_size_lf(body, cx, *size),
|
||||||
Body => ChunkedState::read_body(body, size, buf),
|
Body => ChunkedState::read_body(body, cx, size, buf),
|
||||||
BodyCr => ChunkedState::read_body_cr(body),
|
BodyCr => ChunkedState::read_body_cr(body, cx),
|
||||||
BodyLf => ChunkedState::read_body_lf(body),
|
BodyLf => ChunkedState::read_body_lf(body, cx),
|
||||||
EndCr => ChunkedState::read_end_cr(body),
|
EndCr => ChunkedState::read_end_cr(body, cx),
|
||||||
EndLf => ChunkedState::read_end_lf(body),
|
EndLf => ChunkedState::read_end_lf(body, cx),
|
||||||
End => Ok(Async::Ready(ChunkedState::End)),
|
End => Ok(Async::Ready(ChunkedState::End)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn read_size<R: MemRead>(rdr: &mut R, size: &mut u64) -> Poll<ChunkedState, io::Error> {
|
fn read_size<R: MemRead>(rdr: &mut R, cx: &mut task::Context, size: &mut u64) -> Poll<ChunkedState, io::Error> {
|
||||||
trace!("Read chunk hex size");
|
trace!("Read chunk hex size");
|
||||||
let radix = 16;
|
let radix = 16;
|
||||||
match byte!(rdr) {
|
match byte!(rdr, cx) {
|
||||||
b @ b'0'...b'9' => {
|
b @ b'0'...b'9' => {
|
||||||
*size *= radix;
|
*size *= radix;
|
||||||
*size += (b - b'0') as u64;
|
*size += (b - b'0') as u64;
|
||||||
@@ -209,9 +211,9 @@ impl ChunkedState {
|
|||||||
}
|
}
|
||||||
Ok(Async::Ready(ChunkedState::Size))
|
Ok(Async::Ready(ChunkedState::Size))
|
||||||
}
|
}
|
||||||
fn read_size_lws<R: MemRead>(rdr: &mut R) -> Poll<ChunkedState, io::Error> {
|
fn read_size_lws<R: MemRead>(rdr: &mut R, cx: &mut task::Context) -> Poll<ChunkedState, io::Error> {
|
||||||
trace!("read_size_lws");
|
trace!("read_size_lws");
|
||||||
match byte!(rdr) {
|
match byte!(rdr, cx) {
|
||||||
// LWS can follow the chunk size, but no more digits can come
|
// LWS can follow the chunk size, but no more digits can come
|
||||||
b'\t' | b' ' => Ok(Async::Ready(ChunkedState::SizeLws)),
|
b'\t' | b' ' => Ok(Async::Ready(ChunkedState::SizeLws)),
|
||||||
b';' => Ok(Async::Ready(ChunkedState::Extension)),
|
b';' => Ok(Async::Ready(ChunkedState::Extension)),
|
||||||
@@ -222,16 +224,16 @@ impl ChunkedState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn read_extension<R: MemRead>(rdr: &mut R) -> Poll<ChunkedState, io::Error> {
|
fn read_extension<R: MemRead>(rdr: &mut R, cx: &mut task::Context) -> Poll<ChunkedState, io::Error> {
|
||||||
trace!("read_extension");
|
trace!("read_extension");
|
||||||
match byte!(rdr) {
|
match byte!(rdr, cx) {
|
||||||
b'\r' => Ok(Async::Ready(ChunkedState::SizeLf)),
|
b'\r' => Ok(Async::Ready(ChunkedState::SizeLf)),
|
||||||
_ => Ok(Async::Ready(ChunkedState::Extension)), // no supported extensions
|
_ => Ok(Async::Ready(ChunkedState::Extension)), // no supported extensions
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn read_size_lf<R: MemRead>(rdr: &mut R, size: u64) -> Poll<ChunkedState, io::Error> {
|
fn read_size_lf<R: MemRead>(rdr: &mut R, cx: &mut task::Context, size: u64) -> Poll<ChunkedState, io::Error> {
|
||||||
trace!("Chunk size is {:?}", size);
|
trace!("Chunk size is {:?}", size);
|
||||||
match byte!(rdr) {
|
match byte!(rdr, cx) {
|
||||||
b'\n' => {
|
b'\n' => {
|
||||||
if size == 0 {
|
if size == 0 {
|
||||||
Ok(Async::Ready(ChunkedState::EndCr))
|
Ok(Async::Ready(ChunkedState::EndCr))
|
||||||
@@ -244,7 +246,7 @@ impl ChunkedState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_body<R: MemRead>(rdr: &mut R,
|
fn read_body<R: MemRead>(rdr: &mut R, cx: &mut task::Context,
|
||||||
rem: &mut u64,
|
rem: &mut u64,
|
||||||
buf: &mut Option<Bytes>)
|
buf: &mut Option<Bytes>)
|
||||||
-> Poll<ChunkedState, io::Error> {
|
-> Poll<ChunkedState, io::Error> {
|
||||||
@@ -257,7 +259,7 @@ impl ChunkedState {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let to_read = rem_cap;
|
let to_read = rem_cap;
|
||||||
let slice = try_ready!(rdr.read_mem(to_read));
|
let slice = try_ready!(rdr.read_mem(cx, to_read));
|
||||||
let count = slice.len();
|
let count = slice.len();
|
||||||
|
|
||||||
if count == 0 {
|
if count == 0 {
|
||||||
@@ -273,27 +275,27 @@ impl ChunkedState {
|
|||||||
Ok(Async::Ready(ChunkedState::BodyCr))
|
Ok(Async::Ready(ChunkedState::BodyCr))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn read_body_cr<R: MemRead>(rdr: &mut R) -> Poll<ChunkedState, io::Error> {
|
fn read_body_cr<R: MemRead>(rdr: &mut R, cx: &mut task::Context) -> Poll<ChunkedState, io::Error> {
|
||||||
match byte!(rdr) {
|
match byte!(rdr, cx) {
|
||||||
b'\r' => Ok(Async::Ready(ChunkedState::BodyLf)),
|
b'\r' => Ok(Async::Ready(ChunkedState::BodyLf)),
|
||||||
_ => Err(io::Error::new(io::ErrorKind::InvalidInput, "Invalid chunk body CR")),
|
_ => Err(io::Error::new(io::ErrorKind::InvalidInput, "Invalid chunk body CR")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn read_body_lf<R: MemRead>(rdr: &mut R) -> Poll<ChunkedState, io::Error> {
|
fn read_body_lf<R: MemRead>(rdr: &mut R, cx: &mut task::Context) -> Poll<ChunkedState, io::Error> {
|
||||||
match byte!(rdr) {
|
match byte!(rdr, cx) {
|
||||||
b'\n' => Ok(Async::Ready(ChunkedState::Size)),
|
b'\n' => Ok(Async::Ready(ChunkedState::Size)),
|
||||||
_ => Err(io::Error::new(io::ErrorKind::InvalidInput, "Invalid chunk body LF")),
|
_ => Err(io::Error::new(io::ErrorKind::InvalidInput, "Invalid chunk body LF")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_end_cr<R: MemRead>(rdr: &mut R) -> Poll<ChunkedState, io::Error> {
|
fn read_end_cr<R: MemRead>(rdr: &mut R, cx: &mut task::Context) -> Poll<ChunkedState, io::Error> {
|
||||||
match byte!(rdr) {
|
match byte!(rdr, cx) {
|
||||||
b'\r' => Ok(Async::Ready(ChunkedState::EndLf)),
|
b'\r' => Ok(Async::Ready(ChunkedState::EndLf)),
|
||||||
_ => Err(io::Error::new(io::ErrorKind::InvalidInput, "Invalid chunk end CR")),
|
_ => Err(io::Error::new(io::ErrorKind::InvalidInput, "Invalid chunk end CR")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn read_end_lf<R: MemRead>(rdr: &mut R) -> Poll<ChunkedState, io::Error> {
|
fn read_end_lf<R: MemRead>(rdr: &mut R, cx: &mut task::Context) -> Poll<ChunkedState, io::Error> {
|
||||||
match byte!(rdr) {
|
match byte!(rdr, cx) {
|
||||||
b'\n' => Ok(Async::Ready(ChunkedState::End)),
|
b'\n' => Ok(Async::Ready(ChunkedState::End)),
|
||||||
_ => Err(io::Error::new(io::ErrorKind::InvalidInput, "Invalid chunk end LF")),
|
_ => Err(io::Error::new(io::ErrorKind::InvalidInput, "Invalid chunk end LF")),
|
||||||
}
|
}
|
||||||
@@ -323,11 +325,14 @@ mod tests {
|
|||||||
use super::ChunkedState;
|
use super::ChunkedState;
|
||||||
use super::super::io::MemRead;
|
use super::super::io::MemRead;
|
||||||
use futures::{Async, Poll};
|
use futures::{Async, Poll};
|
||||||
|
use futures::task;
|
||||||
|
use futures::future::lazy;
|
||||||
|
use futures::executor::block_on;
|
||||||
use bytes::{BytesMut, Bytes};
|
use bytes::{BytesMut, Bytes};
|
||||||
use mock::AsyncIo;
|
use mock::AsyncIo;
|
||||||
|
|
||||||
impl<'a> MemRead for &'a [u8] {
|
impl<'a> MemRead for &'a [u8] {
|
||||||
fn read_mem(&mut self, len: usize) -> Poll<Bytes, io::Error> {
|
fn read_mem(&mut self, _cx: &mut task::Context, len: usize) -> Poll<Bytes, io::Error> {
|
||||||
let n = ::std::cmp::min(len, self.len());
|
let n = ::std::cmp::min(len, self.len());
|
||||||
if n > 0 {
|
if n > 0 {
|
||||||
let (a, b) = self.split_at(n);
|
let (a, b) = self.split_at(n);
|
||||||
@@ -347,7 +352,7 @@ mod tests {
|
|||||||
fn unwrap(self) -> Bytes {
|
fn unwrap(self) -> Bytes {
|
||||||
match self {
|
match self {
|
||||||
Async::Ready(bytes) => bytes,
|
Async::Ready(bytes) => bytes,
|
||||||
Async::NotReady => panic!(),
|
Async::Pending => panic!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -355,7 +360,7 @@ mod tests {
|
|||||||
fn unwrap(self) -> ChunkedState {
|
fn unwrap(self) -> ChunkedState {
|
||||||
match self {
|
match self {
|
||||||
Async::Ready(state) => state,
|
Async::Ready(state) => state,
|
||||||
Async::NotReady => panic!(),
|
Async::Pending => panic!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -364,12 +369,12 @@ mod tests {
|
|||||||
fn test_read_chunk_size() {
|
fn test_read_chunk_size() {
|
||||||
use std::io::ErrorKind::{UnexpectedEof, InvalidInput};
|
use std::io::ErrorKind::{UnexpectedEof, InvalidInput};
|
||||||
|
|
||||||
fn read(s: &str) -> u64 {
|
fn read(cx: &mut task::Context, s: &str) -> u64 {
|
||||||
let mut state = ChunkedState::Size;
|
let mut state = ChunkedState::Size;
|
||||||
let rdr = &mut s.as_bytes();
|
let rdr = &mut s.as_bytes();
|
||||||
let mut size = 0;
|
let mut size = 0;
|
||||||
loop {
|
loop {
|
||||||
let result = state.step(rdr, &mut size, &mut None);
|
let result = state.step(rdr, cx, &mut size, &mut None);
|
||||||
let desc = format!("read_size failed for {:?}", s);
|
let desc = format!("read_size failed for {:?}", s);
|
||||||
state = result.expect(desc.as_str()).unwrap();
|
state = result.expect(desc.as_str()).unwrap();
|
||||||
if state == ChunkedState::Body || state == ChunkedState::EndCr {
|
if state == ChunkedState::Body || state == ChunkedState::EndCr {
|
||||||
@@ -379,12 +384,12 @@ mod tests {
|
|||||||
size
|
size
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_err(s: &str, expected_err: io::ErrorKind) {
|
fn read_err(cx: &mut task::Context, s: &str, expected_err: io::ErrorKind) {
|
||||||
let mut state = ChunkedState::Size;
|
let mut state = ChunkedState::Size;
|
||||||
let rdr = &mut s.as_bytes();
|
let rdr = &mut s.as_bytes();
|
||||||
let mut size = 0;
|
let mut size = 0;
|
||||||
loop {
|
loop {
|
||||||
let result = state.step(rdr, &mut size, &mut None);
|
let result = state.step(rdr, cx, &mut size, &mut None);
|
||||||
state = match result {
|
state = match result {
|
||||||
Ok(s) => s.unwrap(),
|
Ok(s) => s.unwrap(),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
@@ -399,90 +404,111 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
assert_eq!(1, read("1\r\n"));
|
block_on(lazy(|cx| {
|
||||||
assert_eq!(1, read("01\r\n"));
|
assert_eq!(1, read(cx, "1\r\n"));
|
||||||
assert_eq!(0, read("0\r\n"));
|
assert_eq!(1, read(cx, "01\r\n"));
|
||||||
assert_eq!(0, read("00\r\n"));
|
assert_eq!(0, read(cx, "0\r\n"));
|
||||||
assert_eq!(10, read("A\r\n"));
|
assert_eq!(0, read(cx, "00\r\n"));
|
||||||
assert_eq!(10, read("a\r\n"));
|
assert_eq!(10, read(cx, "A\r\n"));
|
||||||
assert_eq!(255, read("Ff\r\n"));
|
assert_eq!(10, read(cx, "a\r\n"));
|
||||||
assert_eq!(255, read("Ff \r\n"));
|
assert_eq!(255, read(cx, "Ff\r\n"));
|
||||||
// Missing LF or CRLF
|
assert_eq!(255, read(cx, "Ff \r\n"));
|
||||||
read_err("F\rF", InvalidInput);
|
// Missing LF or CRLF
|
||||||
read_err("F", UnexpectedEof);
|
read_err(cx, "F\rF", InvalidInput);
|
||||||
// Invalid hex digit
|
read_err(cx, "F", UnexpectedEof);
|
||||||
read_err("X\r\n", InvalidInput);
|
// Invalid hex digit
|
||||||
read_err("1X\r\n", InvalidInput);
|
read_err(cx, "X\r\n", InvalidInput);
|
||||||
read_err("-\r\n", InvalidInput);
|
read_err(cx, "1X\r\n", InvalidInput);
|
||||||
read_err("-1\r\n", InvalidInput);
|
read_err(cx, "-\r\n", InvalidInput);
|
||||||
// Acceptable (if not fully valid) extensions do not influence the size
|
read_err(cx, "-1\r\n", InvalidInput);
|
||||||
assert_eq!(1, read("1;extension\r\n"));
|
// Acceptable (if not fully valid) extensions do not influence the size
|
||||||
assert_eq!(10, read("a;ext name=value\r\n"));
|
assert_eq!(1, read(cx, "1;extension\r\n"));
|
||||||
assert_eq!(1, read("1;extension;extension2\r\n"));
|
assert_eq!(10, read(cx, "a;ext name=value\r\n"));
|
||||||
assert_eq!(1, read("1;;; ;\r\n"));
|
assert_eq!(1, read(cx, "1;extension;extension2\r\n"));
|
||||||
assert_eq!(2, read("2; extension...\r\n"));
|
assert_eq!(1, read(cx, "1;;; ;\r\n"));
|
||||||
assert_eq!(3, read("3 ; extension=123\r\n"));
|
assert_eq!(2, read(cx, "2; extension...\r\n"));
|
||||||
assert_eq!(3, read("3 ;\r\n"));
|
assert_eq!(3, read(cx, "3 ; extension=123\r\n"));
|
||||||
assert_eq!(3, read("3 ; \r\n"));
|
assert_eq!(3, read(cx, "3 ;\r\n"));
|
||||||
// Invalid extensions cause an error
|
assert_eq!(3, read(cx, "3 ; \r\n"));
|
||||||
read_err("1 invalid extension\r\n", InvalidInput);
|
// Invalid extensions cause an error
|
||||||
read_err("1 A\r\n", InvalidInput);
|
read_err(cx, "1 invalid extension\r\n", InvalidInput);
|
||||||
read_err("1;no CRLF", UnexpectedEof);
|
read_err(cx, "1 A\r\n", InvalidInput);
|
||||||
|
read_err(cx, "1;no CRLF", UnexpectedEof);
|
||||||
|
|
||||||
|
Ok::<_, ()>(())
|
||||||
|
})).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_read_sized_early_eof() {
|
fn test_read_sized_early_eof() {
|
||||||
let mut bytes = &b"foo bar"[..];
|
block_on(lazy(|cx| {
|
||||||
let mut decoder = Decoder::length(10);
|
let mut bytes = &b"foo bar"[..];
|
||||||
assert_eq!(decoder.decode(&mut bytes).unwrap().unwrap().len(), 7);
|
let mut decoder = Decoder::length(10);
|
||||||
let e = decoder.decode(&mut bytes).unwrap_err();
|
assert_eq!(decoder.decode(&mut bytes, cx).unwrap().unwrap().len(), 7);
|
||||||
assert_eq!(e.kind(), io::ErrorKind::UnexpectedEof);
|
let e = decoder.decode(&mut bytes, cx).unwrap_err();
|
||||||
|
assert_eq!(e.kind(), io::ErrorKind::UnexpectedEof);
|
||||||
|
|
||||||
|
Ok::<_, ()>(())
|
||||||
|
})).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_read_chunked_early_eof() {
|
fn test_read_chunked_early_eof() {
|
||||||
let mut bytes = &b"\
|
block_on(lazy(|cx| {
|
||||||
9\r\n\
|
let mut bytes = &b"\
|
||||||
foo bar\
|
9\r\n\
|
||||||
"[..];
|
foo bar\
|
||||||
let mut decoder = Decoder::chunked();
|
"[..];
|
||||||
assert_eq!(decoder.decode(&mut bytes).unwrap().unwrap().len(), 7);
|
let mut decoder = Decoder::chunked();
|
||||||
let e = decoder.decode(&mut bytes).unwrap_err();
|
assert_eq!(decoder.decode(&mut bytes, cx).unwrap().unwrap().len(), 7);
|
||||||
assert_eq!(e.kind(), io::ErrorKind::UnexpectedEof);
|
let e = decoder.decode(&mut bytes, cx).unwrap_err();
|
||||||
|
assert_eq!(e.kind(), io::ErrorKind::UnexpectedEof);
|
||||||
|
|
||||||
|
Ok::<_, ()>(())
|
||||||
|
})).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_read_chunked_single_read() {
|
fn test_read_chunked_single_read() {
|
||||||
let mut mock_buf = &b"10\r\n1234567890abcdef\r\n0\r\n"[..];
|
block_on(lazy(|cx| {
|
||||||
let buf = Decoder::chunked().decode(&mut mock_buf).expect("decode").unwrap();
|
let mut mock_buf = &b"10\r\n1234567890abcdef\r\n0\r\n"[..];
|
||||||
assert_eq!(16, buf.len());
|
let buf = Decoder::chunked().decode(&mut mock_buf, cx).expect("decode").unwrap();
|
||||||
let result = String::from_utf8(buf.as_ref().to_vec()).expect("decode String");
|
assert_eq!(16, buf.len());
|
||||||
assert_eq!("1234567890abcdef", &result);
|
let result = String::from_utf8(buf.as_ref().to_vec()).expect("decode String");
|
||||||
|
assert_eq!("1234567890abcdef", &result);
|
||||||
|
|
||||||
|
Ok::<_, ()>(())
|
||||||
|
})).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_read_chunked_after_eof() {
|
fn test_read_chunked_after_eof() {
|
||||||
let mut mock_buf = &b"10\r\n1234567890abcdef\r\n0\r\n\r\n"[..];
|
block_on(lazy(|cx| {
|
||||||
let mut decoder = Decoder::chunked();
|
let mut mock_buf = &b"10\r\n1234567890abcdef\r\n0\r\n\r\n"[..];
|
||||||
|
let mut decoder = Decoder::chunked();
|
||||||
|
|
||||||
// normal read
|
// normal read
|
||||||
let buf = decoder.decode(&mut mock_buf).expect("decode").unwrap();
|
let buf = decoder.decode(&mut mock_buf, cx).expect("decode").unwrap();
|
||||||
assert_eq!(16, buf.len());
|
assert_eq!(16, buf.len());
|
||||||
let result = String::from_utf8(buf.as_ref().to_vec()).expect("decode String");
|
let result = String::from_utf8(buf.as_ref().to_vec()).expect("decode String");
|
||||||
assert_eq!("1234567890abcdef", &result);
|
assert_eq!("1234567890abcdef", &result);
|
||||||
|
|
||||||
// eof read
|
// eof read
|
||||||
let buf = decoder.decode(&mut mock_buf).expect("decode").unwrap();
|
let buf = decoder.decode(&mut mock_buf, cx).expect("decode").unwrap();
|
||||||
assert_eq!(0, buf.len());
|
assert_eq!(0, buf.len());
|
||||||
|
|
||||||
// ensure read after eof also returns eof
|
// ensure read after eof also returns eof
|
||||||
let buf = decoder.decode(&mut mock_buf).expect("decode").unwrap();
|
let buf = decoder.decode(&mut mock_buf, cx).expect("decode").unwrap();
|
||||||
assert_eq!(0, buf.len());
|
assert_eq!(0, buf.len());
|
||||||
|
|
||||||
|
Ok::<_, ()>(())
|
||||||
|
})).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
// perform an async read using a custom buffer size and causing a blocking
|
// perform an async read using a custom buffer size and causing a blocking
|
||||||
// read at the specified byte
|
// read at the specified byte
|
||||||
fn read_async(mut decoder: Decoder,
|
fn read_async(mut decoder: Decoder,
|
||||||
|
cx: &mut task::Context,
|
||||||
content: &[u8],
|
content: &[u8],
|
||||||
block_at: usize)
|
block_at: usize)
|
||||||
-> String {
|
-> String {
|
||||||
@@ -490,14 +516,14 @@ mod tests {
|
|||||||
let mut ins = AsyncIo::new(content, block_at);
|
let mut ins = AsyncIo::new(content, block_at);
|
||||||
let mut outs = Vec::new();
|
let mut outs = Vec::new();
|
||||||
loop {
|
loop {
|
||||||
match decoder.decode(&mut ins).expect("unexpected decode error: {}") {
|
match decoder.decode(&mut ins, cx).expect("unexpected decode error: {}") {
|
||||||
Async::Ready(buf) => {
|
Async::Ready(buf) => {
|
||||||
if buf.is_empty() {
|
if buf.is_empty() {
|
||||||
break; // eof
|
break; // eof
|
||||||
}
|
}
|
||||||
outs.write(buf.as_ref()).expect("write buffer");
|
outs.write(buf.as_ref()).expect("write buffer");
|
||||||
},
|
},
|
||||||
Async::NotReady => {
|
Async::Pending => {
|
||||||
ins.block_in(content_len); // we only block once
|
ins.block_in(content_len); // we only block once
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -508,11 +534,14 @@ mod tests {
|
|||||||
// iterate over the different ways that this async read could go.
|
// iterate over the different ways that this async read could go.
|
||||||
// tests blocking a read at each byte along the content - The shotgun approach
|
// tests blocking a read at each byte along the content - The shotgun approach
|
||||||
fn all_async_cases(content: &str, expected: &str, decoder: Decoder) {
|
fn all_async_cases(content: &str, expected: &str, decoder: Decoder) {
|
||||||
let content_len = content.len();
|
block_on(lazy(|cx| {
|
||||||
for block_at in 0..content_len {
|
let content_len = content.len();
|
||||||
let actual = read_async(decoder.clone(), content.as_bytes(), block_at);
|
for block_at in 0..content_len {
|
||||||
assert_eq!(expected, &actual) //, "Failed async. Blocking at {}", block_at);
|
let actual = read_async(decoder.clone(), cx, content.as_bytes(), block_at);
|
||||||
}
|
assert_eq!(expected, &actual) //, "Failed async. Blocking at {}", block_at);
|
||||||
|
}
|
||||||
|
Ok::<_, ()>(())
|
||||||
|
})).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -2,13 +2,15 @@ use std::io;
|
|||||||
|
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use futures::{Async, Future, Poll, Stream};
|
use futures::{Async, Future, Poll, Stream};
|
||||||
|
use futures::task;
|
||||||
|
use futures::io::{AsyncRead, AsyncWrite};
|
||||||
use http::{Request, Response, StatusCode};
|
use http::{Request, Response, StatusCode};
|
||||||
use tokio_io::{AsyncRead, AsyncWrite};
|
|
||||||
use tokio_service::Service;
|
|
||||||
|
|
||||||
use proto::body::Entity;
|
use proto::body::Entity;
|
||||||
use proto::{Body, BodyLength, Conn, Http1Transaction, MessageHead, RequestHead, RequestLine, ResponseHead};
|
use proto::{Body, BodyLength, Conn, Http1Transaction, MessageHead, RequestHead, RequestLine, ResponseHead};
|
||||||
|
|
||||||
|
use ::service::Service;
|
||||||
|
|
||||||
pub struct Dispatcher<D, Bs, I, B, T> {
|
pub struct Dispatcher<D, Bs, I, B, T> {
|
||||||
conn: Conn<I, B, T>,
|
conn: Conn<I, B, T>,
|
||||||
dispatch: D,
|
dispatch: D,
|
||||||
@@ -21,9 +23,9 @@ pub trait Dispatch {
|
|||||||
type PollItem;
|
type PollItem;
|
||||||
type PollBody;
|
type PollBody;
|
||||||
type RecvItem;
|
type RecvItem;
|
||||||
fn poll_msg(&mut self) -> Poll<Option<(Self::PollItem, Option<Self::PollBody>)>, ::Error>;
|
fn poll_msg(&mut self, cx: &mut task::Context) -> Poll<Option<(Self::PollItem, Option<Self::PollBody>)>, ::Error>;
|
||||||
fn recv_msg(&mut self, msg: ::Result<(Self::RecvItem, Body)>) -> ::Result<()>;
|
fn recv_msg(&mut self, cx: &mut task::Context, msg: ::Result<(Self::RecvItem, Body)>) -> ::Result<()>;
|
||||||
fn poll_ready(&mut self) -> Poll<(), ()>;
|
fn poll_ready(&mut self, cx: &mut task::Context) -> Poll<(), ()>;
|
||||||
fn should_poll(&self) -> bool;
|
fn should_poll(&self) -> bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,57 +71,57 @@ where
|
|||||||
|
|
||||||
/// The "Future" poll function. Runs this dispatcher until the
|
/// The "Future" poll function. Runs this dispatcher until the
|
||||||
/// connection is shutdown, or an error occurs.
|
/// connection is shutdown, or an error occurs.
|
||||||
pub fn poll_until_shutdown(&mut self) -> Poll<(), ::Error> {
|
pub fn poll_until_shutdown(&mut self, cx: &mut task::Context) -> Poll<(), ::Error> {
|
||||||
self.poll_catch(true)
|
self.poll_catch(cx, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Run this dispatcher until HTTP says this connection is done,
|
/// Run this dispatcher until HTTP says this connection is done,
|
||||||
/// but don't call `AsyncWrite::shutdown` on the underlying IO.
|
/// but don't call `AsyncWrite::shutdown` on the underlying IO.
|
||||||
///
|
///
|
||||||
/// This is useful for HTTP upgrades.
|
/// This is useful for HTTP upgrades.
|
||||||
pub fn poll_without_shutdown(&mut self) -> Poll<(), ::Error> {
|
pub fn poll_without_shutdown(&mut self, cx: &mut task::Context) -> Poll<(), ::Error> {
|
||||||
self.poll_catch(false)
|
self.poll_catch(cx, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn poll_catch(&mut self, should_shutdown: bool) -> Poll<(), ::Error> {
|
fn poll_catch(&mut self, cx: &mut task::Context, should_shutdown: bool) -> Poll<(), ::Error> {
|
||||||
self.poll_inner(should_shutdown).or_else(|e| {
|
self.poll_inner(cx, should_shutdown).or_else(|e| {
|
||||||
// An error means we're shutting down either way.
|
// An error means we're shutting down either way.
|
||||||
// We just try to give the error to the user,
|
// We just try to give the error to the user,
|
||||||
// and close the connection with an Ok. If we
|
// and close the connection with an Ok. If we
|
||||||
// cannot give it to the user, then return the Err.
|
// cannot give it to the user, then return the Err.
|
||||||
self.dispatch.recv_msg(Err(e)).map(Async::Ready)
|
self.dispatch.recv_msg(cx, Err(e)).map(Async::Ready)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn poll_inner(&mut self, should_shutdown: bool) -> Poll<(), ::Error> {
|
fn poll_inner(&mut self, cx: &mut task::Context, should_shutdown: bool) -> Poll<(), ::Error> {
|
||||||
self.poll_read()?;
|
self.poll_read(cx)?;
|
||||||
self.poll_write()?;
|
self.poll_write(cx)?;
|
||||||
self.poll_flush()?;
|
self.poll_flush(cx)?;
|
||||||
|
|
||||||
if self.is_done() {
|
if self.is_done() {
|
||||||
if should_shutdown {
|
if should_shutdown {
|
||||||
try_ready!(self.conn.shutdown());
|
try_ready!(self.conn.shutdown(cx));
|
||||||
}
|
}
|
||||||
self.conn.take_error()?;
|
self.conn.take_error()?;
|
||||||
Ok(Async::Ready(()))
|
Ok(Async::Ready(()))
|
||||||
} else {
|
} else {
|
||||||
Ok(Async::NotReady)
|
Ok(Async::Pending)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn poll_read(&mut self) -> Poll<(), ::Error> {
|
fn poll_read(&mut self, cx: &mut task::Context) -> Poll<(), ::Error> {
|
||||||
loop {
|
loop {
|
||||||
if self.is_closing {
|
if self.is_closing {
|
||||||
return Ok(Async::Ready(()));
|
return Ok(Async::Ready(()));
|
||||||
} else if self.conn.can_read_head() {
|
} else if self.conn.can_read_head() {
|
||||||
try_ready!(self.poll_read_head());
|
try_ready!(self.poll_read_head(cx));
|
||||||
} else if let Some(mut body) = self.body_tx.take() {
|
} else if let Some(mut body) = self.body_tx.take() {
|
||||||
if self.conn.can_read_body() {
|
if self.conn.can_read_body() {
|
||||||
match body.poll_ready() {
|
match body.poll_ready(cx) {
|
||||||
Ok(Async::Ready(())) => (),
|
Ok(Async::Ready(())) => (),
|
||||||
Ok(Async::NotReady) => {
|
Ok(Async::Pending) => {
|
||||||
self.body_tx = Some(body);
|
self.body_tx = Some(body);
|
||||||
return Ok(Async::NotReady);
|
return Ok(Async::Pending);
|
||||||
},
|
},
|
||||||
Err(_canceled) => {
|
Err(_canceled) => {
|
||||||
// user doesn't care about the body
|
// user doesn't care about the body
|
||||||
@@ -129,7 +131,7 @@ where
|
|||||||
return Ok(Async::Ready(()));
|
return Ok(Async::Ready(()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
match self.conn.read_body() {
|
match self.conn.read_body(cx) {
|
||||||
Ok(Async::Ready(Some(chunk))) => {
|
Ok(Async::Ready(Some(chunk))) => {
|
||||||
match body.send_data(chunk) {
|
match body.send_data(chunk) {
|
||||||
Ok(()) => {
|
Ok(()) => {
|
||||||
@@ -147,9 +149,9 @@ where
|
|||||||
Ok(Async::Ready(None)) => {
|
Ok(Async::Ready(None)) => {
|
||||||
// just drop, the body will close automatically
|
// just drop, the body will close automatically
|
||||||
},
|
},
|
||||||
Ok(Async::NotReady) => {
|
Ok(Async::Pending) => {
|
||||||
self.body_tx = Some(body);
|
self.body_tx = Some(body);
|
||||||
return Ok(Async::NotReady);
|
return Ok(Async::Pending);
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
body.send_error(::Error::Io(e));
|
body.send_error(::Error::Io(e));
|
||||||
@@ -159,16 +161,16 @@ where
|
|||||||
// just drop, the body will close automatically
|
// just drop, the body will close automatically
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return self.conn.read_keep_alive().map(Async::Ready);
|
return self.conn.read_keep_alive(cx).map(Async::Ready);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn poll_read_head(&mut self) -> Poll<(), ::Error> {
|
fn poll_read_head(&mut self, cx: &mut task::Context) -> Poll<(), ::Error> {
|
||||||
// can dispatch receive, or does it still care about, an incoming message?
|
// can dispatch receive, or does it still care about, an incoming message?
|
||||||
match self.dispatch.poll_ready() {
|
match self.dispatch.poll_ready(cx) {
|
||||||
Ok(Async::Ready(())) => (),
|
Ok(Async::Ready(())) => (),
|
||||||
Ok(Async::NotReady) => unreachable!("dispatch not ready when conn is"),
|
Ok(Async::Pending) => unreachable!("dispatch not ready when conn is"),
|
||||||
Err(()) => {
|
Err(()) => {
|
||||||
trace!("dispatch no longer receiving messages");
|
trace!("dispatch no longer receiving messages");
|
||||||
self.close();
|
self.close();
|
||||||
@@ -176,27 +178,27 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// dispatch is ready for a message, try to read one
|
// dispatch is ready for a message, try to read one
|
||||||
match self.conn.read_head() {
|
match self.conn.read_head(cx) {
|
||||||
Ok(Async::Ready(Some((head, has_body)))) => {
|
Ok(Async::Ready(Some((head, has_body)))) => {
|
||||||
let body = if has_body {
|
let body = if has_body {
|
||||||
let (mut tx, rx) = Body::channel();
|
let (mut tx, rx) = Body::channel();
|
||||||
let _ = tx.poll_ready(); // register this task if rx is dropped
|
let _ = tx.poll_ready(cx); // register this task if rx is dropped
|
||||||
self.body_tx = Some(tx);
|
self.body_tx = Some(tx);
|
||||||
rx
|
rx
|
||||||
} else {
|
} else {
|
||||||
Body::empty()
|
Body::empty()
|
||||||
};
|
};
|
||||||
self.dispatch.recv_msg(Ok((head, body)))?;
|
self.dispatch.recv_msg(cx, Ok((head, body)))?;
|
||||||
Ok(Async::Ready(()))
|
Ok(Async::Ready(()))
|
||||||
},
|
},
|
||||||
Ok(Async::Ready(None)) => {
|
Ok(Async::Ready(None)) => {
|
||||||
// read eof, conn will start to shutdown automatically
|
// read eof, conn will start to shutdown automatically
|
||||||
Ok(Async::Ready(()))
|
Ok(Async::Ready(()))
|
||||||
}
|
}
|
||||||
Ok(Async::NotReady) => Ok(Async::NotReady),
|
Ok(Async::Pending) => Ok(Async::Pending),
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
debug!("read_head error: {}", err);
|
debug!("read_head error: {}", err);
|
||||||
self.dispatch.recv_msg(Err(err))?;
|
self.dispatch.recv_msg(cx, Err(err))?;
|
||||||
// if here, the dispatcher gave the user the error
|
// if here, the dispatcher gave the user the error
|
||||||
// somewhere else. we still need to shutdown, but
|
// somewhere else. we still need to shutdown, but
|
||||||
// not as a second error.
|
// not as a second error.
|
||||||
@@ -205,12 +207,12 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn poll_write(&mut self) -> Poll<(), ::Error> {
|
fn poll_write(&mut self, cx: &mut task::Context) -> Poll<(), ::Error> {
|
||||||
loop {
|
loop {
|
||||||
if self.is_closing {
|
if self.is_closing {
|
||||||
return Ok(Async::Ready(()));
|
return Ok(Async::Ready(()));
|
||||||
} else if self.body_rx.is_none() && self.conn.can_write_head() && self.dispatch.should_poll() {
|
} else if self.body_rx.is_none() && self.conn.can_write_head() && self.dispatch.should_poll() {
|
||||||
if let Some((head, body)) = try_ready!(self.dispatch.poll_msg()) {
|
if let Some((head, body)) = try_ready!(self.dispatch.poll_msg(cx)) {
|
||||||
let body_type = body.as_ref().map(|body| {
|
let body_type = body.as_ref().map(|body| {
|
||||||
body.content_length()
|
body.content_length()
|
||||||
.map(BodyLength::Known)
|
.map(BodyLength::Known)
|
||||||
@@ -223,27 +225,27 @@ where
|
|||||||
return Ok(Async::Ready(()));
|
return Ok(Async::Ready(()));
|
||||||
}
|
}
|
||||||
} else if !self.conn.can_buffer_body() {
|
} else if !self.conn.can_buffer_body() {
|
||||||
try_ready!(self.poll_flush());
|
try_ready!(self.poll_flush(cx));
|
||||||
} else if let Some(mut body) = self.body_rx.take() {
|
} else if let Some(mut body) = self.body_rx.take() {
|
||||||
let chunk = match body.poll_data()? {
|
let chunk = match body.poll_data(cx)? {
|
||||||
Async::Ready(Some(chunk)) => {
|
Async::Ready(Some(chunk)) => {
|
||||||
self.body_rx = Some(body);
|
self.body_rx = Some(body);
|
||||||
chunk
|
chunk
|
||||||
},
|
},
|
||||||
Async::Ready(None) => {
|
Async::Ready(None) => {
|
||||||
if self.conn.can_write_body() {
|
if self.conn.can_write_body() {
|
||||||
self.conn.write_body(None)?;
|
self.conn.write_body(cx, None)?;
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
},
|
},
|
||||||
Async::NotReady => {
|
Async::Pending => {
|
||||||
self.body_rx = Some(body);
|
self.body_rx = Some(body);
|
||||||
return Ok(Async::NotReady);
|
return Ok(Async::Pending);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if self.conn.can_write_body() {
|
if self.conn.can_write_body() {
|
||||||
assert!(self.conn.write_body(Some(chunk))?.is_ready());
|
self.conn.write_body(cx, Some(chunk))?;
|
||||||
// This allows when chunk is `None`, or `Some([])`.
|
// This allows when chunk is `None`, or `Some([])`.
|
||||||
} else if chunk.as_ref().len() == 0 {
|
} else if chunk.as_ref().len() == 0 {
|
||||||
// ok
|
// ok
|
||||||
@@ -251,13 +253,13 @@ where
|
|||||||
warn!("unexpected chunk when body cannot write");
|
warn!("unexpected chunk when body cannot write");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return Ok(Async::NotReady);
|
return Ok(Async::Pending);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn poll_flush(&mut self) -> Poll<(), ::Error> {
|
fn poll_flush(&mut self, cx: &mut task::Context) -> Poll<(), ::Error> {
|
||||||
self.conn.flush().map_err(|err| {
|
self.conn.flush(cx).map_err(|err| {
|
||||||
debug!("error writing: {}", err);
|
debug!("error writing: {}", err);
|
||||||
err.into()
|
err.into()
|
||||||
})
|
})
|
||||||
@@ -300,8 +302,8 @@ where
|
|||||||
type Error = ::Error;
|
type Error = ::Error;
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
fn poll(&mut self, cx: &mut task::Context) -> Poll<Self::Item, Self::Error> {
|
||||||
self.poll_until_shutdown()
|
self.poll_until_shutdown(cx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -325,13 +327,13 @@ where
|
|||||||
type PollBody = Bs;
|
type PollBody = Bs;
|
||||||
type RecvItem = RequestHead;
|
type RecvItem = RequestHead;
|
||||||
|
|
||||||
fn poll_msg(&mut self) -> Poll<Option<(Self::PollItem, Option<Self::PollBody>)>, ::Error> {
|
fn poll_msg(&mut self, cx: &mut task::Context) -> Poll<Option<(Self::PollItem, Option<Self::PollBody>)>, ::Error> {
|
||||||
if let Some(mut fut) = self.in_flight.take() {
|
if let Some(mut fut) = self.in_flight.take() {
|
||||||
let resp = match fut.poll()? {
|
let resp = match fut.poll(cx)? {
|
||||||
Async::Ready(res) => res,
|
Async::Ready(res) => res,
|
||||||
Async::NotReady => {
|
Async::Pending => {
|
||||||
self.in_flight = Some(fut);
|
self.in_flight = Some(fut);
|
||||||
return Ok(Async::NotReady);
|
return Ok(Async::Pending);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let (parts, body) = resp.into_parts();
|
let (parts, body) = resp.into_parts();
|
||||||
@@ -351,7 +353,7 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn recv_msg(&mut self, msg: ::Result<(Self::RecvItem, Body)>) -> ::Result<()> {
|
fn recv_msg(&mut self, _cx: &mut task::Context, msg: ::Result<(Self::RecvItem, Body)>) -> ::Result<()> {
|
||||||
let (msg, body) = msg?;
|
let (msg, body) = msg?;
|
||||||
let mut req = Request::new(body);
|
let mut req = Request::new(body);
|
||||||
*req.method_mut() = msg.subject.0;
|
*req.method_mut() = msg.subject.0;
|
||||||
@@ -362,9 +364,9 @@ where
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn poll_ready(&mut self) -> Poll<(), ()> {
|
fn poll_ready(&mut self, _cx: &mut task::Context) -> Poll<(), ()> {
|
||||||
if self.in_flight.is_some() {
|
if self.in_flight.is_some() {
|
||||||
Ok(Async::NotReady)
|
Ok(Async::Pending)
|
||||||
} else {
|
} else {
|
||||||
Ok(Async::Ready(()))
|
Ok(Async::Ready(()))
|
||||||
}
|
}
|
||||||
@@ -395,16 +397,16 @@ where
|
|||||||
type PollBody = B;
|
type PollBody = B;
|
||||||
type RecvItem = ResponseHead;
|
type RecvItem = ResponseHead;
|
||||||
|
|
||||||
fn poll_msg(&mut self) -> Poll<Option<(Self::PollItem, Option<Self::PollBody>)>, ::Error> {
|
fn poll_msg(&mut self, cx: &mut task::Context) -> Poll<Option<(Self::PollItem, Option<Self::PollBody>)>, ::Error> {
|
||||||
match self.rx.poll() {
|
match self.rx.poll_next(cx) {
|
||||||
Ok(Async::Ready(Some((req, mut cb)))) => {
|
Ok(Async::Ready(Some((req, mut cb)))) => {
|
||||||
// check that future hasn't been canceled already
|
// check that future hasn't been canceled already
|
||||||
match cb.poll_cancel().expect("poll_cancel cannot error") {
|
match cb.poll_cancel(cx).expect("poll_cancel cannot error") {
|
||||||
Async::Ready(()) => {
|
Async::Ready(()) => {
|
||||||
trace!("request canceled");
|
trace!("request canceled");
|
||||||
Ok(Async::Ready(None))
|
Ok(Async::Ready(None))
|
||||||
},
|
},
|
||||||
Async::NotReady => {
|
Async::Pending => {
|
||||||
let (parts, body) = req.into_parts();
|
let (parts, body) = req.into_parts();
|
||||||
let head = RequestHead {
|
let head = RequestHead {
|
||||||
version: parts.version,
|
version: parts.version,
|
||||||
@@ -427,12 +429,12 @@ where
|
|||||||
// user has dropped sender handle
|
// user has dropped sender handle
|
||||||
Ok(Async::Ready(None))
|
Ok(Async::Ready(None))
|
||||||
},
|
},
|
||||||
Ok(Async::NotReady) => return Ok(Async::NotReady),
|
Ok(Async::Pending) => return Ok(Async::Pending),
|
||||||
Err(_) => unreachable!("receiver cannot error"),
|
Err(_) => unreachable!("receiver cannot error"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn recv_msg(&mut self, msg: ::Result<(Self::RecvItem, Body)>) -> ::Result<()> {
|
fn recv_msg(&mut self, cx: &mut task::Context, msg: ::Result<(Self::RecvItem, Body)>) -> ::Result<()> {
|
||||||
match msg {
|
match msg {
|
||||||
Ok((msg, body)) => {
|
Ok((msg, body)) => {
|
||||||
if let Some(cb) = self.callback.take() {
|
if let Some(cb) = self.callback.take() {
|
||||||
@@ -450,7 +452,7 @@ where
|
|||||||
if let Some(cb) = self.callback.take() {
|
if let Some(cb) = self.callback.take() {
|
||||||
let _ = cb.send(Err((err, None)));
|
let _ = cb.send(Err((err, None)));
|
||||||
Ok(())
|
Ok(())
|
||||||
} else if let Ok(Async::Ready(Some((req, cb)))) = self.rx.poll() {
|
} else if let Ok(Async::Ready(Some((req, cb)))) = self.rx.poll_next(cx) {
|
||||||
trace!("canceling queued request with connection error: {}", err);
|
trace!("canceling queued request with connection error: {}", err);
|
||||||
// in this case, the message was never even started, so it's safe to tell
|
// in this case, the message was never even started, so it's safe to tell
|
||||||
// the user that the request was completely canceled
|
// the user that the request was completely canceled
|
||||||
@@ -463,14 +465,14 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn poll_ready(&mut self) -> Poll<(), ()> {
|
fn poll_ready(&mut self, cx: &mut task::Context) -> Poll<(), ()> {
|
||||||
match self.callback {
|
match self.callback {
|
||||||
Some(ref mut cb) => match cb.poll_cancel() {
|
Some(ref mut cb) => match cb.poll_cancel(cx) {
|
||||||
Ok(Async::Ready(())) => {
|
Ok(Async::Ready(())) => {
|
||||||
trace!("callback receiver has dropped");
|
trace!("callback receiver has dropped");
|
||||||
Err(())
|
Err(())
|
||||||
},
|
},
|
||||||
Ok(Async::NotReady) => Ok(Async::Ready(())),
|
Ok(Async::Pending) => Ok(Async::Ready(())),
|
||||||
Err(_) => unreachable!("oneshot poll_cancel cannot error"),
|
Err(_) => unreachable!("oneshot poll_cancel cannot error"),
|
||||||
},
|
},
|
||||||
None => Err(()),
|
None => Err(()),
|
||||||
@@ -487,31 +489,32 @@ mod tests {
|
|||||||
extern crate pretty_env_logger;
|
extern crate pretty_env_logger;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use futures::executor::block_on;
|
||||||
|
use futures::future::lazy;
|
||||||
use mock::AsyncIo;
|
use mock::AsyncIo;
|
||||||
use proto::ClientTransaction;
|
use proto::ClientTransaction;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn client_read_bytes_before_writing_request() {
|
fn client_read_bytes_before_writing_request() {
|
||||||
let _ = pretty_env_logger::try_init();
|
let _ = pretty_env_logger::try_init();
|
||||||
::futures::lazy(|| {
|
block_on(lazy(|cx| {
|
||||||
let io = AsyncIo::new_buf(b"HTTP/1.1 200 OK\r\n\r\n".to_vec(), 100);
|
let io = AsyncIo::new_buf(b"HTTP/1.1 200 OK\r\n\r\n".to_vec(), 100);
|
||||||
let (mut tx, rx) = ::client::dispatch::channel();
|
let (mut tx, rx) = ::client::dispatch::channel();
|
||||||
let conn = Conn::<_, ::Chunk, ClientTransaction>::new(io);
|
let conn = Conn::<_, ::Chunk, ClientTransaction>::new(io);
|
||||||
let mut dispatcher = Dispatcher::new(Client::new(rx), conn);
|
let mut dispatcher = Dispatcher::new(Client::new(rx), conn);
|
||||||
|
|
||||||
let res_rx = tx.try_send(::Request::new(::Body::empty())).unwrap();
|
let mut res_rx = tx.try_send(::Request::new(::Body::empty())).unwrap();
|
||||||
|
|
||||||
let a1 = dispatcher.poll().expect("error should be sent on channel");
|
let a1 = dispatcher.poll(cx).expect("error should be sent on channel");
|
||||||
assert!(a1.is_ready(), "dispatcher should be closed");
|
assert!(a1.is_ready(), "dispatcher should be closed");
|
||||||
let err = res_rx.wait()
|
let result = res_rx.poll(cx)
|
||||||
.expect("callback poll")
|
.expect("callback poll");
|
||||||
.expect_err("callback response");
|
|
||||||
|
|
||||||
match err {
|
match result {
|
||||||
(::Error::Cancel(_), Some(_)) => (),
|
Async::Ready(Err((::Error::Cancel(_), Some(_)))) => (),
|
||||||
other => panic!("expected Canceled, got {:?}", other),
|
other => panic!("expected Err(Canceled), got {:?}", other),
|
||||||
}
|
}
|
||||||
Ok::<(), ()>(())
|
Ok::<_, ()>(())
|
||||||
}).wait().unwrap();
|
})).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
use std::cell::Cell;
|
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::io;
|
use std::io;
|
||||||
|
|
||||||
use bytes::{Buf, BufMut, Bytes, BytesMut};
|
use bytes::{Buf, BufMut, Bytes, BytesMut};
|
||||||
use futures::{Async, Poll};
|
use futures::{Async, Poll};
|
||||||
|
use futures::task;
|
||||||
|
use futures::io::{AsyncRead, AsyncWrite};
|
||||||
use iovec::IoVec;
|
use iovec::IoVec;
|
||||||
use tokio_io::{AsyncRead, AsyncWrite};
|
|
||||||
|
|
||||||
use proto::{Http1Transaction, MessageHead};
|
use proto::{Http1Transaction, MessageHead};
|
||||||
|
|
||||||
@@ -108,7 +108,7 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse<S: Http1Transaction>(&mut self) -> Poll<MessageHead<S::Incoming>, ::Error> {
|
pub fn parse<S: Http1Transaction>(&mut self, cx: &mut task::Context) -> Poll<MessageHead<S::Incoming>, ::Error> {
|
||||||
loop {
|
loop {
|
||||||
match try!(S::parse(&mut self.read_buf)) {
|
match try!(S::parse(&mut self.read_buf)) {
|
||||||
Some((head, len)) => {
|
Some((head, len)) => {
|
||||||
@@ -122,7 +122,7 @@ where
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
match try_ready!(self.read_from_io()) {
|
match try_ready!(self.read_from_io(cx)) {
|
||||||
0 => {
|
0 => {
|
||||||
trace!("parse eof");
|
trace!("parse eof");
|
||||||
return Err(::Error::Incomplete);
|
return Err(::Error::Incomplete);
|
||||||
@@ -132,21 +132,21 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_from_io(&mut self) -> Poll<usize, io::Error> {
|
pub fn read_from_io(&mut self, cx: &mut task::Context) -> Poll<usize, io::Error> {
|
||||||
use bytes::BufMut;
|
use bytes::BufMut;
|
||||||
self.read_blocked = false;
|
self.read_blocked = false;
|
||||||
if self.read_buf.remaining_mut() < INIT_BUFFER_SIZE {
|
if self.read_buf.remaining_mut() < INIT_BUFFER_SIZE {
|
||||||
self.read_buf.reserve(INIT_BUFFER_SIZE);
|
self.read_buf.reserve(INIT_BUFFER_SIZE);
|
||||||
}
|
}
|
||||||
self.io.read_buf(&mut self.read_buf).map(|ok| {
|
read_buf(&mut self.io, cx, &mut self.read_buf).map(|ok| {
|
||||||
match ok {
|
match ok {
|
||||||
Async::Ready(n) => {
|
Async::Ready(n) => {
|
||||||
debug!("read {} bytes", n);
|
debug!("read {} bytes", n);
|
||||||
Async::Ready(n)
|
Async::Ready(n)
|
||||||
},
|
},
|
||||||
Async::NotReady => {
|
Async::Pending => {
|
||||||
self.read_blocked = true;
|
self.read_blocked = true;
|
||||||
Async::NotReady
|
Async::Pending
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -164,14 +164,14 @@ where
|
|||||||
self.read_blocked
|
self.read_blocked
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn flush(&mut self) -> Poll<(), io::Error> {
|
pub fn flush(&mut self, cx: &mut task::Context) -> Poll<(), io::Error> {
|
||||||
if self.flush_pipeline && !self.read_buf.is_empty() {
|
if self.flush_pipeline && !self.read_buf.is_empty() {
|
||||||
//Ok(())
|
//Ok(())
|
||||||
} else if self.write_buf.remaining() == 0 {
|
} else if self.write_buf.remaining() == 0 {
|
||||||
try_nb!(self.io.flush());
|
try_ready!(self.io.poll_flush(cx));
|
||||||
} else {
|
} else {
|
||||||
loop {
|
loop {
|
||||||
let n = try_ready!(self.io.write_buf(&mut self.write_buf.auto()));
|
let n = try_ready!(self.write_buf.poll_flush_into(&mut self.io, cx));
|
||||||
debug!("flushed {} bytes", n);
|
debug!("flushed {} bytes", n);
|
||||||
if self.write_buf.remaining() == 0 {
|
if self.write_buf.remaining() == 0 {
|
||||||
break;
|
break;
|
||||||
@@ -180,14 +180,33 @@ where
|
|||||||
return Err(io::ErrorKind::WriteZero.into())
|
return Err(io::ErrorKind::WriteZero.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
try_nb!(self.io.flush())
|
try_ready!(self.io.poll_flush(cx))
|
||||||
}
|
}
|
||||||
Ok(Async::Ready(()))
|
Ok(Async::Ready(()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn read_buf<I: AsyncRead, B: BufMut>(io: &mut I, cx: &mut task::Context, buf: &mut B) -> Poll<usize, io::Error> {
|
||||||
|
if !buf.has_remaining_mut() {
|
||||||
|
return Ok(Async::Ready(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let n = {
|
||||||
|
let b = buf.bytes_mut();
|
||||||
|
|
||||||
|
io.initializer().initialize(b);
|
||||||
|
|
||||||
|
try_ready!(io.poll_read(cx, b))
|
||||||
|
};
|
||||||
|
|
||||||
|
buf.advance_mut(n);
|
||||||
|
Ok(Async::Ready(n))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub trait MemRead {
|
pub trait MemRead {
|
||||||
fn read_mem(&mut self, len: usize) -> Poll<Bytes, io::Error>;
|
fn read_mem(&mut self, cx: &mut task::Context, len: usize) -> Poll<Bytes, io::Error>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, B> MemRead for Buffered<T, B>
|
impl<T, B> MemRead for Buffered<T, B>
|
||||||
@@ -195,12 +214,12 @@ where
|
|||||||
T: AsyncRead + AsyncWrite,
|
T: AsyncRead + AsyncWrite,
|
||||||
B: Buf,
|
B: Buf,
|
||||||
{
|
{
|
||||||
fn read_mem(&mut self, len: usize) -> Poll<Bytes, io::Error> {
|
fn read_mem(&mut self, cx: &mut task::Context, len: usize) -> Poll<Bytes, io::Error> {
|
||||||
if !self.read_buf.is_empty() {
|
if !self.read_buf.is_empty() {
|
||||||
let n = ::std::cmp::min(len, self.read_buf.len());
|
let n = ::std::cmp::min(len, self.read_buf.len());
|
||||||
Ok(Async::Ready(self.read_buf.split_to(n).freeze()))
|
Ok(Async::Ready(self.read_buf.split_to(n).freeze()))
|
||||||
} else {
|
} else {
|
||||||
let n = try_ready!(self.read_from_io());
|
let n = try_ready!(self.read_from_io(cx));
|
||||||
Ok(Async::Ready(self.read_buf.split_to(::std::cmp::min(len, n)).freeze()))
|
Ok(Async::Ready(self.read_buf.split_to(::std::cmp::min(len, n)).freeze()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -294,11 +313,6 @@ where
|
|||||||
self.strategy = strategy;
|
self.strategy = strategy;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn auto(&mut self) -> WriteBufAuto<B> {
|
|
||||||
WriteBufAuto::new(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn buffer(&mut self, buf: B) {
|
fn buffer(&mut self, buf: B) {
|
||||||
match self.strategy {
|
match self.strategy {
|
||||||
Strategy::Flatten => {
|
Strategy::Flatten => {
|
||||||
@@ -343,6 +357,48 @@ where
|
|||||||
unreachable!("head_buf just pushed on back");
|
unreachable!("head_buf just pushed on back");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn poll_flush_into<I: AsyncWrite>(&mut self, io: &mut I, cx: &mut task::Context) -> Poll<usize, io::Error> {
|
||||||
|
if !self.has_remaining() {
|
||||||
|
return Ok(Async::Ready(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
let (num_bufs_avail, num_bytes_written, len_first_buf) = {
|
||||||
|
static PLACEHOLDER: &[u8] = &[0];
|
||||||
|
let mut bufs = [From::from(PLACEHOLDER); 64];
|
||||||
|
let num_bufs_avail = self.bytes_vec(&mut bufs[..]);
|
||||||
|
let num_bytes_written = try_ready!(io.poll_vectored_write(cx, &bufs[..num_bufs_avail]));
|
||||||
|
(num_bufs_avail, num_bytes_written, bufs[0].len())
|
||||||
|
};
|
||||||
|
self.advance(num_bytes_written);
|
||||||
|
|
||||||
|
if let Strategy::Auto = self.strategy {
|
||||||
|
if num_bufs_avail > 1 {
|
||||||
|
// If there's more than one IoVec available, attempt to
|
||||||
|
// determine the best buffering strategy based on whether
|
||||||
|
// the underlying AsyncWrite object supports vectored I/O.
|
||||||
|
if num_bytes_written == len_first_buf {
|
||||||
|
// If only the first of many IoVec was written, we can assume
|
||||||
|
// with some certainty that vectored I/O _is not_ supported.
|
||||||
|
//
|
||||||
|
// Switch to a flattening strategy for buffering data.
|
||||||
|
trace!("detected no usage of vectored write, flattening");
|
||||||
|
let mut vec = Vec::new();
|
||||||
|
vec.put(&mut self.buf);
|
||||||
|
self.buf.bufs.push_back(VecOrBuf::Vec(Cursor::new(vec)));
|
||||||
|
self.strategy = Strategy::Flatten;
|
||||||
|
} else if num_bytes_written > len_first_buf {
|
||||||
|
// If more than the first IoVec was written, we can assume
|
||||||
|
// with some certainty that vectored I/O _is_ supported.
|
||||||
|
//
|
||||||
|
// Switch to a queuing strategy for buffering data.
|
||||||
|
self.strategy = Strategy::Queue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Async::Ready(num_bytes_written))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<B: Buf> fmt::Debug for WriteBuf<B> {
|
impl<B: Buf> fmt::Debug for WriteBuf<B> {
|
||||||
@@ -376,65 +432,6 @@ impl<B: Buf> Buf for WriteBuf<B> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Detects when wrapped `WriteBuf` is used for vectored IO, and
|
|
||||||
/// adjusts the `WriteBuf` strategy if not.
|
|
||||||
struct WriteBufAuto<'a, B: Buf + 'a> {
|
|
||||||
bytes_called: Cell<bool>,
|
|
||||||
bytes_vec_called: Cell<bool>,
|
|
||||||
inner: &'a mut WriteBuf<B>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, B: Buf> WriteBufAuto<'a, B> {
|
|
||||||
fn new(inner: &'a mut WriteBuf<B>) -> WriteBufAuto<'a, B> {
|
|
||||||
WriteBufAuto {
|
|
||||||
bytes_called: Cell::new(false),
|
|
||||||
bytes_vec_called: Cell::new(false),
|
|
||||||
inner: inner,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, B: Buf> Buf for WriteBufAuto<'a, B> {
|
|
||||||
#[inline]
|
|
||||||
fn remaining(&self) -> usize {
|
|
||||||
self.inner.remaining()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn bytes(&self) -> &[u8] {
|
|
||||||
self.bytes_called.set(true);
|
|
||||||
self.inner.bytes()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn advance(&mut self, cnt: usize) {
|
|
||||||
self.inner.advance(cnt)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn bytes_vec<'t>(&'t self, dst: &mut [&'t IoVec]) -> usize {
|
|
||||||
self.bytes_vec_called.set(true);
|
|
||||||
self.inner.bytes_vec(dst)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, B: Buf + 'a> Drop for WriteBufAuto<'a, B> {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
if let Strategy::Auto = self.inner.strategy {
|
|
||||||
if self.bytes_vec_called.get() {
|
|
||||||
self.inner.strategy = Strategy::Queue;
|
|
||||||
} else if self.bytes_called.get() {
|
|
||||||
trace!("detected no usage of vectored write, flattening");
|
|
||||||
self.inner.strategy = Strategy::Flatten;
|
|
||||||
let mut vec = Vec::new();
|
|
||||||
vec.put(&mut self.inner.buf);
|
|
||||||
self.inner.buf.bufs.push_back(VecOrBuf::Vec(Cursor::new(vec)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum Strategy {
|
enum Strategy {
|
||||||
Auto,
|
Auto,
|
||||||
@@ -568,51 +565,68 @@ impl<T: Buf> Buf for BufDeque<T> {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
|
use futures::task;
|
||||||
|
use futures::future;
|
||||||
|
use futures::executor::block_on;
|
||||||
|
use futures::io::AsyncRead;
|
||||||
use mock::AsyncIo;
|
use mock::AsyncIo;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
impl<T: Read> MemRead for ::mock::AsyncIo<T> {
|
impl<T: Read> MemRead for ::mock::AsyncIo<T> {
|
||||||
fn read_mem(&mut self, len: usize) -> Poll<Bytes, io::Error> {
|
fn read_mem(&mut self, cx: &mut task::Context, len: usize) -> Poll<Bytes, io::Error> {
|
||||||
let mut v = vec![0; len];
|
let mut v = vec![0; len];
|
||||||
let n = try_nb!(self.read(v.as_mut_slice()));
|
let n = try_ready!(self.poll_read(cx, v.as_mut_slice()));
|
||||||
Ok(Async::Ready(BytesMut::from(&v[..n]).freeze()))
|
Ok(Async::Ready(BytesMut::from(&v[..n]).freeze()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn iobuf_write_empty_slice() {
|
fn iobuf_write_empty_slice() {
|
||||||
let mut mock = AsyncIo::new_buf(vec![], 256);
|
block_on(future::lazy(|cx| {
|
||||||
mock.error(io::Error::new(io::ErrorKind::Other, "logic error"));
|
let mut mock = AsyncIo::new_buf(vec![], 256);
|
||||||
|
mock.error(io::Error::new(io::ErrorKind::Other, "logic error"));
|
||||||
|
|
||||||
let mut io_buf = Buffered::<_, Cursor<Vec<u8>>>::new(mock);
|
let mut io_buf = Buffered::<_, Cursor<Vec<u8>>>::new(mock);
|
||||||
|
|
||||||
// underlying io will return the logic error upon write,
|
// underlying io will return the logic error upon write,
|
||||||
// so we are testing that the io_buf does not trigger a write
|
// so we are testing that the io_buf does not trigger a write
|
||||||
// when there is nothing to flush
|
// when there is nothing to flush
|
||||||
io_buf.flush().expect("should short-circuit flush");
|
io_buf.flush(cx).expect("should short-circuit flush");
|
||||||
|
|
||||||
|
Ok::<_, ()>(())
|
||||||
|
})).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_reads_until_blocked() {
|
fn parse_reads_until_blocked() {
|
||||||
// missing last line ending
|
block_on(future::lazy(|cx| {
|
||||||
let raw = "HTTP/1.1 200 OK\r\n";
|
// missing last line ending
|
||||||
|
let raw = "HTTP/1.1 200 OK\r\n";
|
||||||
|
|
||||||
let mock = AsyncIo::new_buf(raw, raw.len());
|
let mock = AsyncIo::new_buf(raw, raw.len());
|
||||||
let mut buffered = Buffered::<_, Cursor<Vec<u8>>>::new(mock);
|
let mut buffered = Buffered::<_, Cursor<Vec<u8>>>::new(mock);
|
||||||
assert_eq!(buffered.parse::<::proto::ClientTransaction>().unwrap(), Async::NotReady);
|
|
||||||
assert!(buffered.io.blocked());
|
assert_eq!(buffered.parse::<::proto::ClientTransaction>(cx).unwrap(), Async::Pending);
|
||||||
|
assert!(buffered.io.blocked());
|
||||||
|
|
||||||
|
Ok::<_, ()>(())
|
||||||
|
})).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn write_buf_skips_empty_bufs() {
|
fn write_buf_skips_empty_bufs() {
|
||||||
let mut mock = AsyncIo::new_buf(vec![], 1024);
|
block_on(future::lazy(|cx| {
|
||||||
mock.max_read_vecs(0); // disable vectored IO
|
let mut mock = AsyncIo::new_buf(vec![], 1024);
|
||||||
let mut buffered = Buffered::<_, Cursor<Vec<u8>>>::new(mock);
|
mock.max_read_vecs(0); // disable vectored IO
|
||||||
|
let mut buffered = Buffered::<_, Cursor<Vec<u8>>>::new(mock);
|
||||||
|
|
||||||
buffered.buffer(Cursor::new(Vec::new()));
|
buffered.buffer(Cursor::new(Vec::new()));
|
||||||
buffered.buffer(Cursor::new(b"hello".to_vec()));
|
buffered.buffer(Cursor::new(b"hello".to_vec()));
|
||||||
buffered.flush().unwrap();
|
buffered.flush(cx).unwrap();
|
||||||
assert_eq!(buffered.io, b"hello");
|
assert_eq!(buffered.io, b"hello");
|
||||||
|
|
||||||
|
Ok::<_, ()>(())
|
||||||
|
})).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -620,17 +634,22 @@ mod tests {
|
|||||||
extern crate pretty_env_logger;
|
extern crate pretty_env_logger;
|
||||||
let _ = pretty_env_logger::try_init();
|
let _ = pretty_env_logger::try_init();
|
||||||
|
|
||||||
let mock = AsyncIo::new_buf(vec![], 1024);
|
block_on(future::lazy(|cx| {
|
||||||
let mut buffered = Buffered::<_, Cursor<Vec<u8>>>::new(mock);
|
let mock = AsyncIo::new_buf(vec![], 1024);
|
||||||
|
let mut buffered = Buffered::<_, Cursor<Vec<u8>>>::new(mock);
|
||||||
|
|
||||||
buffered.write_buf_mut().extend(b"hello ");
|
buffered.write_buf_mut().extend(b"hello ");
|
||||||
buffered.buffer(Cursor::new(b"world, ".to_vec()));
|
buffered.buffer(Cursor::new(b"world, ".to_vec()));
|
||||||
buffered.write_buf_mut().extend(b"it's ");
|
buffered.write_buf_mut().extend(b"it's ");
|
||||||
buffered.buffer(Cursor::new(b"hyper!".to_vec()));
|
buffered.buffer(Cursor::new(b"hyper!".to_vec()));
|
||||||
buffered.flush().unwrap();
|
assert_eq!(buffered.io.num_writes(), 0);
|
||||||
|
buffered.flush(cx).unwrap();
|
||||||
|
|
||||||
assert_eq!(buffered.io, b"hello world, it's hyper!");
|
assert_eq!(buffered.io, b"hello world, it's hyper!");
|
||||||
assert_eq!(buffered.io.num_writes(), 1);
|
assert_eq!(buffered.io.num_writes(), 1);
|
||||||
|
|
||||||
|
Ok::<_, ()>(())
|
||||||
|
})).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -638,27 +657,31 @@ mod tests {
|
|||||||
extern crate pretty_env_logger;
|
extern crate pretty_env_logger;
|
||||||
let _ = pretty_env_logger::try_init();
|
let _ = pretty_env_logger::try_init();
|
||||||
|
|
||||||
let mock = AsyncIo::new_buf(vec![], 1024);
|
block_on(future::lazy(|cx| {
|
||||||
let mut buffered = Buffered::<_, Cursor<Vec<u8>>>::new(mock);
|
let mock = AsyncIo::new_buf(vec![], 1024);
|
||||||
|
let mut buffered = Buffered::<_, Cursor<Vec<u8>>>::new(mock);
|
||||||
|
|
||||||
buffered.write_buf_mut().extend(b"hello ");
|
buffered.write_buf_mut().extend(b"hello ");
|
||||||
assert_eq!(buffered.write_buf.buf.bufs.len(), 1);
|
assert_eq!(buffered.write_buf.buf.bufs.len(), 1);
|
||||||
buffered.write_buf_mut().extend(b"world, ");
|
buffered.write_buf_mut().extend(b"world, ");
|
||||||
assert_eq!(buffered.write_buf.buf.bufs.len(), 1);
|
assert_eq!(buffered.write_buf.buf.bufs.len(), 1);
|
||||||
|
|
||||||
// after flushing, reclaim the Vec
|
// after flushing, reclaim the Vec
|
||||||
buffered.flush().unwrap();
|
buffered.flush(cx).unwrap();
|
||||||
assert_eq!(buffered.write_buf.remaining(), 0);
|
assert_eq!(buffered.write_buf.remaining(), 0);
|
||||||
assert_eq!(buffered.write_buf.buf.bufs.len(), 1);
|
assert_eq!(buffered.write_buf.buf.bufs.len(), 1);
|
||||||
|
|
||||||
// add a user buf in the way
|
// add a user buf in the way
|
||||||
buffered.buffer(Cursor::new(b"it's ".to_vec()));
|
buffered.buffer(Cursor::new(b"it's ".to_vec()));
|
||||||
// and then add more hyper bytes
|
// and then add more hyper bytes
|
||||||
buffered.write_buf_mut().extend(b"hyper!");
|
buffered.write_buf_mut().extend(b"hyper!");
|
||||||
buffered.flush().unwrap();
|
buffered.flush(cx).unwrap();
|
||||||
assert_eq!(buffered.write_buf.buf.bufs.len(), 1);
|
assert_eq!(buffered.write_buf.buf.bufs.len(), 1);
|
||||||
|
|
||||||
assert_eq!(buffered.io, b"hello world, it's hyper!");
|
assert_eq!(buffered.io, b"hello world, it's hyper!");
|
||||||
|
|
||||||
|
Ok::<_, ()>(())
|
||||||
|
})).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -666,21 +689,25 @@ mod tests {
|
|||||||
extern crate pretty_env_logger;
|
extern crate pretty_env_logger;
|
||||||
let _ = pretty_env_logger::try_init();
|
let _ = pretty_env_logger::try_init();
|
||||||
|
|
||||||
let mock = AsyncIo::new_buf(vec![], 1024);
|
block_on(future::lazy(|cx| {
|
||||||
let mut buffered = Buffered::<_, Cursor<Vec<u8>>>::new(mock);
|
let mock = AsyncIo::new_buf(vec![], 1024);
|
||||||
buffered.write_buf.set_strategy(Strategy::Flatten);
|
let mut buffered = Buffered::<_, Cursor<Vec<u8>>>::new(mock);
|
||||||
|
buffered.write_buf.set_strategy(Strategy::Flatten);
|
||||||
|
|
||||||
buffered.write_buf_mut().extend(b"hello ");
|
buffered.write_buf_mut().extend(b"hello ");
|
||||||
buffered.buffer(Cursor::new(b"world, ".to_vec()));
|
buffered.buffer(Cursor::new(b"world, ".to_vec()));
|
||||||
buffered.write_buf_mut().extend(b"it's ");
|
buffered.write_buf_mut().extend(b"it's ");
|
||||||
buffered.buffer(Cursor::new(b"hyper!".to_vec()));
|
buffered.buffer(Cursor::new(b"hyper!".to_vec()));
|
||||||
assert_eq!(buffered.write_buf.buf.bufs.len(), 1);
|
assert_eq!(buffered.write_buf.buf.bufs.len(), 1);
|
||||||
|
|
||||||
buffered.flush().unwrap();
|
buffered.flush(cx).unwrap();
|
||||||
|
|
||||||
assert_eq!(buffered.io, b"hello world, it's hyper!");
|
assert_eq!(buffered.io, b"hello world, it's hyper!");
|
||||||
assert_eq!(buffered.io.num_writes(), 1);
|
assert_eq!(buffered.io.num_writes(), 1);
|
||||||
assert_eq!(buffered.write_buf.buf.bufs.len(), 1);
|
assert_eq!(buffered.write_buf.buf.bufs.len(), 1);
|
||||||
|
|
||||||
|
Ok::<_, ()>(())
|
||||||
|
})).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -688,22 +715,26 @@ mod tests {
|
|||||||
extern crate pretty_env_logger;
|
extern crate pretty_env_logger;
|
||||||
let _ = pretty_env_logger::try_init();
|
let _ = pretty_env_logger::try_init();
|
||||||
|
|
||||||
let mut mock = AsyncIo::new_buf(vec![], 1024);
|
block_on(future::lazy(|cx| {
|
||||||
mock.max_read_vecs(0); // disable vectored IO
|
let mut mock = AsyncIo::new_buf(vec![], 1024);
|
||||||
let mut buffered = Buffered::<_, Cursor<Vec<u8>>>::new(mock);
|
mock.max_read_vecs(0); // disable vectored IO
|
||||||
|
let mut buffered = Buffered::<_, Cursor<Vec<u8>>>::new(mock);
|
||||||
|
|
||||||
// we have 4 buffers, but hope to detect that vectored IO isn't
|
// we have 4 buffers, but hope to detect that vectored IO isn't
|
||||||
// being used, and switch to flattening automatically,
|
// being used, and switch to flattening automatically,
|
||||||
// resulting in only 2 writes
|
// resulting in only 2 writes
|
||||||
buffered.write_buf_mut().extend(b"hello ");
|
buffered.write_buf_mut().extend(b"hello ");
|
||||||
buffered.buffer(Cursor::new(b"world, ".to_vec()));
|
buffered.buffer(Cursor::new(b"world, ".to_vec()));
|
||||||
buffered.write_buf_mut().extend(b"it's hyper!");
|
buffered.write_buf_mut().extend(b"it's hyper!");
|
||||||
//buffered.buffer(Cursor::new(b"hyper!".to_vec()));
|
//buffered.buffer(Cursor::new(b"hyper!".to_vec()));
|
||||||
buffered.flush().unwrap();
|
buffered.flush(cx).unwrap();
|
||||||
|
|
||||||
assert_eq!(buffered.io, b"hello world, it's hyper!");
|
assert_eq!(buffered.io, b"hello world, it's hyper!");
|
||||||
assert_eq!(buffered.io.num_writes(), 2);
|
assert_eq!(buffered.io.num_writes(), 2);
|
||||||
assert_eq!(buffered.write_buf.buf.bufs.len(), 1);
|
assert_eq!(buffered.write_buf.buf.bufs.len(), 1);
|
||||||
|
|
||||||
|
Ok::<_, ()>(())
|
||||||
|
})).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -711,20 +742,24 @@ mod tests {
|
|||||||
extern crate pretty_env_logger;
|
extern crate pretty_env_logger;
|
||||||
let _ = pretty_env_logger::try_init();
|
let _ = pretty_env_logger::try_init();
|
||||||
|
|
||||||
let mut mock = AsyncIo::new_buf(vec![], 1024);
|
block_on(future::lazy(move |cx| {
|
||||||
mock.max_read_vecs(0); // disable vectored IO
|
let mut mock = AsyncIo::new_buf(vec![], 1024);
|
||||||
let mut buffered = Buffered::<_, Cursor<Vec<u8>>>::new(mock);
|
mock.max_read_vecs(0); // disable vectored IO
|
||||||
buffered.write_buf.set_strategy(Strategy::Queue);
|
let mut buffered = Buffered::<_, Cursor<Vec<u8>>>::new(mock);
|
||||||
|
buffered.write_buf.set_strategy(Strategy::Queue);
|
||||||
|
|
||||||
// we have 4 buffers, and vec IO disabled, but explicitly said
|
// we have 4 buffers, and vec IO disabled, but explicitly said
|
||||||
// don't try to auto detect (via setting strategy above)
|
// don't try to auto detect (via setting strategy above)
|
||||||
buffered.write_buf_mut().extend(b"hello ");
|
buffered.write_buf_mut().extend(b"hello ");
|
||||||
buffered.buffer(Cursor::new(b"world, ".to_vec()));
|
buffered.buffer(Cursor::new(b"world, ".to_vec()));
|
||||||
buffered.write_buf_mut().extend(b"it's ");
|
buffered.write_buf_mut().extend(b"it's ");
|
||||||
buffered.buffer(Cursor::new(b"hyper!".to_vec()));
|
buffered.buffer(Cursor::new(b"hyper!".to_vec()));
|
||||||
buffered.flush().unwrap();
|
buffered.flush(cx).unwrap();
|
||||||
|
|
||||||
assert_eq!(buffered.io, b"hello world, it's hyper!");
|
assert_eq!(buffered.io, b"hello world, it's hyper!");
|
||||||
assert_eq!(buffered.io.num_writes(), 4);
|
assert_eq!(buffered.io.num_writes(), 4);
|
||||||
|
|
||||||
|
Ok::<_, ()>(())
|
||||||
|
})).unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,8 @@ use std::fmt;
|
|||||||
|
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use futures::{Future, Poll};
|
use futures::{Future, Poll};
|
||||||
use tokio_io::{AsyncRead, AsyncWrite};
|
use futures::task;
|
||||||
|
use futures::io::{AsyncRead, AsyncWrite};
|
||||||
|
|
||||||
use proto;
|
use proto;
|
||||||
use proto::body::{Body, Entity};
|
use proto::body::{Body, Entity};
|
||||||
@@ -89,8 +90,8 @@ where S: Service<Request = Request<Body>, Response = Response<B>, Error = ::Erro
|
|||||||
/// upgrade. Once the upgrade is completed, the connection would be "done",
|
/// upgrade. Once the upgrade is completed, the connection would be "done",
|
||||||
/// but it is not desired to actally shutdown the IO object. Instead you
|
/// but it is not desired to actally shutdown the IO object. Instead you
|
||||||
/// would take it back using `into_parts`.
|
/// would take it back using `into_parts`.
|
||||||
pub fn poll_without_shutdown(&mut self) -> Poll<(), ::Error> {
|
pub fn poll_without_shutdown(&mut self, cx: &mut task::Context) -> Poll<(), ::Error> {
|
||||||
try_ready!(self.conn.poll_without_shutdown());
|
try_ready!(self.conn.poll_without_shutdown(cx));
|
||||||
Ok(().into())
|
Ok(().into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -103,8 +104,8 @@ where S: Service<Request = Request<Body>, Response = Response<B>, Error = ::Erro
|
|||||||
type Item = ();
|
type Item = ();
|
||||||
type Error = ::Error;
|
type Error = ::Error;
|
||||||
|
|
||||||
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
fn poll(&mut self, cx: &mut task::Context) -> Poll<Self::Item, Self::Error> {
|
||||||
self.conn.poll()
|
self.conn.poll(cx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
//! them off to a `Service`.
|
//! them off to a `Service`.
|
||||||
|
|
||||||
pub mod conn;
|
pub mod conn;
|
||||||
mod service;
|
|
||||||
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::io;
|
use std::io;
|
||||||
@@ -13,16 +12,15 @@ use std::net::{SocketAddr, TcpListener as StdTcpListener};
|
|||||||
use std::sync::{Arc, Mutex, Weak};
|
use std::sync::{Arc, Mutex, Weak};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use futures::task::{self, Task};
|
use futures::task::{self, Waker};
|
||||||
use futures::future::{self};
|
use futures::future::{self};
|
||||||
use futures::{Future, Stream, Poll, Async};
|
use futures::{Future, FutureExt, Stream, StreamExt, Poll, Async};
|
||||||
|
use futures::io::{AsyncRead, AsyncWrite};
|
||||||
|
use futures::executor::spawn;
|
||||||
use futures_timer::Delay;
|
use futures_timer::Delay;
|
||||||
use http::{Request, Response};
|
use http::{Request, Response};
|
||||||
use tokio_io::{AsyncRead, AsyncWrite};
|
|
||||||
use tokio::spawn;
|
|
||||||
use tokio::reactor::Handle;
|
use tokio::reactor::Handle;
|
||||||
use tokio::net::TcpListener;
|
use tokio::net::TcpListener;
|
||||||
pub use tokio_service::{NewService, Service};
|
|
||||||
|
|
||||||
use proto::body::{Body, Entity};
|
use proto::body::{Body, Entity};
|
||||||
use proto;
|
use proto;
|
||||||
@@ -30,7 +28,8 @@ use self::addr_stream::AddrStream;
|
|||||||
use self::hyper_service::HyperService;
|
use self::hyper_service::HyperService;
|
||||||
|
|
||||||
pub use self::conn::Connection;
|
pub use self::conn::Connection;
|
||||||
pub use self::service::{const_service, service_fn};
|
pub use super::service::{NewService, Service};
|
||||||
|
pub use super::service::{const_service, service_fn};
|
||||||
|
|
||||||
/// A configuration of the HTTP protocol.
|
/// A configuration of the HTTP protocol.
|
||||||
///
|
///
|
||||||
@@ -255,11 +254,10 @@ impl<B: AsRef<[u8]> + 'static> Http<B> {
|
|||||||
/// # extern crate futures;
|
/// # extern crate futures;
|
||||||
/// # extern crate hyper;
|
/// # extern crate hyper;
|
||||||
/// # extern crate tokio;
|
/// # extern crate tokio;
|
||||||
/// # extern crate tokio_io;
|
/// # use futures::FutureExt;
|
||||||
/// # use futures::Future;
|
/// # use futures::io::{AsyncRead, AsyncWrite};
|
||||||
/// # use hyper::{Body, Request, Response};
|
/// # use hyper::{Body, Request, Response};
|
||||||
/// # use hyper::server::{Http, Service};
|
/// # use hyper::server::{Http, Service};
|
||||||
/// # use tokio_io::{AsyncRead, AsyncWrite};
|
|
||||||
/// # use tokio::reactor::Handle;
|
/// # use tokio::reactor::Handle;
|
||||||
/// # fn run<I, S>(some_io: I, some_service: S)
|
/// # fn run<I, S>(some_io: I, some_service: S)
|
||||||
/// # where
|
/// # where
|
||||||
@@ -272,9 +270,9 @@ impl<B: AsRef<[u8]> + 'static> Http<B> {
|
|||||||
///
|
///
|
||||||
/// let fut = conn
|
/// let fut = conn
|
||||||
/// .map(|_| ())
|
/// .map(|_| ())
|
||||||
/// .map_err(|e| eprintln!("server connection error: {}", e));
|
/// .map_err(|e| panic!("server connection error: {}", e));
|
||||||
///
|
///
|
||||||
/// tokio::spawn(fut);
|
/// tokio::spawn2(fut);
|
||||||
/// # }
|
/// # }
|
||||||
/// # fn main() {}
|
/// # fn main() {}
|
||||||
/// ```
|
/// ```
|
||||||
@@ -334,8 +332,8 @@ impl Future for Run {
|
|||||||
type Item = ();
|
type Item = ();
|
||||||
type Error = ::Error;
|
type Error = ::Error;
|
||||||
|
|
||||||
fn poll(&mut self) -> Poll<(), ::Error> {
|
fn poll(&mut self, cx: &mut task::Context) -> Poll<(), ::Error> {
|
||||||
self.0.poll()
|
self.0.poll(cx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -412,17 +410,21 @@ impl<S, B> Server<S, B>
|
|||||||
let addr = socket.remote_addr;
|
let addr = socket.remote_addr;
|
||||||
debug!("accepted new connection ({})", addr);
|
debug!("accepted new connection ({})", addr);
|
||||||
|
|
||||||
let service = new_service.new_service()?;
|
let service = match new_service.new_service() {
|
||||||
|
Ok(service) => service,
|
||||||
|
Err(err) => return future::err(err).left()
|
||||||
|
};
|
||||||
|
|
||||||
let s = NotifyService {
|
let s = NotifyService {
|
||||||
inner: service,
|
inner: service,
|
||||||
info: Arc::downgrade(&info_cloned),
|
info: Arc::downgrade(&info_cloned),
|
||||||
};
|
};
|
||||||
info_cloned.lock().unwrap().active += 1;
|
info_cloned.lock().unwrap().active += 1;
|
||||||
let fut = protocol.serve_connection(socket, s)
|
let fut = protocol.serve_connection(socket, s).recover(move |err| {
|
||||||
.map(|_| ())
|
error!("server connection error: ({}) {}", addr, err);
|
||||||
.map_err(move |err| error!("server connection error: ({}) {}", addr, err));
|
});
|
||||||
spawn(fut);
|
|
||||||
Ok(())
|
spawn(fut).map_err(|err| err.never_into()).right()
|
||||||
});
|
});
|
||||||
|
|
||||||
// for now, we don't care if the shutdown signal succeeds or errors
|
// for now, we don't care if the shutdown signal succeeds or errors
|
||||||
@@ -438,8 +440,11 @@ impl<S, B> Server<S, B>
|
|||||||
// stop accepting incoming connections.
|
// stop accepting incoming connections.
|
||||||
let main_execution = shutdown_signal.select(srv).then(move |result| {
|
let main_execution = shutdown_signal.select(srv).then(move |result| {
|
||||||
match result {
|
match result {
|
||||||
Ok(((), _incoming)) => {},
|
Ok(_) => {},
|
||||||
Err((e, _other)) => return future::Either::A(future::err(e.into()))
|
Err(future::Either::Left((e, _other))) =>
|
||||||
|
return future::Either::Left(future::err(e)),
|
||||||
|
Err(future::Either::Right((e, _other))) =>
|
||||||
|
return future::Either::Left(future::err(e.into())),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ok we've stopped accepting new connections at this point, but we want
|
// Ok we've stopped accepting new connections at this point, but we want
|
||||||
@@ -451,10 +456,11 @@ impl<S, B> Server<S, B>
|
|||||||
// here have been destroyed.
|
// here have been destroyed.
|
||||||
let timeout = Delay::new(shutdown_timeout);
|
let timeout = Delay::new(shutdown_timeout);
|
||||||
let wait = WaitUntilZero { info: info.clone() };
|
let wait = WaitUntilZero { info: info.clone() };
|
||||||
future::Either::B(wait.select(timeout).then(|result| {
|
future::Either::Right(wait.select(timeout).then(|result| {
|
||||||
match result {
|
match result {
|
||||||
Ok(_) => Ok(()),
|
Ok(_) => Ok(()),
|
||||||
Err((e, _)) => Err(e.into())
|
Err(future::Either::Left((e, _))) => Err(e.into()),
|
||||||
|
Err(future::Either::Right((e, _))) => Err(e.into())
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
});
|
});
|
||||||
@@ -505,8 +511,8 @@ where
|
|||||||
type Item = Connection<I::Item, S::Instance>;
|
type Item = Connection<I::Item, S::Instance>;
|
||||||
type Error = ::Error;
|
type Error = ::Error;
|
||||||
|
|
||||||
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
|
fn poll_next(&mut self, cx: &mut task::Context) -> Poll<Option<Self::Item>, Self::Error> {
|
||||||
if let Some(io) = try_ready!(self.incoming.poll()) {
|
if let Some(io) = try_ready!(self.incoming.poll_next(cx)) {
|
||||||
let service = self.new_service.new_service()?;
|
let service = self.new_service.new_service()?;
|
||||||
Ok(Async::Ready(Some(self.protocol.serve_connection(io, service))))
|
Ok(Async::Ready(Some(self.protocol.serve_connection(io, service))))
|
||||||
} else {
|
} else {
|
||||||
@@ -586,17 +592,17 @@ impl Stream for AddrIncoming {
|
|||||||
type Item = AddrStream;
|
type Item = AddrStream;
|
||||||
type Error = ::std::io::Error;
|
type Error = ::std::io::Error;
|
||||||
|
|
||||||
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
|
fn poll_next(&mut self, cx: &mut task::Context) -> Poll<Option<Self::Item>, Self::Error> {
|
||||||
// Check if a previous timeout is active that was set by IO errors.
|
// Check if a previous timeout is active that was set by IO errors.
|
||||||
if let Some(ref mut to) = self.timeout {
|
if let Some(ref mut to) = self.timeout {
|
||||||
match to.poll().expect("timeout never fails") {
|
match to.poll(cx).expect("timeout never fails") {
|
||||||
Async::Ready(_) => {}
|
Async::Ready(_) => {}
|
||||||
Async::NotReady => return Ok(Async::NotReady),
|
Async::Pending => return Ok(Async::Pending),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.timeout = None;
|
self.timeout = None;
|
||||||
loop {
|
loop {
|
||||||
match self.listener.poll_accept() {
|
match self.listener.poll_accept2(cx) {
|
||||||
Ok(Async::Ready((socket, addr))) => {
|
Ok(Async::Ready((socket, addr))) => {
|
||||||
if let Some(dur) = self.keep_alive_timeout {
|
if let Some(dur) = self.keep_alive_timeout {
|
||||||
if let Err(e) = socket.set_keepalive(Some(dur)) {
|
if let Err(e) = socket.set_keepalive(Some(dur)) {
|
||||||
@@ -605,7 +611,7 @@ impl Stream for AddrIncoming {
|
|||||||
}
|
}
|
||||||
return Ok(Async::Ready(Some(AddrStream::new(socket, addr))));
|
return Ok(Async::Ready(Some(AddrStream::new(socket, addr))));
|
||||||
},
|
},
|
||||||
Ok(Async::NotReady) => return Ok(Async::NotReady),
|
Ok(Async::Pending) => return Ok(Async::Pending),
|
||||||
Err(ref e) if self.sleep_on_errors => {
|
Err(ref e) if self.sleep_on_errors => {
|
||||||
// Connection errors can be ignored directly, continue by
|
// Connection errors can be ignored directly, continue by
|
||||||
// accepting the next request.
|
// accepting the next request.
|
||||||
@@ -617,13 +623,13 @@ impl Stream for AddrIncoming {
|
|||||||
debug!("accept error: {}; sleeping {:?}",
|
debug!("accept error: {}; sleeping {:?}",
|
||||||
e, delay);
|
e, delay);
|
||||||
let mut timeout = Delay::new(delay);
|
let mut timeout = Delay::new(delay);
|
||||||
let result = timeout.poll()
|
let result = timeout.poll(cx)
|
||||||
.expect("timeout never fails");
|
.expect("timeout never fails");
|
||||||
match result {
|
match result {
|
||||||
Async::Ready(()) => continue,
|
Async::Ready(()) => continue,
|
||||||
Async::NotReady => {
|
Async::Pending => {
|
||||||
self.timeout = Some(timeout);
|
self.timeout = Some(timeout);
|
||||||
return Ok(Async::NotReady);
|
return Ok(Async::Pending);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -647,12 +653,13 @@ fn connection_error(e: &io::Error) -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mod addr_stream {
|
mod addr_stream {
|
||||||
use std::io::{self, Read, Write};
|
use std::io;
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
use bytes::{Buf, BufMut};
|
|
||||||
use futures::Poll;
|
use futures::Poll;
|
||||||
|
use futures::task;
|
||||||
|
use futures::io::{AsyncRead, AsyncWrite, Initializer};
|
||||||
|
use iovec::IoVec;
|
||||||
use tokio::net::TcpStream;
|
use tokio::net::TcpStream;
|
||||||
use tokio_io::{AsyncRead, AsyncWrite};
|
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -670,46 +677,42 @@ mod addr_stream {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Read for AddrStream {
|
|
||||||
#[inline]
|
|
||||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
|
||||||
self.inner.read(buf)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Write for AddrStream {
|
|
||||||
#[inline]
|
|
||||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
|
||||||
self.inner.write(buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn flush(&mut self ) -> io::Result<()> {
|
|
||||||
self.inner.flush()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsyncRead for AddrStream {
|
impl AsyncRead for AddrStream {
|
||||||
#[inline]
|
#[inline]
|
||||||
unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool {
|
fn poll_read(&mut self, cx: &mut task::Context, buf: &mut [u8]) -> Poll<usize, io::Error> {
|
||||||
self.inner.prepare_uninitialized_buffer(buf)
|
self.inner.poll_read(cx, buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn read_buf<B: BufMut>(&mut self, buf: &mut B) -> Poll<usize, io::Error> {
|
unsafe fn initializer(&self) -> Initializer {
|
||||||
self.inner.read_buf(buf)
|
AsyncRead::initializer(&self.inner)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn poll_vectored_read(&mut self, cx: &mut task::Context, vec: &mut [&mut IoVec]) -> Poll<usize, io::Error> {
|
||||||
|
self.inner.poll_vectored_read(cx, vec)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsyncWrite for AddrStream {
|
impl AsyncWrite for AddrStream {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn shutdown(&mut self) -> Poll<(), io::Error> {
|
fn poll_write(&mut self, cx: &mut task::Context, buf: &[u8]) -> Poll<usize, io::Error> {
|
||||||
AsyncWrite::shutdown(&mut self.inner)
|
self.inner.poll_write(cx, buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn write_buf<B: Buf>(&mut self, buf: &mut B) -> Poll<usize, io::Error> {
|
fn poll_flush(&mut self, cx: &mut task::Context) -> Poll<(), io::Error> {
|
||||||
self.inner.write_buf(buf)
|
self.inner.poll_flush(cx)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn poll_close(&mut self, cx: &mut task::Context) -> Poll<(), io::Error> {
|
||||||
|
self.inner.poll_close(cx)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn poll_vectored_write(&mut self, cx: &mut task::Context, vec: &[&IoVec]) -> Poll<usize, io::Error> {
|
||||||
|
self.inner.poll_vectored_write(cx, vec)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -727,7 +730,7 @@ struct WaitUntilZero {
|
|||||||
|
|
||||||
struct Info {
|
struct Info {
|
||||||
active: usize,
|
active: usize,
|
||||||
blocker: Option<Task>,
|
blocker: Option<Waker>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S: Service> Service for NotifyService<S> {
|
impl<S: Service> Service for NotifyService<S> {
|
||||||
@@ -750,8 +753,8 @@ impl<S> Drop for NotifyService<S> {
|
|||||||
let mut info = info.lock().unwrap();
|
let mut info = info.lock().unwrap();
|
||||||
info.active -= 1;
|
info.active -= 1;
|
||||||
if info.active == 0 {
|
if info.active == 0 {
|
||||||
if let Some(task) = info.blocker.take() {
|
if let Some(waker) = info.blocker.take() {
|
||||||
task.notify();
|
waker.wake();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -761,13 +764,13 @@ impl Future for WaitUntilZero {
|
|||||||
type Item = ();
|
type Item = ();
|
||||||
type Error = io::Error;
|
type Error = io::Error;
|
||||||
|
|
||||||
fn poll(&mut self) -> Poll<(), io::Error> {
|
fn poll(&mut self, cx: &mut task::Context) -> Poll<(), io::Error> {
|
||||||
let mut info = self.info.lock().unwrap();
|
let mut info = self.info.lock().unwrap();
|
||||||
if info.active == 0 {
|
if info.active == 0 {
|
||||||
Ok(().into())
|
Ok(().into())
|
||||||
} else {
|
} else {
|
||||||
info.blocker = Some(task::current());
|
info.blocker = Some(cx.waker().clone());
|
||||||
Ok(Async::NotReady)
|
Ok(Async::Pending)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,64 +0,0 @@
|
|||||||
use std::marker::PhantomData;
|
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use futures::IntoFuture;
|
|
||||||
use tokio_service::{NewService, Service};
|
|
||||||
|
|
||||||
/// Create a `Service` from a function.
|
|
||||||
pub fn service_fn<F, R, S>(f: F) -> ServiceFn<F, R>
|
|
||||||
where
|
|
||||||
F: Fn(R) -> S,
|
|
||||||
S: IntoFuture,
|
|
||||||
{
|
|
||||||
ServiceFn {
|
|
||||||
f: f,
|
|
||||||
_req: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a `NewService` by sharing references of `service.
|
|
||||||
pub fn const_service<S>(service: S) -> ConstService<S> {
|
|
||||||
ConstService {
|
|
||||||
svc: Arc::new(service),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct ServiceFn<F, R> {
|
|
||||||
f: F,
|
|
||||||
_req: PhantomData<fn() -> R>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<F, R, S> Service for ServiceFn<F, R>
|
|
||||||
where
|
|
||||||
F: Fn(R) -> S,
|
|
||||||
S: IntoFuture,
|
|
||||||
{
|
|
||||||
type Request = R;
|
|
||||||
type Response = S::Item;
|
|
||||||
type Error = S::Error;
|
|
||||||
type Future = S::Future;
|
|
||||||
|
|
||||||
fn call(&self, req: Self::Request) -> Self::Future {
|
|
||||||
(self.f)(req).into_future()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct ConstService<S> {
|
|
||||||
svc: Arc<S>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<S> NewService for ConstService<S>
|
|
||||||
where
|
|
||||||
S: Service,
|
|
||||||
{
|
|
||||||
type Request = S::Request;
|
|
||||||
type Response = S::Response;
|
|
||||||
type Error = S::Error;
|
|
||||||
type Instance = Arc<S>;
|
|
||||||
|
|
||||||
fn new_service(&self) -> ::std::io::Result<Self::Instance> {
|
|
||||||
Ok(self.svc.clone())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
116
src/service.rs
Normal file
116
src/service.rs
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
use std::marker::PhantomData;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use futures::{Future, IntoFuture};
|
||||||
|
|
||||||
|
/// An asynchronous function from `Request` to a `Response`.
|
||||||
|
pub trait Service {
|
||||||
|
/// Requests handled by the service.
|
||||||
|
type Request;
|
||||||
|
/// Responses given by the service.
|
||||||
|
type Response;
|
||||||
|
/// Errors produced by the service.
|
||||||
|
type Error;
|
||||||
|
/// The future response value.
|
||||||
|
type Future: Future<Item = Self::Response, Error = Self::Error>;
|
||||||
|
/// Process the request and return the response asynchronously.
|
||||||
|
fn call(&self, req: Self::Request) -> Self::Future;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S: Service + ?Sized> Service for Arc<S> {
|
||||||
|
type Request = S::Request;
|
||||||
|
type Response = S::Response;
|
||||||
|
type Error = S::Error;
|
||||||
|
type Future = S::Future;
|
||||||
|
|
||||||
|
fn call(&self, request: S::Request) -> S::Future {
|
||||||
|
(**self).call(request)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates new `Service` values.
|
||||||
|
pub trait NewService {
|
||||||
|
/// Requests handled by the service.
|
||||||
|
type Request;
|
||||||
|
/// Responses given by the service.
|
||||||
|
type Response;
|
||||||
|
/// Errors produced by the service.
|
||||||
|
type Error;
|
||||||
|
/// The `Service` value created by this factory.
|
||||||
|
type Instance: Service<Request = Self::Request, Response = Self::Response, Error = Self::Error>;
|
||||||
|
/// Create and return a new service value.
|
||||||
|
fn new_service(&self) -> ::std::io::Result<Self::Instance>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F, R> NewService for F
|
||||||
|
where F: Fn() -> ::std::io::Result<R>,
|
||||||
|
R: Service,
|
||||||
|
{
|
||||||
|
type Request = R::Request;
|
||||||
|
type Response = R::Response;
|
||||||
|
type Error = R::Error;
|
||||||
|
type Instance = R;
|
||||||
|
|
||||||
|
fn new_service(&self) -> ::std::io::Result<R> {
|
||||||
|
(*self)()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a `Service` from a function.
|
||||||
|
pub fn service_fn<F, R, S>(f: F) -> ServiceFn<F, R>
|
||||||
|
where
|
||||||
|
F: Fn(R) -> S,
|
||||||
|
S: IntoFuture,
|
||||||
|
{
|
||||||
|
ServiceFn {
|
||||||
|
f: f,
|
||||||
|
_req: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a `NewService` by sharing references of `service.
|
||||||
|
pub fn const_service<S>(service: S) -> ConstService<S> {
|
||||||
|
ConstService {
|
||||||
|
svc: Arc::new(service),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ServiceFn<F, R> {
|
||||||
|
f: F,
|
||||||
|
_req: PhantomData<fn() -> R>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F, R, S> Service for ServiceFn<F, R>
|
||||||
|
where
|
||||||
|
F: Fn(R) -> S,
|
||||||
|
S: IntoFuture,
|
||||||
|
{
|
||||||
|
type Request = R;
|
||||||
|
type Response = S::Item;
|
||||||
|
type Error = S::Error;
|
||||||
|
type Future = S::Future;
|
||||||
|
|
||||||
|
fn call(&self, req: Self::Request) -> Self::Future {
|
||||||
|
(self.f)(req).into_future()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ConstService<S> {
|
||||||
|
svc: Arc<S>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S> NewService for ConstService<S>
|
||||||
|
where
|
||||||
|
S: Service,
|
||||||
|
{
|
||||||
|
type Request = S::Request;
|
||||||
|
type Response = S::Response;
|
||||||
|
type Error = S::Error;
|
||||||
|
type Instance = Arc<S>;
|
||||||
|
|
||||||
|
fn new_service(&self) -> ::std::io::Result<Self::Instance> {
|
||||||
|
Ok(self.svc.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
341
tests/client.rs
341
tests/client.rs
@@ -5,7 +5,6 @@ extern crate futures;
|
|||||||
extern crate futures_timer;
|
extern crate futures_timer;
|
||||||
extern crate net2;
|
extern crate net2;
|
||||||
extern crate tokio;
|
extern crate tokio;
|
||||||
extern crate tokio_io;
|
|
||||||
extern crate pretty_env_logger;
|
extern crate pretty_env_logger;
|
||||||
|
|
||||||
use std::io::{self, Read, Write};
|
use std::io::{self, Read, Write};
|
||||||
@@ -15,8 +14,9 @@ use std::time::Duration;
|
|||||||
|
|
||||||
use hyper::{Body, Client, Method, Request, StatusCode};
|
use hyper::{Body, Client, Method, Request, StatusCode};
|
||||||
|
|
||||||
use futures::{Future, Stream};
|
use futures::{FutureExt, StreamExt};
|
||||||
use futures::sync::oneshot;
|
use futures::channel::oneshot;
|
||||||
|
use futures::executor::block_on;
|
||||||
use tokio::reactor::Handle;
|
use tokio::reactor::Handle;
|
||||||
use tokio::runtime::Runtime;
|
use tokio::runtime::Runtime;
|
||||||
use tokio::net::{ConnectFuture, TcpStream};
|
use tokio::net::{ConnectFuture, TcpStream};
|
||||||
@@ -111,11 +111,7 @@ macro_rules! test {
|
|||||||
assert_eq!(res.headers()[$response_header_name], $response_header_val);
|
assert_eq!(res.headers()[$response_header_name], $response_header_val);
|
||||||
)*
|
)*
|
||||||
|
|
||||||
let body = res
|
let body = block_on(res.into_body().into_stream().concat())
|
||||||
.into_body()
|
|
||||||
.into_stream()
|
|
||||||
.concat2()
|
|
||||||
.wait()
|
|
||||||
.expect("body concat wait");
|
.expect("body concat wait");
|
||||||
|
|
||||||
let expected_res_body = Option::<&[u8]>::from($response_body)
|
let expected_res_body = Option::<&[u8]>::from($response_body)
|
||||||
@@ -186,7 +182,7 @@ macro_rules! test {
|
|||||||
if !$set_host {
|
if !$set_host {
|
||||||
config = config.set_host(false);
|
config = config.set_host(false);
|
||||||
}
|
}
|
||||||
let client = config.build_with_executor(&runtime.handle(), runtime.executor());
|
let client = config.build(&runtime.handle());
|
||||||
|
|
||||||
let body = if let Some(body) = $request_body {
|
let body = if let Some(body) = $request_body {
|
||||||
let body: &'static str = body;
|
let body: &'static str = body;
|
||||||
@@ -203,7 +199,7 @@ macro_rules! test {
|
|||||||
.body(body)
|
.body(body)
|
||||||
.expect("request builder");
|
.expect("request builder");
|
||||||
|
|
||||||
let res = client.request(req);
|
let res = client.request(req).with_executor(runtime.executor());
|
||||||
|
|
||||||
let (tx, rx) = oneshot::channel();
|
let (tx, rx) = oneshot::channel();
|
||||||
|
|
||||||
@@ -230,7 +226,7 @@ macro_rules! test {
|
|||||||
|
|
||||||
let rx = rx.map_err(|_| hyper::Error::Io(io::Error::new(io::ErrorKind::Other, "thread panicked")));
|
let rx = rx.map_err(|_| hyper::Error::Io(io::Error::new(io::ErrorKind::Other, "thread panicked")));
|
||||||
|
|
||||||
res.join(rx).map(|r| r.0).wait()
|
block_on(res.join(rx).map(|r| r.0))
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -642,12 +638,15 @@ mod dispatch_impl {
|
|||||||
use std::thread;
|
use std::thread;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use futures::{self, Future};
|
use futures::{self, Future, FutureExt, Stream, StreamExt};
|
||||||
use futures::sync::{mpsc, oneshot};
|
use futures::channel::{mpsc, oneshot};
|
||||||
|
use futures::io::{AsyncRead, AsyncWrite};
|
||||||
|
use futures::future::lazy;
|
||||||
|
use futures::executor::block_on;
|
||||||
|
use futures::task;
|
||||||
use futures_timer::Delay;
|
use futures_timer::Delay;
|
||||||
use tokio::net::TcpStream;
|
use tokio::net::TcpStream;
|
||||||
use tokio::runtime::Runtime;
|
use tokio::runtime::Runtime;
|
||||||
use tokio_io::{AsyncRead, AsyncWrite};
|
|
||||||
|
|
||||||
use hyper::client::connect::{Connect, Connected, Destination, HttpConnector};
|
use hyper::client::connect::{Connect, Connected, Destination, HttpConnector};
|
||||||
use hyper::Client;
|
use hyper::Client;
|
||||||
@@ -666,7 +665,7 @@ mod dispatch_impl {
|
|||||||
let (closes_tx, closes) = mpsc::channel(10);
|
let (closes_tx, closes) = mpsc::channel(10);
|
||||||
let client = Client::configure()
|
let client = Client::configure()
|
||||||
.connector(DebugConnector::with_http_and_closes(HttpConnector::new(1, &runtime.handle()), closes_tx))
|
.connector(DebugConnector::with_http_and_closes(HttpConnector::new(1, &runtime.handle()), closes_tx))
|
||||||
.executor(runtime.executor());
|
.build();
|
||||||
|
|
||||||
let (tx1, rx1) = oneshot::channel();
|
let (tx1, rx1) = oneshot::channel();
|
||||||
|
|
||||||
@@ -686,15 +685,15 @@ mod dispatch_impl {
|
|||||||
.uri(&*format!("http://{}/a", addr))
|
.uri(&*format!("http://{}/a", addr))
|
||||||
.body(Body::empty())
|
.body(Body::empty())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let res = client.request(req).and_then(move |res| {
|
let res = client.request(req).with_executor(runtime.executor()).and_then(move |res| {
|
||||||
assert_eq!(res.status(), hyper::StatusCode::OK);
|
assert_eq!(res.status(), hyper::StatusCode::OK);
|
||||||
Delay::new(Duration::from_secs(1))
|
Delay::new(Duration::from_secs(1))
|
||||||
.from_err()
|
.err_into()
|
||||||
});
|
});
|
||||||
let rx = rx1.map_err(|_| hyper::Error::Io(io::Error::new(io::ErrorKind::Other, "thread panicked")));
|
let rx = rx1.map_err(|_| hyper::Error::Io(io::Error::new(io::ErrorKind::Other, "thread panicked")));
|
||||||
res.join(rx).map(|r| r.0).wait().unwrap();
|
block_on(res.join(rx).map(|r| r.0)).unwrap();
|
||||||
|
|
||||||
closes.into_future().wait().unwrap().0.expect("closes");
|
block_on(closes.next()).unwrap().0.expect("closes");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -725,25 +724,25 @@ mod dispatch_impl {
|
|||||||
let res = {
|
let res = {
|
||||||
let client = Client::configure()
|
let client = Client::configure()
|
||||||
.connector(DebugConnector::with_http_and_closes(HttpConnector::new(1, &handle), closes_tx))
|
.connector(DebugConnector::with_http_and_closes(HttpConnector::new(1, &handle), closes_tx))
|
||||||
.executor(runtime.executor());
|
.build();
|
||||||
|
|
||||||
let req = Request::builder()
|
let req = Request::builder()
|
||||||
.uri(&*format!("http://{}/a", addr))
|
.uri(&*format!("http://{}/a", addr))
|
||||||
.body(Body::empty())
|
.body(Body::empty())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
client.request(req).and_then(move |res| {
|
client.request(req).with_executor(runtime.executor()).and_then(move |res| {
|
||||||
assert_eq!(res.status(), hyper::StatusCode::OK);
|
assert_eq!(res.status(), hyper::StatusCode::OK);
|
||||||
res.into_body().into_stream().concat2()
|
res.into_body().into_stream().concat()
|
||||||
}).and_then(|_| {
|
}).and_then(|_| {
|
||||||
Delay::new(Duration::from_secs(1))
|
Delay::new(Duration::from_secs(1))
|
||||||
.from_err()
|
.err_into()
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
// client is dropped
|
// client is dropped
|
||||||
let rx = rx1.map_err(|_| hyper::Error::Io(io::Error::new(io::ErrorKind::Other, "thread panicked")));
|
let rx = rx1.map_err(|_| hyper::Error::Io(io::Error::new(io::ErrorKind::Other, "thread panicked")));
|
||||||
res.join(rx).map(|r| r.0).wait().unwrap();
|
block_on(res.join(rx).map(|r| r.0)).unwrap();
|
||||||
|
|
||||||
closes.into_future().wait().unwrap().0.expect("closes");
|
block_on(closes.next()).unwrap().0.expect("closes");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -773,41 +772,41 @@ mod dispatch_impl {
|
|||||||
|
|
||||||
// prevent this thread from closing until end of test, so the connection
|
// prevent this thread from closing until end of test, so the connection
|
||||||
// stays open and idle until Client is dropped
|
// stays open and idle until Client is dropped
|
||||||
let _ = client_drop_rx.wait();
|
let _ = block_on(client_drop_rx);
|
||||||
});
|
});
|
||||||
|
|
||||||
let client = Client::configure()
|
let client = Client::configure()
|
||||||
.connector(DebugConnector::with_http_and_closes(HttpConnector::new(1, &handle), closes_tx))
|
.connector(DebugConnector::with_http_and_closes(HttpConnector::new(1, &handle), closes_tx))
|
||||||
.executor(runtime.executor());
|
.build();
|
||||||
|
|
||||||
let req = Request::builder()
|
let req = Request::builder()
|
||||||
.uri(&*format!("http://{}/a", addr))
|
.uri(&*format!("http://{}/a", addr))
|
||||||
.body(Body::empty())
|
.body(Body::empty())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let res = client.request(req).and_then(move |res| {
|
let res = client.request(req).with_executor(runtime.executor()).and_then(move |res| {
|
||||||
assert_eq!(res.status(), hyper::StatusCode::OK);
|
assert_eq!(res.status(), hyper::StatusCode::OK);
|
||||||
res.into_body().into_stream().concat2()
|
res.into_body().into_stream().concat()
|
||||||
});
|
});
|
||||||
let rx = rx1.map_err(|_| hyper::Error::Io(io::Error::new(io::ErrorKind::Other, "thread panicked")));
|
let rx = rx1.map_err(|_| hyper::Error::Io(io::Error::new(io::ErrorKind::Other, "thread panicked")));
|
||||||
res.join(rx).map(|r| r.0).wait().unwrap();
|
block_on(res.join(rx).map(|r| r.0)).unwrap();
|
||||||
|
|
||||||
// not closed yet, just idle
|
// not closed yet, just idle
|
||||||
{
|
{
|
||||||
futures::future::poll_fn(|| {
|
block_on(lazy(|cx| {
|
||||||
assert!(closes.poll()?.is_not_ready());
|
assert!(closes.poll_next(cx).unwrap().is_pending());
|
||||||
Ok::<_, ()>(().into())
|
Ok::<_, ()>(())
|
||||||
}).wait().unwrap();
|
})).unwrap();
|
||||||
}
|
}
|
||||||
drop(client);
|
drop(client);
|
||||||
|
|
||||||
let t = Delay::new(Duration::from_millis(100))
|
let t = Delay::new(Duration::from_millis(100))
|
||||||
.map(|_| panic!("time out"));
|
.map(|_| panic!("time out"));
|
||||||
let close = closes.into_future()
|
let close = closes.next()
|
||||||
.map(|(opt, _)| {
|
.map(|(opt, _)| {
|
||||||
opt.expect("closes");
|
opt.expect("closes");
|
||||||
})
|
})
|
||||||
.map_err(|_| panic!("closes dropped"));
|
.map_err(|_| panic!("closes dropped"));
|
||||||
let _ = t.select(close).wait();
|
let _ = block_on(t.select(close));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -836,32 +835,32 @@ mod dispatch_impl {
|
|||||||
|
|
||||||
// prevent this thread from closing until end of test, so the connection
|
// prevent this thread from closing until end of test, so the connection
|
||||||
// stays open and idle until Client is dropped
|
// stays open and idle until Client is dropped
|
||||||
let _ = client_drop_rx.wait();
|
let _ = block_on(client_drop_rx);
|
||||||
});
|
});
|
||||||
|
|
||||||
let res = {
|
let res = {
|
||||||
let client = Client::configure()
|
let client = Client::configure()
|
||||||
.connector(DebugConnector::with_http_and_closes(HttpConnector::new(1, &handle), closes_tx))
|
.connector(DebugConnector::with_http_and_closes(HttpConnector::new(1, &handle), closes_tx))
|
||||||
.executor(runtime.executor());
|
.build();
|
||||||
|
|
||||||
let req = Request::builder()
|
let req = Request::builder()
|
||||||
.uri(&*format!("http://{}/a", addr))
|
.uri(&*format!("http://{}/a", addr))
|
||||||
.body(Body::empty())
|
.body(Body::empty())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
client.request(req)
|
client.request(req).with_executor(runtime.executor())
|
||||||
};
|
};
|
||||||
|
|
||||||
//let rx = rx1.map_err(|_| hyper::Error::Io(io::Error::new(io::ErrorKind::Other, "thread panicked")));
|
//let rx = rx1.map_err(|_| hyper::Error::Io(io::Error::new(io::ErrorKind::Other, "thread panicked")));
|
||||||
res.select2(rx1).wait().unwrap();
|
block_on(res.select(rx1)).unwrap();
|
||||||
// res now dropped
|
// res now dropped
|
||||||
let t = Delay::new(Duration::from_millis(100))
|
let t = Delay::new(Duration::from_millis(100))
|
||||||
.map(|_| panic!("time out"));
|
.map(|_| panic!("time out"));
|
||||||
let close = closes.into_future()
|
let close = closes.next()
|
||||||
.map(|(opt, _)| {
|
.map(|(opt, _)| {
|
||||||
opt.expect("closes");
|
opt.expect("closes");
|
||||||
})
|
})
|
||||||
.map_err(|_| panic!("closes dropped"));
|
.map_err(|_| panic!("closes dropped"));
|
||||||
let _ = t.select(close).wait();
|
let _ = block_on(t.select(close));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -888,33 +887,33 @@ mod dispatch_impl {
|
|||||||
|
|
||||||
// prevent this thread from closing until end of test, so the connection
|
// prevent this thread from closing until end of test, so the connection
|
||||||
// stays open and idle until Client is dropped
|
// stays open and idle until Client is dropped
|
||||||
let _ = client_drop_rx.wait();
|
let _ = block_on(client_drop_rx);
|
||||||
});
|
});
|
||||||
|
|
||||||
let res = {
|
let res = {
|
||||||
let client = Client::configure()
|
let client = Client::configure()
|
||||||
.connector(DebugConnector::with_http_and_closes(HttpConnector::new(1, &handle), closes_tx))
|
.connector(DebugConnector::with_http_and_closes(HttpConnector::new(1, &handle), closes_tx))
|
||||||
.executor(runtime.executor());
|
.build();
|
||||||
|
|
||||||
let req = Request::builder()
|
let req = Request::builder()
|
||||||
.uri(&*format!("http://{}/a", addr))
|
.uri(&*format!("http://{}/a", addr))
|
||||||
.body(Body::empty())
|
.body(Body::empty())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
// notably, havent read body yet
|
// notably, havent read body yet
|
||||||
client.request(req)
|
client.request(req).with_executor(runtime.executor())
|
||||||
};
|
};
|
||||||
|
|
||||||
let rx = rx1.map_err(|_| hyper::Error::Io(io::Error::new(io::ErrorKind::Other, "thread panicked")));
|
let rx = rx1.map_err(|_| hyper::Error::Io(io::Error::new(io::ErrorKind::Other, "thread panicked")));
|
||||||
res.join(rx).map(|r| r.0).wait().unwrap();
|
block_on(res.join(rx).map(|r| r.0)).unwrap();
|
||||||
|
|
||||||
let t = Delay::new(Duration::from_millis(100))
|
let t = Delay::new(Duration::from_millis(100))
|
||||||
.map(|_| panic!("time out"));
|
.map(|_| panic!("time out"));
|
||||||
let close = closes.into_future()
|
let close = closes.next()
|
||||||
.map(|(opt, _)| {
|
.map(|(opt, _)| {
|
||||||
opt.expect("closes");
|
opt.expect("closes");
|
||||||
})
|
})
|
||||||
.map_err(|_| panic!("closes dropped"));
|
.map_err(|_| panic!("closes dropped"));
|
||||||
let _ = t.select(close).wait();
|
let _ = block_on(t.select(close));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -939,33 +938,33 @@ mod dispatch_impl {
|
|||||||
sock.read(&mut buf).expect("read 1");
|
sock.read(&mut buf).expect("read 1");
|
||||||
sock.write_all(b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n").unwrap();
|
sock.write_all(b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n").unwrap();
|
||||||
let _ = tx1.send(());
|
let _ = tx1.send(());
|
||||||
let _ = rx2.wait();
|
let _ = block_on(rx2);
|
||||||
});
|
});
|
||||||
|
|
||||||
let client = Client::configure()
|
let client = Client::configure()
|
||||||
.connector(DebugConnector::with_http_and_closes(HttpConnector::new(1, &handle), closes_tx))
|
.connector(DebugConnector::with_http_and_closes(HttpConnector::new(1, &handle), closes_tx))
|
||||||
.keep_alive(false)
|
.keep_alive(false)
|
||||||
.executor(runtime.executor());
|
.build();
|
||||||
|
|
||||||
let req = Request::builder()
|
let req = Request::builder()
|
||||||
.uri(&*format!("http://{}/a", addr))
|
.uri(&*format!("http://{}/a", addr))
|
||||||
.body(Body::empty())
|
.body(Body::empty())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let res = client.request(req).and_then(move |res| {
|
let res = client.request(req).with_executor(runtime.executor()).and_then(move |res| {
|
||||||
assert_eq!(res.status(), hyper::StatusCode::OK);
|
assert_eq!(res.status(), hyper::StatusCode::OK);
|
||||||
res.into_body().into_stream().concat2()
|
res.into_body().into_stream().concat()
|
||||||
});
|
});
|
||||||
let rx = rx1.map_err(|_| hyper::Error::Io(io::Error::new(io::ErrorKind::Other, "thread panicked")));
|
let rx = rx1.map_err(|_| hyper::Error::Io(io::Error::new(io::ErrorKind::Other, "thread panicked")));
|
||||||
res.join(rx).map(|r| r.0).wait().unwrap();
|
block_on(res.join(rx).map(|r| r.0)).unwrap();
|
||||||
|
|
||||||
let t = Delay::new(Duration::from_millis(100))
|
let t = Delay::new(Duration::from_millis(100))
|
||||||
.map(|_| panic!("time out"));
|
.map(|_| panic!("time out"));
|
||||||
let close = closes.into_future()
|
let close = closes.next()
|
||||||
.map(|(opt, _)| {
|
.map(|(opt, _)| {
|
||||||
opt.expect("closes");
|
opt.expect("closes");
|
||||||
})
|
})
|
||||||
.map_err(|_| panic!("closes dropped"));
|
.map_err(|_| panic!("closes dropped"));
|
||||||
let _ = t.select(close).wait();
|
let _ = block_on(t.select(close));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -993,28 +992,28 @@ mod dispatch_impl {
|
|||||||
|
|
||||||
let client = Client::configure()
|
let client = Client::configure()
|
||||||
.connector(DebugConnector::with_http_and_closes(HttpConnector::new(1, &handle), closes_tx))
|
.connector(DebugConnector::with_http_and_closes(HttpConnector::new(1, &handle), closes_tx))
|
||||||
.executor(runtime.executor());
|
.build();
|
||||||
|
|
||||||
let req = Request::builder()
|
let req = Request::builder()
|
||||||
.uri(&*format!("http://{}/a", addr))
|
.uri(&*format!("http://{}/a", addr))
|
||||||
.body(Body::empty())
|
.body(Body::empty())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let res = client.request(req).and_then(move |res| {
|
let res = client.request(req).with_executor(runtime.executor()).and_then(move |res| {
|
||||||
assert_eq!(res.status(), hyper::StatusCode::OK);
|
assert_eq!(res.status(), hyper::StatusCode::OK);
|
||||||
res.into_body().into_stream().concat2()
|
res.into_body().into_stream().concat()
|
||||||
});
|
});
|
||||||
let rx = rx1.map_err(|_| hyper::Error::Io(io::Error::new(io::ErrorKind::Other, "thread panicked")));
|
let rx = rx1.map_err(|_| hyper::Error::Io(io::Error::new(io::ErrorKind::Other, "thread panicked")));
|
||||||
res.join(rx).map(|r| r.0).wait().unwrap();
|
block_on(res.join(rx).map(|r| r.0)).unwrap();
|
||||||
|
|
||||||
|
|
||||||
let t = Delay::new(Duration::from_millis(100))
|
let t = Delay::new(Duration::from_millis(100))
|
||||||
.map(|_| panic!("time out"));
|
.map(|_| panic!("time out"));
|
||||||
let close = closes.into_future()
|
let close = closes.next()
|
||||||
.map(|(opt, _)| {
|
.map(|(opt, _)| {
|
||||||
opt.expect("closes");
|
opt.expect("closes");
|
||||||
})
|
})
|
||||||
.map_err(|_| panic!("closes dropped"));
|
.map_err(|_| panic!("closes dropped"));
|
||||||
let _ = t.select(close).wait();
|
let _ = block_on(t.select(close));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -1026,11 +1025,13 @@ mod dispatch_impl {
|
|||||||
// See https://github.com/hyperium/hyper/issues/1429
|
// See https://github.com/hyperium/hyper/issues/1429
|
||||||
|
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
use tokio::prelude::Future;
|
||||||
let _ = pretty_env_logger::try_init();
|
let _ = pretty_env_logger::try_init();
|
||||||
|
|
||||||
let server = TcpListener::bind("127.0.0.1:0").unwrap();
|
let server = TcpListener::bind("127.0.0.1:0").unwrap();
|
||||||
let addr = server.local_addr().unwrap();
|
let addr = server.local_addr().unwrap();
|
||||||
let runtime = Runtime::new().unwrap();
|
let runtime = Runtime::new().unwrap();
|
||||||
|
let executor = runtime.executor();
|
||||||
let handle = runtime.handle().clone();
|
let handle = runtime.handle().clone();
|
||||||
|
|
||||||
let (tx1, rx1) = oneshot::channel();
|
let (tx1, rx1) = oneshot::channel();
|
||||||
@@ -1042,27 +1043,27 @@ mod dispatch_impl {
|
|||||||
let mut buf = [0; 4096];
|
let mut buf = [0; 4096];
|
||||||
sock.read(&mut buf).expect("read 1");
|
sock.read(&mut buf).expect("read 1");
|
||||||
sock.write_all(b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n").unwrap();
|
sock.write_all(b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n").unwrap();
|
||||||
sock.read(&mut buf).expect("read 2");
|
//sock.read(&mut buf).expect("read 2");
|
||||||
let _ = tx1.send(());
|
let _ = tx1.send(());
|
||||||
});
|
});
|
||||||
|
|
||||||
let uri = format!("http://{}/a", addr).parse::<hyper::Uri>().unwrap();
|
let uri = format!("http://{}/a", addr).parse::<hyper::Uri>().unwrap();
|
||||||
|
|
||||||
let client = Client::configure().build_with_executor(&handle, runtime.executor());
|
let client = Client::configure().build(&handle);
|
||||||
|
|
||||||
let req = Request::builder()
|
let req = Request::builder()
|
||||||
.uri(uri.clone())
|
.uri(uri.clone())
|
||||||
.body(Body::empty())
|
.body(Body::empty())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let res = client.request(req).and_then(move |res| {
|
let res = client.request(req).with_executor(executor.clone()).and_then(move |res| {
|
||||||
assert_eq!(res.status(), hyper::StatusCode::OK);
|
assert_eq!(res.status(), hyper::StatusCode::OK);
|
||||||
res.into_body().into_stream().concat2()
|
res.into_body().into_stream().concat()
|
||||||
});
|
});
|
||||||
|
|
||||||
res.wait().unwrap();
|
block_on(res).unwrap();
|
||||||
|
|
||||||
// drop previous runtime
|
// shut down runtime
|
||||||
drop(runtime);
|
runtime.shutdown_now().wait().unwrap();
|
||||||
let timeout = Delay::new(Duration::from_millis(200));
|
let timeout = Delay::new(Duration::from_millis(200));
|
||||||
let rx = rx1.map_err(|_| hyper::Error::Io(io::Error::new(io::ErrorKind::Other, "thread panicked")));
|
let rx = rx1.map_err(|_| hyper::Error::Io(io::Error::new(io::ErrorKind::Other, "thread panicked")));
|
||||||
let rx = rx.and_then(move |_| timeout.map_err(|e| e.into()));
|
let rx = rx.and_then(move |_| timeout.map_err(|e| e.into()));
|
||||||
@@ -1071,10 +1072,10 @@ mod dispatch_impl {
|
|||||||
.uri(uri)
|
.uri(uri)
|
||||||
.body(Body::empty())
|
.body(Body::empty())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let res = client.request(req);
|
let res = client.request(req).with_executor(executor);
|
||||||
// this does trigger an 'event loop gone' error, but before, it would
|
// this does trigger an 'event loop gone' error, but before, it would
|
||||||
// panic internally on a `SendError`, which is what we're testing against.
|
// panic internally on a `SendError`, which is what we're testing against.
|
||||||
let err = res.join(rx).map(|r| r.0).wait().unwrap_err();
|
let err = block_on(res.join(rx).map(|r| r.0)).unwrap_err();
|
||||||
assert_eq!(err.description(), "event loop gone");
|
assert_eq!(err.description(), "event loop gone");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1100,31 +1101,31 @@ mod dispatch_impl {
|
|||||||
|
|
||||||
let client = Client::configure()
|
let client = Client::configure()
|
||||||
.connector(DebugConnector::with_http_and_closes(HttpConnector::new(1, &handle), closes_tx))
|
.connector(DebugConnector::with_http_and_closes(HttpConnector::new(1, &handle), closes_tx))
|
||||||
.executor(runtime.executor());
|
.build();
|
||||||
|
|
||||||
let req = Request::builder()
|
let req = Request::builder()
|
||||||
.uri(&*format!("http://{}/a", addr))
|
.uri(&*format!("http://{}/a", addr))
|
||||||
.body(Body::empty())
|
.body(Body::empty())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let res = client.request(req).and_then(move |res| {
|
let res = client.request(req).with_executor(runtime.executor()).and_then(move |res| {
|
||||||
assert_eq!(res.status(), hyper::StatusCode::OK);
|
assert_eq!(res.status(), hyper::StatusCode::OK);
|
||||||
res.into_body().into_stream().concat2()
|
res.into_body().into_stream().concat()
|
||||||
});
|
});
|
||||||
let rx = rx1.map_err(|_| hyper::Error::Io(io::Error::new(io::ErrorKind::Other, "thread panicked")));
|
let rx = rx1.map_err(|_| hyper::Error::Io(io::Error::new(io::ErrorKind::Other, "thread panicked")));
|
||||||
|
|
||||||
let timeout = Delay::new(Duration::from_millis(200));
|
let timeout = Delay::new(Duration::from_millis(200));
|
||||||
let rx = rx.and_then(move |_| timeout.map_err(|e| e.into()));
|
let rx = rx.and_then(move |_| timeout.map_err(|e| e.into()));
|
||||||
res.join(rx).map(|r| r.0).wait().unwrap();
|
block_on(res.join(rx).map(|r| r.0)).unwrap();
|
||||||
|
|
||||||
|
|
||||||
let t = Delay::new(Duration::from_millis(100))
|
let t = Delay::new(Duration::from_millis(100))
|
||||||
.map(|_| panic!("time out"));
|
.map(|_| panic!("time out"));
|
||||||
let close = closes.into_future()
|
let close = closes.next()
|
||||||
.map(|(opt, _)| {
|
.map(|(opt, _)| {
|
||||||
opt.expect("closes");
|
opt.expect("closes");
|
||||||
})
|
})
|
||||||
.map_err(|_| panic!("closes dropped"));
|
.map_err(|_| panic!("closes dropped"));
|
||||||
let _ = t.select(close).wait();
|
let _ = block_on(t.select(close));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -1140,14 +1141,14 @@ mod dispatch_impl {
|
|||||||
|
|
||||||
let client = Client::configure()
|
let client = Client::configure()
|
||||||
.connector(connector)
|
.connector(connector)
|
||||||
.executor(runtime.executor());
|
.build();
|
||||||
|
|
||||||
assert_eq!(connects.load(Ordering::Relaxed), 0);
|
assert_eq!(connects.load(Ordering::Relaxed), 0);
|
||||||
let req = Request::builder()
|
let req = Request::builder()
|
||||||
.uri("http://hyper.local/a")
|
.uri("http://hyper.local/a")
|
||||||
.body(Default::default())
|
.body(Default::default())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let _fut = client.request(req);
|
let _fut = client.request(req).with_executor(runtime.executor());
|
||||||
// internal Connect::connect should have been lazy, and not
|
// internal Connect::connect should have been lazy, and not
|
||||||
// triggered an actual connect yet.
|
// triggered an actual connect yet.
|
||||||
assert_eq!(connects.load(Ordering::Relaxed), 0);
|
assert_eq!(connects.load(Ordering::Relaxed), 0);
|
||||||
@@ -1165,7 +1166,7 @@ mod dispatch_impl {
|
|||||||
|
|
||||||
let client = Client::configure()
|
let client = Client::configure()
|
||||||
.connector(connector)
|
.connector(connector)
|
||||||
.executor(runtime.executor());
|
.build();
|
||||||
|
|
||||||
let (tx1, rx1) = oneshot::channel();
|
let (tx1, rx1) = oneshot::channel();
|
||||||
let (tx2, rx2) = oneshot::channel();
|
let (tx2, rx2) = oneshot::channel();
|
||||||
@@ -1195,8 +1196,8 @@ mod dispatch_impl {
|
|||||||
.uri(&*format!("http://{}/a", addr))
|
.uri(&*format!("http://{}/a", addr))
|
||||||
.body(Body::empty())
|
.body(Body::empty())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let res = client.request(req);
|
let res = client.request(req).with_executor(runtime.executor());
|
||||||
res.join(rx).map(|r| r.0).wait().unwrap();
|
block_on(res.join(rx).map(|r| r.0)).unwrap();
|
||||||
|
|
||||||
assert_eq!(connects.load(Ordering::SeqCst), 1);
|
assert_eq!(connects.load(Ordering::SeqCst), 1);
|
||||||
|
|
||||||
@@ -1209,8 +1210,8 @@ mod dispatch_impl {
|
|||||||
.uri(&*format!("http://{}/b", addr))
|
.uri(&*format!("http://{}/b", addr))
|
||||||
.body(Body::empty())
|
.body(Body::empty())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let res = client.request(req);
|
let res = client.request(req).with_executor(runtime.executor());
|
||||||
res.join(rx).map(|r| r.0).wait().unwrap();
|
block_on(res.join(rx).map(|r| r.0)).unwrap();
|
||||||
|
|
||||||
assert_eq!(connects.load(Ordering::SeqCst), 1, "second request should still only have 1 connect");
|
assert_eq!(connects.load(Ordering::SeqCst), 1, "second request should still only have 1 connect");
|
||||||
}
|
}
|
||||||
@@ -1228,7 +1229,7 @@ mod dispatch_impl {
|
|||||||
|
|
||||||
let client = Client::configure()
|
let client = Client::configure()
|
||||||
.connector(connector)
|
.connector(connector)
|
||||||
.executor(runtime.executor());
|
.build();
|
||||||
|
|
||||||
let (tx1, rx1) = oneshot::channel();
|
let (tx1, rx1) = oneshot::channel();
|
||||||
let (tx2, rx2) = oneshot::channel();
|
let (tx2, rx2) = oneshot::channel();
|
||||||
@@ -1261,8 +1262,8 @@ mod dispatch_impl {
|
|||||||
.uri(&*format!("http://{}/a", addr))
|
.uri(&*format!("http://{}/a", addr))
|
||||||
.body(Body::empty())
|
.body(Body::empty())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let res = client.request(req);
|
let res = client.request(req).with_executor(runtime.executor());
|
||||||
res.join(rx).map(|r| r.0).wait().unwrap();
|
block_on(res.join(rx).map(|r| r.0)).unwrap();
|
||||||
|
|
||||||
assert_eq!(connects.load(Ordering::Relaxed), 1);
|
assert_eq!(connects.load(Ordering::Relaxed), 1);
|
||||||
|
|
||||||
@@ -1271,8 +1272,8 @@ mod dispatch_impl {
|
|||||||
.uri(&*format!("http://{}/b", addr))
|
.uri(&*format!("http://{}/b", addr))
|
||||||
.body(Body::empty())
|
.body(Body::empty())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let res = client.request(req);
|
let res = client.request(req).with_executor(runtime.executor());
|
||||||
res.join(rx).map(|r| r.0).wait().unwrap();
|
block_on(res.join(rx).map(|r| r.0)).unwrap();
|
||||||
|
|
||||||
assert_eq!(connects.load(Ordering::Relaxed), 2);
|
assert_eq!(connects.load(Ordering::Relaxed), 2);
|
||||||
}
|
}
|
||||||
@@ -1289,7 +1290,7 @@ mod dispatch_impl {
|
|||||||
|
|
||||||
let client = Client::configure()
|
let client = Client::configure()
|
||||||
.connector(connector)
|
.connector(connector)
|
||||||
.executor(runtime.executor());
|
.build();
|
||||||
|
|
||||||
let (tx1, rx1) = oneshot::channel();
|
let (tx1, rx1) = oneshot::channel();
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
@@ -1312,8 +1313,8 @@ mod dispatch_impl {
|
|||||||
.uri(&*format!("http://{}/foo/bar", addr))
|
.uri(&*format!("http://{}/foo/bar", addr))
|
||||||
.body(Body::empty())
|
.body(Body::empty())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let res = client.request(req);
|
let res = client.request(req).with_executor(runtime.executor());
|
||||||
res.join(rx).map(|r| r.0).wait().unwrap();
|
block_on(res.join(rx).map(|r| r.0)).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1369,33 +1370,25 @@ mod dispatch_impl {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Write for DebugStream {
|
|
||||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
|
||||||
self.0.write(buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn flush(&mut self) -> io::Result<()> {
|
|
||||||
self.0.flush()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsyncWrite for DebugStream {
|
impl AsyncWrite for DebugStream {
|
||||||
fn shutdown(&mut self) -> futures::Poll<(), io::Error> {
|
fn poll_write(&mut self, cx: &mut task::Context, buf: &[u8]) -> futures::Poll<usize, io::Error> {
|
||||||
AsyncWrite::shutdown(&mut self.0)
|
self.0.poll_write(cx, buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_buf<B: ::bytes::Buf>(&mut self, buf: &mut B) -> futures::Poll<usize, io::Error> {
|
fn poll_flush(&mut self, cx: &mut task::Context) -> futures::Poll<(), io::Error> {
|
||||||
self.0.write_buf(buf)
|
self.0.poll_flush(cx)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn poll_close(&mut self, cx: &mut task::Context) -> futures::Poll<(), io::Error> {
|
||||||
|
self.0.poll_close(cx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Read for DebugStream {
|
impl AsyncRead for DebugStream {
|
||||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
fn poll_read(&mut self, cx: &mut task::Context, buf: &mut [u8]) -> futures::Poll<usize, io::Error> {
|
||||||
self.0.read(buf)
|
self.0.poll_read(cx, buf)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsyncRead for DebugStream {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mod conn {
|
mod conn {
|
||||||
@@ -1404,13 +1397,15 @@ mod conn {
|
|||||||
use std::thread;
|
use std::thread;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use futures::{Async, Future, Poll, Stream};
|
use futures::{FutureExt, Poll, StreamExt};
|
||||||
use futures::future::poll_fn;
|
use futures::future::{poll_fn, lazy};
|
||||||
use futures::sync::oneshot;
|
use futures::channel::oneshot;
|
||||||
|
use futures::task;
|
||||||
|
use futures::io::{AsyncRead, AsyncWrite};
|
||||||
|
use futures::executor::block_on;
|
||||||
use futures_timer::Delay;
|
use futures_timer::Delay;
|
||||||
use tokio::runtime::Runtime;
|
use tokio::runtime::Runtime;
|
||||||
use tokio::net::TcpStream;
|
use tokio::net::TcpStream;
|
||||||
use tokio_io::{AsyncRead, AsyncWrite};
|
|
||||||
|
|
||||||
use hyper::{self, Request};
|
use hyper::{self, Request};
|
||||||
use hyper::client::conn;
|
use hyper::client::conn;
|
||||||
@@ -1442,11 +1437,11 @@ mod conn {
|
|||||||
let _ = tx1.send(());
|
let _ = tx1.send(());
|
||||||
});
|
});
|
||||||
|
|
||||||
let tcp = tcp_connect(&addr).wait().unwrap();
|
let tcp = block_on(tcp_connect(&addr)).unwrap();
|
||||||
|
|
||||||
let (mut client, conn) = conn::handshake(tcp).wait().unwrap();
|
let (mut client, conn) = block_on(conn::handshake(tcp)).unwrap();
|
||||||
|
|
||||||
runtime.spawn(conn.map(|_| ()).map_err(|e| panic!("conn error: {}", e)));
|
runtime.spawn2(conn.map(|_| ()).map_err(|e| panic!("conn error: {}", e)));
|
||||||
|
|
||||||
let req = Request::builder()
|
let req = Request::builder()
|
||||||
.uri("/a")
|
.uri("/a")
|
||||||
@@ -1454,13 +1449,13 @@ mod conn {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
let res = client.send_request(req).and_then(move |res| {
|
let res = client.send_request(req).and_then(move |res| {
|
||||||
assert_eq!(res.status(), hyper::StatusCode::OK);
|
assert_eq!(res.status(), hyper::StatusCode::OK);
|
||||||
res.into_body().into_stream().concat2()
|
res.into_body().into_stream().concat()
|
||||||
});
|
});
|
||||||
let rx = rx1.map_err(|_| hyper::Error::Io(io::Error::new(io::ErrorKind::Other, "thread panicked")));
|
let rx = rx1.map_err(|_| hyper::Error::Io(io::Error::new(io::ErrorKind::Other, "thread panicked")));
|
||||||
|
|
||||||
let timeout = Delay::new(Duration::from_millis(200));
|
let timeout = Delay::new(Duration::from_millis(200));
|
||||||
let rx = rx.and_then(move |_| timeout.map_err(|e| e.into()));
|
let rx = rx.and_then(move |_| timeout.map_err(|e| e.into()));
|
||||||
res.join(rx).map(|r| r.0).wait().unwrap();
|
block_on(res.join(rx).map(|r| r.0)).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -1487,11 +1482,11 @@ mod conn {
|
|||||||
let _ = tx1.send(());
|
let _ = tx1.send(());
|
||||||
});
|
});
|
||||||
|
|
||||||
let tcp = tcp_connect(&addr).wait().unwrap();
|
let tcp = block_on(tcp_connect(&addr)).unwrap();
|
||||||
|
|
||||||
let (mut client, conn) = conn::handshake(tcp).wait().unwrap();
|
let (mut client, conn) = block_on(conn::handshake(tcp)).unwrap();
|
||||||
|
|
||||||
runtime.spawn(conn.map(|_| ()).map_err(|e| panic!("conn error: {}", e)));
|
runtime.spawn2(conn.map(|_| ()).map_err(|e| panic!("conn error: {}", e)));
|
||||||
|
|
||||||
let req = Request::builder()
|
let req = Request::builder()
|
||||||
.uri("http://hyper.local/a")
|
.uri("http://hyper.local/a")
|
||||||
@@ -1500,13 +1495,13 @@ mod conn {
|
|||||||
|
|
||||||
let res = client.send_request(req).and_then(move |res| {
|
let res = client.send_request(req).and_then(move |res| {
|
||||||
assert_eq!(res.status(), hyper::StatusCode::OK);
|
assert_eq!(res.status(), hyper::StatusCode::OK);
|
||||||
res.into_body().into_stream().concat2()
|
res.into_body().into_stream().concat()
|
||||||
});
|
});
|
||||||
let rx = rx1.map_err(|_| hyper::Error::Io(io::Error::new(io::ErrorKind::Other, "thread panicked")));
|
let rx = rx1.map_err(|_| hyper::Error::Io(io::Error::new(io::ErrorKind::Other, "thread panicked")));
|
||||||
|
|
||||||
let timeout = Delay::new(Duration::from_millis(200));
|
let timeout = Delay::new(Duration::from_millis(200));
|
||||||
let rx = rx.and_then(move |_| timeout.map_err(|e| e.into()));
|
let rx = rx.and_then(move |_| timeout.map_err(|e| e.into()));
|
||||||
res.join(rx).map(|r| r.0).wait().unwrap();
|
block_on(res.join(rx).map(|r| r.0)).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -1528,11 +1523,11 @@ mod conn {
|
|||||||
let _ = tx1.send(());
|
let _ = tx1.send(());
|
||||||
});
|
});
|
||||||
|
|
||||||
let tcp = tcp_connect(&addr).wait().unwrap();
|
let tcp = block_on(tcp_connect(&addr)).unwrap();
|
||||||
|
|
||||||
let (mut client, conn) = conn::handshake(tcp).wait().unwrap();
|
let (mut client, conn) = block_on(conn::handshake(tcp)).unwrap();
|
||||||
|
|
||||||
runtime.spawn(conn.map(|_| ()).map_err(|e| panic!("conn error: {}", e)));
|
runtime.spawn2(conn.map(|_| ()).map_err(|e| panic!("conn error: {}", e)));
|
||||||
|
|
||||||
let req = Request::builder()
|
let req = Request::builder()
|
||||||
.uri("/a")
|
.uri("/a")
|
||||||
@@ -1540,10 +1535,10 @@ mod conn {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
let res1 = client.send_request(req).and_then(move |res| {
|
let res1 = client.send_request(req).and_then(move |res| {
|
||||||
assert_eq!(res.status(), hyper::StatusCode::OK);
|
assert_eq!(res.status(), hyper::StatusCode::OK);
|
||||||
res.into_body().into_stream().concat2()
|
res.into_body().into_stream().concat()
|
||||||
});
|
});
|
||||||
|
|
||||||
// pipelined request will hit NotReady, and thus should return an Error::Cancel
|
// pipelined request will hit Pending, and thus should return an Error::Cancel
|
||||||
let req = Request::builder()
|
let req = Request::builder()
|
||||||
.uri("/b")
|
.uri("/b")
|
||||||
.body(Default::default())
|
.body(Default::default())
|
||||||
@@ -1562,12 +1557,12 @@ mod conn {
|
|||||||
|
|
||||||
let timeout = Delay::new(Duration::from_millis(200));
|
let timeout = Delay::new(Duration::from_millis(200));
|
||||||
let rx = rx.and_then(move |_| timeout.map_err(|e| e.into()));
|
let rx = rx.and_then(move |_| timeout.map_err(|e| e.into()));
|
||||||
res1.join(res2).join(rx).map(|r| r.0).wait().unwrap();
|
block_on(res1.join(res2).join(rx).map(|r| r.0)).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn upgrade() {
|
fn upgrade() {
|
||||||
use tokio_io::io::{read_to_end, write_all};
|
use futures::io::{AsyncReadExt, AsyncWriteExt};
|
||||||
let _ = ::pretty_env_logger::try_init();
|
let _ = ::pretty_env_logger::try_init();
|
||||||
|
|
||||||
let server = TcpListener::bind("127.0.0.1:0").unwrap();
|
let server = TcpListener::bind("127.0.0.1:0").unwrap();
|
||||||
@@ -1595,18 +1590,18 @@ mod conn {
|
|||||||
sock.write_all(b"bar=foo").expect("write 2");
|
sock.write_all(b"bar=foo").expect("write 2");
|
||||||
});
|
});
|
||||||
|
|
||||||
let tcp = tcp_connect(&addr).wait().unwrap();
|
let tcp = block_on(tcp_connect(&addr)).unwrap();
|
||||||
|
|
||||||
let io = DebugStream {
|
let io = DebugStream {
|
||||||
tcp: tcp,
|
tcp: tcp,
|
||||||
shutdown_called: false,
|
shutdown_called: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
let (mut client, mut conn) = conn::handshake(io).wait().unwrap();
|
let (mut client, mut conn) = block_on(conn::handshake(io)).unwrap();
|
||||||
|
|
||||||
{
|
{
|
||||||
let until_upgrade = poll_fn(|| {
|
let until_upgrade = poll_fn(|cx| {
|
||||||
conn.poll_without_shutdown()
|
conn.poll_without_shutdown(cx)
|
||||||
});
|
});
|
||||||
|
|
||||||
let req = Request::builder()
|
let req = Request::builder()
|
||||||
@@ -1616,20 +1611,20 @@ mod conn {
|
|||||||
let res = client.send_request(req).and_then(move |res| {
|
let res = client.send_request(req).and_then(move |res| {
|
||||||
assert_eq!(res.status(), hyper::StatusCode::SWITCHING_PROTOCOLS);
|
assert_eq!(res.status(), hyper::StatusCode::SWITCHING_PROTOCOLS);
|
||||||
assert_eq!(res.headers()["Upgrade"], "foobar");
|
assert_eq!(res.headers()["Upgrade"], "foobar");
|
||||||
res.into_body().into_stream().concat2()
|
res.into_body().into_stream().concat()
|
||||||
});
|
});
|
||||||
|
|
||||||
let rx = rx1.map_err(|_| hyper::Error::Io(io::Error::new(io::ErrorKind::Other, "thread panicked")));
|
let rx = rx1.map_err(|_| hyper::Error::Io(io::Error::new(io::ErrorKind::Other, "thread panicked")));
|
||||||
|
|
||||||
let timeout = Delay::new(Duration::from_millis(200));
|
let timeout = Delay::new(Duration::from_millis(200));
|
||||||
let rx = rx.and_then(move |_| timeout.map_err(|e| e.into()));
|
let rx = rx.and_then(move |_| timeout.map_err(|e| e.into()));
|
||||||
until_upgrade.join(res).join(rx).map(|r| r.0).wait().unwrap();
|
block_on(until_upgrade.join(res).join(rx).map(|r| r.0)).unwrap();
|
||||||
|
|
||||||
// should not be ready now
|
// should not be ready now
|
||||||
poll_fn(|| {
|
block_on(lazy(|cx| {
|
||||||
assert!(client.poll_ready().unwrap().is_not_ready());
|
assert!(client.poll_ready(cx).unwrap().is_pending());
|
||||||
Ok::<_, ()>(Async::Ready(()))
|
Ok::<_, ()>(())
|
||||||
}).wait().unwrap();
|
})).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
let parts = conn.into_parts();
|
let parts = conn.into_parts();
|
||||||
@@ -1638,16 +1633,16 @@ mod conn {
|
|||||||
|
|
||||||
assert_eq!(buf, b"foobar=ready"[..]);
|
assert_eq!(buf, b"foobar=ready"[..]);
|
||||||
assert!(!io.shutdown_called, "upgrade shouldn't shutdown AsyncWrite");
|
assert!(!io.shutdown_called, "upgrade shouldn't shutdown AsyncWrite");
|
||||||
assert!(client.poll_ready().is_err());
|
assert!(block_on(poll_fn(|cx| client.poll_ready(cx))).is_err());
|
||||||
|
|
||||||
let io = write_all(io, b"foo=bar").wait().unwrap().0;
|
let io = block_on(io.write_all(b"foo=bar")).unwrap().0;
|
||||||
let vec = read_to_end(io, vec![]).wait().unwrap().1;
|
let vec = block_on(io.read_to_end(vec![])).unwrap().1;
|
||||||
assert_eq!(vec, b"bar=foo");
|
assert_eq!(vec, b"bar=foo");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn connect_method() {
|
fn connect_method() {
|
||||||
use tokio_io::io::{read_to_end, write_all};
|
use futures::io::{AsyncReadExt, AsyncWriteExt};
|
||||||
let _ = ::pretty_env_logger::try_init();
|
let _ = ::pretty_env_logger::try_init();
|
||||||
|
|
||||||
let server = TcpListener::bind("127.0.0.1:0").unwrap();
|
let server = TcpListener::bind("127.0.0.1:0").unwrap();
|
||||||
@@ -1674,18 +1669,18 @@ mod conn {
|
|||||||
sock.write_all(b"bar=foo").expect("write 2");
|
sock.write_all(b"bar=foo").expect("write 2");
|
||||||
});
|
});
|
||||||
|
|
||||||
let tcp = tcp_connect(&addr).wait().unwrap();
|
let tcp = block_on(tcp_connect(&addr)).unwrap();
|
||||||
|
|
||||||
let io = DebugStream {
|
let io = DebugStream {
|
||||||
tcp: tcp,
|
tcp: tcp,
|
||||||
shutdown_called: false,
|
shutdown_called: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
let (mut client, mut conn) = conn::handshake(io).wait().unwrap();
|
let (mut client, mut conn) = block_on(conn::handshake(io)).unwrap();
|
||||||
|
|
||||||
{
|
{
|
||||||
let until_tunneled = poll_fn(|| {
|
let until_tunneled = poll_fn(|cx| {
|
||||||
conn.poll_without_shutdown()
|
conn.poll_without_shutdown(cx)
|
||||||
});
|
});
|
||||||
|
|
||||||
let req = Request::builder()
|
let req = Request::builder()
|
||||||
@@ -1696,7 +1691,7 @@ mod conn {
|
|||||||
let res = client.send_request(req)
|
let res = client.send_request(req)
|
||||||
.and_then(move |res| {
|
.and_then(move |res| {
|
||||||
assert_eq!(res.status(), hyper::StatusCode::OK);
|
assert_eq!(res.status(), hyper::StatusCode::OK);
|
||||||
res.into_body().into_stream().concat2()
|
res.into_body().into_stream().concat()
|
||||||
})
|
})
|
||||||
.map(|body| {
|
.map(|body| {
|
||||||
assert_eq!(body.as_ref(), b"");
|
assert_eq!(body.as_ref(), b"");
|
||||||
@@ -1706,13 +1701,13 @@ mod conn {
|
|||||||
|
|
||||||
let timeout = Delay::new(Duration::from_millis(200));
|
let timeout = Delay::new(Duration::from_millis(200));
|
||||||
let rx = rx.and_then(move |_| timeout.map_err(|e| e.into()));
|
let rx = rx.and_then(move |_| timeout.map_err(|e| e.into()));
|
||||||
until_tunneled.join(res).join(rx).map(|r| r.0).wait().unwrap();
|
block_on(until_tunneled.join(res).join(rx).map(|r| r.0)).unwrap();
|
||||||
|
|
||||||
// should not be ready now
|
// should not be ready now
|
||||||
poll_fn(|| {
|
block_on(lazy(|cx| {
|
||||||
assert!(client.poll_ready().unwrap().is_not_ready());
|
assert!(client.poll_ready(cx).unwrap().is_pending());
|
||||||
Ok::<_, ()>(Async::Ready(()))
|
Ok::<_, ()>(())
|
||||||
}).wait().unwrap();
|
})).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
let parts = conn.into_parts();
|
let parts = conn.into_parts();
|
||||||
@@ -1721,10 +1716,10 @@ mod conn {
|
|||||||
|
|
||||||
assert_eq!(buf, b"foobar=ready"[..]);
|
assert_eq!(buf, b"foobar=ready"[..]);
|
||||||
assert!(!io.shutdown_called, "tunnel shouldn't shutdown AsyncWrite");
|
assert!(!io.shutdown_called, "tunnel shouldn't shutdown AsyncWrite");
|
||||||
assert!(client.poll_ready().is_err());
|
assert!(block_on(poll_fn(|cx| client.poll_ready(cx))).is_err());
|
||||||
|
|
||||||
let io = write_all(io, b"foo=bar").wait().unwrap().0;
|
let io = block_on(io.write_all(b"foo=bar")).unwrap().0;
|
||||||
let vec = read_to_end(io, vec![]).wait().unwrap().1;
|
let vec = block_on(io.read_to_end(vec![])).unwrap().1;
|
||||||
assert_eq!(vec, b"bar=foo");
|
assert_eq!(vec, b"bar=foo");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1733,28 +1728,24 @@ mod conn {
|
|||||||
shutdown_called: bool,
|
shutdown_called: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Write for DebugStream {
|
|
||||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
|
||||||
self.tcp.write(buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn flush(&mut self) -> io::Result<()> {
|
|
||||||
self.tcp.flush()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsyncWrite for DebugStream {
|
impl AsyncWrite for DebugStream {
|
||||||
fn shutdown(&mut self) -> Poll<(), io::Error> {
|
fn poll_write(&mut self, cx: &mut task::Context, buf: &[u8]) -> Poll<usize, io::Error> {
|
||||||
|
self.tcp.poll_write(cx, buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn poll_flush(&mut self, cx: &mut task::Context) -> Poll<(), io::Error> {
|
||||||
|
self.tcp.poll_flush(cx)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn poll_close(&mut self, cx: &mut task::Context) -> Poll<(), io::Error> {
|
||||||
self.shutdown_called = true;
|
self.shutdown_called = true;
|
||||||
AsyncWrite::shutdown(&mut self.tcp)
|
self.tcp.poll_close(cx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Read for DebugStream {
|
impl AsyncRead for DebugStream {
|
||||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
fn poll_read(&mut self, cx: &mut task::Context, buf: &mut [u8]) -> Poll<usize, io::Error> {
|
||||||
self.tcp.read(buf)
|
self.tcp.poll_read(cx, buf)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsyncRead for DebugStream {}
|
|
||||||
}
|
}
|
||||||
|
|||||||
132
tests/server.rs
132
tests/server.rs
@@ -8,7 +8,6 @@ extern crate net2;
|
|||||||
extern crate spmc;
|
extern crate spmc;
|
||||||
extern crate pretty_env_logger;
|
extern crate pretty_env_logger;
|
||||||
extern crate tokio;
|
extern crate tokio;
|
||||||
extern crate tokio_io;
|
|
||||||
|
|
||||||
use std::net::{TcpStream, Shutdown, SocketAddr};
|
use std::net::{TcpStream, Shutdown, SocketAddr};
|
||||||
use std::io::{self, Read, Write};
|
use std::io::{self, Read, Write};
|
||||||
@@ -19,16 +18,18 @@ use std::net::{TcpListener as StdTcpListener};
|
|||||||
use std::thread;
|
use std::thread;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use futures::{Future, Stream};
|
use futures::{Future, FutureExt, StreamExt};
|
||||||
use futures::future::{self, FutureResult, Either};
|
use futures::future::{self, FutureResult, Either};
|
||||||
use futures::sync::oneshot;
|
use futures::channel::oneshot;
|
||||||
|
use futures::executor::block_on;
|
||||||
|
use futures::task;
|
||||||
|
use futures::io::{AsyncRead, AsyncWrite};
|
||||||
use futures_timer::Delay;
|
use futures_timer::Delay;
|
||||||
use http::header::{HeaderName, HeaderValue};
|
use http::header::{HeaderName, HeaderValue};
|
||||||
//use net2::TcpBuilder;
|
//use net2::TcpBuilder;
|
||||||
use tokio::net::TcpListener;
|
use tokio::net::TcpListener;
|
||||||
use tokio::runtime::Runtime;
|
use tokio::runtime::Runtime;
|
||||||
use tokio::reactor::Handle;
|
use tokio::reactor::Handle;
|
||||||
use tokio_io::{AsyncRead, AsyncWrite};
|
|
||||||
|
|
||||||
|
|
||||||
use hyper::{Body, Request, Response, StatusCode};
|
use hyper::{Body, Request, Response, StatusCode};
|
||||||
@@ -91,14 +92,14 @@ fn get_implicitly_empty() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
let fut = listener.incoming()
|
let fut = listener.incoming()
|
||||||
.into_future()
|
.next()
|
||||||
.map_err(|_| unreachable!())
|
.map_err(|_| unreachable!())
|
||||||
.and_then(|(item, _incoming)| {
|
.and_then(|(item, _incoming)| {
|
||||||
let socket = item.unwrap();
|
let socket = item.unwrap();
|
||||||
Http::<hyper::Chunk>::new().serve_connection(socket, GetImplicitlyEmpty)
|
Http::<hyper::Chunk>::new().serve_connection(socket, GetImplicitlyEmpty)
|
||||||
});
|
});
|
||||||
|
|
||||||
fut.wait().unwrap();
|
block_on(fut).unwrap();
|
||||||
|
|
||||||
struct GetImplicitlyEmpty;
|
struct GetImplicitlyEmpty;
|
||||||
|
|
||||||
@@ -111,7 +112,7 @@ fn get_implicitly_empty() {
|
|||||||
fn call(&self, req: Request<Body>) -> Self::Future {
|
fn call(&self, req: Request<Body>) -> Self::Future {
|
||||||
Box::new(req.into_body()
|
Box::new(req.into_body()
|
||||||
.into_stream()
|
.into_stream()
|
||||||
.concat2()
|
.concat()
|
||||||
.map(|buf| {
|
.map(|buf| {
|
||||||
assert!(buf.is_empty());
|
assert!(buf.is_empty());
|
||||||
Response::new(Body::empty())
|
Response::new(Body::empty())
|
||||||
@@ -765,34 +766,34 @@ fn disable_keep_alive_mid_request() {
|
|||||||
let mut req = connect(&addr);
|
let mut req = connect(&addr);
|
||||||
req.write_all(b"GET / HTTP/1.1\r\n").unwrap();
|
req.write_all(b"GET / HTTP/1.1\r\n").unwrap();
|
||||||
tx1.send(()).unwrap();
|
tx1.send(()).unwrap();
|
||||||
rx2.wait().unwrap();
|
block_on(rx2).unwrap();
|
||||||
req.write_all(b"Host: localhost\r\n\r\n").unwrap();
|
req.write_all(b"Host: localhost\r\n\r\n").unwrap();
|
||||||
let mut buf = vec![];
|
let mut buf = vec![];
|
||||||
req.read_to_end(&mut buf).unwrap();
|
req.read_to_end(&mut buf).unwrap();
|
||||||
});
|
});
|
||||||
|
|
||||||
let fut = listener.incoming()
|
let fut = listener.incoming()
|
||||||
.into_future()
|
.next()
|
||||||
.map_err(|_| unreachable!())
|
.map_err(|_| unreachable!())
|
||||||
.and_then(|(item, _incoming)| {
|
.and_then(|(item, _incoming)| {
|
||||||
let socket = item.unwrap();
|
let socket = item.unwrap();
|
||||||
Http::<hyper::Chunk>::new().serve_connection(socket, HelloWorld)
|
Http::<hyper::Chunk>::new().serve_connection(socket, HelloWorld)
|
||||||
.select2(rx1)
|
.select(rx1)
|
||||||
.then(|r| {
|
.then(|r| {
|
||||||
match r {
|
match r {
|
||||||
Ok(Either::A(_)) => panic!("expected rx first"),
|
Ok(Either::Left(_)) => panic!("expected rx first"),
|
||||||
Ok(Either::B(((), mut conn))) => {
|
Ok(Either::Right(((), mut conn))) => {
|
||||||
conn.disable_keep_alive();
|
conn.disable_keep_alive();
|
||||||
tx2.send(()).unwrap();
|
tx2.send(()).unwrap();
|
||||||
conn
|
conn
|
||||||
}
|
}
|
||||||
Err(Either::A((e, _))) => panic!("unexpected error {}", e),
|
Err(Either::Left((e, _))) => panic!("unexpected error {}", e),
|
||||||
Err(Either::B((e, _))) => panic!("unexpected error {}", e),
|
Err(Either::Right((e, _))) => panic!("unexpected error {}", e),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
fut.wait().unwrap();
|
block_on(fut).unwrap();
|
||||||
child.join().unwrap();
|
child.join().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -833,7 +834,7 @@ fn disable_keep_alive_post_request() {
|
|||||||
let dropped = Dropped::new();
|
let dropped = Dropped::new();
|
||||||
let dropped2 = dropped.clone();
|
let dropped2 = dropped.clone();
|
||||||
let fut = listener.incoming()
|
let fut = listener.incoming()
|
||||||
.into_future()
|
.next()
|
||||||
.map_err(|_| unreachable!())
|
.map_err(|_| unreachable!())
|
||||||
.and_then(|(item, _incoming)| {
|
.and_then(|(item, _incoming)| {
|
||||||
let socket = item.expect("accepted socket");
|
let socket = item.expect("accepted socket");
|
||||||
@@ -842,28 +843,28 @@ fn disable_keep_alive_post_request() {
|
|||||||
_debug: dropped2,
|
_debug: dropped2,
|
||||||
};
|
};
|
||||||
Http::<hyper::Chunk>::new().serve_connection(transport, HelloWorld)
|
Http::<hyper::Chunk>::new().serve_connection(transport, HelloWorld)
|
||||||
.select2(rx1)
|
.select(rx1)
|
||||||
.then(|r| {
|
.then(|r| {
|
||||||
match r {
|
match r {
|
||||||
Ok(Either::A(_)) => panic!("expected rx first"),
|
Ok(Either::Left(_)) => panic!("expected rx first"),
|
||||||
Ok(Either::B(((), mut conn))) => {
|
Ok(Either::Right(((), mut conn))) => {
|
||||||
conn.disable_keep_alive();
|
conn.disable_keep_alive();
|
||||||
conn
|
conn
|
||||||
}
|
}
|
||||||
Err(Either::A((e, _))) => panic!("unexpected error {}", e),
|
Err(Either::Left((e, _))) => panic!("unexpected error {}", e),
|
||||||
Err(Either::B((e, _))) => panic!("unexpected error {}", e),
|
Err(Either::Right((e, _))) => panic!("unexpected error {}", e),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
assert!(!dropped.load());
|
assert!(!dropped.load());
|
||||||
fut.wait().unwrap();
|
block_on(fut).unwrap();
|
||||||
// we must poll the Core one more time in order for Windows to drop
|
// we must poll the Core one more time in order for Windows to drop
|
||||||
// the read-blocked socket.
|
// the read-blocked socket.
|
||||||
//
|
//
|
||||||
// See https://github.com/carllerche/mio/issues/776
|
// See https://github.com/carllerche/mio/issues/776
|
||||||
let timeout = Delay::new(Duration::from_millis(10));
|
let timeout = Delay::new(Duration::from_millis(10));
|
||||||
timeout.wait().unwrap();
|
block_on(timeout).unwrap();
|
||||||
assert!(dropped.load());
|
assert!(dropped.load());
|
||||||
child.join().unwrap();
|
child.join().unwrap();
|
||||||
}
|
}
|
||||||
@@ -879,14 +880,14 @@ fn empty_parse_eof_does_not_return_error() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
let fut = listener.incoming()
|
let fut = listener.incoming()
|
||||||
.into_future()
|
.next()
|
||||||
.map_err(|_| unreachable!())
|
.map_err(|_| unreachable!())
|
||||||
.and_then(|(item, _incoming)| {
|
.and_then(|(item, _incoming)| {
|
||||||
let socket = item.unwrap();
|
let socket = item.unwrap();
|
||||||
Http::<hyper::Chunk>::new().serve_connection(socket, HelloWorld)
|
Http::<hyper::Chunk>::new().serve_connection(socket, HelloWorld)
|
||||||
});
|
});
|
||||||
|
|
||||||
fut.wait().unwrap();
|
block_on(fut).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -901,7 +902,7 @@ fn nonempty_parse_eof_returns_error() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
let fut = listener.incoming()
|
let fut = listener.incoming()
|
||||||
.into_future()
|
.next()
|
||||||
.map_err(|_| unreachable!())
|
.map_err(|_| unreachable!())
|
||||||
.and_then(|(item, _incoming)| {
|
.and_then(|(item, _incoming)| {
|
||||||
let socket = item.unwrap();
|
let socket = item.unwrap();
|
||||||
@@ -909,7 +910,7 @@ fn nonempty_parse_eof_returns_error() {
|
|||||||
.map(|_| ())
|
.map(|_| ())
|
||||||
});
|
});
|
||||||
|
|
||||||
fut.wait().unwrap_err();
|
block_on(fut).unwrap_err();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -929,7 +930,7 @@ fn returning_1xx_response_is_error() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
let fut = listener.incoming()
|
let fut = listener.incoming()
|
||||||
.into_future()
|
.next()
|
||||||
.map_err(|_| unreachable!())
|
.map_err(|_| unreachable!())
|
||||||
.and_then(|(item, _incoming)| {
|
.and_then(|(item, _incoming)| {
|
||||||
let socket = item.unwrap();
|
let socket = item.unwrap();
|
||||||
@@ -943,12 +944,12 @@ fn returning_1xx_response_is_error() {
|
|||||||
.map(|_| ())
|
.map(|_| ())
|
||||||
});
|
});
|
||||||
|
|
||||||
fut.wait().unwrap_err();
|
block_on(fut).unwrap_err();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn upgrades() {
|
fn upgrades() {
|
||||||
use tokio_io::io::{read_to_end, write_all};
|
use futures::io::{AsyncReadExt, AsyncWriteExt};
|
||||||
let _ = pretty_env_logger::try_init();
|
let _ = pretty_env_logger::try_init();
|
||||||
let runtime = Runtime::new().unwrap();
|
let runtime = Runtime::new().unwrap();
|
||||||
let listener = tcp_bind(&"127.0.0.1:0".parse().unwrap(), &runtime.handle()).unwrap();
|
let listener = tcp_bind(&"127.0.0.1:0".parse().unwrap(), &runtime.handle()).unwrap();
|
||||||
@@ -977,7 +978,7 @@ fn upgrades() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
let fut = listener.incoming()
|
let fut = listener.incoming()
|
||||||
.into_future()
|
.next()
|
||||||
.map_err(|_| -> hyper::Error { unreachable!() })
|
.map_err(|_| -> hyper::Error { unreachable!() })
|
||||||
.and_then(|(item, _incoming)| {
|
.and_then(|(item, _incoming)| {
|
||||||
let socket = item.unwrap();
|
let socket = item.unwrap();
|
||||||
@@ -992,24 +993,24 @@ fn upgrades() {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
let mut conn_opt = Some(conn);
|
let mut conn_opt = Some(conn);
|
||||||
future::poll_fn(move || {
|
future::poll_fn(move |cx| {
|
||||||
try_ready!(conn_opt.as_mut().unwrap().poll_without_shutdown());
|
try_ready!(conn_opt.as_mut().unwrap().poll_without_shutdown(cx));
|
||||||
// conn is done with HTTP now
|
// conn is done with HTTP now
|
||||||
Ok(conn_opt.take().unwrap().into())
|
Ok(conn_opt.take().unwrap().into())
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
let conn = fut.wait().unwrap();
|
let conn = block_on(fut).unwrap();
|
||||||
|
|
||||||
// wait so that we don't write until other side saw 101 response
|
// wait so that we don't write until other side saw 101 response
|
||||||
rx.wait().unwrap();
|
block_on(rx).unwrap();
|
||||||
|
|
||||||
let parts = conn.into_parts();
|
let parts = conn.into_parts();
|
||||||
let io = parts.io;
|
let io = parts.io;
|
||||||
assert_eq!(parts.read_buf, "eagerly optimistic");
|
assert_eq!(parts.read_buf, "eagerly optimistic");
|
||||||
|
|
||||||
let io = write_all(io, b"foo=bar").wait().unwrap().0;
|
let io = block_on(io.write_all(b"foo=bar")).unwrap().0;
|
||||||
let vec = read_to_end(io, vec![]).wait().unwrap().1;
|
let vec = block_on(io.read_to_end(vec![])).unwrap().1;
|
||||||
assert_eq!(vec, b"bar=foo");
|
assert_eq!(vec, b"bar=foo");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1030,7 +1031,7 @@ fn parse_errors_send_4xx_response() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
let fut = listener.incoming()
|
let fut = listener.incoming()
|
||||||
.into_future()
|
.next()
|
||||||
.map_err(|_| unreachable!())
|
.map_err(|_| unreachable!())
|
||||||
.and_then(|(item, _incoming)| {
|
.and_then(|(item, _incoming)| {
|
||||||
let socket = item.unwrap();
|
let socket = item.unwrap();
|
||||||
@@ -1039,7 +1040,7 @@ fn parse_errors_send_4xx_response() {
|
|||||||
.map(|_| ())
|
.map(|_| ())
|
||||||
});
|
});
|
||||||
|
|
||||||
fut.wait().unwrap_err();
|
block_on(fut).unwrap_err();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -1059,7 +1060,7 @@ fn illegal_request_length_returns_400_response() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
let fut = listener.incoming()
|
let fut = listener.incoming()
|
||||||
.into_future()
|
.next()
|
||||||
.map_err(|_| unreachable!())
|
.map_err(|_| unreachable!())
|
||||||
.and_then(|(item, _incoming)| {
|
.and_then(|(item, _incoming)| {
|
||||||
let socket = item.unwrap();
|
let socket = item.unwrap();
|
||||||
@@ -1068,7 +1069,7 @@ fn illegal_request_length_returns_400_response() {
|
|||||||
.map(|_| ())
|
.map(|_| ())
|
||||||
});
|
});
|
||||||
|
|
||||||
fut.wait().unwrap_err();
|
block_on(fut).unwrap_err();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -1092,7 +1093,7 @@ fn max_buf_size() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
let fut = listener.incoming()
|
let fut = listener.incoming()
|
||||||
.into_future()
|
.next()
|
||||||
.map_err(|_| unreachable!())
|
.map_err(|_| unreachable!())
|
||||||
.and_then(|(item, _incoming)| {
|
.and_then(|(item, _incoming)| {
|
||||||
let socket = item.unwrap();
|
let socket = item.unwrap();
|
||||||
@@ -1102,7 +1103,7 @@ fn max_buf_size() {
|
|||||||
.map(|_| ())
|
.map(|_| ())
|
||||||
});
|
});
|
||||||
|
|
||||||
fut.wait().unwrap_err();
|
block_on(fut).unwrap_err();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -1136,7 +1137,7 @@ fn streaming_body() {
|
|||||||
let rx = rx.map_err(|_| panic!("thread panicked"));
|
let rx = rx.map_err(|_| panic!("thread panicked"));
|
||||||
|
|
||||||
let fut = listener.incoming()
|
let fut = listener.incoming()
|
||||||
.into_future()
|
.next()
|
||||||
.map_err(|_| unreachable!())
|
.map_err(|_| unreachable!())
|
||||||
.and_then(|(item, _incoming)| {
|
.and_then(|(item, _incoming)| {
|
||||||
let socket = item.unwrap();
|
let socket = item.unwrap();
|
||||||
@@ -1152,7 +1153,7 @@ fn streaming_body() {
|
|||||||
.map(|_| ())
|
.map(|_| ())
|
||||||
});
|
});
|
||||||
|
|
||||||
fut.join(rx).wait().unwrap();
|
block_on(fut.join(rx)).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------------------------------------------------
|
// -------------------------------------------------
|
||||||
@@ -1292,7 +1293,7 @@ impl Service for TestService {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}).then(move |result| {
|
}).then(move |result| {
|
||||||
let msg = match result {
|
let msg = match result {
|
||||||
Ok(()) => Msg::End,
|
Ok(_) => Msg::End,
|
||||||
Err(e) => Msg::Error(e),
|
Err(e) => Msg::Error(e),
|
||||||
};
|
};
|
||||||
tx2.lock().unwrap().send(msg).unwrap();
|
tx2.lock().unwrap().send(msg).unwrap();
|
||||||
@@ -1379,7 +1380,7 @@ fn serve_with_options(options: ServeOptions) -> Serve {
|
|||||||
|
|
||||||
let thread_name = format!("test-server-{:?}", dur);
|
let thread_name = format!("test-server-{:?}", dur);
|
||||||
let thread = thread::Builder::new().name(thread_name).spawn(move || {
|
let thread = thread::Builder::new().name(thread_name).spawn(move || {
|
||||||
tokio::run(::futures::future::lazy(move || {
|
tokio::runtime::run2(::futures::future::lazy(move |_| {
|
||||||
let srv = Http::new()
|
let srv = Http::new()
|
||||||
.keep_alive(keep_alive)
|
.keep_alive(keep_alive)
|
||||||
.pipeline(pipeline)
|
.pipeline(pipeline)
|
||||||
@@ -1390,7 +1391,7 @@ fn serve_with_options(options: ServeOptions) -> Serve {
|
|||||||
}).unwrap();
|
}).unwrap();
|
||||||
addr_tx.send(srv.local_addr().unwrap()).unwrap();
|
addr_tx.send(srv.local_addr().unwrap()).unwrap();
|
||||||
srv.run_until(shutdown_rx.then(|_| Ok(())))
|
srv.run_until(shutdown_rx.then(|_| Ok(())))
|
||||||
.map_err(|err| println!("error {}", err))
|
.map_err(|err| panic!("error {}", err))
|
||||||
}))
|
}))
|
||||||
}).unwrap();
|
}).unwrap();
|
||||||
|
|
||||||
@@ -1420,31 +1421,26 @@ struct DebugStream<T, D> {
|
|||||||
_debug: D,
|
_debug: D,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Read, D> Read for DebugStream<T, D> {
|
|
||||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
|
||||||
self.stream.read(buf)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Write, D> Write for DebugStream<T, D> {
|
|
||||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
|
||||||
self.stream.write(buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn flush(&mut self) -> io::Result<()> {
|
|
||||||
self.stream.flush()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
impl<T: AsyncWrite, D> AsyncWrite for DebugStream<T, D> {
|
impl<T: AsyncWrite, D> AsyncWrite for DebugStream<T, D> {
|
||||||
fn shutdown(&mut self) -> futures::Poll<(), io::Error> {
|
fn poll_write(&mut self, cx: &mut task::Context, buf: &[u8]) -> futures::Poll<usize, io::Error> {
|
||||||
self.stream.shutdown()
|
self.stream.poll_write(cx, buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn poll_flush(&mut self, cx: &mut task::Context) -> futures::Poll<(), io::Error> {
|
||||||
|
self.stream.poll_flush(cx)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn poll_close(&mut self, cx: &mut task::Context) -> futures::Poll<(), io::Error> {
|
||||||
|
self.stream.poll_close(cx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl<T: AsyncRead, D> AsyncRead for DebugStream<T, D> {}
|
impl<T: AsyncRead, D> AsyncRead for DebugStream<T, D> {
|
||||||
|
fn poll_read(&mut self, cx: &mut task::Context, buf: &mut [u8]) -> futures::Poll<usize, io::Error> {
|
||||||
|
self.stream.poll_read(cx, buf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct Dropped(Arc<AtomicBool>);
|
struct Dropped(Arc<AtomicBool>);
|
||||||
|
|||||||
Reference in New Issue
Block a user