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: improve tests and docs #42858

Merged
merged 3 commits into from
May 24, 2022
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
29 changes: 25 additions & 4 deletions doc/api/http2.md
Original file line number Diff line number Diff line change
Expand Up @@ -1479,6 +1479,9 @@ the client should send the request body.
added: v8.4.0
-->

* `headers` {HTTP/2 Headers Object}
* `flags` {number}

The `'headers'` event is emitted when an additional block of headers is received
for a stream, such as when a block of `1xx` informational headers is received.
The listener callback is passed the [HTTP/2 Headers Object][] and flags
Expand All @@ -1496,6 +1499,9 @@ stream.on('headers', (headers, flags) => {
added: v8.4.0
-->

* `headers` {HTTP/2 Headers Object}
* `flags` {number}

The `'push'` event is emitted when response headers for a Server Push stream
are received. The listener callback is passed the [HTTP/2 Headers Object][] and
flags associated with the headers.
Expand All @@ -1512,6 +1518,9 @@ stream.on('push', (headers, flags) => {
added: v8.4.0
-->

* `headers` {HTTP/2 Headers Object}
* `flags` {number}

The `'response'` event is emitted when a response `HEADERS` frame has been
received for this stream from the connected HTTP/2 server. The listener is
invoked with two arguments: an `Object` containing the received
Expand Down Expand Up @@ -1652,10 +1661,10 @@ server.on('stream', (stream) => {
});
```

When the `options.waitForTrailers` option is set, the `'wantTrailers'` event
will be emitted immediately after queuing the last chunk of payload data to be
sent. The `http2stream.sendTrailers()` method can then be used to sent trailing
header fields to the peer.
Initiates a response. When the `options.waitForTrailers` option is set, the
`'wantTrailers'` event will be emitted immediately after queuing the last chunk
of payload data to be sent. The `http2stream.sendTrailers()` method can then be
used to sent trailing header fields to the peer.

When `options.waitForTrailers` is set, the `Http2Stream` will not automatically
close when the final `DATA` frame is transmitted. User code must call either
Expand Down Expand Up @@ -1973,6 +1982,8 @@ per session. See the [Compatibility API][].
added: v8.4.0
-->

* `session` {ServerHttp2Session}

The `'session'` event is emitted when a new `Http2Session` is created by the
`Http2Server`.

Expand All @@ -1982,6 +1993,9 @@ The `'session'` event is emitted when a new `Http2Session` is created by the
added: v8.4.0
-->

* `error` {Error}
* `session` {ServerHttp2Session}

The `'sessionError'` event is emitted when an `'error'` event is emitted by
an `Http2Session` object associated with the `Http2Server`.

Expand Down Expand Up @@ -2188,6 +2202,8 @@ per session. See the [Compatibility API][].
added: v8.4.0
-->

* `session` {ServerHttp2Session}

The `'session'` event is emitted when a new `Http2Session` is created by the
`Http2SecureServer`.

Expand All @@ -2197,6 +2213,9 @@ The `'session'` event is emitted when a new `Http2Session` is created by the
added: v8.4.0
-->

* `error` {Error}
* `session` {ServerHttp2Session}

The `'sessionError'` event is emitted when an `'error'` event is emitted by
an `Http2Session` object associated with the `Http2SecureServer`.

Expand Down Expand Up @@ -2258,6 +2277,8 @@ a given number of milliseconds set using `http2secureServer.setTimeout()`.
added: v8.4.0
-->

* `socket` {stream.Duplex}

The `'unknownProtocol'` event is emitted when a connecting client fails to
negotiate an allowed protocol (i.e. HTTP/2 or HTTP/1.1). The event handler
receives the socket for handling. If no listener is registered for this event,
Expand Down
1 change: 1 addition & 0 deletions lib/internal/http2/core.js
Original file line number Diff line number Diff line change
Expand Up @@ -3401,6 +3401,7 @@ module.exports = {
sensitiveHeaders: kSensitiveHeaders,
Http2Session,
Http2Stream,
ServerHttp2Session,
Http2ServerRequest,
Http2ServerResponse
};
Expand Down
2 changes: 2 additions & 0 deletions test/parallel/test-http2-https-fallback.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const { createSecureServer, connect } = require('http2');
const { get } = require('https');
const { parse } = require('url');
const { connect: tls } = require('tls');
const { Duplex } = require('stream');

const countdown = (count, done) => () => --count === 0 && done();

Expand Down Expand Up @@ -115,6 +116,7 @@ function onSession(session, next) {
);

server.once('unknownProtocol', common.mustCall((socket) => {
strictEqual(socket instanceof Duplex, true);
socket.destroy();
}));

Expand Down
57 changes: 55 additions & 2 deletions test/parallel/test-http2-options-max-headers-exceeds-nghttp2.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
// Flags: --expose-internals
'use strict';

const common = require('../common');
if (!common.hasCrypto)
common.skip('missing crypto');
if (!common.hasCrypto) common.skip('missing crypto');
const h2 = require('http2');
const assert = require('assert');
const { ServerHttp2Session } = require('internal/http2/core');

const server = h2.createServer();

Expand Down Expand Up @@ -44,3 +46,54 @@ server.listen(0, common.mustCall(() => {
}));
req.end();
}));

{
const options = {
maxSendHeaderBlockLength: 100000,
};

const server = h2.createServer(options);

server.on('error', common.mustNotCall());
server.on(
'session',
common.mustCall((session) => {
assert.strictEqual(session instanceof ServerHttp2Session, true);
}),
);
server.on(
'stream',
common.mustCall((stream) => {
stream.additionalHeaders({
// Greater than 65536 bytes
'test-header': 'A'.repeat(90000),
});
stream.respond();
stream.end();
}),
);

server.on(
'sessionError',
common.mustCall((err, session) => {
assert.strictEqual(err.code, 'ERR_HTTP2_SESSION_ERROR');
assert.strictEqual(err.name, 'Error');
assert.strictEqual(err.message, 'Session closed with error code 9');
assert.strictEqual(session instanceof ServerHttp2Session, true);
server.close();
}),
);

server.listen(
0,
common.mustCall(() => {
const client = h2.connect(`http://localhost:${server.address().port}`);
daeyeon marked this conversation as resolved.
Show resolved Hide resolved
client.on('error', common.mustNotCall());

const req = client.request();
req.on('response', common.mustNotCall());
req.on('error', common.mustNotCall());
req.end();
}),
);
}
3 changes: 2 additions & 1 deletion test/parallel/test-http2-sent-headers.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,9 @@ server.listen(0, common.mustCall(() => {
const client = h2.connect(`http://localhost:${server.address().port}`);
const req = client.request();

req.on('headers', common.mustCall((headers) => {
req.on('headers', common.mustCall((headers, flags) => {
assert.strictEqual(headers[':status'], 102);
assert.strictEqual(typeof flags === 'number', true);
}));

assert.strictEqual(req.sentHeaders[':method'], 'GET');
Expand Down
3 changes: 2 additions & 1 deletion test/parallel/test-http2-server-push-stream.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,11 @@ server.listen(0, common.mustCall(() => {
assert.strictEqual(headers[':scheme'], 'http');
assert.strictEqual(headers[':path'], '/foobar');
assert.strictEqual(headers[':authority'], `localhost:${port}`);
stream.on('push', common.mustCall((headers) => {
stream.on('push', common.mustCall((headers, flags) => {
assert.strictEqual(headers[':status'], 200);
assert.strictEqual(headers['content-type'], 'text/html');
assert.strictEqual(headers['x-push-data'], 'pushed by server');
assert.strictEqual(typeof flags === 'number', true);
}));
stream.on('aborted', common.mustNotCall());
// We have to read the data of the push stream to end gracefully.
Expand Down
9 changes: 9 additions & 0 deletions test/parallel/test-http2-server-sessionerror.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,17 @@ const common = require('../common');
if (!common.hasCrypto)
common.skip('missing crypto');
const http2 = require('http2');
const assert = require('assert');
const { kSocket } = require('internal/http2/util');
const { ServerHttp2Session } = require('internal/http2/core');

const server = http2.createServer();
server.on('stream', common.mustNotCall());

let test = 0;

server.on('session', common.mustCall((session) => {
assert.strictEqual(session instanceof ServerHttp2Session, true);
switch (++test) {
case 1:
server.on('error', common.mustNotCall());
Expand All @@ -32,6 +35,12 @@ server.on('session', common.mustCall((session) => {
}
}, 2));

server.on('sessionError', common.mustCall((err, session) => {
assert.strictEqual(err.name, 'Error');
assert.strictEqual(err.message, 'test');
assert.strictEqual(session instanceof ServerHttp2Session, true);
}, 2));

server.listen(0, common.mustCall(() => {
const url = `http://localhost:${server.address().port}`;
http2.connect(url)
Expand Down
1 change: 1 addition & 0 deletions tools/doc/type-parser.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ const customTypesMap = {
'Http2Session': 'http2.html#class-http2session',
'Http2Stream': 'http2.html#class-http2stream',
'ServerHttp2Stream': 'http2.html#class-serverhttp2stream',
'ServerHttp2Session': 'http2.html#class-serverhttp2session',

'https.Server': 'https.html#class-httpsserver',

Expand Down