| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -306,8 +306,23 @@ function onStreamClose(code) { | |||
| 306 | 306 | ||
| 307 | 307 | if (state.fd !== undefined) | |
| 308 | 308 | tryClose(state.fd); | |
| 309 | - stream.push(null); | ||
| 310 | - stream[kMaybeDestroy](null, code); | ||
| 309 | + | ||
| 310 | + // Defer destroy we actually emit end. | ||
| 311 | + if (stream._readableState.endEmitted || code !== NGHTTP2_NO_ERROR) { | ||
| 312 | + // If errored or ended, we can destroy immediately. | ||
| 313 | + stream[kMaybeDestroy](null, code); | ||
| 314 | + } else { | ||
| 315 | + // Wait for end to destroy. | ||
| 316 | + stream.on('end', stream[kMaybeDestroy]); | ||
| 317 | + // Push a null so the stream can end whenever the client consumes | ||
| 318 | + // it completely. | ||
| 319 | + stream.push(null); | ||
| 320 | + | ||
| 321 | + // Same as net. | ||
| 322 | + if (stream._readableState.length === 0) { | ||
| 323 | + stream.read(0); | ||
| 324 | + } | ||
| 325 | + } | ||
| 311 | 326 | } | |
| 312 | 327 | ||
| 313 | 328 | // Receives a chunk of data for a given stream and forwards it on | |
@@ -325,11 +340,19 @@ function onStreamRead(nread, buf) { | |||
| 325 | 340 | } | |
| 326 | 341 | return; | |
| 327 | 342 | } | |
| 343 | + | ||
| 328 | 344 | // Last chunk was received. End the readable side. | |
| 329 | 345 | debug(`Http2Stream ${stream[kID]} [Http2Session ` + | |
| 330 | 346 | `${sessionName(stream[kSession][kType])}]: ending readable.`); | |
| 331 | - stream.push(null); | ||
| 332 | - stream[kMaybeDestroy](); | ||
| 347 | + | ||
| 348 | + // defer this until we actually emit end | ||
| 349 | + if (stream._readableState.endEmitted) { | ||
| 350 | + stream[kMaybeDestroy](); | ||
| 351 | + } else { | ||
| 352 | + stream.on('end', stream[kMaybeDestroy]); | ||
| 353 | + stream.push(null); | ||
| 354 | + stream.read(0); | ||
| 355 | + } | ||
| 333 | 356 | } | |
| 334 | 357 | ||
| 335 | 358 | // Called when the remote peer settings have been updated. | |
@@ -1826,21 +1849,25 @@ class Http2Stream extends Duplex { | |||
| 1826 | 1849 | session[kMaybeDestroy](); | |
| 1827 | 1850 | process.nextTick(emit, this, 'close', code); | |
| 1828 | 1851 | callback(err); | |
| 1829 | - } | ||
| 1830 | 1852 | ||
| 1853 | + } | ||
| 1831 | 1854 | // The Http2Stream can be destroyed if it has closed and if the readable | |
| 1832 | 1855 | // side has received the final chunk. | |
| 1833 | 1856 | [kMaybeDestroy](error, code = NGHTTP2_NO_ERROR) { | |
| 1834 | - if (error == null) { | ||
| 1835 | - if (code === NGHTTP2_NO_ERROR && | ||
| 1836 | - (!this._readableState.ended || | ||
| 1837 | - !this._writableState.ended || | ||
| 1838 | - this._writableState.pendingcb > 0 || | ||
| 1839 | - !this.closed)) { | ||
| 1840 | - return; | ||
| 1841 | - } | ||
| 1857 | + if (error || code !== NGHTTP2_NO_ERROR) { | ||
| 1858 | + this.destroy(error); | ||
| 1859 | + return; | ||
| 1860 | + } | ||
| 1861 | + | ||
| 1862 | + // TODO(mcollina): remove usage of _*State properties | ||
| 1863 | + if (this._readableState.ended && | ||
| 1864 | + this._writableState.ended && | ||
| 1865 | + this._writableState.pendingcb === 0 && | ||
| 1866 | + this.closed) { | ||
| 1867 | + this.destroy(); | ||
| 1868 | + // This should return, but eslint complains. | ||
| 1869 | + // return | ||
| 1842 | 1870 | } | |
| 1843 | - this.destroy(error); | ||
| 1844 | 1871 | } | |
| 1845 | 1872 | } | |
| 1846 | 1873 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,50 @@ | |||
| 1 | + 'use strict'; | ||
| 2 | + | ||
| 3 | + const common = require('../common'); | ||
| 4 | + if (!common.hasCrypto) | ||
| 5 | + common.skip('missing crypto'); | ||
| 6 | + const assert = require('assert'); | ||
| 7 | + const http2 = require('http2'); | ||
| 8 | + const { Readable } = require('stream'); | ||
| 9 | + | ||
| 10 | + const server = http2.createServer(common.mustCall((req, res) => { | ||
| 11 | + res.setHeader('content-type', 'text/html'); | ||
| 12 | + const input = new Readable({ | ||
| 13 | + read() { | ||
| 14 | + this.push('test'); | ||
| 15 | + this.push(null); | ||
| 16 | + } | ||
| 17 | + }); | ||
| 18 | + input.pipe(res); | ||
| 19 | + })); | ||
| 20 | + | ||
| 21 | + server.listen(0, common.mustCall(() => { | ||
| 22 | + const port = server.address().port; | ||
| 23 | + const client = http2.connect(`http://localhost:${port}`); | ||
| 24 | + | ||
| 25 | + const req = client.request(); | ||
| 26 | + | ||
| 27 | + req.on('response', common.mustCall((headers) => { | ||
| 28 | + assert.strictEqual(headers[':status'], 200); | ||
| 29 | + assert.strictEqual(headers['content-type'], 'text/html'); | ||
| 30 | + })); | ||
| 31 | + | ||
| 32 | + let data = ''; | ||
| 33 | + | ||
| 34 | + const notCallClose = common.mustNotCall(); | ||
| 35 | + | ||
| 36 | + setTimeout(() => { | ||
| 37 | + req.setEncoding('utf8'); | ||
| 38 | + req.removeListener('close', notCallClose); | ||
| 39 | + req.on('close', common.mustCall(() => { | ||
| 40 | + server.close(); | ||
| 41 | + client.close(); | ||
| 42 | + })); | ||
| 43 | + req.on('data', common.mustCallAtLeast((d) => data += d)); | ||
| 44 | + req.on('end', common.mustCall(() => { | ||
| 45 | + assert.strictEqual(data, 'test'); | ||
| 46 | + })); | ||
| 47 | + }, common.platformTimeout(100)); | ||
| 48 | + | ||
| 49 | + req.on('close', notCallClose); | ||
| 50 | + })); | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,55 @@ | |||
| 1 | + 'use strict'; | ||
| 2 | + | ||
| 3 | + const common = require('../common'); | ||
| 4 | + if (!common.hasCrypto) | ||
| 5 | + common.skip('missing crypto'); | ||
| 6 | + const assert = require('assert'); | ||
| 7 | + const http2 = require('http2'); | ||
| 8 | + const { Readable } = require('stream'); | ||
| 9 | + | ||
| 10 | + const server = http2.createServer(); | ||
| 11 | + server.on('stream', common.mustCall((stream) => { | ||
| 12 | + stream.respond({ | ||
| 13 | + ':status': 200, | ||
| 14 | + 'content-type': 'text/html' | ||
| 15 | + }); | ||
| 16 | + const input = new Readable({ | ||
| 17 | + read() { | ||
| 18 | + this.push('test'); | ||
| 19 | + this.push(null); | ||
| 20 | + } | ||
| 21 | + }); | ||
| 22 | + input.pipe(stream); | ||
| 23 | + })); | ||
| 24 | + | ||
| 25 | + | ||
| 26 | + server.listen(0, common.mustCall(() => { | ||
| 27 | + const port = server.address().port; | ||
| 28 | + const client = http2.connect(`http://localhost:${port}`); | ||
| 29 | + | ||
| 30 | + const req = client.request(); | ||
| 31 | + | ||
| 32 | + req.on('response', common.mustCall((headers) => { | ||
| 33 | + assert.strictEqual(headers[':status'], 200); | ||
| 34 | + assert.strictEqual(headers['content-type'], 'text/html'); | ||
| 35 | + })); | ||
| 36 | + | ||
| 37 | + let data = ''; | ||
| 38 | + | ||
| 39 | + const notCallClose = common.mustNotCall(); | ||
| 40 | + | ||
| 41 | + setTimeout(() => { | ||
| 42 | + req.setEncoding('utf8'); | ||
| 43 | + req.removeListener('close', notCallClose); | ||
| 44 | + req.on('close', common.mustCall(() => { | ||
| 45 | + server.close(); | ||
| 46 | + client.close(); | ||
| 47 | + })); | ||
| 48 | + req.on('data', common.mustCallAtLeast((d) => data += d)); | ||
| 49 | + req.on('end', common.mustCall(() => { | ||
| 50 | + assert.strictEqual(data, 'test'); | ||
| 51 | + })); | ||
| 52 | + }, common.platformTimeout(100)); | ||
| 53 | + | ||
| 54 | + req.on('close', notCallClose); | ||
| 55 | + })); | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments