Skip to content

Commit 6a64a22

Browse files
docs(examples): Add graceful_shutdown example. (hyperium#3303)
1 parent 82b7fa5 commit 6a64a22

File tree

3 files changed

+102
-0
lines changed

3 files changed

+102
-0
lines changed

Cargo.toml

+5
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,11 @@ name = "gateway"
125125
path = "examples/gateway.rs"
126126
required-features = ["full"]
127127

128+
[[example]]
129+
name = "graceful_shutdown"
130+
path = "examples/graceful_shutdown.rs"
131+
required-features = ["full"]
132+
128133
[[example]]
129134
name = "hello"
130135
path = "examples/hello.rs"

examples/README.md

+2
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ futures-util = { version = "0.3", default-features = false, features = ["alloc"]
4242

4343
* [`gateway`](gateway.rs) - A server gateway (reverse proxy) that proxies to the `hello` service above.
4444

45+
* [`graceful_shutdown`](graceful_shutdown.rs) - A server that has a timeout for incoming connections and does graceful connection shutdown.
46+
4547
* [`http_proxy`](http_proxy.rs) - A simple HTTP(S) proxy that handle and upgrade `CONNECT` requests and then proxy data between client and remote server.
4648

4749
* [`multi_server`](multi_server.rs) - A server that listens to two different ports, a different `Service` per port.

examples/graceful_shutdown.rs

+95
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
#![deny(warnings)]
2+
3+
use std::convert::Infallible;
4+
use std::net::SocketAddr;
5+
use std::time::Duration;
6+
7+
use bytes::Bytes;
8+
use http_body_util::Full;
9+
use hyper::server::conn::http1;
10+
use hyper::service::service_fn;
11+
use hyper::{Request, Response};
12+
use tokio::net::TcpListener;
13+
use tokio::pin;
14+
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+
// Sleep for 6 seconds to simulate long processing.
23+
// This is longer than the initial 5 second connection timeout,
24+
// but within the 2 second graceful shutdown timeout.
25+
println!("in hello before sleep");
26+
tokio::time::sleep(Duration::from_secs(6)).await;
27+
println!("in hello after sleep");
28+
Ok(Response::new(Full::new(Bytes::from("Hello World!"))))
29+
}
30+
31+
#[tokio::main]
32+
pub async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
33+
pretty_env_logger::init();
34+
35+
// This address is localhost
36+
let addr: SocketAddr = ([127, 0, 0, 1], 3000).into();
37+
38+
// Use a 5 second timeout for incoming connections to the server.
39+
// If a request is in progress when the 5 second timeout elapses,
40+
// use a 2 second timeout for processing the final request and graceful shutdown.
41+
let connection_timeouts = vec![Duration::from_secs(5), Duration::from_secs(2)];
42+
43+
// Bind to the port and listen for incoming TCP connections
44+
let listener = TcpListener::bind(addr).await?;
45+
println!("Listening on http://{}", addr);
46+
loop {
47+
// When an incoming TCP connection is received grab a TCP stream for
48+
// client<->server communication.
49+
let (tcp, remote_address) = listener.accept().await?;
50+
51+
// Use an adapter to access something implementing `tokio::io` traits as if they implement
52+
// `hyper::rt` IO traits.
53+
let io = TokioIo::new(tcp);
54+
55+
// Print the remote address connecting to our server.
56+
println!("accepted connection from {:?}", remote_address);
57+
58+
// Clone the connection_timeouts so they can be passed to the new task.
59+
let connection_timeouts_clone = connection_timeouts.clone();
60+
61+
// Spin up a new task in Tokio so we can continue to listen for new TCP connection on the
62+
// current task without waiting for the processing of the HTTP1 connection we just received
63+
// to finish
64+
tokio::task::spawn(async move {
65+
// Pin the connection object so we can use tokio::select! below.
66+
let conn = http1::Builder::new().serve_connection(io, service_fn(hello));
67+
pin!(conn);
68+
69+
// Iterate the timeouts. Use tokio::select! to wait on the
70+
// result of polling the connection itself,
71+
// and also on tokio::time::sleep for the current timeout duration.
72+
for (iter, sleep_duration) in connection_timeouts_clone.iter().enumerate() {
73+
println!("iter = {} sleep_duration = {:?}", iter, sleep_duration);
74+
tokio::select! {
75+
res = conn.as_mut() => {
76+
// Polling the connection returned a result.
77+
// In this case print either the successful or error result for the connection
78+
// and break out of the loop.
79+
match res {
80+
Ok(()) => println!("after polling conn, no error"),
81+
Err(e) => println!("error serving connection: {:?}", e),
82+
};
83+
break;
84+
}
85+
_ = tokio::time::sleep(*sleep_duration) => {
86+
// tokio::time::sleep returned a result.
87+
// Call graceful_shutdown on the connection and continue the loop.
88+
println!("iter = {} got timeout_interval, calling conn.graceful_shutdown", iter);
89+
conn.as_mut().graceful_shutdown();
90+
}
91+
}
92+
}
93+
});
94+
}
95+
}

0 commit comments

Comments
 (0)