@@ -3,8 +3,9 @@ import type QUICConnectionMap from './QUICConnectionMap';
3
3
import type QUICConnectionId from './QUICConnectionId' ;
4
4
// This is specialized type
5
5
import type { QUICConfig } from './config' ;
6
- import type { Host , Port , StreamId , RemoteInfo } from './types' ;
7
- import type { Connection , SendInfo , ConnectionErrorCode } from './native/types' ;
6
+ import type { Host , Port , RemoteInfo , StreamId } from './types' ;
7
+ import type { Connection , ConnectionErrorCode , SendInfo } from './native/types' ;
8
+ import type { StreamCodeToReason , StreamReasonToCode } from './types' ;
8
9
import {
9
10
CreateDestroy ,
10
11
ready ,
@@ -13,12 +14,13 @@ import {
13
14
import Logger from '@matrixai/logger' ;
14
15
import { Lock } from '@matrixai/async-locks' ;
15
16
import { destroyed } from '@matrixai/async-init' ;
17
+ import { buildQuicheConfig } from './config' ;
16
18
import QUICStream from './QUICStream' ;
17
19
import { quiche } from './native' ;
18
20
import * as events from './events' ;
19
21
import * as utils from './utils' ;
20
22
import * as errors from './errors' ;
21
- import { buildQuicheConfig } from './config ' ;
23
+ import { promise } from './utils ' ;
22
24
23
25
/**
24
26
* Think of this as equivalent to `net.Socket`.
@@ -39,6 +41,9 @@ class QUICConnection extends EventTarget {
39
41
public conn : Connection ;
40
42
public connectionMap : QUICConnectionMap ;
41
43
public streamMap : Map < StreamId , QUICStream > = new Map ( ) ;
44
+ protected reasonToCode : StreamReasonToCode ;
45
+ protected codeToReason : StreamCodeToReason ;
46
+ protected destroyingMap : Map < StreamId , QUICStream > = new Map ( ) ;
42
47
43
48
// This basically allows one to await this promise
44
49
// once resolved, always resolved...
@@ -94,12 +99,17 @@ class QUICConnection extends EventTarget {
94
99
socket,
95
100
remoteInfo,
96
101
config,
102
+ reasonToCode = ( ) => 0 ,
103
+ codeToReason = ( type , code ) =>
104
+ new Error ( `${ type . toString ( ) } ${ code . toString ( ) } ` ) ,
97
105
logger = new Logger ( `${ this . name } ${ scid } ` ) ,
98
106
} : {
99
107
scid : QUICConnectionId ;
100
108
socket : QUICSocket ;
101
109
remoteInfo : RemoteInfo ;
102
110
config : QUICConfig ;
111
+ reasonToCode ?: StreamReasonToCode ;
112
+ codeToReason ?: StreamCodeToReason ;
103
113
logger ?: Logger ;
104
114
} ) {
105
115
logger . info ( `Connect ${ this . name } ` ) ;
@@ -127,6 +137,8 @@ class QUICConnection extends EventTarget {
127
137
connectionId : scid ,
128
138
socket,
129
139
remoteInfo,
140
+ reasonToCode,
141
+ codeToReason,
130
142
logger,
131
143
} ) ;
132
144
socket . connectionMap . set ( connection . connectionId , connection ) ;
@@ -143,13 +155,18 @@ class QUICConnection extends EventTarget {
143
155
socket,
144
156
remoteInfo,
145
157
config,
158
+ reasonToCode = ( ) => 0 ,
159
+ codeToReason = ( type , code ) =>
160
+ new Error ( `${ type . toString ( ) } ${ code . toString ( ) } ` ) ,
146
161
logger = new Logger ( `${ this . name } ${ scid } ` ) ,
147
162
} : {
148
163
scid : QUICConnectionId ;
149
164
dcid : QUICConnectionId ;
150
165
socket : QUICSocket ;
151
166
remoteInfo : RemoteInfo ;
152
167
config : QUICConfig ;
168
+ reasonToCode ?: StreamReasonToCode ;
169
+ codeToReason ?: StreamCodeToReason ;
153
170
logger ?: Logger ;
154
171
} ) : Promise < QUICConnection > {
155
172
logger . info ( `Accept ${ this . name } ` ) ;
@@ -177,6 +194,8 @@ class QUICConnection extends EventTarget {
177
194
connectionId : scid ,
178
195
socket,
179
196
remoteInfo,
197
+ reasonToCode,
198
+ codeToReason,
180
199
logger,
181
200
} ) ;
182
201
socket . connectionMap . set ( connection . connectionId , connection ) ;
@@ -190,13 +209,17 @@ class QUICConnection extends EventTarget {
190
209
connectionId,
191
210
socket,
192
211
remoteInfo,
212
+ reasonToCode,
213
+ codeToReason,
193
214
logger,
194
215
} : {
195
216
type : 'client' | 'server' ;
196
217
conn : Connection ;
197
218
connectionId : QUICConnectionId ;
198
219
socket : QUICSocket ;
199
220
remoteInfo : RemoteInfo ;
221
+ reasonToCode : StreamReasonToCode ;
222
+ codeToReason : StreamCodeToReason ;
200
223
logger : Logger ;
201
224
} ) {
202
225
super ( ) ;
@@ -208,6 +231,8 @@ class QUICConnection extends EventTarget {
208
231
this . socket = socket ;
209
232
this . _remoteHost = remoteInfo . host ;
210
233
this . _remotePort = remoteInfo . port ;
234
+ this . reasonToCode = reasonToCode ;
235
+ this . codeToReason = codeToReason ;
211
236
// Sets the timeout on the first
212
237
this . checkTimeout ( ) ;
213
238
@@ -233,7 +258,7 @@ class QUICConnection extends EventTarget {
233
258
234
259
// Immediately call this after construction
235
260
// if you want to pass the key log to something
236
- // note that you must close the file descriptor afterwards
261
+ // note that you must close the file descriptor afterward
237
262
public setKeylog ( path ) {
238
263
this . conn . setKeylog ( path ) ;
239
264
}
@@ -261,17 +286,28 @@ class QUICConnection extends EventTarget {
261
286
appError = false ,
262
287
errorCode = quiche . ConnectionErrorCode . NoError ,
263
288
errorMessage = '' ,
289
+ force = false ,
264
290
} : {
265
291
appError ?: boolean ;
266
292
errorCode ?: ConnectionErrorCode ;
267
293
errorMessage ?: string ;
294
+ force ?: boolean ;
268
295
} = { } ) {
269
296
this . logger . info ( `Destroy ${ this . constructor . name } ` ) ;
270
- // Console.log(this.conn.localError())
271
- // console.log(this.conn.peerError())
297
+ // Handle destruction concurrently
298
+ const destroyProms : Array < Promise < void > > = [ ] ;
272
299
for ( const stream of this . streamMap . values ( ) ) {
273
- await stream . destroy ( ) ;
300
+ if ( force ) {
301
+ destroyProms . push ( stream . destroy ( ) ) ;
302
+ } else {
303
+ const destroyProm = promise ( ) ;
304
+ stream . addEventListener ( 'destroy' , ( ) => destroyProm . resolveP ( ) , {
305
+ once : true ,
306
+ } ) ;
307
+ destroyProms . push ( destroyProm . p ) ;
308
+ }
274
309
}
310
+ await Promise . all ( destroyProms ) ;
275
311
try {
276
312
// If this is already closed, then `Done` will be thrown
277
313
// Otherwise it can send `CONNECTION_CLOSE` frame
@@ -322,7 +358,7 @@ class QUICConnection extends EventTarget {
322
358
* UDP -> Connection -> Stream
323
359
* This pushes data to the streams.
324
360
* When the connection is draining, we can still receive data.
325
- * However no streams are allowed to read or write.
361
+ * However, no streams are allowed to read or write.
326
362
*/
327
363
@ready ( new errors . ErrorQUICConnectionDestroyed ( ) , false , [ 'destroying' ] )
328
364
public async recv ( data : Uint8Array , remoteInfo : RemoteInfo ) {
@@ -345,10 +381,10 @@ class QUICConnection extends EventTarget {
345
381
} ,
346
382
} ;
347
383
try {
348
- this . logger . debug ( `Did a recv ${ data . byteLength } ` ) ;
349
384
this . conn . recv ( data , recvInfo ) ;
385
+ this . logger . debug ( `RECEIVED ${ data . byteLength } of data` ) ;
350
386
} catch ( e ) {
351
- this . logger . error ( e . message ) ;
387
+ this . logger . error ( `recv error ${ e . message } ` ) ;
352
388
// Depending on the exception, the `this.conn.recv`
353
389
// may have automatically started closing the connection
354
390
if ( e . message === 'TlsFail' ) {
@@ -381,7 +417,6 @@ class QUICConnection extends EventTarget {
381
417
this . resolveEstablishedP ( ) ;
382
418
}
383
419
if ( this . conn . isClosed ( ) ) {
384
- this . logger . debug ( 'recv CLOSED!!!!!' ) ;
385
420
if ( this . resolveCloseP != null ) this . resolveCloseP ( ) ;
386
421
return ;
387
422
}
@@ -396,7 +431,14 @@ class QUICConnection extends EventTarget {
396
431
quicStream = await QUICStream . createQUICStream ( {
397
432
streamId,
398
433
connection : this ,
399
- logger : this . logger . getChild ( `${ QUICStream . name } ${ streamId } ` ) ,
434
+ destroyingMap : this . destroyingMap ,
435
+ codeToReason : this . codeToReason ,
436
+ reasonToCode : this . reasonToCode ,
437
+ logger : this . logger . getChild (
438
+ `${ QUICStream . name } ${ streamId } -${ Math . floor (
439
+ Math . random ( ) * 100 ,
440
+ ) } `,
441
+ ) ,
400
442
} ) ;
401
443
this . dispatchEvent (
402
444
new events . QUICConnectionStreamEvent ( { detail : quicStream } ) ,
@@ -411,14 +453,29 @@ class QUICConnection extends EventTarget {
411
453
quicStream = await QUICStream . createQUICStream ( {
412
454
streamId,
413
455
connection : this ,
414
- logger : this . logger . getChild ( `${ QUICStream . name } ${ streamId } ` ) ,
456
+ codeToReason : this . codeToReason ,
457
+ reasonToCode : this . reasonToCode ,
458
+ destroyingMap : this . destroyingMap ,
459
+ logger : this . logger . getChild (
460
+ `${ QUICStream . name } ${ streamId } -${ Math . floor (
461
+ Math . random ( ) * 100 ,
462
+ ) } `,
463
+ ) ,
415
464
} ) ;
416
465
this . dispatchEvent (
417
466
new events . QUICConnectionStreamEvent ( { detail : quicStream } ) ,
418
467
) ;
419
468
}
420
469
quicStream . write ( ) ;
421
470
}
471
+ // Checking shortlist if streams have finished.
472
+ for ( const [ streamId , stream ] of this . destroyingMap ) {
473
+ if ( stream . isFinished ( ) ) {
474
+ // If it has finished, it will trigger its own clean up.
475
+ // Remove the stream from the shortlist.
476
+ this . destroyingMap . delete ( streamId ) ;
477
+ }
478
+ }
422
479
}
423
480
} finally {
424
481
this . logger . debug ( 'RECV FINALLY' ) ;
@@ -534,8 +591,9 @@ class QUICConnection extends EventTarget {
534
591
sendInfo . to . port ,
535
592
sendInfo . to . host ,
536
593
) ;
594
+ this . logger . info ( `SENT ${ sendLength } of data` ) ;
537
595
} catch ( e ) {
538
- this . logger . error ( e . message ) ;
596
+ this . logger . error ( `send error ${ e . message } ` ) ;
539
597
this . dispatchEvent (
540
598
new events . QUICConnectionErrorEvent ( { detail : e } ) ,
541
599
) ;
@@ -544,18 +602,11 @@ class QUICConnection extends EventTarget {
544
602
}
545
603
} finally {
546
604
this . logger . debug ( 'SEND FINALLY' ) ;
547
- this . logger . debug (
548
- ` ________ ED: ${ this . conn . isInEarlyData ( ) } TO: ${ this . conn . isTimedOut ( ) } EST: ${ this . conn . isEstablished ( ) } ` ,
549
- ) ;
550
605
this . checkTimeout ( ) ;
551
- this . logger . debug (
552
- `state are draining: ${ this . conn . isDraining ( ) } , closed: ${ this . conn . isClosed ( ) } ` ,
553
- ) ;
554
606
if (
555
607
this [ status ] !== 'destroying' &&
556
608
( this . conn . isClosed ( ) || this . conn . isDraining ( ) )
557
609
) {
558
- this . logger . debug ( 'CALLING DESTROY' ) ;
559
610
// Ignore errors and run in background
560
611
void this . destroy ( ) . catch ( ( ) => { } ) ;
561
612
} else if (
@@ -605,24 +656,13 @@ class QUICConnection extends EventTarget {
605
656
const quicStream = await QUICStream . createQUICStream ( {
606
657
streamId : streamId ! ,
607
658
connection : this ,
608
- logger : this . logger . getChild ( `${ QUICStream . name } ${ streamId ! } ` ) ,
659
+ codeToReason : this . codeToReason ,
660
+ reasonToCode : this . reasonToCode ,
661
+ destroyingMap : this . destroyingMap ,
662
+ logger : this . logger . getChild (
663
+ `${ QUICStream . name } ${ streamId ! } -${ Math . floor ( Math . random ( ) * 100 ) } ` ,
664
+ ) ,
609
665
} ) ;
610
- const writer = quicStream . writable . getWriter ( ) ;
611
-
612
- try {
613
- // This will now wait until the 0-length buffer is actually sent
614
- await writer . write ( new Uint8Array ( 0 ) ) ;
615
- writer . releaseLock ( ) ;
616
- } catch ( e ) {
617
- // You must release the lock even before you run destroy
618
- writer . releaseLock ( ) ;
619
- // If the write failed, it will only close the sending side
620
- // But in this case, it means we actually failed to open the stream entirely
621
- // In which case we destroy the stream
622
- // Do we need to release the writer?
623
- await quicStream . destroy ( ) ;
624
- throw e ;
625
- }
626
666
// Ok the stream is opened and working
627
667
if ( this . type === 'client' && streamType === 'bidi' ) {
628
668
this . streamIdClientBidi = ( this . streamIdClientBidi + 4 ) as StreamId ;
@@ -647,9 +687,7 @@ class QUICConnection extends EventTarget {
647
687
this . logger . debug (
648
688
`state are draining: ${ this . conn . isDraining ( ) } , closed: ${ this . conn . isClosed ( ) } ` ,
649
689
) ;
650
- this . logger . debug ( 'timeout SEND' ) ;
651
690
if ( this [ destroyed ] === false ) await this . send ( ) ;
652
- this . logger . debug ( 'timeout SENDAFTER' ) ;
653
691
if (
654
692
this [ status ] !== 'destroying' &&
655
693
( this . conn . isClosed ( ) || this . conn . isDraining ( ) )
0 commit comments