@@ -38,17 +38,17 @@ use crate::{
3838 PeerId ,
3939} ;
4040
41- use futures:: { Stream , StreamExt } ;
41+ use futures:: { task :: AtomicWaker , Stream , StreamExt } ;
4242use indexmap:: IndexMap ;
4343use str0m:: {
44- channel:: { ChannelConfig , ChannelId } ,
44+ channel:: { Channel , ChannelConfig , ChannelId } ,
4545 net:: { Protocol as Str0mProtocol , Receive } ,
4646 Event , IceConnectionState , Input , Output , Rtc ,
4747} ;
4848use tokio:: { net:: UdpSocket , sync:: mpsc:: Receiver } ;
4949
5050use std:: {
51- collections:: HashMap ,
51+ collections:: { HashMap , HashSet , VecDeque } ,
5252 net:: SocketAddr ,
5353 pin:: Pin ,
5454 sync:: Arc ,
@@ -59,6 +59,12 @@ use std::{
5959/// Logging target for the file.
6060const LOG_TARGET : & str = "litep2p::webrtc::connection" ;
6161
62+ /// Threshold under which str0m emits Event::ChannelBufferedAmountLow.
63+ const BACKPRESSURE_THRESHOLD : usize = 16 * ( 1 << 10 ) ; // 16 KB
64+
65+ /// Maximum number of pending messages supported per channel.
66+ const MAX_PENDING_PER_CHANNEL : usize = 16 ;
67+
6268/// Opening channel context.
6369#[ derive( Debug ) ]
6470struct ChannelContext {
@@ -88,6 +94,12 @@ struct SubstreamHandleSet {
8894
8995 /// Substream handles.
9096 handles : IndexMap < ChannelId , SubstreamHandle > ,
97+
98+ /// Substreams that have pending messages.
99+ pending : HashSet < ChannelId > ,
100+
101+ /// Waker used to drive the stream when no handle can make progress.
102+ waker : AtomicWaker ,
91103}
92104
93105impl SubstreamHandleSet {
@@ -96,6 +108,8 @@ impl SubstreamHandleSet {
96108 Self {
97109 index : 0usize ,
98110 handles : IndexMap :: new ( ) ,
111+ pending : HashSet :: new ( ) ,
112+ waker : AtomicWaker :: new ( ) ,
99113 }
100114 }
101115
@@ -107,11 +121,25 @@ impl SubstreamHandleSet {
107121 /// Insert new handle to [`SubstreamHandleSet`].
108122 pub fn insert ( & mut self , key : ChannelId , handle : SubstreamHandle ) {
109123 assert ! ( self . handles. insert( key, handle) . is_none( ) ) ;
124+ self . waker . wake ( ) ;
110125 }
111126
112127 /// Remove handle from [`SubstreamHandleSet`].
113128 pub fn remove ( & mut self , key : & ChannelId ) -> Option < SubstreamHandle > {
114- self . handles . shift_remove ( key)
129+ self . pending . remove ( key) ;
130+ self . handles . swap_remove ( key)
131+ }
132+
133+ /// Mark channel as having pending messages.
134+ pub fn add_pending ( & mut self , key : ChannelId ) {
135+ self . pending . insert ( key) ;
136+ }
137+
138+ /// Unmark channel as having pending messages.
139+ pub fn clear_pending ( & mut self , key : & ChannelId ) {
140+ if self . pending . remove ( key) {
141+ self . waker . wake ( ) ;
142+ }
115143 }
116144}
117145
@@ -120,7 +148,10 @@ impl Stream for SubstreamHandleSet {
120148
121149 fn poll_next ( mut self : Pin < & mut Self > , cx : & mut Context < ' _ > ) -> Poll < Option < Self :: Item > > {
122150 let len = match self . handles . len ( ) {
123- 0 => return Poll :: Pending ,
151+ 0 => {
152+ self . waker . register ( cx. waker ( ) ) ;
153+ return Poll :: Pending ;
154+ }
124155 len => len,
125156 } ;
126157 let start_index = self . index ;
@@ -129,13 +160,19 @@ impl Stream for SubstreamHandleSet {
129160 let index = self . index % len;
130161 self . index += 1 ;
131162
132- let ( key, stream) = self . handles . get_index_mut ( index) . expect ( "handle to exist" ) ;
133- match stream. poll_next_unpin ( cx) {
134- Poll :: Pending => { }
135- Poll :: Ready ( event) => return Poll :: Ready ( Some ( ( * key, event) ) ) ,
163+ let key =
164+ self . handles . get_index ( index) . map ( |( k, _) | k) . cloned ( ) . expect ( "handle to exist" ) ;
165+
166+ if !self . pending . contains ( & key) {
167+ let ( key, stream) = self . handles . get_index_mut ( index) . expect ( "handle to exist" ) ;
168+ match stream. poll_next_unpin ( cx) {
169+ Poll :: Pending => { }
170+ Poll :: Ready ( event) => return Poll :: Ready ( Some ( ( * key, event) ) ) ,
171+ }
136172 }
137173
138174 if self . index == start_index + len {
175+ self . waker . register ( cx. waker ( ) ) ;
139176 break Poll :: Pending ;
140177 }
141178 }
@@ -205,6 +242,10 @@ pub struct WebRtcConnection {
205242 /// Pending outbound channels.
206243 pending_outbound : HashMap < ChannelId , ChannelContext > ,
207244
245+ /// Pending outbound messages,
246+ /// at most [`MAX_PENDING_PER_CHANNEL`] per channel.
247+ pending_messages : HashMap < ChannelId , VecDeque < Vec < u8 > > > ,
248+
208249 /// Open channels.
209250 channels : HashMap < ChannelId , ChannelState > ,
210251
@@ -234,6 +275,7 @@ impl WebRtcConnection {
234275 endpoint,
235276 dgram_rx,
236277 pending_outbound : HashMap :: new ( ) ,
278+ pending_messages : HashMap :: new ( ) ,
237279 channels : HashMap :: new ( ) ,
238280 handles : SubstreamHandleSet :: new ( ) ,
239281 }
@@ -259,6 +301,10 @@ impl WebRtcConnection {
259301 "channel opened" ,
260302 ) ;
261303
304+ if let Some ( mut channel) = self . rtc . channel ( channel_id) {
305+ channel. set_buffered_amount_low_threshold ( BACKPRESSURE_THRESHOLD ) ;
306+ }
307+
262308 let Some ( mut context) = self . pending_outbound . remove ( & channel_id) else {
263309 tracing:: trace!(
264310 target: LOG_TARGET ,
@@ -281,11 +327,7 @@ impl WebRtcConnection {
281327 WebRtcDialerState :: propose ( context. protocol . clone ( ) , fallback_names) ?;
282328 let message = WebRtcMessage :: encode ( message, None ) ;
283329
284- self . rtc
285- . channel ( channel_id)
286- . ok_or ( Error :: ChannelDoesntExist ) ?
287- . write ( true , message. as_ref ( ) )
288- . map_err ( Error :: WebRtc ) ?;
330+ self . write ( channel_id, message) ?;
289331
290332 self . channels . insert (
291333 channel_id,
@@ -298,6 +340,108 @@ impl WebRtcConnection {
298340 Ok ( ( ) )
299341 }
300342
343+ // Attempt to write a message over the specified channel,
344+ // save the message as pending if `WebRtcConnection` didn't have
345+ // enough space.
346+ fn write ( & mut self , channel_id : ChannelId , message : Vec < u8 > ) -> Result < ( ) , Error > {
347+ let Some ( mut channel) = self . rtc . channel ( channel_id) else {
348+ tracing:: trace!(
349+ target: LOG_TARGET ,
350+ peer = ?self . peer,
351+ ?channel_id,
352+ "protocol rejected received for non-existing channel" ,
353+ ) ;
354+ return Err ( Error :: ChannelDoesntExist ) ;
355+ } ;
356+
357+ match self . pending_messages . get_mut ( & channel_id) {
358+ Some ( messages) if !messages. is_empty ( ) => {
359+ if messages. len ( ) >= MAX_PENDING_PER_CHANNEL {
360+ return Err ( Error :: ChannelClogged ) ;
361+ }
362+
363+ messages. push_back ( message) ;
364+ return Ok ( ( ) ) ;
365+ }
366+ _ => ( ) ,
367+ }
368+
369+ let succeeded = Self :: channel_write ( & mut channel, channel_id, & message, self . peer ) ?;
370+
371+ if !succeeded {
372+ let pending_messages = self . pending_messages . entry ( channel_id) . or_default ( ) ;
373+ if pending_messages. len ( ) >= MAX_PENDING_PER_CHANNEL {
374+ return Err ( Error :: ChannelClogged ) ;
375+ }
376+
377+ pending_messages. push_back ( message) ;
378+ self . handles . add_pending ( channel_id) ;
379+ return Ok ( ( ) ) ;
380+ }
381+
382+ Ok ( ( ) )
383+ }
384+
385+ fn channel_write (
386+ channel : & mut Channel < ' _ > ,
387+ channel_id : ChannelId ,
388+ message : & [ u8 ] ,
389+ peer : PeerId ,
390+ ) -> Result < bool , Error > {
391+ match channel. write ( true , message) {
392+ Ok ( succeeded) => Ok ( succeeded) ,
393+ Err ( e) => {
394+ tracing:: trace!(
395+ target: LOG_TARGET ,
396+ peer = ?peer,
397+ ?channel_id,
398+ ?e,
399+ "failed to write message to webrtc channel" ,
400+ ) ;
401+ Err ( Error :: WebRtc ( e) )
402+ }
403+ }
404+ }
405+
406+ // Attempt to write all pending messages of the specified ChannelId.
407+ // Returns whether all messages have been sent or not.
408+ fn write_pending ( & mut self , channel_id : ChannelId ) -> Result < bool , Error > {
409+ let Some ( mut channel) = self . rtc . channel ( channel_id) else {
410+ tracing:: trace!(
411+ target: LOG_TARGET ,
412+ peer = ?self . peer,
413+ ?channel_id,
414+ "protocol rejected received for non-existing channel" ,
415+ ) ;
416+ return Err ( Error :: ChannelDoesntExist ) ;
417+ } ;
418+
419+ loop {
420+ let Some ( pending_messages) = self . pending_messages . get_mut ( & channel_id) else {
421+ // This should never happen, `write_pending` should be called
422+ // for only the channel with pending messages. Treat as a no-op
423+ // instead of panicking to stay defensive.
424+ self . handles . clear_pending ( & channel_id) ;
425+ return Ok ( true ) ;
426+ } ;
427+
428+ let Some ( message) = pending_messages. front ( ) else {
429+ self . pending_messages . remove ( & channel_id) ;
430+ self . handles . clear_pending ( & channel_id) ;
431+ break Ok ( true ) ;
432+ } ;
433+
434+ let succeeded = Self :: channel_write ( & mut channel, channel_id, message, self . peer ) ?;
435+ if succeeded {
436+ self . pending_messages
437+ . get_mut ( & channel_id)
438+ . and_then ( |messages| messages. pop_front ( ) ) ;
439+ } else {
440+ break Ok ( false ) ;
441+ }
442+ }
443+ }
444+
301445 /// Handle closed channel.
302446 async fn on_channel_closed ( & mut self , channel_id : ChannelId ) -> crate :: Result < ( ) > {
303447 tracing:: trace!(
@@ -309,6 +453,7 @@ impl WebRtcConnection {
309453
310454 self . pending_outbound . remove ( & channel_id) ;
311455 self . channels . remove ( & channel_id) ;
456+ self . pending_messages . remove ( & channel_id) ;
312457 self . handles . remove ( & channel_id) ;
313458
314459 Ok ( ( ) )
@@ -351,14 +496,8 @@ impl WebRtcConnection {
351496 | ListenerSelectResult :: PendingProtocol { message } => ( message, None ) ,
352497 } ;
353498
354- self . rtc
355- . channel ( channel_id)
356- . ok_or ( Error :: ChannelDoesntExist ) ?
357- . write (
358- true ,
359- WebRtcMessage :: encode ( response. to_vec ( ) , None ) . as_ref ( ) ,
360- )
361- . map_err ( Error :: WebRtc ) ?;
499+ let message = WebRtcMessage :: encode ( response. to_vec ( ) , None ) ;
500+ self . write ( channel_id, message) ?;
362501
363502 let Some ( protocol) = negotiated else {
364503 tracing:: trace!(
@@ -468,30 +607,9 @@ impl WebRtcConnection {
468607
469608 let message = WebRtcMessage :: encode ( message, None ) ;
470609
471- let Some ( mut channel) = self . rtc . channel ( channel_id) else {
472- tracing:: trace!(
473- target: LOG_TARGET ,
474- peer = ?self . peer,
475- ?channel_id,
476- "protocol rejected received for non-existing channel" ,
477- ) ;
478- return Err ( SubstreamError :: NegotiationError (
479- NegotiationError :: Failed . into ( ) ,
480- ) ) ;
481- } ;
482-
483- if let Err ( err) = channel. write ( true , message. as_ref ( ) ) {
484- tracing:: trace!(
485- target: LOG_TARGET ,
486- peer = ?self . peer,
487- ?channel_id,
488- ?err,
489- "failed to write multistream-select fallback proposal" ,
490- ) ;
491- return Err ( SubstreamError :: NegotiationError (
492- NegotiationError :: Failed . into ( ) ,
493- ) ) ;
494- } ;
610+ self . write ( channel_id, message) . map_err ( |_| {
611+ SubstreamError :: NegotiationError ( NegotiationError :: Failed . into ( ) )
612+ } ) ?;
495613
496614 self . channels . insert (
497615 channel_id,
@@ -755,12 +873,8 @@ impl WebRtcConnection {
755873 "send data" ,
756874 ) ;
757875
758- self . rtc
759- . channel ( channel_id)
760- . ok_or ( Error :: ChannelDoesntExist ) ?
761- . write ( true , WebRtcMessage :: encode ( data, flag) . as_ref ( ) )
762- . map_err ( Error :: WebRtc )
763- . map ( |_| ( ) )
876+ let message = WebRtcMessage :: encode ( data, flag) ;
877+ self . write ( channel_id, message)
764878 }
765879
766880 /// Open outbound substream.
@@ -868,6 +982,8 @@ impl WebRtcConnection {
868982 continue ;
869983 }
870984 Event :: ChannelClose ( channel_id) => {
985+ // This event is emitted once the rtc instance
986+ // completes the call to `close_data_channel(channel_id)`.
871987 if let Err ( error) = self . on_channel_closed ( channel_id) . await {
872988 tracing:: debug!(
873989 target: LOG_TARGET ,
@@ -893,6 +1009,13 @@ impl WebRtcConnection {
8931009
8941010 continue ;
8951011 }
1012+ Event :: ChannelBufferedAmountLow ( _channel_id) => {
1013+ let channel_ids: Vec < _ > = self . pending_messages . keys ( ) . cloned ( ) . collect ( ) ;
1014+ for channel_id in channel_ids {
1015+ let _ = self . write_pending ( channel_id) ;
1016+ }
1017+ continue ;
1018+ }
8961019 event => {
8971020 tracing:: debug!(
8981021 target: LOG_TARGET ,
@@ -959,6 +1082,9 @@ impl WebRtcConnection {
9591082 ?error,
9601083 "failed to send data to remote peer" ,
9611084 ) ;
1085+
1086+ self . channels. insert( channel_id, ChannelState :: Closing ) ;
1087+ self . rtc. direct_api( ) . close_data_channel( channel_id) ;
9621088 }
9631089 }
9641090 Some ( ( _, Some ( SubstreamEvent :: RecvClosed ) ) ) => { }
0 commit comments