1+ use std:: collections:: VecDeque ;
2+
13use openmina_core:: { bug_condition, fuzz_maybe, fuzzed_maybe, Substate , SubstateAccess } ;
24
35use crate :: P2pLimits ;
@@ -155,14 +157,14 @@ impl P2pNetworkYamuxState {
155157 data,
156158 mut flags,
157159 } => {
158- let yamux_state = yamux_state
160+ let stream_state = yamux_state
159161 . streams
160162 . get ( & stream_id)
161163 . ok_or_else ( || format ! ( "Stream with id {stream_id} not found for `P2pNetworkYamuxAction::OutgoingData`" ) ) ?;
162164
163- if !yamux_state . incoming && !yamux_state . established && !yamux_state . syn_sent {
165+ if !stream_state . incoming && !stream_state . established && !stream_state . syn_sent {
164166 flags. insert ( YamuxFlags :: SYN ) ;
165- } else if yamux_state . incoming && !yamux_state . established {
167+ } else if stream_state . incoming && !stream_state . established {
166168 flags. insert ( YamuxFlags :: ACK ) ;
167169 }
168170
@@ -180,6 +182,7 @@ impl P2pNetworkYamuxState {
180182 Ok ( ( ) )
181183 }
182184 P2pNetworkYamuxAction :: IncomingFrame { addr, frame } => {
185+ let mut pending_outgoing = VecDeque :: default ( ) ;
183186 if let Some ( frame) = yamux_state. incoming . pop_front ( ) {
184187 if frame. flags . contains ( YamuxFlags :: SYN ) {
185188 yamux_state
@@ -211,11 +214,32 @@ impl P2pNetworkYamuxState {
211214 }
212215 }
213216 YamuxFrameInner :: WindowUpdate { difference } => {
214- yamux_state
217+ let stream = yamux_state
215218 . streams
216219 . entry ( frame. stream_id )
217- . or_insert_with ( YamuxStreamState :: incoming)
218- . update_window ( false , difference) ;
220+ . or_insert_with ( YamuxStreamState :: incoming) ;
221+ stream. update_window ( false , difference) ;
222+ if difference > 0 {
223+ // have some fresh space in the window
224+ // try send as many frames as can
225+ let mut window = stream. window_theirs ;
226+ while let Some ( mut frame) = stream. pending . pop_front ( ) {
227+ let len = frame. len ( ) as u32 ;
228+ if let Some ( new_window) = window. checked_sub ( len) {
229+ pending_outgoing. push_back ( frame) ;
230+ window = new_window;
231+ } else {
232+ if let Some ( remaining) =
233+ frame. split_at ( ( len - window) as usize )
234+ {
235+ stream. pending . push_front ( remaining) ;
236+ }
237+ pending_outgoing. push_back ( frame) ;
238+
239+ break ;
240+ }
241+ }
242+ }
219243 }
220244 YamuxFrameInner :: Ping { .. } => { }
221245 YamuxFrameInner :: GoAway ( res) => yamux_state. set_res ( res) ,
@@ -282,15 +306,17 @@ impl P2pNetworkYamuxState {
282306 }
283307 match & frame. inner {
284308 YamuxFrameInner :: Data ( data) => {
309+ // here we are very permissive
310+ // always when our window is smaller 64 kb, just increase it by 256 kb
311+ // if we need fine grained back pressure, it should be implemented here
285312 if stream. window_ours < 64 * 1024 {
313+ let difference = 256 * 1024 ;
286314 dispatcher. push ( P2pNetworkYamuxAction :: OutgoingFrame {
287315 addr,
288316 frame : YamuxFrame {
289317 stream_id : frame. stream_id ,
290318 flags : YamuxFlags :: empty ( ) ,
291- inner : YamuxFrameInner :: WindowUpdate {
292- difference : 256 * 1024 ,
293- } ,
319+ inner : YamuxFrameInner :: WindowUpdate { difference } ,
294320 } ,
295321 } ) ;
296322 }
@@ -318,21 +344,61 @@ impl P2pNetworkYamuxState {
318344 } ) ;
319345 }
320346 }
347+ YamuxFrameInner :: WindowUpdate { difference } => {
348+ if * difference < 0 {
349+ let error =
350+ P2pNetworkConnectionError :: YamuxBadWindowUpdate ( frame. stream_id ) ;
351+ dispatcher. push ( P2pNetworkSchedulerAction :: Error { addr, error } ) ;
352+ } else {
353+ while let Some ( frame) = pending_outgoing. pop_front ( ) {
354+ dispatcher
355+ . push ( P2pNetworkYamuxAction :: OutgoingFrame { addr, frame } ) ;
356+ }
357+ }
358+ }
321359 _ => { }
322360 }
323361
324362 Ok ( ( ) )
325363 }
326- P2pNetworkYamuxAction :: OutgoingFrame { frame, addr } => {
327- let Some ( stream) = yamux_state. streams . get_mut ( & frame. stream_id ) else {
364+ P2pNetworkYamuxAction :: OutgoingFrame { mut frame, addr } => {
365+ let stream_id = frame. stream_id ;
366+ let Some ( stream) = yamux_state. streams . get_mut ( & stream_id) else {
328367 return Ok ( ( ) ) ;
329368 } ;
330- match & frame. inner {
369+ match & mut frame. inner {
331370 YamuxFrameInner :: Data ( data) => {
332- // must not underflow
333- // the action must not dispatch if it doesn't fit in the window
334- // TODO: add pending queue, where frames will wait for window increase
335- stream. window_theirs = stream. window_theirs . wrapping_sub ( data. len ( ) as u32 ) ;
371+ if let Some ( new_window) =
372+ stream. window_theirs . checked_sub ( data. len ( ) as u32 )
373+ {
374+ // their window is big enough, decrease the size
375+ // and send the whole frame
376+ stream. window_theirs = new_window;
377+ } else if stream. window_theirs != 0 && stream. pending . is_empty ( ) {
378+ // their window is not big enough, but has some space,
379+ // and the queue is empty,
380+ // do not send the whole frame,
381+ // split it and put remaining in the queue,
382+ if let Some ( remaining) = frame. split_at ( stream. window_theirs as usize ) {
383+ stream. pending . push_back ( remaining) ;
384+ }
385+ // the window will be zero after sending
386+ stream. window_theirs = 0 ;
387+ } else {
388+ // either the window cannot accept any byte,
389+ // or the queue is already not empty
390+ // in both cases the whole frame goes in the queue and nothing to send
391+ stream. pending . push_back ( frame) ;
392+ if stream. pending . iter ( ) . map ( YamuxFrame :: len) . sum :: < usize > ( )
393+ > yamux_state. pending_outgoing_limit
394+ {
395+ let dispatcher = state_context. into_dispatcher ( ) ;
396+ let error = P2pNetworkConnectionError :: YamuxOverflow ( stream_id) ;
397+ dispatcher. push ( P2pNetworkSchedulerAction :: Error { addr, error } ) ;
398+ }
399+
400+ return Ok ( ( ) ) ;
401+ }
336402 }
337403 YamuxFrameInner :: WindowUpdate { difference } => {
338404 stream. update_window ( true , * difference) ;
0 commit comments