diff --git a/doc/api/http2.md b/doc/api/http2.md index a186060eadecfd..3c82d0fb3b32cc 100644 --- a/doc/api/http2.md +++ b/doc/api/http2.md @@ -1445,6 +1445,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 @@ -1462,6 +1465,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. @@ -1478,6 +1484,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 @@ -1612,10 +1621,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 @@ -1933,6 +1942,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`. @@ -1942,6 +1953,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`. @@ -2141,6 +2155,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`. @@ -2150,6 +2166,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`. @@ -2211,6 +2230,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, diff --git a/lib/internal/http2/core.js b/lib/internal/http2/core.js index cc0bfe743fff17..661a6d111dee93 100644 --- a/lib/internal/http2/core.js +++ b/lib/internal/http2/core.js @@ -3393,6 +3393,7 @@ module.exports = { sensitiveHeaders: kSensitiveHeaders, Http2Session, Http2Stream, + ServerHttp2Session, Http2ServerRequest, Http2ServerResponse }; diff --git a/test/parallel/test-http2-https-fallback.js b/test/parallel/test-http2-https-fallback.js index a872d686d34f85..16879b05f12c8d 100644 --- a/test/parallel/test-http2-https-fallback.js +++ b/test/parallel/test-http2-https-fallback.js @@ -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(); @@ -115,6 +116,7 @@ function onSession(session, next) { ); server.once('unknownProtocol', common.mustCall((socket) => { + strictEqual(socket instanceof Duplex, true); socket.destroy(); })); diff --git a/test/parallel/test-http2-options-max-headers-exceeds-nghttp2.js b/test/parallel/test-http2-options-max-headers-exceeds-nghttp2.js index e503e226ef2da1..df3aefff11e7ad 100644 --- a/test/parallel/test-http2-options-max-headers-exceeds-nghttp2.js +++ b/test/parallel/test-http2-options-max-headers-exceeds-nghttp2.js @@ -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(); @@ -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}`); + client.on('error', common.mustNotCall()); + + const req = client.request(); + req.on('response', common.mustNotCall()); + req.on('error', common.mustNotCall()); + req.end(); + }), + ); +} diff --git a/test/parallel/test-http2-sent-headers.js b/test/parallel/test-http2-sent-headers.js index 6ec674394336f2..6a492cf13c1ecd 100644 --- a/test/parallel/test-http2-sent-headers.js +++ b/test/parallel/test-http2-sent-headers.js @@ -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'); diff --git a/test/parallel/test-http2-server-push-stream.js b/test/parallel/test-http2-server-push-stream.js index 7c75f66aef5e7d..54b996fa642eff 100644 --- a/test/parallel/test-http2-server-push-stream.js +++ b/test/parallel/test-http2-server-push-stream.js @@ -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. diff --git a/test/parallel/test-http2-server-sessionerror.js b/test/parallel/test-http2-server-sessionerror.js index bbd180937e1cd6..e266661b0cc2c3 100644 --- a/test/parallel/test-http2-server-sessionerror.js +++ b/test/parallel/test-http2-server-sessionerror.js @@ -6,7 +6,9 @@ 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()); @@ -14,6 +16,7 @@ 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()); @@ -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) diff --git a/tools/doc/type-parser.mjs b/tools/doc/type-parser.mjs index fe22a6928e093a..de88470e4b5679 100644 --- a/tools/doc/type-parser.mjs +++ b/tools/doc/type-parser.mjs @@ -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',