| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 5a95a2b commit 39f61fb
4 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1110,10 +1110,12 @@ creates and returns an `Http2Stream` instance that can be used to send an | |||
| 1110 | 1110 | HTTP/2 request to the connected server. | |
| 1111 | 1111 | ||
| 1112 | 1112 | When a `ClientHttp2Session` is first created, the socket may not yet be | |
| 1113 | - connected. if `clienthttp2session.request()` is called during this time, the | ||
| 1113 | + connected. If `clienthttp2session.request()` is called during this time, the | ||
| 1114 | 1114 | actual request will be deferred until the socket is ready to go. | |
| 1115 | - If the `session` is closed before the actual request be executed, an | ||
| 1116 | - `ERR_HTTP2_GOAWAY_SESSION` is thrown. | ||
| 1115 | + | ||
| 1116 | + If the session becomes unavailable before the request can be created, the | ||
| 1117 | + returned stream will emit `ERR_HTTP2_GOAWAY_SESSION` or | ||
| 1118 | + `ERR_HTTP2_INVALID_SESSION` asynchronously. | ||
| 1117 | 1119 | ||
| 1118 | 1120 | This method is only available if `http2session.type` is equal to | |
| 1119 | 1121 | `http2.constants.NGHTTP2_SESSION_CLIENT`. | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -845,6 +845,10 @@ function requestOnConnect(headersList, options) { | |||
| 845 | 845 | } | |
| 846 | 846 | } | |
| 847 | 847 | ||
| 848 | + function requestOnError(error) { | ||
| 849 | + this.destroy(error); | ||
| 850 | + } | ||
| 851 | + | ||
| 848 | 852 | // Validates that priority options are correct, specifically: | |
| 849 | 853 | // 1. options.weight must be a number | |
| 850 | 854 | // 2. options.parent must be a positive number | |
@@ -1160,7 +1164,7 @@ function setupHandle(socket, type, options) { | |||
| 1160 | 1164 | process.nextTick(emit, this, 'connect', this, socket); | |
| 1161 | 1165 | } | |
| 1162 | 1166 | ||
| 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 | ||
| 1164 | 1168 | // by Http2Session.prototype.destroy() | |
| 1165 | 1169 | function emitClose(self, error) { | |
| 1166 | 1170 | if (error) | |
@@ -1231,17 +1235,16 @@ function closeSession(session, code, error) { | |||
| 1231 | 1235 | session.setTimeout(0); | |
| 1232 | 1236 | session.removeAllListeners('timeout'); | |
| 1233 | 1237 | ||
| 1238 | + const socket = session[kSocket]; | ||
| 1239 | + const handle = session[kHandle]; | ||
| 1240 | + | ||
| 1234 | 1241 | // Destroy any pending and open streams | |
| 1235 | 1242 | if (state.pendingStreams.size > 0 || state.streams.size > 0) { | |
| 1236 | 1243 | const cancel = new ERR_HTTP2_STREAM_CANCEL(error); | |
| 1237 | 1244 | state.pendingStreams.forEach((stream) => stream.destroy(cancel)); | |
| 1238 | 1245 | state.streams.forEach((stream) => stream.destroy(error)); | |
| 1239 | 1246 | } | |
| 1240 | 1247 | ||
| 1241 | - // Disassociate from the socket and server. | ||
| 1242 | - const socket = session[kSocket]; | ||
| 1243 | - const handle = session[kHandle]; | ||
| 1244 | - | ||
| 1245 | 1248 | // Destroy the handle if it exists at this point. | |
| 1246 | 1249 | if (handle !== undefined) { | |
| 1247 | 1250 | handle.ondone = finishSessionClose.bind(null, session, error); | |
@@ -1816,11 +1819,15 @@ class ClientHttp2Session extends Http2Session { | |||
| 1816 | 1819 | request(headersParam, options) { | |
| 1817 | 1820 | debugSessionObj(this, 'initiating request'); | |
| 1818 | 1821 | ||
| 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 | + } | ||
| 1824 | 1831 | ||
| 1825 | 1832 | this[kUpdateTimer](); | |
| 1826 | 1833 | ||
@@ -1906,19 +1913,24 @@ class ClientHttp2Session extends Http2Session { | |||
| 1906 | 1913 | } | |
| 1907 | 1914 | } | |
| 1908 | 1915 | ||
| 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 | + } | ||
| 1913 | 1931 | } else { | |
| 1914 | - this[kPendingRequestCalls] = [onConnect]; | ||
| 1915 | - this.once('connect', () => { | ||
| 1916 | - this[kPendingRequestCalls].forEach((f) => f()); | ||
| 1917 | - this[kPendingRequestCalls] = null; | ||
| 1918 | - }); | ||
| 1932 | + onConnect(); | ||
| 1919 | 1933 | } | |
| 1920 | - } else { | ||
| 1921 | - onConnect(); | ||
| 1922 | 1934 | } | |
| 1923 | 1935 | ||
| 1924 | 1936 | if (onClientStreamCreatedChannel.hasSubscribers) { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -81,7 +81,19 @@ const { listenerCount } = require('events'); | |||
| 81 | 81 | assert.throws(() => client.ping(), sessionError); | |
| 82 | 82 | assert.throws(() => client.settings({}), sessionError); | |
| 83 | 83 | assert.throws(() => client.goaway(), sessionError); | |
| 84 | - assert.throws(() => client.request(), sessionError); | ||
| 84 | + | ||
| 85 | + const pendingReq = client.request(); | ||
| 86 | + pendingReq.on('response', common.mustNotCall()); | ||
| 87 | + pendingReq.on('error', common.expectsError(sessionError)); | ||
| 88 | + pendingReq.on('close', common.mustCall()); | ||
| 89 | + | ||
| 90 | + client.on('close', common.mustCall(() => { | ||
| 91 | + const postCloseReq = client.request(); | ||
| 92 | + postCloseReq.on('response', common.mustNotCall()); | ||
| 93 | + postCloseReq.on('error', common.expectsError(sessionError)); | ||
| 94 | + postCloseReq.on('close', common.mustCall()); | ||
| 95 | + })); | ||
| 96 | + | ||
| 85 | 97 | client.close(); // Should be a non-op at this point | |
| 86 | 98 | ||
| 87 | 99 | // Wait for setImmediate call from destroy() to complete | |
@@ -92,7 +104,6 @@ const { listenerCount } = require('events'); | |||
| 92 | 104 | assert.throws(() => client.ping(), sessionError); | |
| 93 | 105 | assert.throws(() => client.settings({}), sessionError); | |
| 94 | 106 | assert.throws(() => client.goaway(), sessionError); | |
| 95 | - assert.throws(() => client.request(), sessionError); | ||
| 96 | 107 | client.close(); // Should be a non-op at this point | |
| 97 | 108 | })); | |
| 98 | 109 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,59 @@ | |||
| 1 | + 'use strict'; | ||
| 2 | + | ||
| 3 | + const common = require('../common'); | ||
| 4 | + if (!common.hasCrypto) | ||
| 5 | + common.skip('missing crypto'); | ||
| 6 | + | ||
| 7 | + const assert = require('assert'); | ||
| 8 | + const http2 = require('http2'); | ||
| 9 | + | ||
| 10 | + const server = http2.createServer(); | ||
| 11 | + let serverSocket; | ||
| 12 | + | ||
| 13 | + server.on('connection', common.mustCall((socket) => { | ||
| 14 | + serverSocket = socket; | ||
| 15 | + socket.on('error', () => {}); | ||
| 16 | + })); | ||
| 17 | + | ||
| 18 | + server.on('sessionError', () => {}); | ||
| 19 | + server.on('stream', common.mustCall((stream, headers) => { | ||
| 20 | + if (headers[':path'] === '/close') { | ||
| 21 | + stream.respond({ ':status': 200 }); | ||
| 22 | + stream.write('partial', common.mustCall(() => { | ||
| 23 | + setImmediate(() => serverSocket.destroy()); | ||
| 24 | + })); | ||
| 25 | + return; | ||
| 26 | + } | ||
| 27 | + | ||
| 28 | + stream.respond({ ':status': 200 }); | ||
| 29 | + stream.end('ok'); | ||
| 30 | + })); | ||
| 31 | + | ||
| 32 | + server.listen(0, common.mustCall(() => { | ||
| 33 | + const session = http2.connect(`http://localhost:${server.address().port}`); | ||
| 34 | + let cachedSession = session; | ||
| 35 | + | ||
| 36 | + session.on('error', () => {}); | ||
| 37 | + session.on('close', common.mustCall(() => { | ||
| 38 | + cachedSession = undefined; | ||
| 39 | + server.close(); | ||
| 40 | + })); | ||
| 41 | + | ||
| 42 | + const req = session.request({ ':path': '/close' }); | ||
| 43 | + req.on('response', common.mustCall()); | ||
| 44 | + req.on('error', () => {}); | ||
| 45 | + req.on('close', common.mustCall(() => { | ||
| 46 | + // This must not throw synchronously even though the session is no longer | ||
| 47 | + // usable. Depending on teardown timing, the returned stream may report a | ||
| 48 | + // closed session before the destroy state is fully observable here. | ||
| 49 | + const req2 = session.request({ ':path': '/again' }); | ||
| 50 | + | ||
| 51 | + req2.on('error', common.mustCall((err) => { | ||
| 52 | + assert.ok( | ||
| 53 | + err.code === 'ERR_HTTP2_INVALID_SESSION' || | ||
| 54 | + err.code === 'ERR_HTTP2_GOAWAY_SESSION'); | ||
| 55 | + assert.strictEqual(cachedSession, undefined); | ||
| 56 | + })); | ||
| 57 | + })); | ||
| 58 | + req.resume(); | ||
| 59 | + })); | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments