@@ -18,6 +18,7 @@ const {
18
18
Number,
19
19
Promise,
20
20
PromiseAll,
21
+ PromiseReject,
21
22
RegExp,
22
23
Set,
23
24
Symbol,
@@ -98,7 +99,6 @@ const {
98
99
const {
99
100
codes : {
100
101
ERR_INVALID_ARG_TYPE ,
101
- ERR_INVALID_CALLBACK ,
102
102
ERR_INVALID_STATE ,
103
103
ERR_OPERATION_FAILED ,
104
104
ERR_QUIC_FAILED_TO_CREATE_SESSION ,
@@ -795,7 +795,7 @@ class QuicEndpoint {
795
795
796
796
[ kClose ] ( ) {
797
797
if ( this . destroyed ) {
798
- return Promise . reject (
798
+ return PromiseReject (
799
799
new ERR_INVALID_STATE ( 'QuicEndpoint is already destroyed' ) ) ;
800
800
}
801
801
const promise = deferredClosePromise ( this [ kInternalState ] ) ;
@@ -1392,7 +1392,7 @@ class QuicSocket extends EventEmitter {
1392
1392
1393
1393
[ kClose ] ( ) {
1394
1394
if ( this . destroyed ) {
1395
- return Promise . reject (
1395
+ return PromiseReject (
1396
1396
new ERR_INVALID_STATE ( 'QuicSocket is already destroyed' ) ) ;
1397
1397
}
1398
1398
const state = this [ kInternalState ] ;
@@ -1413,14 +1413,13 @@ class QuicSocket extends EventEmitter {
1413
1413
return promise ;
1414
1414
}
1415
1415
1416
- // Otherwise, loop through each of the known sessions
1417
- // and close them.
1418
- // TODO(@jasnell): These will be promises soon, but we
1419
- // do not want to await them.
1420
- for ( const session of state . sessions )
1421
- session . close ( ) ;
1422
-
1423
- return promise ;
1416
+ // Otherwise, loop through each of the known sessions and close them.
1417
+ const reqs = [ promise ] ;
1418
+ for ( const session of state . sessions ) {
1419
+ reqs . push ( session . close ( )
1420
+ . catch ( ( error ) => this . destroy ( error ) ) ) ;
1421
+ }
1422
+ return PromiseAll ( reqs ) ;
1424
1423
}
1425
1424
1426
1425
// Initiate an abrupt close and destruction of the QuicSocket.
@@ -1656,7 +1655,9 @@ class QuicSession extends EventEmitter {
1656
1655
cipherVersion : undefined ,
1657
1656
closeCode : NGTCP2_NO_ERROR ,
1658
1657
closeFamily : QUIC_ERROR_APPLICATION ,
1659
- closing : false ,
1658
+ closePromise : undefined ,
1659
+ closePromiseResolve : undefined ,
1660
+ closePromiseReject : undefined ,
1660
1661
destroyed : false ,
1661
1662
earlyData : false ,
1662
1663
handshakeComplete : false ,
@@ -1768,17 +1769,6 @@ class QuicSession extends EventEmitter {
1768
1769
this . destroy ( err ) ;
1769
1770
}
1770
1771
1771
- // Causes the QuicSession to be immediately destroyed, but with
1772
- // additional metadata set.
1773
- [ kDestroy ] ( code , family , silent , statelessReset ) {
1774
- const state = this [ kInternalState ] ;
1775
- state . closeCode = code ;
1776
- state . closeFamily = family ;
1777
- state . silentClose = silent ;
1778
- state . statelessReset = statelessReset ;
1779
- this . destroy ( ) ;
1780
- }
1781
-
1782
1772
// Closes the specified stream with the given code. The
1783
1773
// QuicStream object will be destroyed.
1784
1774
[ kStreamClose ] ( id , code ) {
@@ -1789,23 +1779,23 @@ class QuicSession extends EventEmitter {
1789
1779
stream . destroy ( ) ;
1790
1780
}
1791
1781
1792
- // Delivers a block of headers to the appropriate QuicStream
1793
- // instance. This will only be called if the ALPN selected
1794
- // is known to support headers.
1795
- [ kHeaders ] ( id , headers , kind , push_id ) {
1782
+ [ kStreamReset ] ( id , code ) {
1796
1783
const stream = this [ kInternalState ] . streams . get ( id ) ;
1797
1784
if ( stream === undefined )
1798
1785
return ;
1799
1786
1800
- stream [ kHeaders ] ( headers , kind , push_id ) ;
1787
+ stream [ kStreamReset ] ( code ) ;
1801
1788
}
1802
1789
1803
- [ kStreamReset ] ( id , code ) {
1790
+ // Delivers a block of headers to the appropriate QuicStream
1791
+ // instance. This will only be called if the ALPN selected
1792
+ // is known to support headers.
1793
+ [ kHeaders ] ( id , headers , kind , push_id ) {
1804
1794
const stream = this [ kInternalState ] . streams . get ( id ) ;
1805
1795
if ( stream === undefined )
1806
1796
return ;
1807
1797
1808
- stream [ kStreamReset ] ( code ) ;
1798
+ stream [ kHeaders ] ( headers , kind , push_id ) ;
1809
1799
}
1810
1800
1811
1801
[ kInspect ] ( depth , options ) {
@@ -1850,8 +1840,13 @@ class QuicSession extends EventEmitter {
1850
1840
if ( ! this [ kHandshakePost ] ( ) )
1851
1841
return ;
1852
1842
1853
- process . nextTick (
1854
- emit . bind ( this , 'secure' , servername , alpn , this . cipher ) ) ;
1843
+ process . nextTick ( ( ) => {
1844
+ try {
1845
+ this . emit ( 'secure' , servername , alpn , this . cipher ) ;
1846
+ } catch ( error ) {
1847
+ this . destroy ( error ) ;
1848
+ }
1849
+ } ) ;
1855
1850
}
1856
1851
1857
1852
// Non-op for the default case. QuicClientSession
@@ -1863,10 +1858,10 @@ class QuicSession extends EventEmitter {
1863
1858
1864
1859
[ kRemoveStream ] ( stream ) {
1865
1860
this [ kInternalState ] . streams . delete ( stream . id ) ;
1861
+ this [ kMaybeDestroy ] ( ) ;
1866
1862
}
1867
1863
1868
1864
[ kAddStream ] ( id , stream ) {
1869
- stream . once ( 'close' , this [ kMaybeDestroy ] . bind ( this ) ) ;
1870
1865
this [ kInternalState ] . streams . set ( id , stream ) ;
1871
1866
}
1872
1867
@@ -1875,49 +1870,55 @@ class QuicSession extends EventEmitter {
1875
1870
// informationational notification. It is not called on
1876
1871
// server QuicSession instances.
1877
1872
[ kUsePreferredAddress ] ( address ) {
1878
- process . nextTick (
1879
- emit . bind ( this , 'usePreferredAddress' , address ) ) ;
1873
+ process . nextTick ( ( ) => {
1874
+ try {
1875
+ this . emit ( 'usePreferredAddress' , address ) ;
1876
+ } catch ( error ) {
1877
+ this . destroy ( error ) ;
1878
+ }
1879
+ } ) ;
1880
+ }
1881
+
1882
+ close ( ) {
1883
+ return this [ kInternalState ] . closePromise || this [ kClose ] ( ) ;
1884
+ }
1885
+
1886
+ [ kClose ] ( ) {
1887
+ if ( this . destroyed ) {
1888
+ return PromiseReject (
1889
+ new ERR_INVALID_STATE ( 'QuicSession is already destroyed' ) ) ;
1890
+ }
1891
+ const promise = deferredClosePromise ( this [ kInternalState ] ) ;
1892
+ if ( ! this [ kMaybeDestroy ] ( ) ) {
1893
+ this [ kHandle ] . gracefulClose ( ) ;
1894
+ }
1895
+ return promise ;
1896
+ }
1897
+
1898
+ get closing ( ) {
1899
+ return this [ kInternalState ] . closePromise !== undefined ;
1880
1900
}
1881
1901
1882
1902
// The QuicSession will be destroyed if close() has been
1883
1903
// called and there are no remaining streams
1884
1904
[ kMaybeDestroy ] ( ) {
1885
1905
const state = this [ kInternalState ] ;
1886
- if ( state . closing && state . streams . size === 0 ) {
1906
+ if ( this . closing && state . streams . size === 0 ) {
1887
1907
this . destroy ( ) ;
1888
1908
return true ;
1889
1909
}
1890
1910
return false ;
1891
1911
}
1892
1912
1893
- // Closing allows any existing QuicStream's to gracefully
1894
- // complete while disallowing any new QuicStreams from being
1895
- // opened (in either direction). Calls to openStream() will
1896
- // fail, and new streams from the peer will be rejected/ignored.
1897
- close ( callback ) {
1913
+ // Causes the QuicSession to be immediately destroyed, but with
1914
+ // additional metadata set.
1915
+ [ kDestroy ] ( code , family , silent , statelessReset ) {
1898
1916
const state = this [ kInternalState ] ;
1899
- if ( state . destroyed ) {
1900
- throw new ERR_INVALID_STATE (
1901
- `${ this . constructor . name } is already destroyed` ) ;
1902
- }
1903
-
1904
- if ( callback ) {
1905
- if ( typeof callback !== 'function' )
1906
- throw new ERR_INVALID_CALLBACK ( ) ;
1907
- this . once ( 'close' , callback ) ;
1908
- }
1909
-
1910
- // If we're already closing, do nothing else.
1911
- // Callback will be invoked once the session
1912
- // has been destroyed
1913
- if ( state . closing )
1914
- return ;
1915
- state . closing = true ;
1916
-
1917
- // If there are no active streams, we can close immediately,
1918
- // otherwise set the graceful close flag.
1919
- if ( ! this [ kMaybeDestroy ] ( ) )
1920
- this [ kHandle ] . gracefulClose ( ) ;
1917
+ state . closeCode = code ;
1918
+ state . closeFamily = family ;
1919
+ state . silentClose = silent ;
1920
+ state . statelessReset = statelessReset ;
1921
+ this . destroy ( ) ;
1921
1922
}
1922
1923
1923
1924
// Destroying synchronously shuts down and frees the
@@ -1939,7 +1940,6 @@ class QuicSession extends EventEmitter {
1939
1940
if ( state . destroyed )
1940
1941
return ;
1941
1942
state . destroyed = true ;
1942
- state . closing = false ;
1943
1943
1944
1944
// Destroy any pending streams immediately. These
1945
1945
// are streams that have been created but have not
@@ -1974,7 +1974,13 @@ class QuicSession extends EventEmitter {
1974
1974
1975
1975
// If we are destroying with an error, schedule the
1976
1976
// error to be emitted on process.nextTick.
1977
- if ( error ) process . nextTick ( emit . bind ( this , 'error' , error ) ) ;
1977
+ if ( error ) {
1978
+ if ( typeof state . closePromiseReject === 'function' )
1979
+ state . closePromiseReject ( error ) ;
1980
+ process . nextTick ( emit . bind ( this , 'error' , error ) ) ;
1981
+ } else if ( typeof state . closePromiseResolve === 'function' )
1982
+ state . closePromiseResolve ( ) ;
1983
+
1978
1984
process . nextTick ( emit . bind ( this , 'close' ) ) ;
1979
1985
}
1980
1986
@@ -2100,10 +2106,6 @@ class QuicSession extends EventEmitter {
2100
2106
return this [ kInternalState ] . destroyed ;
2101
2107
}
2102
2108
2103
- get closing ( ) {
2104
- return this [ kInternalState ] . closing ;
2105
- }
2106
-
2107
2109
get closeCode ( ) {
2108
2110
const state = this [ kInternalState ] ;
2109
2111
return {
@@ -2123,11 +2125,11 @@ class QuicSession extends EventEmitter {
2123
2125
2124
2126
openStream ( options ) {
2125
2127
const state = this [ kInternalState ] ;
2126
- if ( state . destroyed ) {
2128
+ if ( this . destroyed ) {
2127
2129
throw new ERR_INVALID_STATE (
2128
2130
`${ this . constructor . name } is already destroyed` ) ;
2129
2131
}
2130
- if ( state . closing ) {
2132
+ if ( this . closing ) {
2131
2133
throw new ERR_INVALID_STATE (
2132
2134
`${ this . constructor . name } is closing` ) ;
2133
2135
}
@@ -2255,11 +2257,11 @@ class QuicSession extends EventEmitter {
2255
2257
updateKey ( ) {
2256
2258
const state = this [ kInternalState ] ;
2257
2259
// Initiates a key update for the connection.
2258
- if ( state . destroyed ) {
2260
+ if ( this . destroyed ) {
2259
2261
throw new ERR_INVALID_STATE (
2260
2262
`${ this . constructor . name } is already destroyed` ) ;
2261
2263
}
2262
- if ( state . closing ) {
2264
+ if ( this . closing ) {
2263
2265
throw new ERR_INVALID_STATE (
2264
2266
`${ this . constructor . name } is closing` ) ;
2265
2267
}
@@ -2282,7 +2284,6 @@ class QuicSession extends EventEmitter {
2282
2284
return this [ kHandle ] . removeFromSocket ( ) ;
2283
2285
}
2284
2286
}
2285
-
2286
2287
class QuicServerSession extends QuicSession {
2287
2288
[ kInternalServerState ] = {
2288
2289
contexts : [ ]
@@ -2914,7 +2915,6 @@ class QuicStream extends Duplex {
2914
2915
2915
2916
_destroy ( error , callback ) {
2916
2917
const state = this [ kInternalState ] ;
2917
- state . session [ kRemoveStream ] ( this ) ;
2918
2918
const handle = this [ kHandle ] ;
2919
2919
// Do not use handle after this point as the underlying C++
2920
2920
// object has been destroyed. Any attempt to use the object
@@ -2925,6 +2925,7 @@ class QuicStream extends Duplex {
2925
2925
state . stats = new BigInt64Array ( handle . stats ) ;
2926
2926
handle . destroy ( ) ;
2927
2927
}
2928
+ state . session [ kRemoveStream ] ( this ) ;
2928
2929
// The destroy callback must be invoked in a nextTick
2929
2930
process . nextTick ( ( ) => callback ( error ) ) ;
2930
2931
}
0 commit comments