@@ -28,7 +28,7 @@ import QUICStream from './QUICStream';
28
28
import { quiche } from './native' ;
29
29
import * as events from './events' ;
30
30
import * as utils from './utils' ;
31
- import { never } from './utils' ;
31
+ import { never , promise } from './utils' ;
32
32
import * as errors from './errors' ;
33
33
34
34
/**
@@ -207,6 +207,103 @@ class QUICConnection extends EventTarget {
207
207
protected count = 0 ;
208
208
protected verifyCallback : VerifyCallback | undefined ;
209
209
210
+ public static createQUICConnection (
211
+ args :
212
+ | {
213
+ type : 'client' ;
214
+ scid : QUICConnectionId ;
215
+ dcid ?: undefined ;
216
+ remoteInfo : RemoteInfo ;
217
+ config : QUICConfig ;
218
+ socket : QUICSocket ;
219
+ reasonToCode ?: StreamReasonToCode ;
220
+ codeToReason ?: StreamCodeToReason ;
221
+ verifyCallback ?: VerifyCallback ;
222
+ logger ?: Logger ;
223
+ }
224
+ | {
225
+ type : 'server' ;
226
+ scid : QUICConnectionId ;
227
+ dcid : QUICConnectionId ;
228
+ remoteInfo : RemoteInfo ;
229
+ config : QUICConfig ;
230
+ socket : QUICSocket ;
231
+ reasonToCode ?: StreamReasonToCode ;
232
+ codeToReason ?: StreamCodeToReason ;
233
+ verifyCallback ?: VerifyCallback ;
234
+ logger ?: Logger ;
235
+ } ,
236
+ ctx ?: Partial < ContextTimed > ,
237
+ ) : PromiseCancellable < QUICConnection > ;
238
+ @timedCancellable ( true , Infinity , errors . ErrorQUICConnectionStartTimeOut )
239
+ public static async createQUICConnection (
240
+ args :
241
+ | {
242
+ type : 'client' ;
243
+ scid : QUICConnectionId ;
244
+ dcid ?: undefined ;
245
+ remoteInfo : RemoteInfo ;
246
+ config : QUICConfig ;
247
+ socket : QUICSocket ;
248
+ reasonToCode ?: StreamReasonToCode ;
249
+ codeToReason ?: StreamCodeToReason ;
250
+ verifyCallback ?: VerifyCallback ;
251
+ logger ?: Logger ;
252
+ }
253
+ | {
254
+ type : 'server' ;
255
+ scid : QUICConnectionId ;
256
+ dcid : QUICConnectionId ;
257
+ remoteInfo : RemoteInfo ;
258
+ config : QUICConfig ;
259
+ socket : QUICSocket ;
260
+ reasonToCode ?: StreamReasonToCode ;
261
+ codeToReason ?: StreamCodeToReason ;
262
+ verifyCallback ?: VerifyCallback ;
263
+ logger ?: Logger ;
264
+ } ,
265
+ @context ctx : ContextTimed ,
266
+ ) : Promise < QUICConnection > {
267
+ ctx . signal . throwIfAborted ( ) ;
268
+ const abortProm = promise < never > ( ) ;
269
+ const abortHandler = ( ) => {
270
+ abortProm . rejectP ( ctx . signal . reason ) ;
271
+ } ;
272
+ ctx . signal . addEventListener ( 'abort' , abortHandler ) ;
273
+ const connection = new this ( args ) ;
274
+ // This ensures that TLS has been established and verified on both sides
275
+ try {
276
+ await Promise . race ( [
277
+ Promise . all ( [
278
+ connection . start ( ) ,
279
+ connection . establishedP ,
280
+ connection . secureEstablishedP ,
281
+ ] ) ,
282
+ abortProm . p ,
283
+ ] ) ;
284
+ } catch ( e ) {
285
+ await connection . stop ( {
286
+ applicationError : false ,
287
+ errorCode : 42 , // FIXME: use a proper code
288
+ errorMessage : e . message ,
289
+ force : true ,
290
+ } ) ;
291
+ throw e ;
292
+ } finally {
293
+ ctx . signal . removeEventListener ( 'abort' , abortHandler ) ;
294
+ }
295
+ connection . logger . warn ( 'secured' ) ;
296
+ // After this is done
297
+ // We need to establish the keep alive interval time
298
+ if ( connection . config . keepAliveIntervalTime != null ) {
299
+ connection . startKeepAliveIntervalTimer (
300
+ connection . config . keepAliveIntervalTime ,
301
+ ) ;
302
+ }
303
+
304
+ return connection ;
305
+ }
306
+
210
307
public constructor ( {
211
308
type,
212
309
scid,
@@ -351,79 +448,13 @@ class QUICConnection extends EventTarget {
351
448
}
352
449
353
450
/**
354
- * This is the same as basically waiting for `secureEstablishedP`
355
- * While this is occurring one can call the `recv` and `send` to make this happen
451
+ * This will set up the connection initiate sending
356
452
*/
357
- public start ( ctx ?: Partial < ContextTimed > ) : PromiseCancellable < void > ;
358
- @timedCancellable ( true , Infinity , errors . ErrorQUICConnectionStartTimeOut )
359
- public async start ( @context ctx : ContextTimed ) : Promise < void > {
453
+ public async start ( ) : Promise < void > {
360
454
this . logger . info ( `Start ${ this . constructor . name } ` ) ;
361
- ctx . signal . throwIfAborted ( ) ;
362
- const abortHandler = ( r ) => {
363
- this . rejectEstablishedP ( r ) ;
364
- this . rejectSecureEstablishedP ( r ) ;
365
-
366
- // Is this actually true?
367
- // Technically the connection is closed
368
- this . rejectClosedP ( r ) ;
369
- } ;
370
- ctx . signal . addEventListener ( 'abort' , abortHandler ) ;
371
455
// Set the connection up
372
456
this . socket . connectionMap . set ( this . connectionId , this ) ;
373
- // Waits for the first short packet after establishment
374
- // This ensures that TLS has been established and verified on both sides
375
457
await this . send ( ) ;
376
- await this . secureEstablishedP
377
- . catch ( ( e ) => {
378
- this . socket . connectionMap . delete ( this . connectionId ) ;
379
-
380
- if ( this . conn . isTimedOut ( ) ) {
381
- // We don't dispatch an event here, it was already done in the timeout.
382
- throw new errors . ErrorQUICConnectionStartTimeOut ( ) ;
383
- }
384
-
385
- // Emit error if local error
386
- const localError = this . conn . localError ( ) ;
387
- if ( localError != null ) {
388
- const message = `connection start failed with localError ${ Buffer . from (
389
- localError . reason ,
390
- ) . toString ( ) } (${ localError . errorCode } )`;
391
- this . logger . info ( message ) ;
392
- throw new errors . ErrorQUICConnectionInternal ( message , {
393
- data : {
394
- type : 'local' ,
395
- ...localError ,
396
- } ,
397
- } ) ;
398
- }
399
- // Emit error if peer error
400
- const peerError = this . conn . peerError ( ) ;
401
- if ( peerError != null ) {
402
- const message = `Connection start failed with peerError ${ Buffer . from (
403
- peerError . reason ,
404
- ) . toString ( ) } (${ peerError . errorCode } )`;
405
- this . logger . info ( message ) ;
406
- throw new errors . ErrorQUICConnectionInternal ( message , {
407
- data : {
408
- type : 'local' ,
409
- ...peerError ,
410
- } ,
411
- } ) ;
412
- }
413
- // Throw the default error if none of the above were true, this shouldn't really happen
414
- throw e ;
415
- } )
416
- . finally ( ( ) => {
417
- ctx . signal . removeEventListener ( 'abort' , abortHandler ) ;
418
- } ) ;
419
- this . logger . warn ( 'secured' ) ;
420
- // After this is done
421
- // We need to established the keep alive interval time
422
- if ( this . config . keepAliveIntervalTime != null ) {
423
- this . startKeepAliveIntervalTimer ( this . config . keepAliveIntervalTime ) ;
424
- }
425
- // Do we remove the on abort event listener?
426
- // I forgot...
427
458
this . logger . info ( `Started ${ this . constructor . name } ` ) ;
428
459
}
429
460
@@ -472,7 +503,8 @@ class QUICConnection extends EventTarget {
472
503
this . stopKeepAliveIntervalTimer ( ) ;
473
504
try {
474
505
mon = mon ?? new Monitor < RWLockWriter > ( this . lockbox , RWLockWriter ) ;
475
- await mon . withF ( this . lockCode , async ( mon ) => {
506
+ // Trigger closing connection in the background and await close later.
507
+ void mon . withF ( this . lockCode , async ( mon ) => {
476
508
// If this is already closed, then `Done` will be thrown
477
509
// Otherwise it can send `CONNECTION_CLOSE` frame
478
510
// This can be 0x1c close at the QUIC layer or no errors
@@ -481,10 +513,19 @@ class QUICConnection extends EventTarget {
481
513
// 1 packet containing a `CONNECTION_CLOSE` frame too
482
514
// (with `NO_ERROR` code if appropriate)
483
515
// It must enter into a draining state, and no other packets can be sent
484
- this . conn . close ( applicationError , errorCode , Buffer . from ( errorMessage ) ) ;
485
- // If we get a `Done` exception we don't bother calling send
486
- // The send only gets sent if the `Done` is not the case
487
- await this . send ( mon ) ;
516
+ try {
517
+ this . conn . close (
518
+ applicationError ,
519
+ errorCode ,
520
+ Buffer . from ( errorMessage ) ,
521
+ ) ;
522
+ // If we get a `Done` exception we don't bother calling send
523
+ // The send only gets sent if the `Done` is not the case
524
+ await this . send ( mon ) ;
525
+ } catch ( e ) {
526
+ // Ignore 'Done' if already closed
527
+ if ( e . message !== 'Done' ) throw e ;
528
+ }
488
529
} ) ;
489
530
} catch ( e ) {
490
531
// If the connection is already closed, `Done` will be thrown
@@ -507,9 +548,15 @@ class QUICConnection extends EventTarget {
507
548
this . socket . connectionMap . delete ( this . connectionId ) ;
508
549
509
550
if ( this . conn . isTimedOut ( ) ) {
551
+ const error = this . secured
552
+ ? new errors . ErrorQUICConnectionIdleTimeOut ( )
553
+ : new errors . ErrorQUICConnectionStartTimeOut ( ) ;
554
+
555
+ this . rejectEstablishedP ( error ) ;
556
+ this . rejectSecureEstablishedP ( error ) ;
510
557
this . dispatchEvent (
511
558
new events . QUICConnectionErrorEvent ( {
512
- detail : new errors . ErrorQUICConnectionIdleTimeOut ( ) ,
559
+ detail : error ,
513
560
} ) ,
514
561
) ;
515
562
}
@@ -521,14 +568,17 @@ class QUICConnection extends EventTarget {
521
568
peerError . reason ,
522
569
) . toString ( ) } (${ peerError . errorCode } )`;
523
570
this . logger . info ( message ) ;
571
+ const error = new errors . ErrorQUICConnectionInternal ( message , {
572
+ data : {
573
+ type : 'local' ,
574
+ ...peerError ,
575
+ } ,
576
+ } ) ;
577
+ this . rejectEstablishedP ( error ) ;
578
+ this . rejectSecureEstablishedP ( error ) ;
524
579
this . dispatchEvent (
525
580
new events . QUICConnectionErrorEvent ( {
526
- detail : new errors . ErrorQUICConnectionInternal ( message , {
527
- data : {
528
- type : 'local' ,
529
- ...peerError ,
530
- } ,
531
- } ) ,
581
+ detail : error ,
532
582
} ) ,
533
583
) ;
534
584
}
@@ -539,14 +589,17 @@ class QUICConnection extends EventTarget {
539
589
localError . reason ,
540
590
) . toString ( ) } (${ localError . errorCode } )`;
541
591
this . logger . info ( message ) ;
592
+ const error = new errors . ErrorQUICConnectionInternal ( message , {
593
+ data : {
594
+ type : 'local' ,
595
+ ...localError ,
596
+ } ,
597
+ } ) ;
598
+ this . rejectEstablishedP ( error ) ;
599
+ this . rejectSecureEstablishedP ( error ) ;
542
600
this . dispatchEvent (
543
601
new events . QUICConnectionErrorEvent ( {
544
- detail : new errors . ErrorQUICConnectionInternal ( message , {
545
- data : {
546
- type : 'local' ,
547
- ...localError ,
548
- } ,
549
- } ) ,
602
+ detail : error ,
550
603
} ) ,
551
604
) ;
552
605
}
@@ -864,7 +917,6 @@ class QUICConnection extends EventTarget {
864
917
// Then we just have to proceed!
865
918
// Plus if we are called here
866
919
this . resolveClosedP ( ) ;
867
-
868
920
await this . stop (
869
921
this . conn . localError ( ) ?? this . conn . peerError ( ) ?? { } ,
870
922
mon ,
0 commit comments