Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

http2: backport changes to v9.x-staging #18050

Closed
wants to merge 27 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
1724a4e
src: add optional keep-alive object to SetImmediate
addaleax Nov 21, 2017
df31133
http2: don't call into JS from GC
addaleax Nov 21, 2017
8c28e30
http2: only schedule write when necessary
addaleax Nov 21, 2017
504ee24
http2: be sure to destroy the Http2Stream
jasnell Nov 27, 2017
ac89b78
http2: cleanup Http2Stream/Http2Session destroy
jasnell Dec 12, 2017
7ab5c62
http2: remove redundant write indirection
addaleax Dec 17, 2017
66be8e5
http2: refactor outgoing write mechanism
addaleax Dec 17, 2017
293c2f9
http2: convert Http2Settings to an AsyncWrap
jasnell Dec 18, 2017
08478c1
http2: fix compiling with `--debug-http2`
addaleax Dec 25, 2017
b61c3fc
http2: keep session objects alive during Http2Scope
addaleax Dec 25, 2017
afa4a7c
http2: implement ref() and unref() on client sessions
kjin Dec 11, 2017
8d8dfea
http2: remove duplicate words in comments
tniessen Jan 1, 2018
d968c02
http2: perf_hooks integration
jasnell Dec 20, 2017
bf7eed3
http2: strictly limit number on concurrent streams
jasnell Nov 21, 2017
8df5d02
http2: add altsvc support
jasnell Dec 29, 2017
ae4e308
tls: set servername on client side too
jasnell Jan 1, 2018
7d6aa20
http2: add initial support for originSet
jasnell Jan 1, 2018
760f678
http2: add aligned padding strategy
jasnell Jan 1, 2018
06ed513
http2: properly handle already closed stream error
jasnell Jan 2, 2018
e929bd0
doc: add docs for common/http2.js utility
jasnell Jan 2, 2018
c1c4a6c
src: silence http2 -Wunused-result warnings
cjihrig Jan 2, 2018
adbc38a
http2: implement maxSessionMemory
jasnell Jan 3, 2018
51dc318
http2: verify that a dependency cycle may exist
jasnell Jan 3, 2018
d1e75eb
doc: grammar fixes in http2.md
Trott Jan 4, 2018
cd6fcf3
http2: verify flood error and unsolicited frames
jasnell Jan 3, 2018
526cf84
doc: correct spelling
sreepurnajasti Dec 29, 2017
d7dd941
perf_hooks: fix scheduling regression
apapirovski Jan 9, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
http2: implement ref() and unref() on client sessions
PR-URL: #17620
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
  • Loading branch information
kjin authored and MylesBorins committed Jan 9, 2018
commit afa4a7c0d2ca9da9ca0907b0ca1f5bc99e430f99
153 changes: 88 additions & 65 deletions doc/api/http2.md
Original file line number Diff line number Diff line change
Expand Up @@ -413,78 +413,23 @@ session.ping(Buffer.from('abcdefgh'), (err, duration, payload) => {
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.

#### http2session.remoteSettings
#### http2session.ref()
<!-- YAML
added: v8.4.0
added: REPLACEME
-->

* Value: {[Settings Object][]}
Calls [`ref()`][`net.Socket.prototype.ref`] on this `Http2Session`
instance's underlying [`net.Socket`].

A prototype-less object describing the current remote settings of this
`Http2Session`. The remote settings are set by the *connected* HTTP/2 peer.

#### http2session.request(headers[, options])
#### http2session.remoteSettings
<!-- 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:
* Value: {[Settings Object][]}

* `:method` = `'GET'`
* `:path` = `/`
A prototype-less object describing the current remote settings of this
`Http2Session`. The remote settings are set by the *connected* HTTP/2 peer.

#### http2session.setTimeout(msecs, callback)
<!-- YAML
Expand Down Expand Up @@ -605,6 +550,82 @@ The `http2session.type` will be equal to
server, and `http2.constants.NGHTTP2_SESSION_CLIENT` if the instance is a
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
<!-- YAML
added: v8.4.0
Expand Down Expand Up @@ -1669,9 +1690,9 @@ changes:
[`Duplex`][] stream that is to be used as the connection for this session.
* ...: Any [`net.connect()`][] or [`tls.connect()`][] options can be provided.
* `listener` {Function}
* Returns {Http2Session}
* Returns {ClientHttp2Session}

Returns a HTTP/2 client `Http2Session` instance.
Returns a `ClientHttp2Session` instance.

```js
const http2 = require('http2');
Expand Down Expand Up @@ -2806,6 +2827,8 @@ if the stream is closed.
[`http2.createServer()`]: #http2_http2_createserver_options_onrequesthandler
[`http2stream.pushStream()`]: #http2_http2stream_pushstream_headers_options_callback
[`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
[`request.socket.getPeerCertificate()`]: tls.html#tls_tlssocket_getpeercertificate_detailed
[`response.end()`]: #http2_response_end_data_encoding_callback
Expand Down
12 changes: 12 additions & 0 deletions lib/internal/http2/core.js
Original file line number Diff line number Diff line change
Expand Up @@ -1124,6 +1124,18 @@ class Http2Session extends EventEmitter {

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
Expand Down
8 changes: 6 additions & 2 deletions lib/net.js
Original file line number Diff line number Diff line change
Expand Up @@ -1133,7 +1133,9 @@ Socket.prototype.ref = function() {
return this;
}

this._handle.ref();
if (typeof this._handle.ref === 'function') {
this._handle.ref();
}

return this;
};
Expand All @@ -1145,7 +1147,9 @@ Socket.prototype.unref = function() {
return this;
}

this._handle.unref();
if (typeof this._handle.unref === 'function') {
this._handle.unref();
}

return this;
};
Expand Down
53 changes: 53 additions & 0 deletions test/parallel/test-http2-session-unref.js
Original file line number Diff line number Diff line change
@@ -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();