@@ -27,6 +27,7 @@ import { buildQuicheConfig, minIdleTimeout } from './config';
2727import QUICConnectionId from './QUICConnectionId' ;
2828import QUICStream from './QUICStream' ;
2929import { quiche , ConnectionErrorCode } from './native' ;
30+ import { Shutdown } from './native/types' ;
3031import * as utils from './utils' ;
3132import * as events from './events' ;
3233import * as errors from './errors' ;
@@ -616,7 +617,27 @@ class QUICConnection {
616617 } = { } ) {
617618 this . logger . info ( `Stop ${ this . constructor . name } ` ) ;
618619 this . stopKeepAliveIntervalTimer ( ) ;
619- // Closing the connection first to avoid accepting new streams
620+
621+ // Yield to allow any background processing to settle before proceeding.
622+ // This will allow any streams to process buffers before continuing
623+ await utils . yieldMicro ( ) ;
624+
625+ // Destroy all streams
626+ const streamsDestroyP : Array < Promise < void > > = [ ] ;
627+ for ( const quicStream of this . streamMap . values ( ) ) {
628+ // The reason is only used if `force` is `true`
629+ // If `force` is not true, this will gracefully wait for
630+ // both readable and writable to gracefully close
631+ streamsDestroyP . push (
632+ quicStream . destroy ( {
633+ reason : this . errorLast ,
634+ force : force || this . conn . isDraining ( ) || this . conn . isClosed ( ) ,
635+ } ) ,
636+ ) ;
637+ }
638+ await Promise . all ( streamsDestroyP ) ;
639+
640+ // Close after processing all streams
620641 if ( ! this . conn . isDraining ( ) && ! this . conn . isClosed ( ) ) {
621642 // If `this.conn.close` is already called, the connection will be draining,
622643 // in that case we just skip doing this local close.
@@ -632,20 +653,7 @@ class QUICConnection {
632653 } ) ;
633654 this . dispatchEvent ( new events . EventQUICConnectionError ( { detail : e } ) ) ;
634655 }
635- // Destroy all streams
636- const streamsDestroyP : Array < Promise < void > > = [ ] ;
637- for ( const quicStream of this . streamMap . values ( ) ) {
638- // The reason is only used if `force` is `true`
639- // If `force` is not true, this will gracefully wait for
640- // both readable and writable to gracefully close
641- streamsDestroyP . push (
642- quicStream . destroy ( {
643- reason : this . errorLast ,
644- force : force || this . conn . isDraining ( ) || this . conn . isClosed ( ) ,
645- } ) ,
646- ) ;
647- }
648- await Promise . all ( streamsDestroyP ) ;
656+
649657 // Waiting for `closedP` to resolve
650658 // Only the `this.connTimeoutTimer` will resolve this promise
651659 await this . closedP ;
@@ -940,6 +948,13 @@ class QUICConnection {
940948 for ( const streamId of this . conn . readable ( ) as Iterable < StreamId > ) {
941949 let quicStream = this . streamMap . get ( streamId ) ;
942950 if ( quicStream == null ) {
951+ if ( this [ running ] === false || this [ status ] === 'stopping' ) {
952+ // We should reject new connections when stopping
953+ this . conn . streamShutdown ( streamId , Shutdown . Write , 1 ) ;
954+ this . conn . streamShutdown ( streamId , Shutdown . Read , 1 ) ;
955+ continue ;
956+ }
957+
943958 quicStream = QUICStream . createQUICStream ( {
944959 initiated : 'peer' ,
945960 streamId,
@@ -969,6 +984,13 @@ class QUICConnection {
969984 for ( const streamId of this . conn . writable ( ) as Iterable < StreamId > ) {
970985 let quicStream = this . streamMap . get ( streamId ) ;
971986 if ( quicStream == null ) {
987+ if ( this [ running ] === false || this [ status ] === 'stopping' ) {
988+ // We should reject new connections when stopping
989+ this . conn . streamShutdown ( streamId , Shutdown . Write , 1 ) ;
990+ this . conn . streamShutdown ( streamId , Shutdown . Read , 1 ) ;
991+ continue ;
992+ }
993+
972994 quicStream = QUICStream . createQUICStream ( {
973995 initiated : 'peer' ,
974996 streamId,
@@ -1132,6 +1154,21 @@ class QUICConnection {
11321154 return quicStream ;
11331155 }
11341156
1157+ /**
1158+ * Destroys all active streams without closing the connection.
1159+ *
1160+ * If there are no active streams then it will do nothing.
1161+ * If the connection is stopped with `force: false` then this can be used
1162+ * to force close any streams `stop` is waiting for to end.
1163+ *
1164+ * Destruction will occur in the background.
1165+ */
1166+ public destroyStreams ( reason ?: any ) {
1167+ for ( const quicStream of this . streamMap . values ( ) ) {
1168+ quicStream . cancel ( reason ) ;
1169+ }
1170+ }
1171+
11351172 /**
11361173 * Starts the keep alive interval timer.
11371174 *
0 commit comments