| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 9b30bc4 commit 46bd862
10 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -206,6 +206,7 @@ const STREAM_FLAGS_CLOSED = 0x2; | |||
| 206 | 206 | const STREAM_FLAGS_HEADERS_SENT = 0x4; | |
| 207 | 207 | const STREAM_FLAGS_HEAD_REQUEST = 0x8; | |
| 208 | 208 | const STREAM_FLAGS_ABORTED = 0x10; | |
| 209 | + const STREAM_FLAGS_HAS_TRAILERS = 0x20; | ||
| 209 | 210 | ||
| 210 | 211 | const SESSION_FLAGS_PENDING = 0x0; | |
| 211 | 212 | const SESSION_FLAGS_READY = 0x1; | |
@@ -330,26 +331,13 @@ function onStreamClose(code) { | |||
| 330 | 331 | if (stream.destroyed) | |
| 331 | 332 | return; | |
| 332 | 333 | ||
| 333 | - const state = stream[kState]; | ||
| 334 | - | ||
| 335 | 334 | debug(`Http2Stream ${stream[kID]} [Http2Session ` + | |
| 336 | 335 | `${sessionName(stream[kSession][kType])}]: closed with code ${code}`); | |
| 337 | 336 | ||
| 338 | - if (!stream.closed) { | ||
| 339 | - // Clear timeout and remove timeout listeners | ||
| 340 | - stream.setTimeout(0); | ||
| 341 | - stream.removeAllListeners('timeout'); | ||
| 337 | + if (!stream.closed) | ||
| 338 | + closeStream(stream, code, false); | ||
| 342 | 339 | ||
| 343 | - // Set the state flags | ||
| 344 | - state.flags |= STREAM_FLAGS_CLOSED; | ||
| 345 | - state.rstCode = code; | ||
| 346 | - | ||
| 347 | - // Close the writable side of the stream | ||
| 348 | - abort(stream); | ||
| 349 | - stream.end(); | ||
| 350 | - } | ||
| 351 | - | ||
| 352 | - state.fd = -1; | ||
| 340 | + stream[kState].fd = -1; | ||
| 353 | 341 | // Defer destroy we actually emit end. | |
| 354 | 342 | if (stream._readableState.endEmitted || code !== NGHTTP2_NO_ERROR) { | |
| 355 | 343 | // If errored or ended, we can destroy immediately. | |
@@ -504,7 +492,7 @@ function requestOnConnect(headers, options) { | |||
| 504 | 492 | ||
| 505 | 493 | // At this point, the stream should have already been destroyed during | |
| 506 | 494 | // the session.destroy() method. Do nothing else. | |
| 507 | - if (session.destroyed) | ||
| 495 | + if (session === undefined || session.destroyed) | ||
| 508 | 496 | return; | |
| 509 | 497 | ||
| 510 | 498 | // If the session was closed while waiting for the connect, destroy | |
@@ -1412,6 +1400,9 @@ class ClientHttp2Session extends Http2Session { | |||
| 1412 | 1400 | if (options.endStream) | |
| 1413 | 1401 | stream.end(); | |
| 1414 | 1402 | ||
| 1403 | + if (options.waitForTrailers) | ||
| 1404 | + stream[kState].flags |= STREAM_FLAGS_HAS_TRAILERS; | ||
| 1405 | + | ||
| 1415 | 1406 | const onConnect = requestOnConnect.bind(stream, headersList, options); | |
| 1416 | 1407 | if (this.connecting) { | |
| 1417 | 1408 | this.on('connect', onConnect); | |
@@ -1445,32 +1436,70 @@ function afterDoStreamWrite(status, handle) { | |||
| 1445 | 1436 | } | |
| 1446 | 1437 | ||
| 1447 | 1438 | function streamOnResume() { | |
| 1448 | - if (!this.destroyed && !this.pending) | ||
| 1439 | + if (!this.destroyed && !this.pending) { | ||
| 1440 | + if (!this[kState].didRead) | ||
| 1441 | + this[kState].didRead = true; | ||
| 1449 | 1442 | this[kHandle].readStart(); | |
| 1443 | + } | ||
| 1450 | 1444 | } | |
| 1451 | 1445 | ||
| 1452 | 1446 | function streamOnPause() { | |
| 1453 | 1447 | if (!this.destroyed && !this.pending) | |
| 1454 | 1448 | this[kHandle].readStop(); | |
| 1455 | 1449 | } | |
| 1456 | 1450 | ||
| 1457 | - // If the writable side of the Http2Stream is still open, emit the | ||
| 1458 | - // 'aborted' event and set the aborted flag. | ||
| 1459 | - function abort(stream) { | ||
| 1460 | - if (!stream.aborted && | ||
| 1461 | - !(stream._writableState.ended || stream._writableState.ending)) { | ||
| 1462 | - stream[kState].flags |= STREAM_FLAGS_ABORTED; | ||
| 1463 | - stream.emit('aborted'); | ||
| 1464 | - } | ||
| 1465 | - } | ||
| 1466 | - | ||
| 1467 | 1451 | function afterShutdown() { | |
| 1468 | 1452 | this.callback(); | |
| 1469 | 1453 | const stream = this.handle[kOwner]; | |
| 1470 | 1454 | if (stream) | |
| 1471 | 1455 | stream[kMaybeDestroy](); | |
| 1472 | 1456 | } | |
| 1473 | 1457 | ||
| 1458 | + function closeStream(stream, code, shouldSubmitRstStream = true) { | ||
| 1459 | + const state = stream[kState]; | ||
| 1460 | + state.flags |= STREAM_FLAGS_CLOSED; | ||
| 1461 | + state.rstCode = code; | ||
| 1462 | + | ||
| 1463 | + // Clear timeout and remove timeout listeners | ||
| 1464 | + stream.setTimeout(0); | ||
| 1465 | + stream.removeAllListeners('timeout'); | ||
| 1466 | + | ||
| 1467 | + const { ending, finished } = stream._writableState; | ||
| 1468 | + | ||
| 1469 | + if (!ending) { | ||
| 1470 | + // If the writable side of the Http2Stream is still open, emit the | ||
| 1471 | + // 'aborted' event and set the aborted flag. | ||
| 1472 | + if (!stream.aborted) { | ||
| 1473 | + state.flags |= STREAM_FLAGS_ABORTED; | ||
| 1474 | + stream.emit('aborted'); | ||
| 1475 | + } | ||
| 1476 | + | ||
| 1477 | + // Close the writable side. | ||
| 1478 | + stream.end(); | ||
| 1479 | + } | ||
| 1480 | + | ||
| 1481 | + if (shouldSubmitRstStream) { | ||
| 1482 | + const finishFn = finishCloseStream.bind(stream, code); | ||
| 1483 | + if (!ending || finished || code !== NGHTTP2_NO_ERROR) | ||
| 1484 | + finishFn(); | ||
| 1485 | + else | ||
| 1486 | + stream.once('finish', finishFn); | ||
| 1487 | + } | ||
| 1488 | + } | ||
| 1489 | + | ||
| 1490 | + function finishCloseStream(code) { | ||
| 1491 | + const rstStreamFn = submitRstStream.bind(this, code); | ||
| 1492 | + // If the handle has not yet been assigned, queue up the request to | ||
| 1493 | + // ensure that the RST_STREAM frame is sent after the stream ID has | ||
| 1494 | + // been determined. | ||
| 1495 | + if (this.pending) { | ||
| 1496 | + this.push(null); | ||
| 1497 | + this.once('ready', rstStreamFn); | ||
| 1498 | + return; | ||
| 1499 | + } | ||
| 1500 | + rstStreamFn(); | ||
| 1501 | + } | ||
| 1502 | + | ||
| 1474 | 1503 | // An Http2Stream is a Duplex stream that is backed by a | |
| 1475 | 1504 | // node::http2::Http2Stream handle implementing StreamBase. | |
| 1476 | 1505 | class Http2Stream extends Duplex { | |
@@ -1490,6 +1519,7 @@ class Http2Stream extends Duplex { | |||
| 1490 | 1519 | this[kTimeout] = null; | |
| 1491 | 1520 | ||
| 1492 | 1521 | this[kState] = { | |
| 1522 | + didRead: false, | ||
| 1493 | 1523 | flags: STREAM_FLAGS_PENDING, | |
| 1494 | 1524 | rstCode: NGHTTP2_NO_ERROR, | |
| 1495 | 1525 | writeQueueSize: 0, | |
@@ -1756,6 +1786,8 @@ class Http2Stream extends Duplex { | |||
| 1756 | 1786 | throw headersList; | |
| 1757 | 1787 | this[kSentTrailers] = headers; | |
| 1758 | 1788 | ||
| 1789 | + this[kState].flags &= ~STREAM_FLAGS_HAS_TRAILERS; | ||
| 1790 | + | ||
| 1759 | 1791 | const ret = this[kHandle].trailers(headersList); | |
| 1760 | 1792 | if (ret < 0) | |
| 1761 | 1793 | this.destroy(new NghttpError(ret)); | |
@@ -1786,38 +1818,13 @@ class Http2Stream extends Duplex { | |||
| 1786 | 1818 | if (callback !== undefined && typeof callback !== 'function') | |
| 1787 | 1819 | throw new ERR_INVALID_CALLBACK(); | |
| 1788 | 1820 | ||
| 1789 | - // Clear timeout and remove timeout listeners | ||
| 1790 | - this.setTimeout(0); | ||
| 1791 | - this.removeAllListeners('timeout'); | ||
| 1792 | - | ||
| 1793 | - // Close the writable | ||
| 1794 | - abort(this); | ||
| 1795 | - this.end(); | ||
| 1796 | - | ||
| 1797 | 1821 | if (this.closed) | |
| 1798 | 1822 | return; | |
| 1799 | 1823 | ||
| 1800 | - const state = this[kState]; | ||
| 1801 | - state.flags |= STREAM_FLAGS_CLOSED; | ||
| 1802 | - state.rstCode = code; | ||
| 1803 | - | ||
| 1804 | - if (callback !== undefined) { | ||
| 1824 | + if (callback !== undefined) | ||
| 1805 | 1825 | this.once('close', callback); | |
| 1806 | - } | ||
| 1807 | - | ||
| 1808 | - if (this[kHandle] === undefined) | ||
| 1809 | - return; | ||
| 1810 | 1826 | ||
| 1811 | - const rstStreamFn = submitRstStream.bind(this, code); | ||
| 1812 | - // If the handle has not yet been assigned, queue up the request to | ||
| 1813 | - // ensure that the RST_STREAM frame is sent after the stream ID has | ||
| 1814 | - // been determined. | ||
| 1815 | - if (this.pending) { | ||
| 1816 | - this.push(null); | ||
| 1817 | - this.once('ready', rstStreamFn); | ||
| 1818 | - return; | ||
| 1819 | - } | ||
| 1820 | - rstStreamFn(); | ||
| 1827 | + closeStream(this, code); | ||
| 1821 | 1828 | } | |
| 1822 | 1829 | ||
| 1823 | 1830 | // Called by this.destroy(). | |
@@ -1832,26 +1839,19 @@ class Http2Stream extends Duplex { | |||
| 1832 | 1839 | debug(`Http2Stream ${this[kID] || '<pending>'} [Http2Session ` + | |
| 1833 | 1840 | `${sessionName(session[kType])}]: destroying stream`); | |
| 1834 | 1841 | const state = this[kState]; | |
| 1835 | - const code = state.rstCode = | ||
| 1836 | - err != null ? | ||
| 1837 | - NGHTTP2_INTERNAL_ERROR : | ||
| 1838 | - state.rstCode || NGHTTP2_NO_ERROR; | ||
| 1839 | - if (handle !== undefined) { | ||
| 1840 | - // If the handle exists, we need to close, then destroy the handle | ||
| 1841 | - this.close(code); | ||
| 1842 | - if (!this._readableState.ended && !this._readableState.ending) | ||
| 1843 | - this.push(null); | ||
| 1842 | + const code = err != null ? | ||
| 1843 | + NGHTTP2_INTERNAL_ERROR : (state.rstCode || NGHTTP2_NO_ERROR); | ||
| 1844 | + | ||
| 1845 | + const hasHandle = handle !== undefined; | ||
| 1846 | + | ||
| 1847 | + if (!this.closed) | ||
| 1848 | + closeStream(this, code, hasHandle); | ||
| 1849 | + this.push(null); | ||
| 1850 | + | ||
| 1851 | + if (hasHandle) { | ||
| 1844 | 1852 | handle.destroy(); | |
| 1845 | 1853 | session[kState].streams.delete(id); | |
| 1846 | 1854 | } else { | |
| 1847 | - // Clear timeout and remove timeout listeners | ||
| 1848 | - this.setTimeout(0); | ||
| 1849 | - this.removeAllListeners('timeout'); | ||
| 1850 | - | ||
| 1851 | - state.flags |= STREAM_FLAGS_CLOSED; | ||
| 1852 | - abort(this); | ||
| 1853 | - this.end(); | ||
| 1854 | - this.push(null); | ||
| 1855 | 1855 | session[kState].pendingStreams.delete(this); | |
| 1856 | 1856 | } | |
| 1857 | 1857 | ||
@@ -1884,13 +1884,23 @@ class Http2Stream extends Duplex { | |||
| 1884 | 1884 | } | |
| 1885 | 1885 | ||
| 1886 | 1886 | // TODO(mcollina): remove usage of _*State properties | |
| 1887 | - if (this._readableState.ended && | ||
| 1888 | - this._writableState.ended && | ||
| 1889 | - this._writableState.pendingcb === 0 && | ||
| 1890 | - this.closed) { | ||
| 1891 | - this.destroy(); | ||
| 1892 | - // This should return, but eslint complains. | ||
| 1893 | - // return | ||
| 1887 | + if (this._writableState.ended && this._writableState.pendingcb === 0) { | ||
| 1888 | + if (this._readableState.ended && this.closed) { | ||
| 1889 | + this.destroy(); | ||
| 1890 | + return; | ||
| 1891 | + } | ||
| 1892 | + | ||
| 1893 | + // We've submitted a response from our server session, have not attempted | ||
| 1894 | + // to process any incoming data, and have no trailers. This means we can | ||
| 1895 | + // attempt to gracefully close the session. | ||
| 1896 | + const state = this[kState]; | ||
| 1897 | + if (this.headersSent && | ||
| 1898 | + this[kSession][kType] === NGHTTP2_SESSION_SERVER && | ||
| 1899 | + !(state.flags & STREAM_FLAGS_HAS_TRAILERS) && | ||
| 1900 | + !state.didRead && | ||
| 1901 | + !this._readableState.resumeScheduled) { | ||
| 1902 | + this.close(); | ||
| 1903 | + } | ||
| 1894 | 1904 | } | |
| 1895 | 1905 | } | |
| 1896 | 1906 | } | |
@@ -2095,7 +2105,6 @@ function afterOpen(session, options, headers, streamOptions, err, fd) { | |||
| 2095 | 2105 | } | |
| 2096 | 2106 | if (this.destroyed || this.closed) { | |
| 2097 | 2107 | tryClose(fd); | |
| 2098 | - abort(this); | ||
| 2099 | 2108 | return; | |
| 2100 | 2109 | } | |
| 2101 | 2110 | state.fd = fd; | |
@@ -2224,8 +2233,10 @@ class ServerHttp2Stream extends Http2Stream { | |||
| 2224 | 2233 | if (options.endStream) | |
| 2225 | 2234 | streamOptions |= STREAM_OPTION_EMPTY_PAYLOAD; | |
| 2226 | 2235 | ||
| 2227 | - if (options.waitForTrailers) | ||
| 2236 | + if (options.waitForTrailers) { | ||
| 2228 | 2237 | streamOptions |= STREAM_OPTION_GET_TRAILERS; | |
| 2238 | + state.flags |= STREAM_FLAGS_HAS_TRAILERS; | ||
| 2239 | + } | ||
| 2229 | 2240 | ||
| 2230 | 2241 | headers = processHeaders(headers); | |
| 2231 | 2242 | const statusCode = headers[HTTP2_HEADER_STATUS] |= 0; | |
@@ -2285,8 +2296,10 @@ class ServerHttp2Stream extends Http2Stream { | |||
| 2285 | 2296 | } | |
| 2286 | 2297 | ||
| 2287 | 2298 | let streamOptions = 0; | |
| 2288 | - if (options.waitForTrailers) | ||
| 2299 | + if (options.waitForTrailers) { | ||
| 2289 | 2300 | streamOptions |= STREAM_OPTION_GET_TRAILERS; | |
| 2301 | + this[kState].flags |= STREAM_FLAGS_HAS_TRAILERS; | ||
| 2302 | + } | ||
| 2290 | 2303 | ||
| 2291 | 2304 | if (typeof fd !== 'number') | |
| 2292 | 2305 | throw new ERR_INVALID_ARG_TYPE('fd', 'number', fd); | |
@@ -2346,8 +2359,10 @@ class ServerHttp2Stream extends Http2Stream { | |||
| 2346 | 2359 | } | |
| 2347 | 2360 | ||
| 2348 | 2361 | let streamOptions = 0; | |
| 2349 | - if (options.waitForTrailers) | ||
| 2362 | + if (options.waitForTrailers) { | ||
| 2350 | 2363 | streamOptions |= STREAM_OPTION_GET_TRAILERS; | |
| 2364 | + this[kState].flags |= STREAM_FLAGS_HAS_TRAILERS; | ||
| 2365 | + } | ||
| 2351 | 2366 | ||
| 2352 | 2367 | const session = this[kSession]; | |
| 2353 | 2368 | debug(`Http2Stream ${this[kID]} [Http2Session ` + | |
| Back | FazBrowse Home | New Git URL |
0 commit comments