Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
7 changes: 7 additions & 0 deletions doc/api/diagnostics_channel.md
Original file line number Diff line number Diff line change
Expand Up @@ -1210,6 +1210,13 @@ Emitted when server sends a response.

Emitted when a stream is created on the client.

`http2.client.stream.start`

* `stream` {ClientHttp2Stream}
* `headers` {HTTP/2 Headers Object}

Emitted when a stream is started on the client.

#### Modules

`module.require.start`
Expand Down
19 changes: 16 additions & 3 deletions lib/internal/http2/core.js
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ const { _connectionListener: httpConnectionListener } = http;

const dc = require('diagnostics_channel');
const onClientStreamCreatedChannel = dc.channel('http2.client.stream.created');
const onClientStreamStartChannel = dc.channel('http2.client.stream.start');

let debug = require('internal/util/debuglog').debuglog('http2', (fn) => {
debug = fn;
Expand Down Expand Up @@ -381,6 +382,12 @@ function onSessionHeaders(handle, id, cat, flags, headers, sensitiveHeaders) {
headers: obj,
});
}
if (onClientStreamStartChannel.hasSubscribers) {
onClientStreamStartChannel.publish({
stream,
headers: obj,
});
}
if (endOfStream) {
stream.push(null);
}
Expand Down Expand Up @@ -717,7 +724,7 @@ function onGoawayData(code, lastStreamID, buf) {
// When a ClientHttp2Session is first created, the socket may not yet be
// connected. If request() is called during this time, the actual request
// will be deferred until the socket is ready to go.
function requestOnConnect(headers, options) {
function requestOnConnect(headersList, headersParam, options) {
const session = this[kSession];

// At this point, the stream should have already been destroyed during
Expand All @@ -744,7 +751,7 @@ function requestOnConnect(headers, options) {

// `ret` will be either the reserved stream ID (if positive)
// or an error code (if negative)
const ret = session[kHandle].request(headers,
const ret = session[kHandle].request(headersList,
streamOptions,
options.parent | 0,
options.weight | 0,
Expand Down Expand Up @@ -776,6 +783,12 @@ function requestOnConnect(headers, options) {
return;
}
this[kInit](ret.id(), ret);
if (onClientStreamStartChannel.hasSubscribers) {
onClientStreamStartChannel.publish({
stream: this,
headers: headersParam,
});
}
}

// Validates that priority options are correct, specifically:
Expand Down Expand Up @@ -1859,7 +1872,7 @@ class ClientHttp2Session extends Http2Session {
}
}

const onConnect = reqAsync.bind(requestOnConnect.bind(stream, headersList, options));
const onConnect = reqAsync.bind(requestOnConnect.bind(stream, headersList, headersParam, options));
if (this.connecting) {
if (this[kPendingRequestCalls] !== null) {
this[kPendingRequestCalls].push(onConnect);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
'use strict';

const common = require('../common');
if (!common.hasCrypto)
common.skip('missing crypto');

// This test ensures that the built-in HTTP/2 diagnostics channels are reporting
// the diagnostics messages for the 'http2.client.stream.start' channel when
// ClientHttp2Streams are started by both:
// - the client calling ClientHttp2Session#request()
// - in response to an incoming 'push' event from the server

const Countdown = require('../common/countdown');
const assert = require('assert');
const dc = require('diagnostics_channel');
const http2 = require('http2');
const { Duplex } = require('stream');

const clientHttp2StreamStartCount = 2;

dc.subscribe('http2.client.stream.start', common.mustCall(({ stream, headers }) => {
// Since ClientHttp2Stream is not exported from any module, this just checks
// if the stream is an instance of Duplex.
assert.ok(stream instanceof Duplex);
assert.strictEqual(stream.constructor.name, 'ClientHttp2Stream');
assert.ok(headers && !Array.isArray(headers) && typeof headers === 'object');
}, clientHttp2StreamStartCount));

const server = http2.createServer();
server.on('stream', common.mustCall((stream) => {
stream.respond();
stream.end();

stream.pushStream({}, common.mustSucceed((pushStream) => {
pushStream.respond();
pushStream.end();
}, 1));
}, 1));

server.listen(0, common.mustCall(() => {
const port = server.address().port;
const client = http2.connect(`http://localhost:${port}`);

const countdown = new Countdown(clientHttp2StreamStartCount, () => {
client.close();
server.close();
});

const stream = client.request({});
stream.on('response', common.mustCall(() => {
countdown.dec();
}));

client.on('stream', common.mustCall((pushStream) => {
pushStream.on('push', common.mustCall(() => {
countdown.dec();
}, 1));
}, 1));
}, 1));
Loading