/
hyper-server.rs
156 lines (139 loc) · 4.46 KB
/
hyper-server.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
//! An HTTP+TLS server based on `hyper` and `async-native-tls`.
//!
//! Run with:
//!
//! ```
//! cargo run --example hyper-server
//! ```
//!
//! Open in the browser any of these addresses:
//!
//! - http://localhost:8000/
//! - https://localhost:8001/ (accept the security prompt in the browser)
//!
//! Refer to `README.md` to see how to the TLS certificate was generated.
use std::net::{TcpListener, TcpStream};
use std::pin::Pin;
use std::sync::Arc;
use std::task::{Context, Poll};
use anyhow::Result;
use async_native_tls::{Identity, TlsAcceptor, TlsStream};
use http_body_util::Full;
use hyper::body::Incoming;
use hyper::service::service_fn;
use hyper::{Request, Response};
use macro_rules_attribute::apply;
use smol::{future, io, prelude::*, Async, Executor};
use smol_hyper::rt::{FuturesIo, SmolTimer};
use smol_macros::main;
/// Serves a request and returns a response.
async fn serve(req: Request<Incoming>) -> Result<Response<Full<&'static [u8]>>> {
println!("Serving {}", req.uri());
Ok(Response::new(Full::new("Hello from hyper!".as_bytes())))
}
/// Handle a new client.
async fn handle_client(client: Async<TcpStream>, tls: Option<TlsAcceptor>) -> Result<()> {
// Wrap it in TLS if necessary.
let client = match &tls {
None => SmolStream::Plain(client),
Some(tls) => {
// In case of HTTPS, establish a secure TLS connection.
SmolStream::Tls(tls.accept(client).await?)
}
};
// Build the server.
hyper::server::conn::http1::Builder::new()
.timer(SmolTimer::new())
.serve_connection(FuturesIo::new(client), service_fn(serve))
.await?;
Ok(())
}
/// Listens for incoming connections and serves them.
async fn listen(
ex: &Arc<Executor<'static>>,
listener: Async<TcpListener>,
tls: Option<TlsAcceptor>,
) -> Result<()> {
// Format the full host address.
let host = &match tls {
None => format!("http://{}", listener.get_ref().local_addr()?),
Some(_) => format!("https://{}", listener.get_ref().local_addr()?),
};
println!("Listening on {}", host);
loop {
// Wait for a new client.
let (client, _) = listener.accept().await?;
// Spawn a task to handle this connection.
ex.spawn({
let tls = tls.clone();
async move {
if let Err(e) = handle_client(client, tls).await {
println!("Error while handling client: {}", e);
}
}
})
.detach();
}
}
#[apply(main!)]
async fn main(ex: &Arc<Executor<'static>>) -> Result<()> {
// Initialize TLS with the local certificate, private key, and password.
let identity = Identity::from_pkcs12(include_bytes!("identity.pfx"), "password")?;
let tls = TlsAcceptor::from(native_tls::TlsAcceptor::new(identity)?);
// Start HTTP and HTTPS servers.
let http = listen(
ex,
Async::<TcpListener>::bind(([127, 0, 0, 1], 8000))?,
None,
);
let https = listen(
ex,
Async::<TcpListener>::bind(([127, 0, 0, 1], 8001))?,
Some(tls),
);
future::try_zip(http, https).await?;
Ok(())
}
/// A TCP or TCP+TLS connection.
enum SmolStream {
/// A plain TCP connection.
Plain(Async<TcpStream>),
/// A TCP connection secured by TLS.
Tls(TlsStream<Async<TcpStream>>),
}
impl AsyncRead for SmolStream {
fn poll_read(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut [u8],
) -> Poll<io::Result<usize>> {
match &mut *self {
Self::Plain(s) => Pin::new(s).poll_read(cx, buf),
Self::Tls(s) => Pin::new(s).poll_read(cx, buf),
}
}
}
impl AsyncWrite for SmolStream {
fn poll_write(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<io::Result<usize>> {
match &mut *self {
Self::Plain(s) => Pin::new(s).poll_write(cx, buf),
Self::Tls(s) => Pin::new(s).poll_write(cx, buf),
}
}
fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
match &mut *self {
Self::Plain(s) => Pin::new(s).poll_close(cx),
Self::Tls(s) => Pin::new(s).poll_close(cx),
}
}
fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
match &mut *self {
Self::Plain(s) => Pin::new(s).poll_close(cx),
Self::Tls(s) => Pin::new(s).poll_close(cx),
}
}
}