b231eb4f04
* 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
79 lines
2.6 KiB
Rust
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;
|
|
}
|