Description
I've put this off for a while claiming that it will be solved in tokio-proto, but I'm now leaning towards may as well fix it in hyper, if a generic way appears in the future, no harm done. So, on to the proposal:
I hinted at a possible solution earlier in another issue, and I think a solution is probably similar: Add a way to combine an Http
, Service
, TcpListener
, and Handle
into a impl Future
.
Proposed API
Http
pub fn bind_handle<S>(&self, addr: &SocketAddr, new_service: S, handle: &Handle) -> Server
Server
pub fn shutdown_signal<F>(&mut self, signal: F) -> &mut Self
impl Future for Server
Usage
// Spawn 2 servers on the same Core
let mut core = Core::new()?;
let handle = core.handle();
let srv1 = Http::new()
.bind_handle(addr1, new_service1, &handle);
let srv2 = Http::new()
.bind_handle(addr2, new_service2, &handle);
handle.spawn(srv1);
handle.spawn(srv2);
core.run(futures::future::empty())?;
// Spawn a Server with a Client
let mut core = Core::new()?;
let handle = core.handle();
let client = Client::new(&handle);
let server = Http::new()
.bind_handle(addr, new_service(client), &handle);
handle.spawn(server);
core.run(futures::future::empty())?;
// Spawn a Server and still get graceful shutdown
let mut core = Core::new()?;
let handle = core.handle();
// send on tx to shutdown server future
let (tx, rx) = oneshot::channel();
let client = Client::new(&handle);
let mut server = Http::new()
.bind_handle(addr, new_service(client), &handle);
server.shutdown_signal(rx);
// can even just run server directly
core.run(server)?;
Implementation
This proposal is reusing the Server
type, which already exists in a form that owns a Core
. Internally, it can just change its field to an enum of 2 variants, 1 with a Core
, and the other with a Handle
. It should be able to use the majority of the same code in both cases.
It might be tricky that you can create a Server
using Http::bind
, which owns a Core, and then you can send that Server
into another Core
, calling handle.spawn(server)
. If you were to do that, the impl Future for Server
could make the outer Core
lock up, since an implementation might decide to call core.run()
on its inner Core
.
To prevent that mistake, the impl Future for Server
should probably panic if the Server
owns a Core
, and not a Handle
, with a nice message explaining don't do that.