|
| 1 | +#![deny(warnings)] |
| 2 | + |
| 3 | +use std::convert::Infallible; |
| 4 | +use std::net::SocketAddr; |
| 5 | + |
| 6 | +use http_body_util::Full; |
| 7 | +use hyper::body::Bytes; |
| 8 | +use hyper::server::conn::http2; |
| 9 | +use hyper::service::service_fn; |
| 10 | +use hyper::{Request, Response}; |
| 11 | +use tokio::net::TcpListener; |
| 12 | + |
| 13 | +// This would normally come from the `hyper-util` crate, but we can't depend |
| 14 | +// on that here because it would be a cyclical dependency. |
| 15 | +#[path = "../benches/support/mod.rs"] |
| 16 | +mod support; |
| 17 | +use support::TokioIo; |
| 18 | + |
| 19 | +// An async function that consumes a request, does nothing with it and returns a |
| 20 | +// response. |
| 21 | +async fn hello(_: Request<hyper::body::Incoming>) -> Result<Response<Full<Bytes>>, Infallible> { |
| 22 | + Ok(Response::new(Full::new(Bytes::from("Hello, World!")))) |
| 23 | +} |
| 24 | + |
| 25 | +#[derive(Clone)] |
| 26 | +// An Executor that uses the tokio runtime. |
| 27 | +pub struct TokioExecutor; |
| 28 | + |
| 29 | +// Implement the `hyper::rt::Executor` trait for `TokioExecutor` so that it can be used to spawn |
| 30 | +// tasks in the hyper runtime. |
| 31 | +// An Executor allows us to manage execution of tasks which can help us improve the efficiency and |
| 32 | +// scalability of the server. |
| 33 | +impl<F> hyper::rt::Executor<F> for TokioExecutor |
| 34 | +where |
| 35 | + F: std::future::Future + Send + 'static, |
| 36 | + F::Output: Send + 'static, |
| 37 | +{ |
| 38 | + fn execute(&self, fut: F) { |
| 39 | + tokio::task::spawn(fut); |
| 40 | + } |
| 41 | +} |
| 42 | + |
| 43 | +#[tokio::main] |
| 44 | +async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> { |
| 45 | + pretty_env_logger::init(); |
| 46 | + |
| 47 | + // This address is localhost |
| 48 | + let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); |
| 49 | + |
| 50 | + // Bind to the port and listen for incoming TCP connections |
| 51 | + let listener = TcpListener::bind(addr).await?; |
| 52 | + |
| 53 | + loop { |
| 54 | + // When an incoming TCP connection is received grab a TCP stream for |
| 55 | + // client-server communication. |
| 56 | + // |
| 57 | + // Note, this is a .await point, this loop will loop forever but is not a busy loop. The |
| 58 | + // .await point allows the Tokio runtime to pull the task off of the thread until the task |
| 59 | + // has work to do. In this case, a connection arrives on the port we are listening on and |
| 60 | + // the task is woken up, at which point the task is then put back on a thread, and is |
| 61 | + // driven forward by the runtime, eventually yielding a TCP stream. |
| 62 | + let (stream, _) = listener.accept().await?; |
| 63 | + // Use an adapter to access something implementing `tokio::io` traits as if they implement |
| 64 | + // `hyper::rt` IO traits. |
| 65 | + let io = TokioIo::new(stream); |
| 66 | + |
| 67 | + // Spin up a new task in Tokio so we can continue to listen for new TCP connection on the |
| 68 | + // current task without waiting for the processing of the HTTP/2 connection we just received |
| 69 | + // to finish |
| 70 | + tokio::task::spawn(async move { |
| 71 | + // Handle the connection from the client using HTTP/2 with an executor and pass any |
| 72 | + // HTTP requests received on that connection to the `hello` function |
| 73 | + if let Err(err) = http2::Builder::new(TokioExecutor) |
| 74 | + .serve_connection(io, service_fn(hello)) |
| 75 | + .await |
| 76 | + { |
| 77 | + eprintln!("Error serving connection: {}", err); |
| 78 | + } |
| 79 | + }); |
| 80 | + } |
| 81 | +} |
0 commit comments