Skip to content

Commit 16daef6

Browse files
authored
docs(example): add an example for server using auto conn (#110)
1 parent 99b77a5 commit 16daef6

File tree

2 files changed

+78
-0
lines changed

2 files changed

+78
-0
lines changed

Cargo.toml

+3
Original file line numberDiff line numberDiff line change
@@ -76,3 +76,6 @@ __internal_happy_eyeballs_tests = []
7676
name = "client"
7777
required-features = ["client-legacy", "http1", "tokio"]
7878

79+
[[example]]
80+
name = "server"
81+
required-features = ["server", "http1", "tokio"]

examples/server.rs

+75
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
//! This example runs a server that responds to any request with "Hello, world!"
2+
3+
use std::{convert::Infallible, error::Error};
4+
5+
use bytes::Bytes;
6+
use http::{header::CONTENT_TYPE, Request, Response};
7+
use http_body_util::{combinators::BoxBody, BodyExt, Full};
8+
use hyper::{body::Incoming, service::service_fn};
9+
use hyper_util::{
10+
rt::{TokioExecutor, TokioIo},
11+
server::conn::auto::Builder,
12+
};
13+
use tokio::{net::TcpListener, task::JoinSet};
14+
15+
/// Function from an incoming request to an outgoing response
16+
///
17+
/// This function gets turned into a [`hyper::service::Service`] later via
18+
/// [`service_fn`]. Instead of doing this, you could also write a type that
19+
/// implements [`hyper::service::Service`] directly and pass that in place of
20+
/// writing a function like this and calling [`service_fn`].
21+
///
22+
/// This function could use [`Full`] as the body type directly since that's
23+
/// the only type that can be returned in this case, but this uses [`BoxBody`]
24+
/// anyway for demonstration purposes, since this is what's usually used when
25+
/// writing a more complex webserver library.
26+
async fn handle_request(
27+
_request: Request<Incoming>,
28+
) -> Result<Response<BoxBody<Bytes, Infallible>>, Infallible> {
29+
let response = Response::builder()
30+
.header(CONTENT_TYPE, "text/plain")
31+
.body(Full::new(Bytes::from("Hello, world!\n")).boxed())
32+
.expect("values provided to the builder should be valid");
33+
34+
Ok(response)
35+
}
36+
37+
#[tokio::main(flavor = "current_thread")]
38+
async fn main() -> Result<(), Box<dyn Error + Send + Sync + 'static>> {
39+
let listen_addr = "127.0.0.1:8000";
40+
let tcp_listener = TcpListener::bind(listen_addr).await?;
41+
println!("listening on http://{listen_addr}");
42+
43+
let mut join_set = JoinSet::new();
44+
loop {
45+
let (stream, addr) = match tcp_listener.accept().await {
46+
Ok(x) => x,
47+
Err(e) => {
48+
eprintln!("failed to accept connection: {e}");
49+
continue;
50+
}
51+
};
52+
53+
let serve_connection = async move {
54+
println!("handling a request from {addr}");
55+
56+
let result = Builder::new(TokioExecutor::new())
57+
.serve_connection(TokioIo::new(stream), service_fn(handle_request))
58+
.await;
59+
60+
if let Err(e) = result {
61+
eprintln!("error serving {addr}: {e}");
62+
}
63+
64+
println!("handled a request from {addr}");
65+
};
66+
67+
join_set.spawn(serve_connection);
68+
}
69+
70+
// If you add a method for breaking the above loop (i.e. graceful shutdown),
71+
// then you may also want to wait for all existing connections to finish
72+
// being served before terminating the program, which can be done like this:
73+
//
74+
// while let Some(_) = join_set.join_next().await {}
75+
}

0 commit comments

Comments
 (0)