http2: implement ref() and unref() on client sessions
PR-URL: https://github.com/nodejs/node/pull/17620 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Anna Henningsen <anna@addaleax.net>
This commit is contained in:
parent
a3535f3f6f
commit
120ea9b5c4
153
doc/api/http2.md
153
doc/api/http2.md
@ -413,6 +413,14 @@ session.ping(Buffer.from('abcdefgh'), (err, duration, payload) => {
|
|||||||
If the `payload` argument is not specified, the default payload will be the
|
If the `payload` argument is not specified, the default payload will be the
|
||||||
64-bit timestamp (little endian) marking the start of the `PING` duration.
|
64-bit timestamp (little endian) marking the start of the `PING` duration.
|
||||||
|
|
||||||
|
#### http2session.ref()
|
||||||
|
<!-- YAML
|
||||||
|
added: REPLACEME
|
||||||
|
-->
|
||||||
|
|
||||||
|
Calls [`ref()`][`net.Socket.prototype.ref`] on this `Http2Session`
|
||||||
|
instance's underlying [`net.Socket`].
|
||||||
|
|
||||||
#### http2session.remoteSettings
|
#### http2session.remoteSettings
|
||||||
<!-- YAML
|
<!-- YAML
|
||||||
added: v8.4.0
|
added: v8.4.0
|
||||||
@ -423,69 +431,6 @@ added: v8.4.0
|
|||||||
A prototype-less object describing the current remote settings of this
|
A prototype-less object describing the current remote settings of this
|
||||||
`Http2Session`. The remote settings are set by the *connected* HTTP/2 peer.
|
`Http2Session`. The remote settings are set by the *connected* HTTP/2 peer.
|
||||||
|
|
||||||
#### http2session.request(headers[, options])
|
|
||||||
<!-- YAML
|
|
||||||
added: v8.4.0
|
|
||||||
-->
|
|
||||||
|
|
||||||
* `headers` {[Headers Object][]}
|
|
||||||
* `options` {Object}
|
|
||||||
* `endStream` {boolean} `true` if the `Http2Stream` *writable* side should
|
|
||||||
be closed initially, such as when sending a `GET` request that should not
|
|
||||||
expect a payload body.
|
|
||||||
* `exclusive` {boolean} When `true` and `parent` identifies a parent Stream,
|
|
||||||
the created stream is made the sole direct dependency of the parent, with
|
|
||||||
all other existing dependents made a dependent of the newly created stream.
|
|
||||||
**Default:** `false`
|
|
||||||
* `parent` {number} Specifies the numeric identifier of a stream the newly
|
|
||||||
created stream is dependent on.
|
|
||||||
* `weight` {number} Specifies the relative dependency of a stream in relation
|
|
||||||
to other streams with the same `parent`. The value is a number between `1`
|
|
||||||
and `256` (inclusive).
|
|
||||||
* `getTrailers` {Function} Callback function invoked to collect trailer
|
|
||||||
headers.
|
|
||||||
|
|
||||||
* Returns: {ClientHttp2Stream}
|
|
||||||
|
|
||||||
For HTTP/2 Client `Http2Session` instances only, the `http2session.request()`
|
|
||||||
creates and returns an `Http2Stream` instance that can be used to send an
|
|
||||||
HTTP/2 request to the connected server.
|
|
||||||
|
|
||||||
This method is only available if `http2session.type` is equal to
|
|
||||||
`http2.constants.NGHTTP2_SESSION_CLIENT`.
|
|
||||||
|
|
||||||
```js
|
|
||||||
const http2 = require('http2');
|
|
||||||
const clientSession = http2.connect('https://localhost:1234');
|
|
||||||
const {
|
|
||||||
HTTP2_HEADER_PATH,
|
|
||||||
HTTP2_HEADER_STATUS
|
|
||||||
} = http2.constants;
|
|
||||||
|
|
||||||
const req = clientSession.request({ [HTTP2_HEADER_PATH]: '/' });
|
|
||||||
req.on('response', (headers) => {
|
|
||||||
console.log(headers[HTTP2_HEADER_STATUS]);
|
|
||||||
req.on('data', (chunk) => { /** .. **/ });
|
|
||||||
req.on('end', () => { /** .. **/ });
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
When set, the `options.getTrailers()` function is called immediately after
|
|
||||||
queuing the last chunk of payload data to be sent. The callback is passed a
|
|
||||||
single object (with a `null` prototype) that the listener may used to specify
|
|
||||||
the trailing header fields to send to the peer.
|
|
||||||
|
|
||||||
*Note*: The HTTP/1 specification forbids trailers from containing HTTP/2
|
|
||||||
"pseudo-header" fields (e.g. `':method'`, `':path'`, etc). An `'error'` event
|
|
||||||
will be emitted if the `getTrailers` callback attempts to set such header
|
|
||||||
fields.
|
|
||||||
|
|
||||||
The the `:method` and `:path` pseudoheaders are not specified within `headers`,
|
|
||||||
they respectively default to:
|
|
||||||
|
|
||||||
* `:method` = `'GET'`
|
|
||||||
* `:path` = `/`
|
|
||||||
|
|
||||||
#### http2session.setTimeout(msecs, callback)
|
#### http2session.setTimeout(msecs, callback)
|
||||||
<!-- YAML
|
<!-- YAML
|
||||||
added: v8.4.0
|
added: v8.4.0
|
||||||
@ -605,6 +550,82 @@ The `http2session.type` will be equal to
|
|||||||
server, and `http2.constants.NGHTTP2_SESSION_CLIENT` if the instance is a
|
server, and `http2.constants.NGHTTP2_SESSION_CLIENT` if the instance is a
|
||||||
client.
|
client.
|
||||||
|
|
||||||
|
#### http2session.unref()
|
||||||
|
<!-- YAML
|
||||||
|
added: REPLACEME
|
||||||
|
-->
|
||||||
|
|
||||||
|
Calls [`unref()`][`net.Socket.prototype.unref`] on this `Http2Session`
|
||||||
|
instance's underlying [`net.Socket`].
|
||||||
|
|
||||||
|
### Class: ClientHttp2Session
|
||||||
|
<!-- YAML
|
||||||
|
added: v8.4.0
|
||||||
|
-->
|
||||||
|
|
||||||
|
#### clienthttp2session.request(headers[, options])
|
||||||
|
<!-- YAML
|
||||||
|
added: v8.4.0
|
||||||
|
-->
|
||||||
|
|
||||||
|
* `headers` {[Headers Object][]}
|
||||||
|
* `options` {Object}
|
||||||
|
* `endStream` {boolean} `true` if the `Http2Stream` *writable* side should
|
||||||
|
be closed initially, such as when sending a `GET` request that should not
|
||||||
|
expect a payload body.
|
||||||
|
* `exclusive` {boolean} When `true` and `parent` identifies a parent Stream,
|
||||||
|
the created stream is made the sole direct dependency of the parent, with
|
||||||
|
all other existing dependents made a dependent of the newly created stream.
|
||||||
|
**Default:** `false`
|
||||||
|
* `parent` {number} Specifies the numeric identifier of a stream the newly
|
||||||
|
created stream is dependent on.
|
||||||
|
* `weight` {number} Specifies the relative dependency of a stream in relation
|
||||||
|
to other streams with the same `parent`. The value is a number between `1`
|
||||||
|
and `256` (inclusive).
|
||||||
|
* `getTrailers` {Function} Callback function invoked to collect trailer
|
||||||
|
headers.
|
||||||
|
|
||||||
|
* Returns: {ClientHttp2Stream}
|
||||||
|
|
||||||
|
For HTTP/2 Client `Http2Session` instances only, the `http2session.request()`
|
||||||
|
creates and returns an `Http2Stream` instance that can be used to send an
|
||||||
|
HTTP/2 request to the connected server.
|
||||||
|
|
||||||
|
This method is only available if `http2session.type` is equal to
|
||||||
|
`http2.constants.NGHTTP2_SESSION_CLIENT`.
|
||||||
|
|
||||||
|
```js
|
||||||
|
const http2 = require('http2');
|
||||||
|
const clientSession = http2.connect('https://localhost:1234');
|
||||||
|
const {
|
||||||
|
HTTP2_HEADER_PATH,
|
||||||
|
HTTP2_HEADER_STATUS
|
||||||
|
} = http2.constants;
|
||||||
|
|
||||||
|
const req = clientSession.request({ [HTTP2_HEADER_PATH]: '/' });
|
||||||
|
req.on('response', (headers) => {
|
||||||
|
console.log(headers[HTTP2_HEADER_STATUS]);
|
||||||
|
req.on('data', (chunk) => { /** .. **/ });
|
||||||
|
req.on('end', () => { /** .. **/ });
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
When set, the `options.getTrailers()` function is called immediately after
|
||||||
|
queuing the last chunk of payload data to be sent. The callback is passed a
|
||||||
|
single object (with a `null` prototype) that the listener may used to specify
|
||||||
|
the trailing header fields to send to the peer.
|
||||||
|
|
||||||
|
*Note*: The HTTP/1 specification forbids trailers from containing HTTP/2
|
||||||
|
"pseudo-header" fields (e.g. `':method'`, `':path'`, etc). An `'error'` event
|
||||||
|
will be emitted if the `getTrailers` callback attempts to set such header
|
||||||
|
fields.
|
||||||
|
|
||||||
|
The `:method` and `:path` pseudoheaders are not specified within `headers`,
|
||||||
|
they respectively default to:
|
||||||
|
|
||||||
|
* `:method` = `'GET'`
|
||||||
|
* `:path` = `/`
|
||||||
|
|
||||||
### Class: Http2Stream
|
### Class: Http2Stream
|
||||||
<!-- YAML
|
<!-- YAML
|
||||||
added: v8.4.0
|
added: v8.4.0
|
||||||
@ -1669,9 +1690,9 @@ changes:
|
|||||||
[`Duplex`][] stream that is to be used as the connection for this session.
|
[`Duplex`][] stream that is to be used as the connection for this session.
|
||||||
* ...: Any [`net.connect()`][] or [`tls.connect()`][] options can be provided.
|
* ...: Any [`net.connect()`][] or [`tls.connect()`][] options can be provided.
|
||||||
* `listener` {Function}
|
* `listener` {Function}
|
||||||
* Returns {Http2Session}
|
* Returns {ClientHttp2Session}
|
||||||
|
|
||||||
Returns a HTTP/2 client `Http2Session` instance.
|
Returns a `ClientHttp2Session` instance.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
const http2 = require('http2');
|
const http2 = require('http2');
|
||||||
@ -2806,6 +2827,8 @@ if the stream is closed.
|
|||||||
[`http2.createServer()`]: #http2_http2_createserver_options_onrequesthandler
|
[`http2.createServer()`]: #http2_http2_createserver_options_onrequesthandler
|
||||||
[`http2stream.pushStream()`]: #http2_http2stream_pushstream_headers_options_callback
|
[`http2stream.pushStream()`]: #http2_http2stream_pushstream_headers_options_callback
|
||||||
[`net.Socket`]: net.html#net_class_net_socket
|
[`net.Socket`]: net.html#net_class_net_socket
|
||||||
|
[`net.Socket.prototype.ref`]: net.html#net_socket_ref
|
||||||
|
[`net.Socket.prototype.unref`]: net.html#net_socket_unref
|
||||||
[`net.connect()`]: net.html#net_net_connect
|
[`net.connect()`]: net.html#net_net_connect
|
||||||
[`request.socket.getPeerCertificate()`]: tls.html#tls_tlssocket_getpeercertificate_detailed
|
[`request.socket.getPeerCertificate()`]: tls.html#tls_tlssocket_getpeercertificate_detailed
|
||||||
[`response.end()`]: #http2_response_end_data_encoding_callback
|
[`response.end()`]: #http2_response_end_data_encoding_callback
|
||||||
|
@ -1128,6 +1128,18 @@ class Http2Session extends EventEmitter {
|
|||||||
|
|
||||||
process.nextTick(emit, this, 'timeout');
|
process.nextTick(emit, this, 'timeout');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ref() {
|
||||||
|
if (this[kSocket]) {
|
||||||
|
this[kSocket].ref();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unref() {
|
||||||
|
if (this[kSocket]) {
|
||||||
|
this[kSocket].unref();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ServerHttp2Session instances should never have to wait for the socket
|
// ServerHttp2Session instances should never have to wait for the socket
|
||||||
|
@ -1160,7 +1160,9 @@ Socket.prototype.ref = function() {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._handle.ref();
|
if (typeof this._handle.ref === 'function') {
|
||||||
|
this._handle.ref();
|
||||||
|
}
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
@ -1172,7 +1174,9 @@ Socket.prototype.unref = function() {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._handle.unref();
|
if (typeof this._handle.unref === 'function') {
|
||||||
|
this._handle.unref();
|
||||||
|
}
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
53
test/parallel/test-http2-session-unref.js
Normal file
53
test/parallel/test-http2-session-unref.js
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
'use strict';
|
||||||
|
// Flags: --expose-internals
|
||||||
|
|
||||||
|
// Tests that calling unref() on Http2Session:
|
||||||
|
// (1) Prevents it from keeping the process alive
|
||||||
|
// (2) Doesn't crash
|
||||||
|
|
||||||
|
const common = require('../common');
|
||||||
|
if (!common.hasCrypto)
|
||||||
|
common.skip('missing crypto');
|
||||||
|
const http2 = require('http2');
|
||||||
|
const makeDuplexPair = require('../common/duplexpair');
|
||||||
|
|
||||||
|
const server = http2.createServer();
|
||||||
|
const { clientSide, serverSide } = makeDuplexPair();
|
||||||
|
|
||||||
|
// 'session' event should be emitted 3 times:
|
||||||
|
// - the vanilla client
|
||||||
|
// - the destroyed client
|
||||||
|
// - manual 'connection' event emission with generic Duplex stream
|
||||||
|
server.on('session', common.mustCallAtLeast((session) => {
|
||||||
|
session.unref();
|
||||||
|
}, 3));
|
||||||
|
|
||||||
|
server.listen(0, common.mustCall(() => {
|
||||||
|
const port = server.address().port;
|
||||||
|
|
||||||
|
// unref new client
|
||||||
|
{
|
||||||
|
const client = http2.connect(`http://localhost:${port}`);
|
||||||
|
client.unref();
|
||||||
|
}
|
||||||
|
|
||||||
|
// unref destroyed client
|
||||||
|
{
|
||||||
|
const client = http2.connect(`http://localhost:${port}`);
|
||||||
|
client.destroy();
|
||||||
|
client.unref();
|
||||||
|
}
|
||||||
|
|
||||||
|
// unref destroyed client
|
||||||
|
{
|
||||||
|
const client = http2.connect(`http://localhost:${port}`, {
|
||||||
|
createConnection: common.mustCall(() => clientSide)
|
||||||
|
});
|
||||||
|
client.destroy();
|
||||||
|
client.unref();
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
server.emit('connection', serverSide);
|
||||||
|
server.unref();
|
||||||
|
|
||||||
|
setTimeout(common.mustNotCall(() => {}), 1000).unref();
|
Loading…
x
Reference in New Issue
Block a user