@@ -48,7 +48,7 @@ const {
48
48
QLogStream,
49
49
} = require ( 'internal/quic/util' ) ;
50
50
const assert = require ( 'internal/assert' ) ;
51
- const EventEmitter = require ( 'events' ) ;
51
+ const { EventEmitter, once } = require ( 'events' ) ;
52
52
const fs = require ( 'fs' ) ;
53
53
const fsPromisesInternal = require ( 'internal/fs/promises' ) ;
54
54
const { Duplex } = require ( 'stream' ) ;
@@ -226,6 +226,7 @@ const kMaybeBind = Symbol('kMaybeBind');
226
226
const kOnFileOpened = Symbol ( 'kOnFileOpened' ) ;
227
227
const kOnFileUnpipe = Symbol ( 'kOnFileUnpipe' ) ;
228
228
const kOnPipedFileHandleRead = Symbol ( 'kOnPipedFileHandleRead' ) ;
229
+ const kReady = Symbol ( 'kReady' ) ;
229
230
const kRemoveSession = Symbol ( 'kRemove' ) ;
230
231
const kRemoveStream = Symbol ( 'kRemoveStream' ) ;
231
232
const kServerBusy = Symbol ( 'kServerBusy' ) ;
@@ -2167,30 +2168,15 @@ class QuicSession extends EventEmitter {
2167
2168
defaultEncoding,
2168
2169
} = validateQuicStreamOptions ( options ) ;
2169
2170
2170
- await this [ kHandshakeComplete ] ( ) ;
2171
-
2172
- if ( this . destroyed ) {
2173
- throw new ERR_INVALID_STATE (
2174
- `${ this . constructor . name } is already destroyed` ) ;
2175
- }
2176
- if ( this . closing ) {
2177
- throw new ERR_INVALID_STATE (
2178
- `${ this . constructor . name } is closing` ) ;
2179
- }
2180
-
2181
- const handle =
2182
- halfOpen ?
2183
- _openUnidirectionalStream ( this [ kHandle ] ) :
2184
- _openBidirectionalStream ( this [ kHandle ] ) ;
2185
-
2186
- if ( handle === undefined )
2187
- throw new ERR_OPERATION_FAILED ( 'Unable to create QuicStream' ) ;
2188
-
2189
- return new QuicStream ( {
2171
+ const stream = new QuicStream ( {
2190
2172
highWaterMark,
2191
2173
defaultEncoding,
2192
2174
readable : ! halfOpen
2193
- } , this , handle ) ;
2175
+ } , this ) ;
2176
+
2177
+ await once ( stream , kReady ) ;
2178
+
2179
+ return stream ;
2194
2180
}
2195
2181
2196
2182
get duration ( ) {
@@ -2532,6 +2518,7 @@ function streamOnPause() {
2532
2518
this [ kHandle ] . readStop ( ) ;
2533
2519
}
2534
2520
class QuicStream extends Duplex {
2521
+ #count = 0 ;
2535
2522
[ kInternalState ] = {
2536
2523
closed : false ,
2537
2524
closePromise : undefined ,
@@ -2547,6 +2534,7 @@ class QuicStream extends Duplex {
2547
2534
dataRateHistogram : undefined ,
2548
2535
dataSizeHistogram : undefined ,
2549
2536
dataAckHistogram : undefined ,
2537
+ ready : false ,
2550
2538
sharedState : undefined ,
2551
2539
stats : undefined ,
2552
2540
} ;
@@ -2578,7 +2566,45 @@ class QuicStream extends Duplex {
2578
2566
this . _readableState . readingMore = true ;
2579
2567
this . on ( 'pause' , streamOnPause ) ;
2580
2568
2581
- this [ kSetHandle ] ( handle ) ;
2569
+ if ( handle !== undefined )
2570
+ this [ kSetHandle ] ( handle ) ;
2571
+ }
2572
+
2573
+ async _construct ( callback ) {
2574
+ try {
2575
+ if ( this [ kInternalState ] . ready )
2576
+ return callback ( ) ;
2577
+
2578
+ // Handle is already initialized
2579
+ const unidirectional = ! this . readable ;
2580
+
2581
+ await this . session [ kHandshakeComplete ] ( ) ;
2582
+
2583
+ if ( this . destroyed ) {
2584
+ throw new ERR_INVALID_STATE ( 'QuicStream was destroyed' ) ;
2585
+ }
2586
+ if ( this . session . destroyed ) {
2587
+ throw new ERR_INVALID_STATE (
2588
+ `${ this . session . constructor . name } was destroyed` ) ;
2589
+ }
2590
+ if ( this . session . closing ) {
2591
+ throw new ERR_INVALID_STATE (
2592
+ `${ this . session . constructor . name } is closing` ) ;
2593
+ }
2594
+
2595
+ const handle =
2596
+ unidirectional ?
2597
+ _openUnidirectionalStream ( this . session [ kHandle ] ) :
2598
+ _openBidirectionalStream ( this . session [ kHandle ] ) ;
2599
+
2600
+ if ( handle === undefined )
2601
+ throw new ERR_OPERATION_FAILED ( 'Unable to create QuicStream' ) ;
2602
+
2603
+ this [ kSetHandle ] ( handle ) ;
2604
+ callback ( ) ;
2605
+ } catch ( error ) {
2606
+ callback ( error ) ;
2607
+ }
2582
2608
}
2583
2609
2584
2610
// Set handle is called once the QuicSession has been able
@@ -2589,6 +2615,8 @@ class QuicStream extends Duplex {
2589
2615
// written will be buffered until kSetHandle is called.
2590
2616
[ kSetHandle ] ( handle ) {
2591
2617
const state = this [ kInternalState ] ;
2618
+ const current = this [ kHandle ] ;
2619
+ this [ kHandle ] = handle ;
2592
2620
if ( handle !== undefined ) {
2593
2621
handle . onread = onStreamRead ;
2594
2622
handle [ owner_symbol ] = this ;
@@ -2599,11 +2627,13 @@ class QuicStream extends Duplex {
2599
2627
state . dataAckHistogram = new Histogram ( handle . ack ) ;
2600
2628
state . sharedState = new QuicStreamSharedState ( handle . state ) ;
2601
2629
state . session [ kAddStream ] ( state . id , this ) ;
2630
+ state . ready = true ;
2631
+ this . emit ( kReady ) ;
2602
2632
} else {
2603
- if ( this [ kHandle ] !== undefined ) {
2604
- this [ kHandle ] . stats [ IDX_QUIC_STREAM_STATS_DESTROYED_AT ] =
2633
+ if ( current !== undefined ) {
2634
+ current . stats [ IDX_QUIC_STREAM_STATS_DESTROYED_AT ] =
2605
2635
process . hrtime . bigint ( ) ;
2606
- state . stats = new BigInt64Array ( this [ kHandle ] . stats ) ;
2636
+ state . stats = new BigInt64Array ( current . stats ) ;
2607
2637
}
2608
2638
state . sharedState = undefined ;
2609
2639
if ( state . dataRateHistogram )
@@ -2613,7 +2643,6 @@ class QuicStream extends Duplex {
2613
2643
if ( state . dataAckHistogram )
2614
2644
state . dataAckHistogram [ kDestroyHistogram ] ( ) ;
2615
2645
}
2616
- this [ kHandle ] = handle ;
2617
2646
}
2618
2647
2619
2648
[ kStreamReset ] ( code ) {
@@ -2643,6 +2672,8 @@ class QuicStream extends Duplex {
2643
2672
this . end ( ) ;
2644
2673
}
2645
2674
2675
+ // TODO(@jasnell): Investigate later if a Promise version
2676
+ // of finished() can work here instead.
2646
2677
return promise ;
2647
2678
}
2648
2679
@@ -2663,6 +2694,7 @@ class QuicStream extends Duplex {
2663
2694
else if ( typeof state . closePromiseResolve === 'function' )
2664
2695
state . closePromiseResolve ( ) ;
2665
2696
2697
+ // TODO(@jasnell): Investigate how we can eliminate the nextTick here
2666
2698
process . nextTick ( ( ) => callback ( error ) ) ;
2667
2699
}
2668
2700
@@ -2754,7 +2786,7 @@ class QuicStream extends Duplex {
2754
2786
}
2755
2787
2756
2788
[ kWriteGeneric ] ( writev , data , encoding , cb ) {
2757
- if ( this . destroyed )
2789
+ if ( this . destroyed || this . detached )
2758
2790
return ; // TODO(addaleax): Can this happen?
2759
2791
2760
2792
this [ kUpdateTimer ] ( ) ;
@@ -2829,6 +2861,8 @@ class QuicStream extends Duplex {
2829
2861
}
2830
2862
2831
2863
sendFile ( path , options = { } ) {
2864
+ if ( this . detached )
2865
+ throw new ERR_INVALID_STATE ( 'Unable to send file' ) ;
2832
2866
fs . open ( path , 'r' , QuicStream [ kOnFileOpened ] . bind ( this , options ) ) ;
2833
2867
}
2834
2868
@@ -2856,6 +2890,9 @@ class QuicStream extends Duplex {
2856
2890
if ( this . destroyed || this [ kInternalState ] . closed )
2857
2891
return ;
2858
2892
2893
+ if ( this . detached )
2894
+ throw new ERR_INVALID_STATE ( 'Unable to send file descriptor' ) ;
2895
+
2859
2896
validateInteger ( offset , 'options.offset' , /* min */ - 1 ) ;
2860
2897
validateInteger ( length , 'options.length' , /* min */ - 1 ) ;
2861
2898
@@ -2947,6 +2984,12 @@ class QuicStream extends Duplex {
2947
2984
if ( this . destroyed )
2948
2985
throw new ERR_INVALID_STATE ( 'QuicStream is already destroyed' ) ;
2949
2986
2987
+ if ( this . detached ) {
2988
+ throw new ERR_INVALID_STATE (
2989
+ 'Push stream could not be opened on this QuicSession. ' +
2990
+ 'Push is either disabled or currently blocked.' ) ;
2991
+ }
2992
+
2950
2993
const state = this [ kInternalState ] ;
2951
2994
const {
2952
2995
highWaterMark = state . highWaterMark ,
@@ -2995,9 +3038,11 @@ class QuicStream extends Duplex {
2995
3038
}
2996
3039
2997
3040
submitInformationalHeaders ( headers = { } ) {
2998
- // TODO(@jasnell): Likely better to throw here instead of return false
2999
3041
if ( this . destroyed )
3000
- return false ;
3042
+ throw new ERR_INVALID_STATE ( 'QuicStream is already destroyed' ) ;
3043
+
3044
+ if ( this . detached )
3045
+ throw new ERR_INVALID_STATE ( 'Unable to submit headers' ) ;
3001
3046
3002
3047
validateObject ( headers , 'headers' ) ;
3003
3048
@@ -3025,9 +3070,11 @@ class QuicStream extends Duplex {
3025
3070
}
3026
3071
3027
3072
submitInitialHeaders ( headers = { } , options = { } ) {
3028
- // TODO(@jasnell): Likely better to throw here instead of return false
3029
3073
if ( this . destroyed )
3030
- return false ;
3074
+ throw new ERR_INVALID_STATE ( 'QuicStream is already destroyed' ) ;
3075
+
3076
+ if ( this . detached )
3077
+ throw new ERR_INVALID_STATE ( 'Unable to submit headers' ) ;
3031
3078
3032
3079
const { terminal } = { ...options } ;
3033
3080
@@ -3062,9 +3109,11 @@ class QuicStream extends Duplex {
3062
3109
}
3063
3110
3064
3111
submitTrailingHeaders ( headers = { } ) {
3065
- // TODO(@jasnell): Likely better to throw here instead of return false
3066
3112
if ( this . destroyed )
3067
- return false ;
3113
+ throw new ERR_INVALID_STATE ( 'QuicStream is already destroyed' ) ;
3114
+
3115
+ if ( this . detached )
3116
+ throw new ERR_INVALID_STATE ( 'Unable to submit headers' ) ;
3068
3117
3069
3118
validateObject ( headers , 'headers' ) ;
3070
3119
0 commit comments