mirror of
https://github.com/n0-computer/noq.git
synced 2026-09-23 19:48:19 +00:00
Add the simplest QUIC connection example
This commit is contained in:
committed by
Dirkjan Ochtman
parent
2dd342f4dc
commit
53e677dac6
@@ -0,0 +1,78 @@
|
||||
//! Commonly used code in most examples.
|
||||
|
||||
use quinn::{
|
||||
Certificate, CertificateChain, ClientConfig, ClientConfigBuilder, Endpoint, EndpointDriver,
|
||||
Incoming, PrivateKey, ServerConfig, ServerConfigBuilder, TransportConfig,
|
||||
};
|
||||
use std::error::Error;
|
||||
use std::net::ToSocketAddrs;
|
||||
use std::sync::Arc;
|
||||
|
||||
/// Constructs a QUIC endpoint configured for use a client only.
|
||||
///
|
||||
/// ## Args
|
||||
///
|
||||
/// - server_certs: list of trusted certificates.
|
||||
#[allow(unused)]
|
||||
pub fn make_client_endpoint<A: ToSocketAddrs>(
|
||||
bind_addr: A,
|
||||
server_certs: &[&[u8]],
|
||||
) -> Result<(Endpoint, EndpointDriver), Box<Error>> {
|
||||
let client_cfg = configure_client(server_certs)?;
|
||||
let mut endpoint_builder = Endpoint::new();
|
||||
endpoint_builder.default_client_config(client_cfg);
|
||||
let (driver, endpoint, _incoming) = endpoint_builder.bind(bind_addr)?;
|
||||
Ok((endpoint, driver))
|
||||
}
|
||||
|
||||
/// Constructs a QUIC endpoint configured to listen for incoming connections on a certain address
|
||||
/// and port.
|
||||
///
|
||||
/// ## Returns
|
||||
///
|
||||
/// - UDP socket driver
|
||||
/// - a sream of incoming QUIC connections
|
||||
/// - server certificate serialized into DER format
|
||||
pub fn make_server_endpoint<A: ToSocketAddrs>(
|
||||
bind_addr: A,
|
||||
) -> Result<(EndpointDriver, Incoming, Vec<u8>), Box<Error>> {
|
||||
let (server_config, server_cert) = configure_server()?;
|
||||
let mut endpoint_builder = Endpoint::new();
|
||||
endpoint_builder.listen(server_config);
|
||||
let (driver, _endpoint, incoming) = endpoint_builder.bind(bind_addr)?;
|
||||
Ok((driver, incoming, server_cert))
|
||||
}
|
||||
|
||||
/// Builds default quinn client config and trusts given certificates.
|
||||
///
|
||||
/// ## Args
|
||||
///
|
||||
/// - server_certs: a list of trusted certificates in DER format.
|
||||
fn configure_client(server_certs: &[&[u8]]) -> Result<ClientConfig, Box<Error>> {
|
||||
let mut cfg_builder = ClientConfigBuilder::default();
|
||||
for cert in server_certs {
|
||||
cfg_builder.add_certificate_authority(Certificate::from_der(&cert)?)?;
|
||||
}
|
||||
Ok(cfg_builder.build())
|
||||
}
|
||||
|
||||
/// Returns default server configuration along with its certificate.
|
||||
fn configure_server() -> Result<(ServerConfig, Vec<u8>), Box<Error>> {
|
||||
let cert = rcgen::generate_simple_self_signed(vec!["localhost".into()]);
|
||||
let cert_der = cert.serialize_der();
|
||||
let priv_key = cert.serialize_private_key_der();
|
||||
let priv_key = PrivateKey::from_der(&priv_key)?;
|
||||
|
||||
let server_config = ServerConfig {
|
||||
transport: Arc::new(TransportConfig {
|
||||
stream_window_uni: 0,
|
||||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
};
|
||||
let mut cfg_builder = ServerConfigBuilder::new(server_config);
|
||||
let cert = Certificate::from_der(&cert_der)?;
|
||||
cfg_builder.certificate(CertificateChain::from_certs(vec![cert]), priv_key)?;
|
||||
|
||||
Ok((cfg_builder.build(), cert_der))
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
//! This example intends to use the smallest amout of code to make a simple QUIC connection.
|
||||
//!
|
||||
//! The server issues it's own certificate and passes it to the client to trust.
|
||||
//!
|
||||
//! Run:
|
||||
//! ```text
|
||||
//! $ cargo run --example connection
|
||||
//! ```
|
||||
//!
|
||||
//! This example will make a QUIC connection on localhost, and you should see output like:
|
||||
//! ```text
|
||||
//! [server] incoming connection: id=3680c7d3b3ebd250 addr=127.0.0.1:50469
|
||||
//! [client] connected: id=61a2df1548935aeb, addr=127.0.0.1:5000
|
||||
//! ```
|
||||
|
||||
use futures::{Future, Stream};
|
||||
use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4};
|
||||
use tokio::runtime::current_thread::{self, Runtime};
|
||||
|
||||
mod common;
|
||||
use common::{make_client_endpoint, make_server_endpoint};
|
||||
|
||||
const SERVER_PORT: u16 = 5000;
|
||||
|
||||
fn main() -> Result<(), Box<std::error::Error>> {
|
||||
let mut runtime = Runtime::new()?;
|
||||
|
||||
let (driver, incoming, server_cert) = make_server_endpoint(("0.0.0.0", SERVER_PORT))?;
|
||||
// drive UDP socket
|
||||
runtime.spawn(driver.map_err(|e| panic!("IO error: {}", e)));
|
||||
let handle_incoming_conns = incoming
|
||||
.take(1)
|
||||
.for_each(move |(conn_driver, conn, _incoming)| {
|
||||
current_thread::spawn(conn_driver.map_err(|_| ()));
|
||||
println!(
|
||||
"[server] incoming connection: id={} addr={}",
|
||||
conn.remote_id(),
|
||||
conn.remote_address()
|
||||
);
|
||||
Ok(())
|
||||
});
|
||||
runtime.spawn(handle_incoming_conns);
|
||||
|
||||
let (endpoint, driver) = make_client_endpoint("0.0.0.0:0", &[&server_cert])?;
|
||||
// drive UDP socket
|
||||
runtime.spawn(driver.map_err(|e| panic!("IO error: {}", e)));
|
||||
|
||||
let server_addr = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), SERVER_PORT));
|
||||
let connect = endpoint
|
||||
.connect(&server_addr, "localhost")?
|
||||
.map_err(|e| panic!("Failed to connect: {}", e))
|
||||
.and_then(|(conn_driver, conn, _)| {
|
||||
current_thread::spawn(conn_driver.map_err(|_| ()));
|
||||
println!(
|
||||
"[client] connected: id={}, addr={}",
|
||||
conn.remote_id(),
|
||||
conn.remote_address()
|
||||
);
|
||||
Ok(())
|
||||
});
|
||||
runtime.spawn(connect);
|
||||
|
||||
// We don't need it anymore and dropping the endpoint will make it's driver finish eventually.
|
||||
drop(endpoint);
|
||||
|
||||
runtime.run()?;
|
||||
Ok(())
|
||||
}
|
||||
Reference in New Issue
Block a user