#![cfg(test)]
use super::{
Config, ConnectionId, Event, InboundTy, MultiStreamConnectionTask, MultiStreamHandshakeKind,
Network, SingleStreamConnectionTask, SingleStreamHandshakeKind, SubstreamFate, SubstreamId,
};
use crate::libp2p::{connection::noise::NoiseKey, read_write::ReadWrite};
use alloc::{collections::BTreeMap, vec::Vec};
use core::{cmp, mem, time::Duration};
const BUF: usize = 65536;
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
enum Side {
Alice,
Bob,
}
struct Node {
network: Network<(), Duration>,
task: Option<SingleStreamConnectionTask<Duration>>,
conn_id: ConnectionId,
}
struct Harness {
alice: Node,
bob: Node,
a2b: Vec<u8>,
b2a: Vec<u8>,
now: Duration,
wake_a: Option<Duration>,
wake_b: Option<Duration>,
}
fn make_node(is_initiator: bool, noise_key: &NoiseKey, seed: [u8; 32]) -> Node {
let mut network = Network::<(), Duration>::new(Config {
randomness_seed: seed,
capacity: 1,
max_inbound_substreams: 64,
max_protocol_name_len: 256,
handshake_timeout: Duration::from_secs(10),
ping_protocol: "/ping/1.0.0".to_string(),
});
let (conn_id, task) = network.insert_single_stream(
Duration::ZERO,
SingleStreamHandshakeKind::MultistreamSelectNoiseYamux {
is_initiator,
noise_key,
},
16,
(),
);
Node {
network,
task: Some(task),
conn_id,
}
}
impl Harness {
fn new() -> Self {
let alice_key = NoiseKey::new(&[1; 32], &[2; 32]);
let bob_key = NoiseKey::new(&[3; 32], &[4; 32]);
Harness {
alice: make_node(true, &alice_key, [7; 32]),
bob: make_node(false, &bob_key, [9; 32]),
a2b: Vec::new(),
b2a: Vec::new(),
now: Duration::ZERO,
wake_a: None,
wake_b: None,
}
}
fn pass_time(&mut self, amount: Duration) {
self.now += amount;
}
fn deliver_coord_to_conn(&mut self, side: Side) -> bool {
let mut progress = false;
loop {
let node = match side {
Side::Alice => &mut self.alice,
Side::Bob => &mut self.bob,
};
let Some((_cid, msg)) = node.network.pull_message_to_connection() else {
break;
};
if let Some(task) = node.task.as_mut() {
task.inject_coordinator_message(&self.now, msg);
}
progress = true;
}
progress
}
fn drain_conn_to_coord(&mut self, side: Side) -> bool {
let mut progress = false;
loop {
let node = match side {
Side::Alice => &mut self.alice,
Side::Bob => &mut self.bob,
};
let Some(task) = node.task.take() else {
break;
};
let (task_back, msg) = task.pull_message_to_coordinator();
node.task = task_back;
match msg {
Some(m) => {
node.network.inject_connection_message(node.conn_id, m);
progress = true;
}
None => break,
}
if node.task.is_none() {
break;
}
}
progress
}
fn read_write_alice(&mut self) -> bool {
let Some(task) = self.alice.task.as_mut() else {
return false;
};
let out_len_before = self.a2b.len();
let mut rw = ReadWrite {
now: self.now,
incoming_buffer: mem::take(&mut self.b2a),
expected_incoming_bytes: Some(0),
read_bytes: 0,
write_bytes_queued: self.a2b.len(),
write_bytes_queueable: Some(BUF - self.a2b.len()),
write_buffers: vec![mem::take(&mut self.a2b)],
wake_up_after: self.wake_a,
};
task.read_write(&mut rw);
let read = rw.read_bytes;
self.wake_a = rw.wake_up_after;
self.b2a = rw.incoming_buffer;
self.a2b = rw.write_buffers.drain(..).flatten().collect();
read != 0 || self.a2b.len() != out_len_before
}
fn read_write_bob(&mut self) -> bool {
let Some(task) = self.bob.task.as_mut() else {
return false;
};
let out_len_before = self.b2a.len();
let mut rw = ReadWrite {
now: self.now,
incoming_buffer: mem::take(&mut self.a2b),
expected_incoming_bytes: Some(0),
read_bytes: 0,
write_bytes_queued: self.b2a.len(),
write_bytes_queueable: Some(BUF - self.b2a.len()),
write_buffers: vec![mem::take(&mut self.b2a)],
wake_up_after: self.wake_b,
};
task.read_write(&mut rw);
let read = rw.read_bytes;
self.wake_b = rw.wake_up_after;
self.a2b = rw.incoming_buffer;
self.b2a = rw.write_buffers.drain(..).flatten().collect();
read != 0 || self.b2a.len() != out_len_before
}
fn pump(&mut self, deliver_alice: bool, deliver_bob: bool) -> Vec<(Side, Event<()>)> {
let mut events = Vec::new();
let ceiling = self.now + Duration::from_millis(100);
let mut idle_advances = 0u32;
loop {
let mut progress = false;
if deliver_alice {
progress |= self.deliver_coord_to_conn(Side::Alice);
}
if deliver_bob {
progress |= self.deliver_coord_to_conn(Side::Bob);
}
progress |= self.read_write_alice();
progress |= self.read_write_bob();
progress |= self.drain_conn_to_coord(Side::Alice);
progress |= self.drain_conn_to_coord(Side::Bob);
while let Some(e) = self.alice.network.next_event() {
events.push((Side::Alice, e));
progress = true;
}
while let Some(e) = self.bob.network.next_event() {
events.push((Side::Bob, e));
progress = true;
}
if progress {
idle_advances = 0;
continue;
}
let next_wake = [self.wake_a, self.wake_b].into_iter().flatten().min();
match next_wake {
Some(w)
if idle_advances < 100
&& cmp::max(self.now, w) + Duration::from_nanos(1) <= ceiling =>
{
self.now = cmp::max(self.now, w) + Duration::from_nanos(1);
idle_advances += 1;
}
_ => break,
}
}
events
}
}
#[test]
fn reject_in_notifications_double_send_after_remote_reset() {
let mut harness = Harness::new();
let events = harness.pump(true, true);
assert!(
events
.iter()
.any(|(s, e)| *s == Side::Alice && matches!(e, Event::HandshakeFinished { .. }))
);
assert!(
events
.iter()
.any(|(s, e)| *s == Side::Bob && matches!(e, Event::HandshakeFinished { .. }))
);
let bob_sub = harness.bob.network.open_out_notifications(
harness.bob.conn_id,
"/test-notif/1.0.0".to_string(),
Duration::from_secs(1),
b"hello".to_vec(),
1024,
);
let events = harness.pump(true, true);
let alice_inbound = events
.iter()
.find_map(|(side, ev)| match ev {
Event::InboundNegotiated {
substream_id,
protocol_name,
..
} if *side == Side::Alice => {
assert_eq!(protocol_name, "/test-notif/1.0.0");
Some(*substream_id)
}
_ => None,
})
.expect("Alice should report InboundNegotiated");
harness.alice.network.accept_inbound(
alice_inbound,
InboundTy::Notifications {
max_handshake_size: 1024,
},
);
let events = harness.pump(true, true);
let s: SubstreamId = events
.iter()
.find_map(|(side, ev)| match ev {
Event::NotificationsInOpen { substream_id, .. } if *side == Side::Alice => {
Some(*substream_id)
}
_ => None,
})
.expect("Alice should report NotificationsInOpen");
harness.alice.network.reject_in_notifications(s);
harness.pass_time(Duration::from_millis(1500));
let events_reset = harness.pump(false, true);
assert!(events_reset.iter().any(|(side, ev)| *side == Side::Bob
&& matches!(
ev,
Event::NotificationsOutResult { substream_id, result: Err(_) }
if *substream_id == bob_sub
)));
let events_final = harness.pump(true, true);
assert!(
!events_reset
.iter()
.chain(&events_final)
.any(|(_, ev)| { matches!(ev, Event::StartShutdown { .. } | Event::Shutdown { .. }) })
);
}
#[test]
fn close_in_notifications_crossing_remote_reset() {
let mut harness = Harness::new();
let events = harness.pump(true, true);
assert!(
events
.iter()
.any(|(s, e)| *s == Side::Alice && matches!(e, Event::HandshakeFinished { .. }))
);
assert!(
events
.iter()
.any(|(s, e)| *s == Side::Bob && matches!(e, Event::HandshakeFinished { .. }))
);
let bob_sub = harness.bob.network.open_out_notifications(
harness.bob.conn_id,
"/test-notif/1.0.0".to_string(),
Duration::from_secs(1),
b"hello".to_vec(),
1024,
);
let events = harness.pump(true, true);
let alice_inbound = events
.iter()
.find_map(|(side, ev)| match ev {
Event::InboundNegotiated {
substream_id,
protocol_name,
..
} if *side == Side::Alice => {
assert_eq!(protocol_name, "/test-notif/1.0.0");
Some(*substream_id)
}
_ => None,
})
.expect("Alice should report InboundNegotiated");
harness.alice.network.accept_inbound(
alice_inbound,
InboundTy::Notifications {
max_handshake_size: 1024,
},
);
let events = harness.pump(true, true);
let s: SubstreamId = events
.iter()
.find_map(|(side, ev)| match ev {
Event::NotificationsInOpen { substream_id, .. } if *side == Side::Alice => {
Some(*substream_id)
}
_ => None,
})
.expect("Alice should report NotificationsInOpen");
harness
.alice
.network
.accept_in_notifications(s, b"hi".to_vec(), 1024);
harness
.alice
.network
.start_close_in_notifications(s, Duration::from_secs(5));
harness.pass_time(Duration::from_millis(1500));
let events_reset = harness.pump(false, true);
assert!(events_reset.iter().any(|(side, ev)| *side == Side::Bob
&& matches!(
ev,
Event::NotificationsOutResult { substream_id, result: Err(_) }
if *substream_id == bob_sub
)));
assert!(events_reset.iter().any(|(side, ev)| *side == Side::Alice
&& matches!(
ev,
Event::NotificationsInClose { substream_id, .. } if *substream_id == s
)));
let events_final = harness.pump(true, true);
assert!(
!events_reset
.iter()
.chain(&events_final)
.any(|(_, ev)| { matches!(ev, Event::StartShutdown { .. } | Event::Shutdown { .. }) })
);
}
struct MultiNode {
network: Network<(), Duration>,
task: Option<MultiStreamConnectionTask<Duration, u32>>,
conn_id: ConnectionId,
}
struct MultiChannel {
a2b: Vec<u8>,
b2a: Vec<u8>,
alice_open: bool,
bob_open: bool,
}
struct MultiHarness {
alice: MultiNode,
bob: MultiNode,
channels: BTreeMap<u32, MultiChannel>,
next_channel_id: u32,
now: Duration,
wake_a: Option<Duration>,
wake_b: Option<Duration>,
}
fn make_multi_node(
is_initiator: bool,
noise_key: &NoiseKey,
seed: [u8; 32],
local_tls_certificate_multihash: Vec<u8>,
remote_tls_certificate_multihash: Vec<u8>,
) -> MultiNode {
let mut network = Network::<(), Duration>::new(Config {
randomness_seed: seed,
capacity: 1,
max_inbound_substreams: 64,
max_protocol_name_len: 256,
handshake_timeout: Duration::from_secs(10),
ping_protocol: "/ping/1.0.0".to_string(),
});
let (conn_id, task) = network.insert_multi_stream(
Duration::ZERO,
MultiStreamHandshakeKind::WebRtc {
is_initiator,
noise_key,
local_tls_certificate_multihash,
remote_tls_certificate_multihash,
},
16,
(),
);
MultiNode {
network,
task: Some(task),
conn_id,
}
}
impl MultiHarness {
fn new() -> Self {
let alice_key = NoiseKey::new(&[1; 32], &[2; 32]);
let bob_key = NoiseKey::new(&[3; 32], &[4; 32]);
let fp_alice = [&[0x12u8, 0x20][..], &[0xaa; 32][..]].concat();
let fp_bob = [&[0x12u8, 0x20][..], &[0xbb; 32][..]].concat();
let mut harness = MultiHarness {
alice: make_multi_node(true, &alice_key, [7; 32], fp_alice.clone(), fp_bob.clone()),
bob: make_multi_node(false, &bob_key, [9; 32], fp_bob, fp_alice),
channels: BTreeMap::new(),
next_channel_id: 0,
now: Duration::ZERO,
wake_a: None,
wake_b: None,
};
let id = harness.alloc_channel();
harness.alice.task.as_mut().unwrap().add_substream(id, true);
harness.bob.task.as_mut().unwrap().add_substream(id, true);
harness
}
fn alloc_channel(&mut self) -> u32 {
let id = self.next_channel_id;
self.next_channel_id += 1;
self.channels.insert(
id,
MultiChannel {
a2b: Vec::new(),
b2a: Vec::new(),
alice_open: true,
bob_open: true,
},
);
id
}
fn kill_channel(&mut self, chan_id: u32) {
assert!(self.channels.remove(&chan_id).is_some());
if let Some(task) = self.alice.task.as_mut() {
task.reset_substream(&chan_id);
}
if let Some(task) = self.bob.task.as_mut() {
task.reset_substream(&chan_id);
}
}
fn deliver_coord_to_conn(&mut self, side: Side) -> bool {
let mut progress = false;
loop {
let node = match side {
Side::Alice => &mut self.alice,
Side::Bob => &mut self.bob,
};
let Some((_cid, msg)) = node.network.pull_message_to_connection() else {
break;
};
if let Some(task) = node.task.as_mut() {
task.inject_coordinator_message(&self.now, msg);
}
progress = true;
}
progress
}
fn drain_conn_to_coord(&mut self, side: Side) -> bool {
let mut progress = false;
loop {
let node = match side {
Side::Alice => &mut self.alice,
Side::Bob => &mut self.bob,
};
let Some(task) = node.task.take() else {
break;
};
let (task_back, msg) = task.pull_message_to_coordinator();
node.task = task_back;
match msg {
Some(m) => {
node.network.inject_connection_message(node.conn_id, m);
progress = true;
}
None => break,
}
if node.task.is_none() {
break;
}
}
progress
}
fn open_desired_substreams(&mut self) -> bool {
let mut progress = false;
for side in [Side::Alice, Side::Bob] {
loop {
let (node, peer) = match side {
Side::Alice => (&mut self.alice, &mut self.bob),
Side::Bob => (&mut self.bob, &mut self.alice),
};
let (Some(task), Some(peer_task)) = (node.task.as_mut(), peer.task.as_mut()) else {
break;
};
if task.desired_outbound_substreams() == 0 {
break;
}
let id = self.next_channel_id;
self.next_channel_id += 1;
task.add_substream(id, true);
peer_task.add_substream(id, false);
self.channels.insert(
id,
MultiChannel {
a2b: Vec::new(),
b2a: Vec::new(),
alice_open: true,
bob_open: true,
},
);
progress = true;
}
}
progress
}
fn channel_read_write(&mut self, side: Side, chan_id: u32) -> bool {
let (node, wake) = match side {
Side::Alice => (&mut self.alice, &mut self.wake_a),
Side::Bob => (&mut self.bob, &mut self.wake_b),
};
let Some(task) = node.task.as_mut() else {
return false;
};
let Some(chan) = self.channels.get_mut(&chan_id) else {
return false;
};
let (open, incoming_buf, outgoing_buf) = match side {
Side::Alice => (&mut chan.alice_open, &mut chan.b2a, &mut chan.a2b),
Side::Bob => (&mut chan.bob_open, &mut chan.a2b, &mut chan.b2a),
};
if !*open {
return false;
}
let out_len_before = outgoing_buf.len();
let mut rw = ReadWrite {
now: self.now,
incoming_buffer: mem::take(incoming_buf),
expected_incoming_bytes: Some(0),
read_bytes: 0,
write_bytes_queued: outgoing_buf.len(),
write_bytes_queueable: Some(BUF - outgoing_buf.len()),
write_buffers: vec![mem::take(outgoing_buf)],
wake_up_after: *wake,
};
let fate = task.substream_read_write(&chan_id, &mut rw);
let read = rw.read_bytes;
*wake = rw.wake_up_after;
*incoming_buf = rw.incoming_buffer;
*outgoing_buf = rw.write_buffers.drain(..).flatten().collect();
let mut progress = read != 0 || outgoing_buf.len() != out_len_before;
if matches!(fate, SubstreamFate::Reset) {
*open = false;
progress = true;
}
progress
}
fn propagate_resets(&mut self) -> bool {
let mut progress = false;
let mut to_remove = Vec::new();
for (&id, chan) in self.channels.iter_mut() {
match (chan.alice_open, chan.bob_open) {
(false, false) => to_remove.push(id),
(false, true) if chan.a2b.is_empty() => {
if let Some(task) = self.bob.task.as_mut() {
task.reset_substream(&id);
}
chan.bob_open = false;
to_remove.push(id);
progress = true;
}
(true, false) if chan.b2a.is_empty() => {
if let Some(task) = self.alice.task.as_mut() {
task.reset_substream(&id);
}
chan.alice_open = false;
to_remove.push(id);
progress = true;
}
_ => {}
}
}
for id in to_remove {
self.channels.remove(&id);
}
progress
}
fn pump(&mut self, deliver_alice: bool, deliver_bob: bool) -> Vec<(Side, Event<()>)> {
let mut events = Vec::new();
let ceiling = self.now + Duration::from_millis(100);
let mut idle_advances = 0u32;
loop {
let mut progress = false;
if deliver_alice {
progress |= self.deliver_coord_to_conn(Side::Alice);
}
if deliver_bob {
progress |= self.deliver_coord_to_conn(Side::Bob);
}
progress |= self.open_desired_substreams();
let chan_ids: Vec<u32> = self.channels.keys().copied().collect();
for id in chan_ids {
progress |= self.channel_read_write(Side::Alice, id);
progress |= self.channel_read_write(Side::Bob, id);
}
progress |= self.propagate_resets();
progress |= self.drain_conn_to_coord(Side::Alice);
progress |= self.drain_conn_to_coord(Side::Bob);
while let Some(e) = self.alice.network.next_event() {
events.push((Side::Alice, e));
progress = true;
}
while let Some(e) = self.bob.network.next_event() {
events.push((Side::Bob, e));
progress = true;
}
if progress {
idle_advances = 0;
continue;
}
let next_wake = [self.wake_a, self.wake_b].into_iter().flatten().min();
match next_wake {
Some(w)
if idle_advances < 100
&& cmp::max(self.now, w) + Duration::from_nanos(1) <= ceiling =>
{
self.now = cmp::max(self.now, w) + Duration::from_nanos(1);
idle_advances += 1;
}
_ => break,
}
}
events
}
}
fn accept_ping_inbounds(harness: &mut MultiHarness, events: &[(Side, Event<()>)]) {
for (side, ev) in events {
if let Event::InboundNegotiated {
substream_id,
protocol_name,
..
} = ev
{
if protocol_name == "/ping/1.0.0" {
let node = match side {
Side::Alice => &mut harness.alice,
Side::Bob => &mut harness.bob,
};
node.network.accept_inbound(*substream_id, InboundTy::Ping);
}
}
}
}
#[test]
fn multi_stream_reject_in_notifications_double_send_after_remote_reset() {
let mut harness = MultiHarness::new();
let events = harness.pump(true, true);
assert!(
events
.iter()
.any(|(s, e)| *s == Side::Alice && matches!(e, Event::HandshakeFinished { .. }))
);
assert!(
events
.iter()
.any(|(s, e)| *s == Side::Bob && matches!(e, Event::HandshakeFinished { .. }))
);
accept_ping_inbounds(&mut harness, &events);
let notif_channel = harness.next_channel_id;
let bob_sub = harness.bob.network.open_out_notifications(
harness.bob.conn_id,
"/test-notif/1.0.0".to_string(),
Duration::from_secs(10),
b"hello".to_vec(),
1024,
);
let events = harness.pump(true, true);
assert_eq!(harness.next_channel_id, notif_channel + 1);
accept_ping_inbounds(&mut harness, &events);
let alice_inbound = events
.iter()
.find_map(|(side, ev)| match ev {
Event::InboundNegotiated {
substream_id,
protocol_name,
..
} if *side == Side::Alice && protocol_name == "/test-notif/1.0.0" => {
Some(*substream_id)
}
_ => None,
})
.expect("Alice should report InboundNegotiated");
harness.alice.network.accept_inbound(
alice_inbound,
InboundTy::Notifications {
max_handshake_size: 1024,
},
);
let events = harness.pump(true, true);
accept_ping_inbounds(&mut harness, &events);
let s: SubstreamId = events
.iter()
.find_map(|(side, ev)| match ev {
Event::NotificationsInOpen { substream_id, .. } if *side == Side::Alice => {
Some(*substream_id)
}
_ => None,
})
.expect("Alice should report NotificationsInOpen");
harness.alice.network.reject_in_notifications(s);
harness.kill_channel(notif_channel);
let events_reset = harness.pump(false, true);
assert!(events_reset.iter().any(|(side, ev)| *side == Side::Bob
&& matches!(
ev,
Event::NotificationsOutResult { substream_id, result: Err(_) }
if *substream_id == bob_sub
)));
let events_final = harness.pump(true, true);
assert!(
!events_reset
.iter()
.chain(&events_final)
.any(|(_, ev)| { matches!(ev, Event::StartShutdown { .. } | Event::Shutdown { .. }) })
);
}
#[test]
fn multi_stream_close_in_notifications_crossing_remote_reset() {
let mut harness = MultiHarness::new();
let events = harness.pump(true, true);
assert!(
events
.iter()
.any(|(s, e)| *s == Side::Alice && matches!(e, Event::HandshakeFinished { .. }))
);
assert!(
events
.iter()
.any(|(s, e)| *s == Side::Bob && matches!(e, Event::HandshakeFinished { .. }))
);
accept_ping_inbounds(&mut harness, &events);
let notif_channel = harness.next_channel_id;
let bob_sub = harness.bob.network.open_out_notifications(
harness.bob.conn_id,
"/test-notif/1.0.0".to_string(),
Duration::from_secs(10),
b"hello".to_vec(),
1024,
);
let events = harness.pump(true, true);
assert_eq!(harness.next_channel_id, notif_channel + 1);
accept_ping_inbounds(&mut harness, &events);
let alice_inbound = events
.iter()
.find_map(|(side, ev)| match ev {
Event::InboundNegotiated {
substream_id,
protocol_name,
..
} if *side == Side::Alice && protocol_name == "/test-notif/1.0.0" => {
Some(*substream_id)
}
_ => None,
})
.expect("Alice should report InboundNegotiated");
harness.alice.network.accept_inbound(
alice_inbound,
InboundTy::Notifications {
max_handshake_size: 1024,
},
);
let events = harness.pump(true, true);
accept_ping_inbounds(&mut harness, &events);
let s: SubstreamId = events
.iter()
.find_map(|(side, ev)| match ev {
Event::NotificationsInOpen { substream_id, .. } if *side == Side::Alice => {
Some(*substream_id)
}
_ => None,
})
.expect("Alice should report NotificationsInOpen");
harness
.alice
.network
.accept_in_notifications(s, b"hi".to_vec(), 1024);
harness
.alice
.network
.start_close_in_notifications(s, Duration::from_secs(5));
harness.kill_channel(notif_channel);
let events_reset = harness.pump(false, true);
assert!(events_reset.iter().any(|(side, ev)| *side == Side::Bob
&& matches!(
ev,
Event::NotificationsOutResult { substream_id, result: Err(_) }
if *substream_id == bob_sub
)));
assert!(events_reset.iter().any(|(side, ev)| *side == Side::Alice
&& matches!(
ev,
Event::NotificationsInClose { substream_id, .. } if *substream_id == s
)));
let events_final = harness.pump(true, true);
assert!(
!events_reset
.iter()
.chain(&events_final)
.any(|(_, ev)| { matches!(ev, Event::StartShutdown { .. } | Event::Shutdown { .. }) })
);
}
#[test]
fn multi_stream_close_out_notifications_crossing_remote_reset() {
let mut harness = MultiHarness::new();
let events = harness.pump(true, true);
assert!(
events
.iter()
.any(|(s, e)| *s == Side::Alice && matches!(e, Event::HandshakeFinished { .. }))
);
assert!(
events
.iter()
.any(|(s, e)| *s == Side::Bob && matches!(e, Event::HandshakeFinished { .. }))
);
accept_ping_inbounds(&mut harness, &events);
let notif_channel = harness.next_channel_id;
let bob_sub = harness.bob.network.open_out_notifications(
harness.bob.conn_id,
"/test-notif/1.0.0".to_string(),
Duration::from_secs(10),
b"hello".to_vec(),
1024,
);
let events = harness.pump(true, true);
assert_eq!(harness.next_channel_id, notif_channel + 1);
accept_ping_inbounds(&mut harness, &events);
let alice_inbound = events
.iter()
.find_map(|(side, ev)| match ev {
Event::InboundNegotiated {
substream_id,
protocol_name,
..
} if *side == Side::Alice && protocol_name == "/test-notif/1.0.0" => {
Some(*substream_id)
}
_ => None,
})
.expect("Alice should report InboundNegotiated");
harness.alice.network.accept_inbound(
alice_inbound,
InboundTy::Notifications {
max_handshake_size: 1024,
},
);
let events = harness.pump(true, true);
accept_ping_inbounds(&mut harness, &events);
let alice_sub: SubstreamId = events
.iter()
.find_map(|(side, ev)| match ev {
Event::NotificationsInOpen { substream_id, .. } if *side == Side::Alice => {
Some(*substream_id)
}
_ => None,
})
.expect("Alice should report NotificationsInOpen");
harness
.alice
.network
.accept_in_notifications(alice_sub, b"hi".to_vec(), 1024);
let events = harness.pump(true, true);
accept_ping_inbounds(&mut harness, &events);
assert!(events.iter().any(|(side, ev)| *side == Side::Bob
&& matches!(
ev,
Event::NotificationsOutResult { substream_id, result: Ok(_) }
if *substream_id == bob_sub
)));
harness.bob.network.close_out_notifications(bob_sub);
harness.kill_channel(notif_channel);
let events_final = harness.pump(true, true);
assert!(
!events_final
.iter()
.any(|(_, ev)| { matches!(ev, Event::StartShutdown { .. } | Event::Shutdown { .. }) })
);
assert!(!events_final.iter().any(|(side, ev)| *side == Side::Bob
&& matches!(
ev,
Event::NotificationsOutReset { substream_id } if *substream_id == bob_sub
)));
}
#[test]
fn multi_stream_queue_notification_crossing_remote_reset() {
let mut harness = MultiHarness::new();
let events = harness.pump(true, true);
assert!(
events
.iter()
.any(|(s, e)| *s == Side::Alice && matches!(e, Event::HandshakeFinished { .. }))
);
assert!(
events
.iter()
.any(|(s, e)| *s == Side::Bob && matches!(e, Event::HandshakeFinished { .. }))
);
accept_ping_inbounds(&mut harness, &events);
let notif_channel = harness.next_channel_id;
let bob_sub = harness.bob.network.open_out_notifications(
harness.bob.conn_id,
"/test-notif/1.0.0".to_string(),
Duration::from_secs(10),
b"hello".to_vec(),
1024,
);
let events = harness.pump(true, true);
assert_eq!(harness.next_channel_id, notif_channel + 1);
accept_ping_inbounds(&mut harness, &events);
let alice_inbound = events
.iter()
.find_map(|(side, ev)| match ev {
Event::InboundNegotiated {
substream_id,
protocol_name,
..
} if *side == Side::Alice && protocol_name == "/test-notif/1.0.0" => {
Some(*substream_id)
}
_ => None,
})
.expect("Alice should report InboundNegotiated");
harness.alice.network.accept_inbound(
alice_inbound,
InboundTy::Notifications {
max_handshake_size: 1024,
},
);
let events = harness.pump(true, true);
accept_ping_inbounds(&mut harness, &events);
let alice_sub: SubstreamId = events
.iter()
.find_map(|(side, ev)| match ev {
Event::NotificationsInOpen { substream_id, .. } if *side == Side::Alice => {
Some(*substream_id)
}
_ => None,
})
.expect("Alice should report NotificationsInOpen");
harness
.alice
.network
.accept_in_notifications(alice_sub, b"hi".to_vec(), 1024);
let events = harness.pump(true, true);
accept_ping_inbounds(&mut harness, &events);
assert!(events.iter().any(|(side, ev)| *side == Side::Bob
&& matches!(
ev,
Event::NotificationsOutResult { substream_id, result: Ok(_) }
if *substream_id == bob_sub
)));
harness
.bob
.network
.queue_notification(bob_sub, b"notif".to_vec())
.unwrap();
harness.kill_channel(notif_channel);
let events_final = harness.pump(true, true);
assert!(
!events_final
.iter()
.any(|(_, ev)| { matches!(ev, Event::StartShutdown { .. } | Event::Shutdown { .. }) })
);
assert!(events_final.iter().any(|(side, ev)| *side == Side::Bob
&& matches!(
ev,
Event::NotificationsOutReset { substream_id } if *substream_id == bob_sub
)));
}