Nils Andreas Svee d269f77c87 feat(server): handle client close
Release the session a client close names. A close is bound to a session only
when the token matches and the datagram arrived from that session's exact
endpoint, so holding a token is not by itself authority to tear the session
down: a close from a foreign source port, address, family or IPv6 scope leaves
the session live, and one close cannot take down a sibling session the same
peer opened separately. A token that is unknown, already closed or zero matches
nothing and leaves the table untouched; zero is reserved for no-test replies and
never issued.

Nothing is sent either way. Protocol version 1 defines no acknowledgement for a
client close, so a successful close returns the same `Ok(None)` as a rejected
one and a delivered close is indistinguishable to the client from a lost one.
A standalone `Reply|Close` datagram is tolerated by clients but has no
demonstrated use, so none is encoded.

Authentication still runs before the session is looked up. That keeps a bad MAC
on a live token indistinguishable from an unknown token, and means a close that
fails authentication — missing, wrongly keyed, corrupted, or unexpected on a
server with no key — cannot remove a session. Trailing bytes after the token
stay acceptable: the structural decoder already tolerates them and the server
does not re-parse the request.

The session table remains the only record of how full the server is, so a close
frees capacity immediately and a refused open can succeed once one is released.
Close draws no token and touches no other state; the endpoint lookup borrows
the table only until the comparison is made, so the removal needs no clone.

Echo remains ignored. The test that pinned close as a no-op is replaced by
`tests/close.rs`, which covers removal, silence, repeated close, echo after
close, endpoint binding, unknown/zero/stale tokens, authentication failure,
trailing bytes, sibling sessions and capacity reuse; `unsupported.rs` keeps
echo and structurally invalid traffic.
2026-08-12 10:30:50 +07:00
2026-08-12 10:30:50 +07:00
2026-08-12 10:30:50 +07:00
2026-08-12 00:47:46 +07:00
2026-08-12 00:47:46 +07:00
2026-05-10 20:56:43 +07:00

irtt-rs

irtt-rs is a Rust implementation of an IRTT-compatible client.

It provides both a command-line client and a reusable Rust library for finite or continuous latency probing. It is intended for interactive diagnostics, scripting, monitoring, and integration into applications such as network autorate controllers.

This is not the upstream IRTT project. For the original implementation, protocol background, and broader documentation, see heistp/irtt.

Features

  • Finite and continuous probe sessions
  • Concurrent probing of multiple targets
  • Reusable client library with a structured event stream
  • Human-readable table output
  • CSV, TSV, and JSON Lines output for scripts and monitoring systems
  • Selectable output columns
  • Automatic final summaries for eligible table-output runs
  • Optional terminal UI
  • Router-friendly multi-applet binary layout

Server support is not implemented.

The installed dispatcher is named irtt-rs rather than irtt to avoid conflicting with or being mistaken for upstream IRTT.

Installation

The build requires Rust 1.88 or newer.

From a local checkout:

git clone https://github.com/Lochnair/irtt-rs.git
cd irtt-rs
cargo install --path crates/irtt-cli

This installs:

  • irtt-rs, the canonical multi-applet dispatcher
  • irtt-cli, the stream and text client

To also install the optional terminal UI:

cargo install --path crates/irtt-cli --features tui

Quick start

Probe a server using the default settings:

irtt-cli netperf-eu.bufferbloat.net:2112

Set the test duration and probe interval:

irtt-cli netperf-eu.bufferbloat.net:2112 \
    --duration 30s \
    --interval 100ms

Run continuously until interrupted:

irtt-cli netperf-eu.bufferbloat.net:2112 --duration 0

Use Ctrl-C to stop gracefully.

Multiple targets

Probe several targets concurrently:

irtt-cli host-a:2112 host-b:2112

Assign stable labels to targets:

irtt-cli \
    --target ams=ams.example.com:2112 \
    --target sg=sg.example.com:2112

Multi-target sessions use staggered pacing by default. To send each target's probes together:

irtt-cli host-a:2112 host-b:2112 --pacing burst

Target labels are included in the default multi-target output.

Output formats

