Note that they don’t use 2 tokio
Core instances, but rather 2 different
Handles to it; each
Handle here refers to the same
Core, and the
Core is the actual event loop (aka reactor).
Handles are typically needed by components that want to interact with the event loop, such as spawning futures onto it. They’re cloneable, and allow each component to have their own value (so don’t need to capture references to them).
serve is a
Stream where the items it yields are (essentially)
TcpStreams (i.e. connections to the peer).
serve.for_each(...) returns a
Future that, for each accepted connection, spawns a handler of that connection - this is the
h2.spawn(conn.map(...)... stuff. By
spawn()'ing each accepted connection, these connections make progress independently inside the event loop (i.e. their processing/event notifications are independent of each other).
ResponseExample is where the actual request handling logic lives, and it looks like it sets up two route handlers: one for a POST to
/web_api and another for a GET of
conn.map(|_| ()).map_err(|err| println!(...)) just ignores the result of that handler, and also logs an error if it errors while handling the request.
server_handle.spawn(...) then submits this outer
serve.for_each(...) future to the event loop, which will cause the event loop to drive it.
Core in tokio 0.1 needs a root future to run - once that future completes, the loop finishes. When there’s no concrete root future, but instead a bunch of background tasks (i.e. futures that are
spawn()'ed), you can keep the loop running forever by giving it an empty future as the root - it never completes, and so the loop keeps going. This is mostly useful for examples, and not exactly how you’d write real code.
Why are you looking at how to use hyper with explicit tokio interaction? Most typical usage, I think, just lets hyper handle the tokio interaction internally.