#![allow(unsafe_code, clippy::missing_safety_doc)]
use i2pd_sys::*;
use std::ffi::CString;
use std::os::raw::c_void;
use std::sync::mpsc;
use std::time::Duration;
struct SendableStream(*mut I2pdStream);
unsafe impl Send for SendableStream {}
unsafe extern "C" fn on_accept(ctx: *mut c_void, stream: *mut I2pdStream) {
let tx = unsafe { &*(ctx as *const mpsc::Sender<SendableStream>) };
let _ = tx.send(SendableStream(stream));
}
#[test]
#[ignore = "needs live I2P network egress and can take several minutes to build real tunnels"]
fn two_transient_destinations_exchange_bytes() {
unsafe {
let app_name = CString::new("tachyon-i2pd-roundtrip-test").unwrap();
i2pd_init(app_name.as_ptr());
i2pd_start();
let dest_a = i2pd_create_transient_destination();
assert!(!dest_a.is_null(), "failed to create destination A");
let dest_b = i2pd_create_transient_destination();
assert!(!dest_b.is_null(), "failed to create destination B");
let (tx, rx) = mpsc::channel::<SendableStream>();
i2pd_accept_stream(dest_b, Some(on_accept), (&tx as *const mpsc::Sender<_>) as *mut c_void);
let mut b_hash = [0u8; 32];
assert_eq!(i2pd_destination_ident_hash(dest_b, b_hash.as_mut_ptr()), 1);
eprintln!("waiting for destination A to build a stream to destination B (up to 180s)...");
let stream_a = i2pd_create_stream(dest_a, b_hash.as_ptr(), 180);
assert!(!stream_a.is_null(), "destination A failed to connect to destination B within 180s");
eprintln!("stream connected from A's side, waiting for B's acceptor to fire...");
let SendableStream(stream_b) = rx
.recv_timeout(Duration::from_secs(30))
.expect("destination B's acceptor did not fire within 30s of A connecting");
assert!(!stream_b.is_null());
let payload = b"hello over a real i2p stream";
let sent = i2pd_stream_send(stream_a, payload.as_ptr(), payload.len());
assert_eq!(sent, payload.len() as std::os::raw::c_long, "short/failed send");
let mut buf = [0u8; 64];
let received = i2pd_stream_receive(stream_b, buf.as_mut_ptr(), buf.len(), 30);
assert_eq!(received, payload.len() as std::os::raw::c_long, "short/failed receive");
assert_eq!(&buf[..received as usize], payload, "round-tripped bytes did not match");
i2pd_stream_close(stream_a);
i2pd_stream_close(stream_b);
i2pd_destroy_stream(stream_a);
i2pd_destroy_stream(stream_b);
i2pd_destroy_destination(dest_a);
i2pd_destroy_destination(dest_b);
i2pd_stop();
i2pd_terminate();
}
}