The CLI supports four event-row formats:

  • table: human-readable terminal output
  • csv: comma-separated output
  • tsv: tab-separated output
  • jsonl: one JSON object per event

Examples:

irtt-cli <server> --format table
irtt-cli <server> --format jsonl
irtt-cli <server> --format csv \
    --columns event,seq,remote,effective_rtt_us

For a stream containing only effective RTT values in microseconds:

irtt-cli <server> \
    --format tsv \
    --columns effective_rtt_us \
    --header never

List all available columns with:

irtt-cli --list-columns

Useful measurement fields include:

  • raw_rtt_us: client-observed send-to-receive RTT
  • adjusted_rtt_us: RTT adjusted for server processing when available
  • effective_rtt_us: adjusted RTT when available, otherwise raw RTT
  • sd_us and rd_us: send and receive one-way delay estimates
  • ipdv_us: inter-packet delay variation
  • server_processing_us: time spent processing the packet at the server

Adjusted RTT can be negative when server processing exceeds the measured raw RTT. One-way delay estimates can be negative because of clock skew between the client and server.

Default table output prints a final summary after completed finite runs and interrupted continuous runs when the run policy permits it. CSV, TSV, and JSON Lines output do not print this summary.

A peer closing a finite CLI or TUI run is accepted as a terminal outcome. In continuous mode, peer closure exits nonzero unless the user requested interruption, allowing a supervisor to restart the client.

Terminal UI

When built with the tui feature, irtt-tui provides a live cumulative dashboard:

irtt-tui <server>

It runs continuously by default. A finite duration can be selected explicitly:

irtt-tui <server> --duration 30s

Multiple targets and pacing options work the same way as in irtt-cli:

irtt-tui host-a:2112 host-b:2112 --pacing burst

Quit with q or Ctrl-C.

Library usage

irtt-client exposes the client session and event layer independently of CLI formatting and statistics.

Add it from a local checkout:

[dependencies]
irtt-client = { path = "crates/irtt-client", features = ["tokio"] }

Minimal managed-client example for a synchronous frontend:

use std::time::Duration;

use irtt_client::managed::{
    BlockingManagedClient, ManagedClientConfig, ManagedCompletionPolicy,
    ManagedTargetConfig, TargetId,
};
use irtt_client::ClientConfig;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let config = ManagedClientConfig {
        client: ClientConfig {
            duration: Some(Duration::from_secs(10)),
            interval: Duration::from_secs(1),
            ..ClientConfig::default()
        },
        completion: ManagedCompletionPolicy::FinishWhenQuiescent,
        ..ManagedClientConfig::default()
    };
    let targets = vec![ManagedTargetConfig::new(
        TargetId::from("edge"),
        "netperf-eu.bufferbloat.net:2112",
    )];
    let owner = BlockingManagedClient::start(config, targets)?;
    let outcome = owner.join()?;
    println!("session ended: {:?}", outcome.end_reason);

    Ok(())
}

For presentation events, use BlockingManagedClient::start_with_subscription, ManagedEvent, and ManagedEventTryRecvError, while consulting the durable handle.status() and final outcome for authoritative state. Presentation events are bounded and lossy, not reliable history. Dynamic desired-target updates are available through ManagedClientHandle::update_targets.

BlockingManagedClient owns a dedicated Tokio runtime and thread for synchronous callers. Callers that already own Tokio can drive ManagedClientTask directly.

Binaries and features

Build Binaries
--no-default-features irtt-rs
Default features irtt-rs, irtt-cli
--features tui irtt-rs, irtt-cli, irtt-tui
--all-features irtt-rs, irtt-cli, irtt-tui

For available command-line options:

irtt-cli --help
irtt-tui --help

Development

Common verification commands:

cargo fmt --check
cargo test --workspace
cargo clippy --workspace --all-targets -- -D warnings
cargo build -p irtt-cli --all-features --release

Project status

The client, event stream, machine-readable output, multi-target execution, local statistics, and optional TUI are implemented.

Server support is not implemented.

S
Description
No description provided
Readme Apache-2.0
8 MiB
0 Stars 2 Watchers 0 Forks
2026-08-20 15:42:21 +00:00
Languages
Rust 95.6%
Python 4.3%