| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 86f2e86 commit ba0682e
10 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -488,7 +488,10 @@ function onStreamClose(code) { | |||
| 488 | 488 | if (!stream || stream.destroyed) | |
| 489 | 489 | return false; | |
| 490 | 490 | ||
| 491 | - debugStreamObj(stream, 'closed with code %d', code); | ||
| 491 | + debugStreamObj( | ||
| 492 | + stream, 'closed with code %d, closed %s, readable %s', | ||
| 493 | + code, stream.closed, stream.readable | ||
| 494 | + ); | ||
| 492 | 495 | ||
| 493 | 496 | if (!stream.closed) | |
| 494 | 497 | closeStream(stream, code, kNoRstStream); | |
@@ -497,7 +500,7 @@ function onStreamClose(code) { | |||
| 497 | 500 | // Defer destroy we actually emit end. | |
| 498 | 501 | if (!stream.readable || code !== NGHTTP2_NO_ERROR) { | |
| 499 | 502 | // If errored or ended, we can destroy immediately. | |
| 500 | - stream[kMaybeDestroy](code); | ||
| 503 | + stream.destroy(); | ||
| 501 | 504 | } else { | |
| 502 | 505 | // Wait for end to destroy. | |
| 503 | 506 | stream.on('end', stream[kMaybeDestroy]); | |
@@ -985,22 +988,76 @@ function emitClose(self, error) { | |||
| 985 | 988 | self.emit('close'); | |
| 986 | 989 | } | |
| 987 | 990 | ||
| 988 | - function finishSessionDestroy(session, error) { | ||
| 989 | - debugSessionObj(session, 'finishSessionDestroy'); | ||
| 990 | - | ||
| 991 | + function cleanupSession(session) { | ||
| 991 | 992 | const socket = session[kSocket]; | |
| 992 | - if (!socket.destroyed) | ||
| 993 | - socket.destroy(error); | ||
| 994 | - | ||
| 993 | + const handle = session[kHandle]; | ||
| 995 | 994 | session[kProxySocket] = undefined; | |
| 996 | 995 | session[kSocket] = undefined; | |
| 997 | 996 | session[kHandle] = undefined; | |
| 998 | 997 | session[kNativeFields] = new Uint8Array(kSessionUint8FieldCount); | |
| 999 | - socket[kSession] = undefined; | ||
| 1000 | - socket[kServer] = undefined; | ||
| 998 | + if (handle) | ||
| 999 | + handle.ondone = null; | ||
| 1000 | + if (socket) { | ||
| 1001 | + socket[kSession] = undefined; | ||
| 1002 | + socket[kServer] = undefined; | ||
| 1003 | + } | ||
| 1004 | + } | ||
| 1005 | + | ||
| 1006 | + function finishSessionClose(session, error) { | ||
| 1007 | + debugSessionObj(session, 'finishSessionClose'); | ||
| 1008 | + | ||
| 1009 | + const socket = session[kSocket]; | ||
| 1010 | + cleanupSession(session); | ||
| 1011 | + | ||
| 1012 | + if (socket && !socket.destroyed) { | ||
| 1013 | + // Always wait for writable side to finish. | ||
| 1014 | + socket.end((err) => { | ||
| 1015 | + debugSessionObj(session, 'finishSessionClose socket end', err); | ||
| 1016 | + // Due to the way the underlying stream is handled in Http2Session we | ||
| 1017 | + // won't get graceful Readable end from the other side even if it was sent | ||
| 1018 | + // as the stream is already considered closed and will neither be read | ||
| 1019 | + // from nor keep the event loop alive. | ||
| 1020 | + // Therefore destroy the socket immediately. | ||
| 1021 | + // Fixing this would require some heavy juggling of ReadStart/ReadStop | ||
| 1022 | + // mostly on Windows as on Unix it will be fine with just ReadStart | ||
| 1023 | + // after this 'ondone' callback. | ||
| 1024 | + socket.destroy(error); | ||
| 1025 | + emitClose(session, error); | ||
| 1026 | + }); | ||
| 1027 | + } else { | ||
| 1028 | + process.nextTick(emitClose, session, error); | ||
| 1029 | + } | ||
| 1030 | + } | ||
| 1031 | + | ||
| 1032 | + function closeSession(session, code, error) { | ||
| 1033 | + debugSessionObj(session, 'start closing/destroying'); | ||
| 1034 | + | ||
| 1035 | + const state = session[kState]; | ||
| 1036 | + state.flags |= SESSION_FLAGS_DESTROYED; | ||
| 1037 | + state.destroyCode = code; | ||
| 1038 | + | ||
| 1039 | + // Clear timeout and remove timeout listeners. | ||
| 1040 | + session.setTimeout(0); | ||
| 1041 | + session.removeAllListeners('timeout'); | ||
| 1042 | + | ||
| 1043 | + // Destroy any pending and open streams | ||
| 1044 | + if (state.pendingStreams.size > 0 || state.streams.size > 0) { | ||
| 1045 | + const cancel = new ERR_HTTP2_STREAM_CANCEL(error); | ||
| 1046 | + state.pendingStreams.forEach((stream) => stream.destroy(cancel)); | ||
| 1047 | + state.streams.forEach((stream) => stream.destroy(error)); | ||
| 1048 | + } | ||
| 1001 | 1049 | ||
| 1002 | - // Finally, emit the close and error events (if necessary) on next tick. | ||
| 1003 | - process.nextTick(emitClose, session, error); | ||
| 1050 | + // Disassociate from the socket and server. | ||
| 1051 | + const socket = session[kSocket]; | ||
| 1052 | + const handle = session[kHandle]; | ||
| 1053 | + | ||
| 1054 | + // Destroy the handle if it exists at this point. | ||
| 1055 | + if (handle !== undefined) { | ||
| 1056 | + handle.ondone = finishSessionClose.bind(null, session, error); | ||
| 1057 | + handle.destroy(code, socket.destroyed); | ||
| 1058 | + } else { | ||
| 1059 | + finishSessionClose(session, error); | ||
| 1060 | + } | ||
| 1004 | 1061 | } | |
| 1005 | 1062 | ||
| 1006 | 1063 | // Upon creation, the Http2Session takes ownership of the socket. The session | |
@@ -1327,6 +1384,7 @@ class Http2Session extends EventEmitter { | |||
| 1327 | 1384 | destroy(error = NGHTTP2_NO_ERROR, code) { | |
| 1328 | 1385 | if (this.destroyed) | |
| 1329 | 1386 | return; | |
| 1387 | + | ||
| 1330 | 1388 | debugSessionObj(this, 'destroying'); | |
| 1331 | 1389 | ||
| 1332 | 1390 | if (typeof error === 'number') { | |
@@ -1338,30 +1396,7 @@ class Http2Session extends EventEmitter { | |||
| 1338 | 1396 | if (code === undefined && error != null) | |
| 1339 | 1397 | code = NGHTTP2_INTERNAL_ERROR; | |
| 1340 | 1398 | ||
| 1341 | - const state = this[kState]; | ||
| 1342 | - state.flags |= SESSION_FLAGS_DESTROYED; | ||
| 1343 | - state.destroyCode = code; | ||
| 1344 | - | ||
| 1345 | - // Clear timeout and remove timeout listeners | ||
| 1346 | - this.setTimeout(0); | ||
| 1347 | - this.removeAllListeners('timeout'); | ||
| 1348 | - | ||
| 1349 | - // Destroy any pending and open streams | ||
| 1350 | - const cancel = new ERR_HTTP2_STREAM_CANCEL(error); | ||
| 1351 | - state.pendingStreams.forEach((stream) => stream.destroy(cancel)); | ||
| 1352 | - state.streams.forEach((stream) => stream.destroy(error)); | ||
| 1353 | - | ||
| 1354 | - // Disassociate from the socket and server | ||
| 1355 | - const socket = this[kSocket]; | ||
| 1356 | - const handle = this[kHandle]; | ||
| 1357 | - | ||
| 1358 | - // Destroy the handle if it exists at this point | ||
| 1359 | - if (handle !== undefined) { | ||
| 1360 | - handle.ondone = finishSessionDestroy.bind(null, this, error); | ||
| 1361 | - handle.destroy(code, socket.destroyed); | ||
| 1362 | - } else { | ||
| 1363 | - finishSessionDestroy(this, error); | ||
| 1364 | - } | ||
| 1399 | + closeSession(this, code, error); | ||
| 1365 | 1400 | } | |
| 1366 | 1401 | ||
| 1367 | 1402 | // Closing the session will: | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1782,7 +1782,7 @@ void Http2Session::OnStreamRead(ssize_t nread, const uv_buf_t& buf_) { | |||
| 1782 | 1782 | Context::Scope context_scope(env()->context()); | |
| 1783 | 1783 | Http2Scope h2scope(this); | |
| 1784 | 1784 | CHECK_NOT_NULL(stream_); | |
| 1785 | - Debug(this, "receiving %d bytes", nread); | ||
| 1785 | + Debug(this, "receiving %d bytes, offset %d", nread, stream_buf_offset_); | ||
| 1786 | 1786 | AllocatedBuffer buf(env(), buf_); | |
| 1787 | 1787 | ||
| 1788 | 1788 | // Only pass data on if nread > 0 | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -72,7 +72,6 @@ events.captureRejections = true; | |||
| 72 | 72 | })); | |
| 73 | 73 | } | |
| 74 | 74 | ||
| 75 | - | ||
| 76 | 75 | { | |
| 77 | 76 | // Test error thrown in 'request' event | |
| 78 | 77 | ||
@@ -136,6 +135,7 @@ events.captureRejections = true; | |||
| 136 | 135 | const session = connect(`http://localhost:${port}`); | |
| 137 | 136 | ||
| 138 | 137 | const req = session.request(); | |
| 138 | + req.resume(); | ||
| 139 | 139 | ||
| 140 | 140 | session.on('stream', common.mustCall(async (stream) => { | |
| 141 | 141 | session.close(); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -145,6 +145,7 @@ const Countdown = require('../common/countdown'); | |||
| 145 | 145 | server.on('stream', common.mustNotCall()); | |
| 146 | 146 | server.listen(0, common.mustCall(() => { | |
| 147 | 147 | const client = h2.connect(`http://localhost:${server.address().port}`); | |
| 148 | + client.on('close', common.mustCall()); | ||
| 148 | 149 | const socket = client[kSocket]; | |
| 149 | 150 | socket.on('close', common.mustCall(() => { | |
| 150 | 151 | assert(socket.destroyed); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -6,6 +6,7 @@ if (!common.hasCrypto) | |||
| 6 | 6 | const assert = require('assert'); | |
| 7 | 7 | const h2 = require('http2'); | |
| 8 | 8 | const NGHTTP2_INTERNAL_ERROR = h2.constants.NGHTTP2_INTERNAL_ERROR; | |
| 9 | + const Countdown = require('../common/countdown'); | ||
| 9 | 10 | ||
| 10 | 11 | const server = h2.createServer(); | |
| 11 | 12 | ||
@@ -27,6 +28,11 @@ server.on('stream', (stream) => { | |||
| 27 | 28 | ||
| 28 | 29 | server.listen(0, common.mustCall(() => { | |
| 29 | 30 | const client = h2.connect(`http://localhost:${server.address().port}`); | |
| 31 | + const countdown = new Countdown(2, () => { | ||
| 32 | + server.close(); | ||
| 33 | + client.close(); | ||
| 34 | + }); | ||
| 35 | + client.on('connect', () => countdown.dec()); | ||
| 30 | 36 | ||
| 31 | 37 | const req = client.request(); | |
| 32 | 38 | req.destroy(new Error('test')); | |
@@ -39,8 +45,7 @@ server.listen(0, common.mustCall(() => { | |||
| 39 | 45 | req.on('close', common.mustCall(() => { | |
| 40 | 46 | assert.strictEqual(req.rstCode, NGHTTP2_INTERNAL_ERROR); | |
| 41 | 47 | assert.strictEqual(req.rstCode, NGHTTP2_INTERNAL_ERROR); | |
| 42 | - server.close(); | ||
| 43 | - client.close(); | ||
| 48 | + countdown.dec(); | ||
| 44 | 49 | })); | |
| 45 | 50 | ||
| 46 | 51 | req.on('response', common.mustNotCall()); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -23,9 +23,11 @@ fs.readFile(loc, common.mustCall((err, data) => { | |||
| 23 | 23 | res.end(); | |
| 24 | 24 | }); | |
| 25 | 25 | })); | |
| 26 | + server.on('close', common.mustCall()); | ||
| 26 | 27 | ||
| 27 | 28 | server.listen(0, common.mustCall(() => { | |
| 28 | 29 | const client = http2.connect(`http://localhost:${server.address().port}`); | |
| 30 | + client.on('close', common.mustCall()); | ||
| 29 | 31 | ||
| 30 | 32 | const req = client.request({ ':method': 'POST' }); | |
| 31 | 33 | req.on('response', common.mustCall((headers) => { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -38,11 +38,13 @@ const URL = url.URL; | |||
| 38 | 38 | const client = | |
| 39 | 39 | h2.connect.apply(null, i) | |
| 40 | 40 | .on('connect', common.mustCall(() => maybeClose(client))); | |
| 41 | + client.on('close', common.mustCall()); | ||
| 41 | 42 | }); | |
| 42 | 43 | ||
| 43 | 44 | // Will fail because protocol does not match the server. | |
| 44 | - h2.connect({ port: port, protocol: 'https:' }) | ||
| 45 | + const client = h2.connect({ port: port, protocol: 'https:' }) | ||
| 45 | 46 | .on('error', common.mustCall(() => serverClose.dec())); | |
| 47 | + client.on('close', common.mustCall()); | ||
| 46 | 48 | })); | |
| 47 | 49 | } | |
| 48 | 50 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -8,20 +8,24 @@ const http2 = require('http2'); | |||
| 8 | 8 | ||
| 9 | 9 | const server = http2.createServer(); | |
| 10 | 10 | const data = Buffer.from([0x1, 0x2, 0x3, 0x4, 0x5]); | |
| 11 | + let session; | ||
| 11 | 12 | ||
| 12 | 13 | server.on('stream', common.mustCall((stream) => { | |
| 13 | - stream.session.goaway(0, 0, data); | ||
| 14 | + session = stream.session; | ||
| 15 | + session.on('close', common.mustCall()); | ||
| 16 | + session.goaway(0, 0, data); | ||
| 14 | 17 | stream.respond(); | |
| 15 | 18 | stream.end(); | |
| 16 | 19 | })); | |
| 20 | + server.on('close', common.mustCall()); | ||
| 17 | 21 | ||
| 18 | 22 | server.listen(0, () => { | |
| 19 | - | ||
| 20 | 23 | const client = http2.connect(`http://localhost:${server.address().port}`); | |
| 21 | 24 | client.once('goaway', common.mustCall((code, lastStreamID, buf) => { | |
| 22 | 25 | assert.deepStrictEqual(code, 0); | |
| 23 | 26 | assert.deepStrictEqual(lastStreamID, 1); | |
| 24 | 27 | assert.deepStrictEqual(data, buf); | |
| 28 | + session.close(); | ||
| 25 | 29 | server.close(); | |
| 26 | 30 | })); | |
| 27 | 31 | const req = client.request(); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -30,7 +30,12 @@ for (const variant of ['ping', 'settings']) { | |||
| 30 | 30 | })); | |
| 31 | 31 | ||
| 32 | 32 | server.listen(0, common.mustCall(() => { | |
| 33 | - http2.connect(`http://localhost:${server.address().port}`, | ||
| 34 | - common.mustCall()); | ||
| 33 | + const client = http2.connect(`http://localhost:${server.address().port}`, | ||
| 34 | + common.mustCall()); | ||
| 35 | + client.on('error', (err) => { | ||
| 36 | + // We destroy the session so it's possible to get ECONNRESET here. | ||
| 37 | + if (err.code !== 'ECONNRESET') | ||
| 38 | + throw err; | ||
| 39 | + }); | ||
| 35 | 40 | })); | |
| 36 | 41 | } | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -55,6 +55,8 @@ server.listen(0, common.mustCall(() => { | |||
| 55 | 55 | assert.strictEqual(headers['x-push-data'], 'pushed by server'); | |
| 56 | 56 | })); | |
| 57 | 57 | stream.on('aborted', common.mustNotCall()); | |
| 58 | + // We have to read the data of the push stream to end gracefully. | ||
| 59 | + stream.resume(); | ||
| 58 | 60 | })); | |
| 59 | 61 | ||
| 60 | 62 | let data = ''; | |
| Back | FazBrowse Home | New Git URL |
0 commit comments