Files
nym/sdk/rust/nym-sdk/examples/stream_idle_timeout.rs
T
mfahampshire b231eb4f04 Max/asyncread asyncwrite nym client (#6318)
* Remove AsyncRead/Write traits from native client - moving them to
stream/

* Substream model first push

* Update / add examples

* Update lockfile

* Clippy

* clippy examples

* remove codecs

* Remove unused bincode setup

* Revert a lot of changes when SDK client itself implemented
AsyncRead/Write

* Remove unnecessary mut

* Use local PollSender in MixnetStream instead of client_input.input_sender

Now that client-core's input_sender is back to mpsc::Sender (reverted
PollSender migration), MixnetStream creates its own PollSender wrapper
for the AsyncWrite impl's poll_ready/start_send calls.

* Remove now-unnecessary parameter

* Clippy

* Cleanup more stragglers from previous setup (Async traits on
MixnetClient)

* Rename files (remove module inception)

* - Shrink StreamId from 16 bytes to u64, add version byte to wire format
  - Introduce MixStreamHeader/MixStreamFrame structs for decode
  - Replace StreamMap type alias with struct using tokio::sync::Mutex
  - Add StreamMap helper methods, eliminate lock().expect() panics
  - Rename stream.rs -> mixnet_stream.rs to avoid module inception
  - Document irrevocable stream mode, add LP integration TODO

* - Remove dummy channel
- Add err variant for reciever alredy taken
- Remove panics

* add timeout to stream

* clippy
2026-03-13 09:40:45 +00:00

79 lines
2.6 KiB
Rust

// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
//! Demonstrates stream idle timeout cleanup.
//!
//! Opens a stream to self, uses it, then stops. After the idle timeout
//! elapses the router removes the stream and reads return EOF.
//!
//! Run with: cargo run --example stream_idle_timeout
use nym_sdk::mixnet;
use std::time::Duration;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
/// Short idle timeout so we don't wait the default
const IDLE_TIMEOUT: Duration = Duration::from_secs(2);
const WAIT_TIMEOUT: Duration = Duration::from_secs(60);
#[tokio::main]
async fn main() {
nym_bin_common::logging::setup_tracing_logger();
// Build a client with a short stream idle timeout.
let mut client = mixnet::MixnetClientBuilder::new_ephemeral()
.with_stream_idle_timeout(IDLE_TIMEOUT)
.build()
.unwrap()
.connect_to_mixnet()
.await
.unwrap();
let our_address = *client.nym_address();
println!("Client address: {our_address}");
// Set up a listener and open a stream to ourselves.
let mut listener = client.listener().unwrap();
let mut outbound = client.open_stream(our_address, None).await.unwrap();
println!("Opened outbound stream: {}", outbound.id());
let mut inbound = tokio::time::timeout(WAIT_TIMEOUT, listener.accept())
.await
.expect("timed out waiting for accept")
.expect("listener shut down");
println!("Accepted inbound stream: {}", inbound.id());
// Use the stream.
let msg = b"hello from idle timeout example";
outbound.write_all(msg).await.unwrap();
outbound.flush().await.unwrap();
let mut buf = vec![0u8; 1024];
let n = tokio::time::timeout(WAIT_TIMEOUT, inbound.read(&mut buf))
.await
.expect("timed out reading")
.expect("read failed");
println!("Received: {:?}", String::from_utf8_lossy(&buf[..n]));
assert_eq!(&buf[..n], msg);
// Now stop using the stream and wait for the idle timeout.
println!(
"\nStream is idle. Waiting {}s for cleanup...",
IDLE_TIMEOUT.as_secs()
);
tokio::time::sleep(IDLE_TIMEOUT + Duration::from_secs(2)).await;
// The router should have cleaned up the stream. The inbound receiver
// is closed, so a read returns 0 bytes (EOF).
let n = inbound.read(&mut buf).await.expect("read failed");
if n == 0 {
println!("Inbound stream returned EOF — cleaned up by idle timeout.");
} else {
println!("Unexpected: got {n} bytes after idle timeout");
}
drop(outbound);
drop(inbound);
client.disconnect().await;
}