-
Notifications
You must be signed in to change notification settings - Fork 11
/
echo-server.rs
143 lines (111 loc) · 4.31 KB
/
echo-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
use std::{
fs,
io::{self, Read},
path,
sync::Arc,
};
use anyhow::Context;
use clap::Parser;
use rustls::pki_types::CertificateDer;
use web_transport_quinn::Session;
#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
struct Args {
#[arg(short, long, default_value = "[::]:4443")]
addr: std::net::SocketAddr,
/// Use the certificates at this path, encoded as PEM.
#[arg(long)]
pub tls_cert: path::PathBuf,
/// Use the private key at this path, encoded as PEM.
#[arg(long)]
pub tls_key: path::PathBuf,
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
// Enable info logging.
let env = env_logger::Env::default().default_filter_or("info");
env_logger::init_from_env(env);
let args = Args::parse();
// Read the PEM certificate chain
let chain = fs::File::open(args.tls_cert).context("failed to open cert file")?;
let mut chain = io::BufReader::new(chain);
let chain: Vec<CertificateDer> = rustls_pemfile::certs(&mut chain)
.collect::<Result<_, _>>()
.context("failed to load certs")?;
anyhow::ensure!(!chain.is_empty(), "could not find certificate");
// Read the PEM private key
let mut keys = fs::File::open(args.tls_key).context("failed to open key file")?;
// Read the keys into a Vec so we can parse it twice.
let mut buf = Vec::new();
keys.read_to_end(&mut buf)?;
// Try to parse a PKCS#8 key
// -----BEGIN PRIVATE KEY-----
let key = rustls_pemfile::private_key(&mut io::Cursor::new(&buf))
.context("failed to load private key")?
.context("missing private key")?;
// Standard Quinn setup
let mut config = rustls::ServerConfig::builder_with_provider(Arc::new(
rustls::crypto::ring::default_provider(),
))
.with_protocol_versions(&[&rustls::version::TLS13])?
.with_no_client_auth()
.with_single_cert(chain, key)?;
config.max_early_data_size = u32::MAX;
config.alpn_protocols = vec![web_transport_quinn::ALPN.to_vec()]; // this one is important
let config: quinn::crypto::rustls::QuicServerConfig = config.try_into()?;
let config = quinn::ServerConfig::with_crypto(Arc::new(config));
log::info!("listening on {}", args.addr);
let server = quinn::Endpoint::server(config, args.addr)?;
// Accept new connections.
while let Some(conn) = server.accept().await {
tokio::spawn(async move {
let err = run_conn(conn).await;
if let Err(err) = err {
log::error!("connection failed: {}", err)
}
});
}
// TODO simple echo server
Ok(())
}
async fn run_conn(conn: quinn::Incoming) -> anyhow::Result<()> {
log::info!("received new QUIC connection");
// Wait for the QUIC handshake to complete.
let conn = conn.await.context("failed to accept connection")?;
log::info!("established QUIC connection");
// Perform the WebTransport handshake.
let request = web_transport_quinn::accept(conn).await?;
log::info!("received WebTransport request: {}", request.url());
// Accept the session.
let session = request.ok().await.context("failed to accept session")?;
log::info!("accepted session");
// Run the session
if let Err(err) = run_session(session).await {
log::info!("closing session: {}", err);
}
Ok(())
}
async fn run_session(session: Session) -> anyhow::Result<()> {
loop {
// Wait for a bidirectional stream or datagram.
tokio::select! {
res = session.accept_bi() => {
let (mut send, mut recv) = res?;
log::info!("accepted stream");
// Read the message and echo it back.
let msg = recv.read_to_end(1024).await?;
log::info!("recv: {}", String::from_utf8_lossy(&msg));
send.write_all(&msg).await?;
log::info!("send: {}", String::from_utf8_lossy(&msg));
},
res = session.read_datagram() => {
let msg = res?;
log::info!("accepted datagram");
log::info!("recv: {}", String::from_utf8_lossy(&msg));
session.send_datagram(msg.clone())?;
log::info!("send: {}", String::from_utf8_lossy(&msg));
},
};
log::info!("echo successful!");
}
}