@@ -252,8 +252,7 @@ const kRejections = Symbol.for('nodejs.rejection');
252
252
const kSocketUnbound = 0 ;
253
253
const kSocketPending = 1 ;
254
254
const kSocketBound = 2 ;
255
- const kSocketClosing = 3 ;
256
- const kSocketDestroyed = 4 ;
255
+ const kSocketDestroyed = 3 ;
257
256
258
257
let diagnosticPacketLossWarned = false ;
259
258
let warnedVerifyHostnameIdentity = false ;
@@ -939,6 +938,9 @@ class QuicSocket extends EventEmitter {
939
938
alpn : undefined ,
940
939
bindPromise : undefined ,
941
940
client : undefined ,
941
+ closePromise : undefined ,
942
+ closePromiseResolve : undefined ,
943
+ closePromiseReject : undefined ,
942
944
defaultEncoding : undefined ,
943
945
endpoints : new Set ( ) ,
944
946
highWaterMark : undefined ,
@@ -1089,8 +1091,10 @@ class QuicSocket extends EventEmitter {
1089
1091
}
1090
1092
1091
1093
[ kRemoveSession ] ( session ) {
1092
- this [ kInternalState ] . sessions . delete ( session ) ;
1093
- this [ kMaybeDestroy ] ( ) ;
1094
+ const state = this [ kInternalState ] ;
1095
+ state . sessions . delete ( session ) ;
1096
+ if ( this . closing && state . sessions . size === 0 )
1097
+ this . destroy ( ) ;
1094
1098
}
1095
1099
1096
1100
[ kMaybeBind ] ( options ) {
@@ -1191,37 +1195,6 @@ class QuicSocket extends EventEmitter {
1191
1195
} ) ;
1192
1196
}
1193
1197
1194
- [ kEndpointClose ] ( endpoint , error ) {
1195
- const state = this [ kInternalState ] ;
1196
- state . endpoints . delete ( endpoint ) ;
1197
- process . nextTick ( ( ) => {
1198
- try {
1199
- this . emit ( 'endpointClose' , endpoint , error ) ;
1200
- } catch ( error ) {
1201
- this . destroy ( error ) ;
1202
- }
1203
- } ) ;
1204
-
1205
- // If there aren't any more endpoints, the QuicSession
1206
- // is no longer usable and needs to be destroyed.
1207
- if ( state . endpoints . size === 0 ) {
1208
- if ( ! this . destroyed )
1209
- return this . destroy ( error ) ;
1210
- this [ kDestroy ] ( error ) ;
1211
- }
1212
- }
1213
-
1214
- // kMaybeDestroy is called one or more times after the close() method
1215
- // is called. The QuicSocket will be destroyed if there are no remaining
1216
- // open sessions.
1217
- [ kMaybeDestroy ] ( ) {
1218
- if ( this . closing && this [ kInternalState ] . sessions . size === 0 ) {
1219
- this . destroy ( ) ;
1220
- return true ;
1221
- }
1222
- return false ;
1223
- }
1224
-
1225
1198
// Called by the C++ internals to notify when server busy status is toggled.
1226
1199
[ kServerBusy ] ( ) {
1227
1200
const busy = this . serverBusy ;
@@ -1419,6 +1392,26 @@ class QuicSocket extends EventEmitter {
1419
1392
return session ;
1420
1393
}
1421
1394
1395
+ [ kEndpointClose ] ( endpoint , error ) {
1396
+ const state = this [ kInternalState ] ;
1397
+ state . endpoints . delete ( endpoint ) ;
1398
+ process . nextTick ( ( ) => {
1399
+ try {
1400
+ this . emit ( 'endpointClose' , endpoint , error ) ;
1401
+ } catch ( error ) {
1402
+ this . destroy ( error ) ;
1403
+ }
1404
+ } ) ;
1405
+
1406
+ // If there aren't any more endpoints, the QuicSession
1407
+ // is no longer usable and needs to be destroyed.
1408
+ if ( state . endpoints . size === 0 ) {
1409
+ if ( ! this . destroyed )
1410
+ return this . destroy ( error ) ;
1411
+ this [ kDestroy ] ( error ) ;
1412
+ }
1413
+ }
1414
+
1422
1415
// Initiate a Graceful Close of the QuicSocket.
1423
1416
// Existing QuicClientSession and QuicServerSession instances will be
1424
1417
// permitted to close naturally and gracefully on their own.
@@ -1427,80 +1420,57 @@ class QuicSocket extends EventEmitter {
1427
1420
// QuicClientSession or QuicServerSession instances, the QuicSocket
1428
1421
// will be immediately closed.
1429
1422
//
1430
- // If specified, the callback will be registered for once('close').
1423
+ // Returns a Promise that will be resolved once the QuicSocket is
1424
+ // destroyed.
1431
1425
//
1432
1426
// No additional QuicServerSession instances will be accepted from
1433
1427
// remote peers, and calls to connect() to create QuicClientSession
1434
1428
// instances will fail. The QuicSocket will be otherwise usable in
1435
1429
// every other way.
1436
1430
//
1437
- // Subsequent calls to close(callback) will register the close callback
1438
- // if one is defined but will otherwise do nothing.
1439
- //
1440
1431
// Once initiated, a graceful close cannot be canceled. The graceful
1441
1432
// close can be interupted, however, by abruptly destroying the
1442
1433
// QuicSocket using the destroy() method.
1443
1434
//
1444
1435
// If close() is called before the QuicSocket has been bound (before
1445
1436
// either connect() or listen() have been called, or the QuicSocket
1446
- // is still in the pending state, the callback is registered for the
1447
- // once('close') event (if specified) and the QuicSocket is destroyed
1437
+ // is still in the pending state, the QuicSocket is destroyed
1448
1438
// immediately.
1449
- close ( callback ) {
1450
- const state = this [ kInternalState ] ;
1451
- if ( this . destroyed )
1452
- throw new ERR_INVALID_STATE ( 'QuicSocket is already destroyed' ) ;
1453
-
1454
- // If a callback function is specified, it is registered as a
1455
- // handler for the once('close') event. If the close occurs
1456
- // immediately, the close event will be emitted as soon as the
1457
- // process.nextTick queue is processed. Otherwise, the close
1458
- // event will occur at some unspecified time in the near future.
1459
- if ( callback ) {
1460
- if ( typeof callback !== 'function' )
1461
- throw new ERR_INVALID_CALLBACK ( ) ;
1462
- this . once ( 'close' , callback ) ;
1463
- }
1464
-
1465
- // If we are already closing, do nothing else and wait
1466
- // for the close event to be invoked.
1467
- if ( state . state === kSocketClosing )
1468
- return ;
1439
+ close ( ) {
1440
+ return this [ kInternalState ] . closePromise || this [ kClose ] ( ) ;
1441
+ }
1469
1442
1470
- // If the QuicSocket is otherwise not bound to the local
1471
- // port, destroy the QuicSocket immediately.
1472
- if ( state . state !== kSocketBound ) {
1473
- this . destroy ( ) ;
1443
+ [ kClose ] ( ) {
1444
+ if ( this . destroyed ) {
1445
+ return Promise . reject (
1446
+ new ERR_INVALID_STATE ( 'QuicSocket is already destroyed' ) ) ;
1474
1447
}
1475
-
1476
- // Mark the QuicSocket as closing to prevent re-entry
1477
- state . state = kSocketClosing ;
1478
-
1479
- // Otherwise, gracefully close each QuicSession, with
1480
- // [kMaybeDestroy]() being called after each closes.
1481
- const maybeDestroy = this [ kMaybeDestroy ] . bind ( this ) ;
1448
+ const state = this [ kInternalState ] ;
1449
+ const promise = deferredClosePromise ( state ) ;
1482
1450
1483
1451
// Tell the underlying QuicSocket C++ object to stop
1484
1452
// listening for new QuicServerSession connections.
1485
1453
// New initial connection packets for currently unknown
1486
1454
// DCID's will be ignored.
1487
1455
if ( this [ kHandle ] )
1488
- this [ kInternalState ] . sharedState . serverListening = false ;
1456
+ state . sharedState . serverListening = false ;
1489
1457
1490
- // If there are no sessions, calling maybeDestroy
1491
- // will immediately and synchronously destroy the
1492
- // QuicSocket.
1493
- if ( maybeDestroy ( ) )
1494
- return ;
1458
+ // If the QuicSocket is otherwise not bound to the local
1459
+ // port, or there are not active sessions, destroy the
1460
+ // QuicSocket immediately and we're done.
1461
+ if ( state . state !== kSocketBound || state . sessions . size === 0 ) {
1462
+ this . destroy ( ) ;
1463
+ return promise ;
1464
+ }
1495
1465
1496
- // If we got this far, there a QuicClientSession and
1497
- // QuicServerSession instances still, we need to trigger
1498
- // a graceful close for each of them. As each closes,
1499
- // they will call the kMaybeDestroy function. When there
1500
- // are no remaining session instances, the QuicSocket
1501
- // will be closed and destroyed.
1466
+ // Otherwise, loop through each of the known sessions
1467
+ // and close them.
1468
+ // TODO(@jasnell): These will be promises soon, but we
1469
+ // do not want to await them.
1502
1470
for ( const session of state . sessions )
1503
- session . close ( maybeDestroy ) ;
1471
+ session . close ( ) ;
1472
+
1473
+ return promise ;
1504
1474
}
1505
1475
1506
1476
// Initiate an abrupt close and destruction of the QuicSocket.
@@ -1546,7 +1516,14 @@ class QuicSocket extends EventEmitter {
1546
1516
}
1547
1517
1548
1518
[ kDestroy ] ( error ) {
1549
- if ( error ) process . nextTick ( emit . bind ( this , 'error' , error ) ) ;
1519
+ const state = this [ kInternalState ] ;
1520
+ if ( error ) {
1521
+ if ( typeof state . closePromiseReject === 'function' )
1522
+ state . closePromiseReject ( error ) ;
1523
+ process . nextTick ( emit . bind ( this , 'error' , error ) ) ;
1524
+ } else if ( typeof state . closePromiseResolve === 'function' ) {
1525
+ state . closePromiseResolve ( ) ;
1526
+ }
1550
1527
process . nextTick ( emit . bind ( this , 'close' ) ) ;
1551
1528
}
1552
1529
@@ -1587,7 +1564,7 @@ class QuicSocket extends EventEmitter {
1587
1564
1588
1565
// True if graceful close has been initiated by calling close()
1589
1566
get closing ( ) {
1590
- return this [ kInternalState ] . state === kSocketClosing ;
1567
+ return this [ kInternalState ] . closePromise !== undefined ;
1591
1568
}
1592
1569
1593
1570
// True if the QuicSocket has been destroyed and is no longer usable
0 commit comments