QuinnでQUICを試す
参考記事: https://tech.aptpod.co.jp/entry/2020/12/04/100000
https://github.com/r74tech/quinn-example
1
2
3
4
5
6
7
.
├── Cargo.toml
└── src
├── client.rs
├── common.rs
├── main.rs
└── 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
[package]
name = "quinn-example"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[[bin]]
name = "server"
path = "src/server.rs"
[[bin]]
name = "client"
path = "src/client.rs"
[dependencies]
anyhow = "1.0"
quinn = "0.10"
tokio = { version = "1.38", features = ["full"] }
futures = "0.3"
rcgen = "0.12"
rand = "0.8"
rustls = { version = "0.21", features = ["dangerous_configuration"] }
1
2
3
4
5
6
7
8
use anyhow::Result;
mod common;
fn main() -> Result<()> {
let (cert_der, key_der) = common::generate_self_signed_cert()?;
common::save_cert_and_key(&cert_der, &key_der)?;
Ok(())
}
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
use quinn::{ClientConfig, ServerConfig, TransportConfig};
use rustls::{Certificate, PrivateKey};
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use std::sync::Arc;
use anyhow::*;
pub const SERVER_PORT: u16 = 5000;
pub const SERVER_CERT_PATH: &str = "cert.der";
pub const SERVER_KEY_PATH: &str = "key.der";
pub fn generate_self_signed_cert() -> Result<(Vec<u8>, Vec<u8>)> {
let cert = rcgen::generate_simple_self_signed(vec!["localhost".into()])?;
let cert_der = cert.serialize_der()?;
let priv_key = cert.serialize_private_key_der();
Ok((cert_der, priv_key))
}
pub fn save_cert_and_key(cert_der: &[u8], key_der: &[u8]) -> Result<()> {
std::fs::write(SERVER_CERT_PATH, cert_der)?;
std::fs::write(SERVER_KEY_PATH, key_der)?;
Ok(())
}
pub fn load_cert_and_key() -> Result<(Vec<u8>, Vec<u8>)> {
let cert_der = std::fs::read(SERVER_CERT_PATH)?;
let key_der = std::fs::read(SERVER_KEY_PATH)?;
Ok((cert_der, key_der))
}
pub fn configure_server(cert_der: Vec<u8>, priv_key: Vec<u8>) -> Result<ServerConfig> {
let mut server_config = ServerConfig::with_single_cert(
vec![Certificate(cert_der)],
PrivateKey(priv_key)
)?;
server_config.transport = Arc::new(TransportConfig::default());
Ok(server_config)
}
pub fn configure_client(cert_der: Vec<u8>) -> Result<ClientConfig> {
let mut roots = rustls::RootCertStore::empty();
roots.add(&Certificate(cert_der))?;
let client_config = ClientConfig::new(Arc::new(
rustls::ClientConfig::builder()
.with_safe_defaults()
.with_root_certificates(roots)
.with_no_client_auth()
));
Ok(client_config)
}
pub fn get_server_addr() -> SocketAddr {
SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), SERVER_PORT)
}
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
use anyhow::*;
use quinn::{Endpoint, Connection};
use tokio::io::AsyncReadExt;
use std::result::Result::Ok;
mod common;
#[tokio::main]
async fn main() -> Result<()> {
let (cert_der, _) = common::load_cert_and_key()?;
let client_config = common::configure_client(cert_der)?;
let mut endpoint = Endpoint::client("0.0.0.0:0".parse()?)?;
endpoint.set_default_client_config(client_config);
let server_addr = common::get_server_addr();
let connection = endpoint.connect(server_addr, "localhost")?.await?;
println!("Connected to {}", connection.remote_address());
run_client(connection).await?;
endpoint.wait_idle().await;
Ok(())
}
async fn run_client(connection: Connection) -> Result<()> {
loop {
println!("Enter a message (or 'quit' to exit):");
let mut input = String::new();
std::io::stdin().read_line(&mut input)?;
let message = input.trim();
if message == "quit" {
break;
}
let response = send_message(&connection, message).await?;
println!("Server response: {}", response);
}
connection.close(0u32.into(), b"Done");
Ok(())
}
async fn send_message(connection: &Connection, message: &str) -> Result<String> {
let (mut send, mut recv) = connection.open_bi().await?;
send.write_all(message.as_bytes()).await?;
send.finish().await?;
let mut response = String::new();
recv.read_to_string(&mut response).await?;
Ok(response)
}
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
use anyhow::*;
use quinn::{Endpoint, Connection};
use std::net::{SocketAddr, IpAddr, Ipv4Addr};
use std::result::Result::Ok;
mod common;
#[tokio::main]
async fn main() -> Result<()> {
let (cert_der, priv_key) = common::load_cert_and_key()?;
let server_config = common::configure_server(cert_der, priv_key)?;
let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), common::SERVER_PORT);
let endpoint = Endpoint::server(server_config, addr)?;
println!("Listening on {}", endpoint.local_addr()?);
run_server(endpoint).await
}
async fn run_server(endpoint: Endpoint) -> Result<()> {
while let Some(conn) = endpoint.accept().await {
tokio::spawn(async move {
match conn.await {
Ok(connection) => {
println!("Connection established from: {}", connection.remote_address());
if let Err(e) = handle_connection(connection).await {
eprintln!("Connection error: {}", e);
}
}
Err(e) => eprintln!("Connection failed: {}", e),
}
});
}
Ok(())
}
async fn handle_connection(connection: Connection) -> Result<()> {
while let Ok((mut send, mut recv)) = connection.accept_bi().await {
let mut buf = Vec::new();
while let Some(chunk) = recv.read_chunk(1024, false).await? {
buf.extend_from_slice(&chunk.bytes);
}
println!("Received: {}", String::from_utf8_lossy(&buf));
send.write_all(&buf).await?;
send.finish().await?;
}
Ok(())
}
1
2
3
$ cargo run --bin quinn-example
$ cargo run --bin server
$ cargo run --bin client
This post is licensed under CC BY-SA 4.0 by the author.