@@ -845,6 +845,10 @@ function requestOnConnect(headersList, options) {
845845 }
846846}
847847
848+ function requestOnError ( error ) {
849+ this . destroy ( error ) ;
850+ }
851+
848852// Validates that priority options are correct, specifically:
849853// 1. options.weight must be a number
850854// 2. options.parent must be a positive number
@@ -1160,7 +1164,7 @@ function setupHandle(socket, type, options) {
11601164 process . nextTick ( emit , this , 'connect' , this , socket ) ;
11611165}
11621166
1163- // Emits a close event followed by an error event if err is truthy. Used
1167+ // Emits an error event followed by a close event if err is truthy. Used
11641168// by Http2Session.prototype.destroy()
11651169function emitClose ( self , error ) {
11661170 if ( error )
@@ -1231,17 +1235,16 @@ function closeSession(session, code, error) {
12311235 session . setTimeout ( 0 ) ;
12321236 session . removeAllListeners ( 'timeout' ) ;
12331237
1238+ const socket = session [ kSocket ] ;
1239+ const handle = session [ kHandle ] ;
1240+
12341241 // Destroy any pending and open streams
12351242 if ( state . pendingStreams . size > 0 || state . streams . size > 0 ) {
12361243 const cancel = new ERR_HTTP2_STREAM_CANCEL ( error ) ;
12371244 state . pendingStreams . forEach ( ( stream ) => stream . destroy ( cancel ) ) ;
12381245 state . streams . forEach ( ( stream ) => stream . destroy ( error ) ) ;
12391246 }
12401247
1241- // Disassociate from the socket and server.
1242- const socket = session [ kSocket ] ;
1243- const handle = session [ kHandle ] ;
1244-
12451248 // Destroy the handle if it exists at this point.
12461249 if ( handle !== undefined ) {
12471250 handle . ondone = finishSessionClose . bind ( null , session , error ) ;
@@ -1816,11 +1819,15 @@ class ClientHttp2Session extends Http2Session {
18161819 request ( headersParam , options ) {
18171820 debugSessionObj ( this , 'initiating request' ) ;
18181821
1819- if ( this . destroyed )
1820- throw new ERR_HTTP2_INVALID_SESSION ( ) ;
1821-
1822- if ( this . closed )
1823- throw new ERR_HTTP2_GOAWAY_SESSION ( ) ;
1822+ // Keep argument validation synchronous, but defer session-state failures
1823+ // to the returned stream so request retries from stream callbacks do not
1824+ // throw before session lifecycle handlers run.
1825+ let requestError ;
1826+ if ( this . destroyed ) {
1827+ requestError = new ERR_HTTP2_INVALID_SESSION ( ) ;
1828+ } else if ( this . closed ) {
1829+ requestError = new ERR_HTTP2_GOAWAY_SESSION ( ) ;
1830+ }
18241831
18251832 this [ kUpdateTimer ] ( ) ;
18261833
@@ -1906,19 +1913,24 @@ class ClientHttp2Session extends Http2Session {
19061913 }
19071914 }
19081915
1909- const onConnect = reqAsync . bind ( requestOnConnect . bind ( stream , headersList , options ) ) ;
1910- if ( this . connecting ) {
1911- if ( this [ kPendingRequestCalls ] !== null ) {
1912- this [ kPendingRequestCalls ] . push ( onConnect ) ;
1916+ if ( requestError ) {
1917+ process . nextTick ( reqAsync . bind ( requestOnError . bind ( stream , requestError ) ) ) ;
1918+ } else {
1919+ const onConnect = reqAsync . bind (
1920+ requestOnConnect . bind ( stream , headersList , options ) ) ;
1921+ if ( this . connecting ) {
1922+ if ( this [ kPendingRequestCalls ] !== null ) {
1923+ this [ kPendingRequestCalls ] . push ( onConnect ) ;
1924+ } else {
1925+ this [ kPendingRequestCalls ] = [ onConnect ] ;
1926+ this . once ( 'connect' , ( ) => {
1927+ this [ kPendingRequestCalls ] . forEach ( ( f ) => f ( ) ) ;
1928+ this [ kPendingRequestCalls ] = null ;
1929+ } ) ;
1930+ }
19131931 } else {
1914- this [ kPendingRequestCalls ] = [ onConnect ] ;
1915- this . once ( 'connect' , ( ) => {
1916- this [ kPendingRequestCalls ] . forEach ( ( f ) => f ( ) ) ;
1917- this [ kPendingRequestCalls ] = null ;
1918- } ) ;
1932+ onConnect ( ) ;
19191933 }
1920- } else {
1921- onConnect ( ) ;
19221934 }
19231935
19241936 if ( onClientStreamCreatedChannel . hasSubscribers ) {
0 commit comments