@@ -24,7 +24,9 @@ import {
24
24
import Logger from '@matrixai/logger' ;
25
25
import { Timer } from '@matrixai/timer' ;
26
26
import { context , timedCancellable } from '@matrixai/contexts/dist/decorators' ;
27
- import { buildQuicheConfig } from './config' ;
27
+ import { withF } from '@matrixai/resources' ;
28
+ import { utils as contextsUtils } from '@matrixai/contexts' ;
29
+ import { buildQuicheConfig , minIdleTimeout } from './config' ;
28
30
import QUICStream from './QUICStream' ;
29
31
import { quiche } from './native' ;
30
32
import * as events from './events' ;
@@ -235,7 +237,11 @@ class QUICConnection extends EventTarget {
235
237
} ,
236
238
ctx ?: Partial < ContextTimedInput > ,
237
239
) : PromiseCancellable < QUICConnection > ;
238
- @timedCancellable ( true , Infinity , errors . ErrorQUICConnectionStartTimeOut )
240
+ @timedCancellable (
241
+ true ,
242
+ minIdleTimeout ,
243
+ errors . ErrorQUICConnectionStartTimeOut ,
244
+ )
239
245
public static async createQUICConnection (
240
246
args :
241
247
| {
@@ -265,40 +271,20 @@ class QUICConnection extends EventTarget {
265
271
} ,
266
272
@context ctx : ContextTimed ,
267
273
) : Promise < QUICConnection > {
268
- const timeoutTime = ctx . timer . getTimeout ( ) ;
269
- if ( timeoutTime !== Infinity && timeoutTime >= args . config . maxIdleTimeout ) {
270
- throw new errors . ErrorQUICConnectionInvalidConfig (
271
- 'connection timeout timer must be strictly less than maxIdleTimeout' ,
272
- ) ;
273
- }
274
274
ctx . signal . throwIfAborted ( ) ;
275
275
const abortProm = promise < never > ( ) ;
276
276
const abortHandler = ( ) => {
277
277
abortProm . rejectP ( ctx . signal . reason ) ;
278
278
} ;
279
279
ctx . signal . addEventListener ( 'abort' , abortHandler ) ;
280
280
const connection = new this ( args ) ;
281
- const startProm = connection . start ( ) . then ( async ( ) => {
282
- // If this is a server connection, we need to process the packet that created it
283
- if ( args . type === 'server' ) {
284
- await utils . withMonitor (
285
- undefined ,
286
- connection . lockbox ,
287
- RWLockWriter ,
288
- async ( mon ) => {
289
- await mon . withF ( connection . lockCode , async ( mon ) => {
290
- await connection . recv ( args . data , args . remoteInfo , mon ) ;
291
- await connection . send ( mon ) ;
292
- } ) ;
293
- } ,
294
- ) ;
295
- }
296
- } ) ;
281
+ // If it's a server connection we want to pass the initial packet
282
+ const data = args . type === 'server' ? args . data : undefined ;
297
283
// This ensures that TLS has been established and verified on both sides
298
284
try {
299
285
await Promise . race ( [
300
286
Promise . all ( [
301
- startProm ,
287
+ connection . start ( data ) ,
302
288
connection . establishedP ,
303
289
connection . secureEstablishedP ,
304
290
] ) ,
@@ -471,12 +457,25 @@ class QUICConnection extends EventTarget {
471
457
472
458
/**
473
459
* This will set up the connection initiate sending
460
+ * @param data - the initial packet that triggered the creation of the connection.
474
461
*/
475
- public async start ( ) : Promise < void > {
462
+ public async start ( data ?: Uint8Array ) : Promise < void > {
476
463
this . logger . info ( `Start ${ this . constructor . name } ` ) ;
477
464
// Set the connection up
478
465
this . socket . connectionMap . set ( this . connectionId , this ) ;
479
- await this . send ( ) ;
466
+ await withF (
467
+ [ contextsUtils . monitor ( this . lockbox , RWLockWriter ) ] ,
468
+ async ( [ mon ] ) => {
469
+ if ( data != null ) {
470
+ await this . recv (
471
+ data ,
472
+ { host : this . _remoteHost , port : this . _remotePort } ,
473
+ mon ,
474
+ ) ;
475
+ }
476
+ await this . send ( mon ) ;
477
+ } ,
478
+ ) ;
480
479
this . logger . info ( `Started ${ this . constructor . name } ` ) ;
481
480
}
482
481
@@ -535,7 +534,7 @@ class QUICConnection extends EventTarget {
535
534
this . stopKeepAliveIntervalTimer ( ) ;
536
535
537
536
// Trigger closing connection in the background and await close later.
538
- void utils . withMonitor ( mon , this . lockbox , RWLockWriter , async ( mon ) => {
537
+ void this . withMonitor ( mon , this . lockbox , RWLockWriter , async ( mon ) => {
539
538
await mon . withF ( this . lockCode , async ( mon ) => {
540
539
// If this is already closed, then `Done` will be thrown
541
540
// Otherwise it can send `CONNECTION_CLOSE` frame
@@ -665,13 +664,15 @@ class QUICConnection extends EventTarget {
665
664
public async recv (
666
665
data : Uint8Array ,
667
666
remoteInfo : RemoteInfo ,
668
- mon : Monitor < RWLockWriter > ,
667
+ mon ? : Monitor < RWLockWriter > ,
669
668
) : Promise < void > {
670
- if ( ! mon . isLocked ( this . lockCode ) ) {
671
- return mon . withF ( this . lockCode , async ( mon ) => {
672
- return this . recv ( data , remoteInfo , mon ) ;
673
- } ) ;
674
- }
669
+ await this . withMonitor ( mon , this . lockbox , RWLockWriter , async ( mon ) => {
670
+ if ( ! mon . isLocked ( this . lockCode ) ) {
671
+ return mon . withF ( this . lockCode , async ( mon ) => {
672
+ return this . recv ( data , remoteInfo , mon ) ;
673
+ } ) ;
674
+ }
675
+ } ) ;
675
676
676
677
try {
677
678
// The remote information may be changed on each receive
@@ -775,7 +776,7 @@ class QUICConnection extends EventTarget {
775
776
* @internal
776
777
*/
777
778
public async send ( mon ?: Monitor < RWLockWriter > ) : Promise < void > {
778
- await utils . withMonitor ( mon , this . lockbox , RWLockWriter , async ( mon ) => {
779
+ await this . withMonitor ( mon , this . lockbox , RWLockWriter , async ( mon ) => {
779
780
if ( ! mon . isLocked ( this . lockCode ) ) {
780
781
return mon . withF ( this . lockCode , async ( mon ) => {
781
782
return this . send ( mon ) ;
@@ -1083,6 +1084,26 @@ class QUICConnection extends EventTarget {
1083
1084
return quicStream ;
1084
1085
} ) ;
1085
1086
}
1087
+
1088
+ /**
1089
+ * Used as a clean way to create a new monitor if it doesn't exist, otherwise uses the existing one.
1090
+ */
1091
+ protected async withMonitor < T > (
1092
+ mon : Monitor < RWLockWriter > | undefined ,
1093
+ lockBox : LockBox < RWLockWriter > ,
1094
+ lockConstructor : { new ( ) : RWLockWriter } ,
1095
+ f : ( mon : Monitor < RWLockWriter > ) => Promise < T > ,
1096
+ locksPending ?: Map < string , { count : number } > ,
1097
+ ) : Promise < T > {
1098
+ if ( mon == null ) {
1099
+ return await withF (
1100
+ [ contextsUtils . monitor ( lockBox , lockConstructor , locksPending ) ] ,
1101
+ ( [ mon ] ) => f ( mon ) ,
1102
+ ) ;
1103
+ } else {
1104
+ return f ( mon ) ;
1105
+ }
1106
+ }
1086
1107
}
1087
1108
1088
1109
export default QUICConnection ;
0 commit comments