From d7d6e766823e7ea9b91ea8962a68d03897ba635e Mon Sep 17 00:00:00 2001 From: Nils Andreas Svee Date: Thu, 24 Sep 2026 16:40:04 +0700 Subject: [PATCH] docs(server): add executable API examples --- crates/irtt-server/src/config.rs | 22 ++++++++++++++++++++++ crates/irtt-server/src/core.rs | 26 ++++++++++++++++++++++++++ crates/irtt-server/src/runtime.rs | 22 ++++++++++++++++++++++ crates/irtt-server/src/set.rs | 23 +++++++++++++++++++++++ 4 files changed, 93 insertions(+) diff --git a/crates/irtt-server/src/config.rs b/crates/irtt-server/src/config.rs index 583bed6..3be109a 100644 --- a/crates/irtt-server/src/config.rs +++ b/crates/irtt-server/src/config.rs @@ -98,6 +98,28 @@ pub enum TimestampAllowance { /// [`max_packet_length`](Self::max_packet_length) like every other reply, and /// `none` is zero-filled here rather than left as residue. An operator /// restriction can be added if a real use case turns up. +/// +/// # Example +/// +/// Configure a modest per-listener session budget and restrict timestamp +/// placement while leaving DSCP marking disabled: +/// +/// ``` +/// use std::time::Duration; +/// +/// use irtt_server::{ServerConfig, TimestampAllowance}; +/// +/// let config = ServerConfig::default() +/// .with_max_sessions(128) +/// .with_idle_timeout(Duration::from_secs(30)) +/// .with_timestamp_allowance(TimestampAllowance::Single) +/// .with_dscp_allowed(false); +/// +/// assert_eq!(config.max_sessions(), 128); +/// assert_eq!(config.idle_timeout(), Duration::from_secs(30)); +/// assert_eq!(config.timestamp_allowance(), TimestampAllowance::Single); +/// assert!(!config.dscp_allowed()); +/// ``` #[derive(Debug, Clone, PartialEq, Eq)] pub struct ServerConfig { hmac_key: Option>, diff --git a/crates/irtt-server/src/core.rs b/crates/irtt-server/src/core.rs index 455c1c8..9517f86 100644 --- a/crates/irtt-server/src/core.rs +++ b/crates/irtt-server/src/core.rs @@ -47,6 +47,32 @@ use crate::{ /// a transport abstraction, and there is no blocking or alternate-runtime /// counterpart. /// +/// # Example +/// +/// The core can be driven directly with a normally encoded open request. It +/// returns a reply to send and records the new session without performing I/O: +/// +/// ``` +/// use std::net::{Ipv4Addr, SocketAddr}; +/// +/// use irtt_proto::{decode_open_reply, encode_request, Params, RequestToEncode}; +/// use irtt_server::{ServerConfig, ServerCore}; +/// +/// let peer = SocketAddr::from((Ipv4Addr::LOCALHOST, 40_000)); +/// let mut core = ServerCore::new(ServerConfig::default()); +/// let params = Params::with_protocol_defaults(); +/// let request = encode_request( +/// RequestToEncode::Open { params: ¶ms, no_test: false }, +/// None, +/// ) +/// .unwrap(); +/// +/// let reply = core.handle_datagram(peer, &request).unwrap().unwrap(); +/// let open = decode_open_reply(reply.bytes(), None).unwrap(); +/// assert_ne!(open.token, 0); +/// assert_eq!(core.session_count(), 1); +/// ``` +/// /// # Scope /// /// The core implements open handling, session creation and negotiation, diff --git a/crates/irtt-server/src/runtime.rs b/crates/irtt-server/src/runtime.rs index a6de0b6..4c5d88d 100644 --- a/crates/irtt-server/src/runtime.rs +++ b/crates/irtt-server/src/runtime.rs @@ -53,6 +53,28 @@ const MAINTENANCE_INTERVAL: Duration = Duration::from_secs(1); /// [`ServerRuntimeError::WildcardSourceSelectionUnsupported`] — rather than /// served by a listener whose replies a client on a second address would /// silently discard. Explicit-address listeners are unaffected everywhere. +/// +/// # Example +/// +/// Bind one listener, inspect the resolved endpoint, and let the caller decide +/// when serving ends. This example type-checks without opening a socket: +/// +/// ```no_run +/// use std::{net::{Ipv4Addr, SocketAddr}, time::Duration}; +/// +/// use irtt_server::{Server, ServerConfig}; +/// +/// # async fn serve() -> Result<(), Box> { +/// let mut server = Server::bind( +/// SocketAddr::from((Ipv4Addr::LOCALHOST, 0)), +/// ServerConfig::default(), +/// ) +/// .await?; +/// println!("listening on {}", server.local_addr()?); +/// server.run(tokio::time::sleep(Duration::from_secs(60))).await?; +/// # Ok(()) +/// # } +/// ``` #[derive(Debug)] pub struct Server { socket: UdpSocket, diff --git a/crates/irtt-server/src/set.rs b/crates/irtt-server/src/set.rs index b3efc68..e16677b 100644 --- a/crates/irtt-server/src/set.rs +++ b/crates/irtt-server/src/set.rs @@ -57,6 +57,29 @@ use crate::{Server, ServerConfig, ServerRuntimeError}; /// whole set: a service configured for IPv4 and IPv6 must not quietly continue /// as IPv4 only. Every listener task is joined before `run` returns, so no task /// outlives it. +/// +/// # Example +/// +/// Bind independent listeners, inspect their resolved addresses, then serve +/// them under one caller-owned shutdown future. This example type-checks +/// without binding sockets: +/// +/// ```no_run +/// use std::{net::{Ipv4Addr, SocketAddr}, time::Duration}; +/// +/// use irtt_server::{ServerConfig, ServerSet}; +/// +/// # async fn serve() -> Result<(), Box> { +/// let addrs = [ +/// SocketAddr::from((Ipv4Addr::LOCALHOST, 0)), +/// SocketAddr::from((Ipv4Addr::LOCALHOST, 0)), +/// ]; +/// let set = ServerSet::bind(addrs, ServerConfig::default()).await?; +/// println!("listening on {:?}", set.local_addrs()); +/// set.run(tokio::time::sleep(Duration::from_secs(60))).await?; +/// # Ok(()) +/// # } +/// ``` #[derive(Debug)] pub struct ServerSet { servers: Vec,