Remove the optional "stats" feature by merging it into the "client" and "tui" features to reduce conditional compilation complexity. Replace the "client-runtime" internal feature with direct dependency sets.
`--bind` becomes repeatable, so one process can serve several addresses:
an explicit IPv4 and IPv6 pair on one port, or two interface addresses
that must stay distinct.
The applet now builds a `ServerSet` for every invocation, including a
single bind. There is deliberately no bind-count branch and no direct
path from the CLI to `Server`: one orchestration path means startup
errors, shutdown and listener-failure handling are identical whatever was
configured, and the single-listener case — the common one — keeps that
path exercised in normal use rather than only in tests.
The configuration stays one `ServerConfig`; addresses never enter it, and
`ServerSet` clones it into each listener. Bounds in it are therefore per
listener, which `--max-sessions` help now says. Listener lines are
printed only once the whole set is up, so a set whose second bind fails
no longer announces the first as listening.
The shutdown flag, its polling future and the current-thread runtime are
unchanged: exactly one shutdown future is created and the set fans it out
internally.
`Server` answers on one address. A host that must serve several — an
explicit IPv4 and IPv6 pair, or two interface addresses — needs several
of them, and something has to own their construction, their shutdown and
each other's failures.
`ServerSet` is that owner, and deliberately not a protocol object: it
holds no session state, no tokens and no clock, and nothing it does is
visible on the wire. Each listener stays a complete `Server` with its own
socket, core, session table and tokens, so a token issued by one is
unknown at the others and every bound in the cloned `ServerConfig` —
`max_sessions` included — applies per listener rather than per process.
Construction binds every listener or none, preserving requested order and
recording the addresses actually bound. `run` moves each listener into
its own task, fans one shutdown future out over a watch channel, and
joins them all: a listener that fails, or that returns before shutdown
was asked for, shuts its siblings down and fails the set, because a
service configured for two families must not continue silently as one.
A genuine IPv6 listener sharing an explicit port with an IPv4 sibling is
created `IPV6_V6ONLY` before it is bound, since a dual-stack socket would
otherwise claim the IPv4 port too. That is the only pre-bind step, it
uses safe `socket2` APIs, and an IPv4-mapped address is read as the IPv4
listener it is — never as the IPv6 half of a pair. Standalone
`Server::bind` semantics are unchanged.
The continuous-request row said a -d 0 request never reaches the wire,
which no observation in this validation supports: the run took no packet
capture and the server was not instrumented for datagram arrival, so
what the client emitted before exiting was never seen.
Record what was actually observed — the argument was refused, the client
exited non-zero, and it printed none of the connection line it prints in
every other scenario — and state plainly that the emission was not
observed. The conclusion that matters is unchanged: continuous mode was
not exercisable through this CLI.
No implementation, test or protocol evidence changes.
The environment notes said restriction scenarios were run both with and
without --loose, but the matrix records a strict run for the
packet-length restriction only; the interval and maximum-duration rows
were --loose alone. Say that, so the artifact does not credit
strict-mode coverage it never recorded.
No implementation, test or protocol evidence changes.
Two claims in the validation record were stronger than the evidence
behind them.
The maximum-duration Close conclusion was stated for every conforming
client. That does not follow: the deadline is evaluated on arrival, not
on transmission, so an echo sent legitimately before the negotiated
Duration elapsed can still be queued long enough to arrive after the
server's two-second grace and carry Close. Scope the result to the
loopback run that was actually performed, and describe the Close as a
termination path for traffic continuing or arriving beyond the grace,
including delayed in-flight traffic.
The document also claimed the validation was reproducible from the
commands above, while carrying only summarized client arguments and no
harness — the harness was disposable on purpose and is not being
promoted into repository infrastructure. Say plainly that this is a
record of the validation performed rather than a reproduction harness.
No implementation, test or protocol evidence changes.
Run the released upstream IRTT 0.9.1 client as a black box against the
Tokio server over explicit loopback listeners, and record the result.
Twelve of thirteen matrix rows pass and one is skipped for a client-side
CLI reason: IPv4 and IPv6 ordinary sessions, HMAC with a wrong-key
negative control, packet-length and interval restrictions both strictly
and with --loose, maximum-duration restriction, no-test, and midpoint
timestamps on each clock selection. No incompatibility was found, so
nothing in the implementation changed.
Two results are worth keeping. The single-field midpoint reply this
server deliberately emits instead of upstream 0.9.1's dual-field form is
accepted by the released client, which derives RTT and IPDV from it. And
the maximum-duration close is unreachable by a conforming client: the
deadline is the configured maximum plus two seconds while negotiation
caps the session Duration at that same maximum, so a client honoring the
Duration it was given always stops early. The two-second grace is the
measured margin already recorded in the protocol evidence, so this is
the implementation matching that evidence rather than a defect, and the
close path stays covered by the lifecycle tests.
Upstream source, tests and history were not inspected; only the
executable's version, help, exit status and normal output were observed.
No test in this repository depends on the upstream binary.