diff --git a/doc/api/http2.md b/doc/api/http2.md index ae143c8bbdd448..0a199fa6dfb30c 100644 --- a/doc/api/http2.md +++ b/doc/api/http2.md @@ -84,7 +84,30 @@ Since there are no browsers known that support [`http2.createSecureServer()`][] is necessary when communicating with browser clients. -```js +```mjs +import { createSecureServer } from 'node:http2'; +import { readFileSync } from 'node:fs'; + +const server = createSecureServer({ + key: readFileSync('localhost-privkey.pem'), + cert: readFileSync('localhost-cert.pem'), +}); + +server.on('error', (err) => console.error(err)); + +server.on('stream', (stream, headers) => { + // stream is a Duplex + stream.respond({ + 'content-type': 'text/html; charset=utf-8', + ':status': 200, + }); + stream.end('

Hello World

'); +}); + +server.listen(8443); +``` + +```cjs const http2 = require('node:http2'); const fs = require('node:fs'); @@ -117,9 +140,37 @@ openssl req -x509 -newkey rsa:2048 -nodes -sha256 -subj '/CN=localhost' \ The following illustrates an HTTP/2 client: -```js +```mjs +import { connect } from 'node:http2'; +import { readFileSync } from 'node:fs'; + +const client = connect('https://localhost:8443', { + ca: readFileSync('localhost-cert.pem'), +}); +client.on('error', (err) => console.error(err)); + +const req = client.request({ ':path': '/' }); + +req.on('response', (headers, flags) => { + for (const name in headers) { + console.log(`${name}: ${headers[name]}`); + } +}); + +req.setEncoding('utf8'); +let data = ''; +req.on('data', (chunk) => { data += chunk; }); +req.on('end', () => { + console.log(`\n${data}`); + client.close(); +}); +req.end(); +``` + +```cjs const http2 = require('node:http2'); const fs = require('node:fs'); + const client = http2.connect('https://localhost:8443', { ca: fs.readFileSync('localhost-cert.pem'), }); @@ -320,7 +371,6 @@ added: v8.4.0 The `'stream'` event is emitted when a new `Http2Stream` is created. ```js -const http2 = require('node:http2'); session.on('stream', (stream, headers, flags) => { const method = headers[':method']; const path = headers[':path']; @@ -339,7 +389,25 @@ and would instead register a handler for the `'stream'` event emitted by the `net.Server` or `tls.Server` instances returned by `http2.createServer()` and `http2.createSecureServer()`, respectively, as in the example below: -```js +```mjs +import { createServer } from 'node:http2'; + +// Create an unencrypted HTTP/2 server +const server = createServer(); + +server.on('stream', (stream, headers) => { + stream.respond({ + 'content-type': 'text/html; charset=utf-8', + ':status': 200, + }); + stream.on('error', (error) => console.error(error)); + stream.end('

Hello World

'); +}); + +server.listen(8000); +``` + +```cjs const http2 = require('node:http2'); // Create an unencrypted HTTP/2 server @@ -606,7 +674,19 @@ Sets the local endpoint's window size. The `windowSize` is the total window size to set, not the delta. -```js +```mjs +import { createServer } from 'node:http2'; + +const server = createServer(); +const expectedWindowSize = 2 ** 20; +server.on('session', (session) => { + + // Set local window size to be 2 ** 20 + session.setLocalWindowSize(expectedWindowSize); +}); +``` + +```cjs const http2 = require('node:http2'); const server = http2.createServer(); @@ -764,7 +844,22 @@ added: v9.4.0 Submits an `ALTSVC` frame (as defined by [RFC 7838][]) to the connected client. -```js +```mjs +import { createServer } from 'node:http2'; + +const server = createServer(); +server.on('session', (session) => { + // Set altsvc for origin https://example.org:80 + session.altsvc('h2=":8000"', 'https://example.org:80'); +}); + +server.on('stream', (stream) => { + // Set altsvc for a specific stream + stream.session.altsvc('h2=":8000"', stream.id); +}); +``` + +```cjs const http2 = require('node:http2'); const server = http2.createServer(); @@ -830,7 +925,20 @@ Submits an `ORIGIN` frame (as defined by [RFC 8336][]) to the connected client to advertise the set of origins for which the server is capable of providing authoritative responses. -```js +```mjs +import { createSecureServer } from 'node:http2'; +const options = getSecureOptionsSomehow(); +const server = createSecureServer(options); +server.on('stream', (stream) => { + stream.respond(); + stream.end('ok'); +}); +server.on('session', (session) => { + session.origin('https://example.com', 'https://example.org'); +}); +``` + +```cjs const http2 = require('node:http2'); const options = getSecureOptionsSomehow(); const server = http2.createSecureServer(options); @@ -857,7 +965,18 @@ ASCII origin. Alternatively, the `origins` option may be used when creating a new HTTP/2 server using the `http2.createSecureServer()` method: -```js +```mjs +import { createSecureServer } from 'node:http2'; +const options = getSecureOptionsSomehow(); +options.origins = ['https://example.com', 'https://example.org']; +const server = createSecureServer(options); +server.on('stream', (stream) => { + stream.respond(); + stream.end('ok'); +}); +``` + +```cjs const http2 = require('node:http2'); const options = getSecureOptionsSomehow(); options.origins = ['https://example.com', 'https://example.org']; @@ -891,7 +1010,18 @@ the client. The event is emitted with the `ALTSVC` value, origin, and stream ID. If no `origin` is provided in the `ALTSVC` frame, `origin` will be an empty string. -```js +```mjs +import { connect } from 'node:http2'; +const client = connect('https://example.org'); + +client.on('altsvc', (alt, origin, streamId) => { + console.log(alt); + console.log(origin); + console.log(streamId); +}); +``` + +```cjs const http2 = require('node:http2'); const client = http2.connect('https://example.org'); @@ -915,7 +1045,17 @@ the client. The event is emitted with an array of `origin` strings. The `http2session.originSet` will be updated to include the received origins. -```js +```mjs +import { connect } from 'node:http2'; +const client = connect('https://example.org'); + +client.on('origin', (origins) => { + for (let n = 0; n < origins.length; n++) + console.log(origins[n]); +}); +``` + +```cjs const http2 = require('node:http2'); const client = http2.connect('https://example.org'); @@ -968,7 +1108,23 @@ If the `session` is closed before the actual request be executed, an This method is only available if `http2session.type` is equal to `http2.constants.NGHTTP2_SESSION_CLIENT`. -```js +```mjs +import { connect, constants } from 'node:http2'; +const clientSession = connect('https://localhost:1234'); +const { + HTTP2_HEADER_PATH, + HTTP2_HEADER_STATUS, +} = 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', () => { /* .. */ }); +}); +``` + +```cjs const http2 = require('node:http2'); const clientSession = http2.connect('https://localhost:1234'); const { @@ -1388,7 +1544,17 @@ changes: * `msecs` {number} * `callback` {Function} -```js +```mjs +import { connect, constants } from 'node:http2'; +const client = connect('http://example.org:8000'); +const { NGHTTP2_CANCEL } = constants; +const req = client.request({ ':path': '/' }); + +// Cancel the stream if there's no activity after 5 seconds +req.setTimeout(5000, () => req.close(NGHTTP2_CANCEL)); +``` + +```cjs const http2 = require('node:http2'); const client = http2.connect('http://example.org:8000'); const { NGHTTP2_CANCEL } = http2.constants; @@ -1437,7 +1603,19 @@ request or sending a response, the `options.waitForTrailers` option must be set in order to keep the `Http2Stream` open after the final `DATA` frame so that trailers can be sent. -```js +```mjs +import { createServer } from 'node:http2'; +const server = createServer(); +server.on('stream', (stream) => { + stream.respond(undefined, { waitForTrailers: true }); + stream.on('wantTrailers', () => { + stream.sendTrailers({ xyz: 'abc' }); + }); + stream.end('Hello World'); +}); +``` + +```cjs const http2 = require('node:http2'); const server = http2.createServer(); server.on('stream', (stream) => { @@ -1528,7 +1706,16 @@ received for this stream from the connected HTTP/2 server. The listener is invoked with two arguments: an `Object` containing the received [HTTP/2 Headers Object][], and flags associated with the headers. -```js +```mjs +import { connect } from 'node:http2'; +const client = connect('https://localhost'); +const req = client.request({ ':path': '/' }); +req.on('response', (headers, flags) => { + console.log(headers[':status']); +}); +``` + +```cjs const http2 = require('node:http2'); const client = http2.connect('https://localhost'); const req = client.request({ ':path': '/' }); @@ -1614,7 +1801,21 @@ Initiates a push stream. The callback is invoked with the new `Http2Stream` instance created for the push stream passed as the second argument, or an `Error` passed as the first argument. -```js +```mjs +import { createServer } from 'node:http2'; +const server = createServer(); +server.on('stream', (stream) => { + stream.respond({ ':status': 200 }); + stream.pushStream({ ':path': '/' }, (err, pushStream, headers) => { + if (err) throw err; + pushStream.respond({ ':status': 200 }); + pushStream.end('some pushed data'); + }); + stream.end('some data'); +}); +``` + +```cjs const http2 = require('node:http2'); const server = http2.createServer(); server.on('stream', (stream) => { @@ -1654,7 +1855,16 @@ changes: * `waitForTrailers` {boolean} When `true`, the `Http2Stream` will emit the `'wantTrailers'` event after the final `DATA` frame has been sent. -```js +```mjs +import { createServer } from 'node:http2'; +const server = createServer(); +server.on('stream', (stream) => { + stream.respond({ ':status': 200 }); + stream.end('some data'); +}); +``` + +```cjs const http2 = require('node:http2'); const server = http2.createServer(); server.on('stream', (stream) => { @@ -1673,7 +1883,19 @@ close when the final `DATA` frame is transmitted. User code must call either `http2stream.sendTrailers()` or `http2stream.close()` to close the `Http2Stream`. -```js +```mjs +import { createServer } from 'node:http2'; +const server = createServer(); +server.on('stream', (stream) => { + stream.respond({ ':status': 200 }, { waitForTrailers: true }); + stream.on('wantTrailers', () => { + stream.sendTrailers({ ABC: 'some value to send' }); + }); + stream.end('some data'); +}); +``` + +```cjs const http2 = require('node:http2'); const server = http2.createServer(); server.on('stream', (stream) => { @@ -1721,7 +1943,26 @@ closed using an `RST_STREAM` frame using the standard `INTERNAL_ERROR` code. When used, the `Http2Stream` object's `Duplex` interface will be closed automatically. -```js +```mjs +import { createServer } from 'node:http2'; +import { openSync, fstatSync, closeSync } from 'node:fs'; + +const server = createServer(); +server.on('stream', (stream) => { + const fd = openSync('/some/file', 'r'); + + const stat = fstatSync(fd); + const headers = { + 'content-length': stat.size, + 'last-modified': stat.mtime.toUTCString(), + 'content-type': 'text/plain; charset=utf-8', + }; + stream.respondWithFD(fd, headers); + stream.on('close', () => closeSync(fd)); +}); +``` + +```cjs const http2 = require('node:http2'); const fs = require('node:fs'); @@ -1766,7 +2007,30 @@ close when the final `DATA` frame is transmitted. User code _must_ call either `http2stream.sendTrailers()` or `http2stream.close()` to close the `Http2Stream`. -```js +```mjs +import { createServer } from 'node:http2'; +import { openSync, fstatSync, closeSync } from 'node:fs'; + +const server = createServer(); +server.on('stream', (stream) => { + const fd = openSync('/some/file', 'r'); + + const stat = fstatSync(fd); + const headers = { + 'content-length': stat.size, + 'last-modified': stat.mtime.toUTCString(), + 'content-type': 'text/plain; charset=utf-8', + }; + stream.respondWithFD(fd, headers, { waitForTrailers: true }); + stream.on('wantTrailers', () => { + stream.sendTrailers({ ABC: 'some value to send' }); + }); + + stream.on('close', () => closeSync(fd)); +}); +``` + +```cjs const http2 = require('node:http2'); const fs = require('node:fs'); @@ -1833,7 +2097,37 @@ the stream will be destroyed. Example using a file path: -```js +```mjs +import { createServer } from 'node:http2'; +const server = createServer(); +server.on('stream', (stream) => { + function statCheck(stat, headers) { + headers['last-modified'] = stat.mtime.toUTCString(); + } + + function onError(err) { + // stream.respond() can throw if the stream has been destroyed by + // the other side. + try { + if (err.code === 'ENOENT') { + stream.respond({ ':status': 404 }); + } else { + stream.respond({ ':status': 500 }); + } + } catch (err) { + // Perform actual error handling. + console.error(err); + } + stream.end(); + } + + stream.respondWithFile('/some/file', + { 'content-type': 'text/plain; charset=utf-8' }, + { statCheck, onError }); +}); +``` + +```cjs const http2 = require('node:http2'); const server = http2.createServer(); server.on('stream', (stream) => { @@ -1868,7 +2162,22 @@ by returning `false`. For instance, a conditional request may check the stat results to determine if the file has been modified to return an appropriate `304` response: -```js +```mjs +import { createServer } from 'node:http2'; +const server = createServer(); +server.on('stream', (stream) => { + function statCheck(stat, headers) { + // Check the stat here... + stream.respond({ ':status': 304 }); + return false; // Cancel the send operation + } + stream.respondWithFile('/some/file', + { 'content-type': 'text/plain; charset=utf-8' }, + { statCheck }); +}); +``` + +```cjs const http2 = require('node:http2'); const server = http2.createServer(); server.on('stream', (stream) => { @@ -1903,7 +2212,20 @@ close when the final `DATA` frame is transmitted. User code must call either `http2stream.sendTrailers()` or `http2stream.close()` to close the `Http2Stream`. -```js +```mjs +import { createServer } from 'node:http2'; +const server = createServer(); +server.on('stream', (stream) => { + stream.respondWithFile('/some/file', + { 'content-type': 'text/plain; charset=utf-8' }, + { waitForTrailers: true }); + stream.on('wantTrailers', () => { + stream.sendTrailers({ ABC: 'some value to send' }); + }); +}); +``` + +```cjs const http2 = require('node:http2'); const server = http2.createServer(); server.on('stream', (stream) => { @@ -2016,9 +2338,32 @@ added: v8.4.0 The `'stream'` event is emitted when a `'stream'` event has been emitted by an `Http2Session` associated with the server. -See also [`Http2Session`'s `'stream'` event][]. +See also [`Http2Session`'s `'stream'` event][]. + +```mjs +import { createServer, constants } from 'node:http2'; +const { + HTTP2_HEADER_METHOD, + HTTP2_HEADER_PATH, + HTTP2_HEADER_STATUS, + HTTP2_HEADER_CONTENT_TYPE, +} = constants; + +const server = createServer(); +server.on('stream', (stream, headers, flags) => { + const method = headers[HTTP2_HEADER_METHOD]; + const path = headers[HTTP2_HEADER_PATH]; + // ... + stream.respond({ + [HTTP2_HEADER_STATUS]: 200, + [HTTP2_HEADER_CONTENT_TYPE]: 'text/plain; charset=utf-8', + }); + stream.write('hello '); + stream.end('world'); +}); +``` -```js +```cjs const http2 = require('node:http2'); const { HTTP2_HEADER_METHOD, @@ -2249,7 +2594,32 @@ an `Http2Session` associated with the server. See also [`Http2Session`'s `'stream'` event][]. -```js +```mjs +import { createSecureServer, constants } from 'node:http2'; +const { + HTTP2_HEADER_METHOD, + HTTP2_HEADER_PATH, + HTTP2_HEADER_STATUS, + HTTP2_HEADER_CONTENT_TYPE, +} = constants; + +const options = getOptionsSomehow(); + +const server = createSecureServer(options); +server.on('stream', (stream, headers, flags) => { + const method = headers[HTTP2_HEADER_METHOD]; + const path = headers[HTTP2_HEADER_PATH]; + // ... + stream.respond({ + [HTTP2_HEADER_STATUS]: 200, + [HTTP2_HEADER_CONTENT_TYPE]: 'text/plain; charset=utf-8', + }); + stream.write('hello '); + stream.end('world'); +}); +``` + +```cjs const http2 = require('node:http2'); const { HTTP2_HEADER_METHOD, @@ -2533,7 +2903,27 @@ Since there are no browsers known that support [`http2.createSecureServer()`][] is necessary when communicating with browser clients. -```js +```mjs +import { createServer } from 'node:http2'; + +// Create an unencrypted HTTP/2 server. +// Since there are no browsers known that support +// unencrypted HTTP/2, the use of `createSecureServer()` +// is necessary when communicating with browser clients. +const server = createServer(); + +server.on('stream', (stream, headers) => { + stream.respond({ + 'content-type': 'text/html; charset=utf-8', + ':status': 200, + }); + stream.end('

Hello World

'); +}); + +server.listen(8000); +``` + +```cjs const http2 = require('node:http2'); // Create an unencrypted HTTP/2 server. @@ -2675,7 +3065,30 @@ changes: Returns a `tls.Server` instance that creates and manages `Http2Session` instances. -```js +```mjs +import { createSecureServer } from 'node:http2'; +import { readFileSync } from 'node:fs'; + +const options = { + key: readFileSync('server-key.pem'), + cert: readFileSync('server-cert.pem'), +}; + +// Create a secure HTTP/2 server +const server = createSecureServer(options); + +server.on('stream', (stream, headers) => { + stream.respond({ + 'content-type': 'text/html; charset=utf-8', + ':status': 200, + }); + stream.end('

Hello World

'); +}); + +server.listen(8443); +``` + +```cjs const http2 = require('node:http2'); const fs = require('node:fs'); @@ -2807,7 +3220,16 @@ changes: Returns a `ClientHttp2Session` instance. -```js +```mjs +import { connect } from 'node:http2'; +const client = connect('https://localhost:1234'); + +/* Use the client */ + +client.close(); +``` + +```cjs const http2 = require('node:http2'); const client = http2.connect('https://localhost:1234'); @@ -2869,7 +3291,16 @@ Returns a `Buffer` instance containing serialized representation of the given HTTP/2 settings as specified in the [HTTP/2][] specification. This is intended for use with the `HTTP2-Settings` header field. -```js +```mjs +import { getPackedSettings } from 'node:http2'; + +const packed = getPackedSettings({ enablePush: false }); + +console.log(packed.toString('base64')); +// Prints: AAIAAAAA +``` + +```cjs const http2 = require('node:http2'); const packed = http2.getPackedSettings({ enablePush: false }); @@ -2958,7 +3389,16 @@ For incoming headers: * For duplicate `cookie` headers, the values are joined together with '; '. * For all other headers, the values are joined together with ', '. -```js +```mjs +import { createServer } from 'node:http2'; +const server = createServer(); +server.on('stream', (stream, headers) => { + console.log(headers[':path']); + console.log(headers.ABC); +}); +``` + +```cjs const http2 = require('node:http2'); const server = http2.createServer(); server.on('stream', (stream, headers) => { @@ -3106,7 +3546,22 @@ characters, per the requirements of the HTTP specification. To receive pushed streams on the client, set a listener for the `'stream'` event on the `ClientHttp2Session`: -```js +```mjs +import { connect } from 'node:http2'; + +const client = connect('http://localhost'); + +client.on('stream', (pushedStream, requestHeaders) => { + pushedStream.on('push', (responseHeaders) => { + // Process response headers + }); + pushedStream.on('data', (chunk) => { /* handle pushed data */ }); +}); + +const req = client.request({ ':path': '/' }); +``` + +```cjs const http2 = require('node:http2'); const client = http2.connect('http://localhost'); @@ -3128,7 +3583,20 @@ for TCP/IP connections. A simple TCP Server: -```js +```mjs +import { createServer } from 'node:net'; + +const server = createServer((socket) => { + let name = ''; + socket.setEncoding('utf8'); + socket.on('data', (chunk) => name += chunk); + socket.on('end', () => socket.end(`hello ${name}`)); +}); + +server.listen(8000); +``` + +```cjs const net = require('node:net'); const server = net.createServer((socket) => { @@ -3143,7 +3611,35 @@ server.listen(8000); An HTTP/2 CONNECT proxy: -```js +```mjs +import { createServer, constants } from 'node:http2'; +const { NGHTTP2_REFUSED_STREAM, NGHTTP2_CONNECT_ERROR } = constants; +import { connect } from 'node:net'; + +const proxy = createServer(); +proxy.on('stream', (stream, headers) => { + if (headers[':method'] !== 'CONNECT') { + // Only accept CONNECT requests + stream.close(NGHTTP2_REFUSED_STREAM); + return; + } + const auth = new URL(`tcp://${headers[':authority']}`); + // It's a very good idea to verify that hostname and port are + // things this proxy should be connecting to. + const socket = connect(auth.port, auth.hostname, () => { + stream.respond(); + socket.pipe(stream); + stream.pipe(socket); + }); + socket.on('error', (error) => { + stream.close(NGHTTP2_CONNECT_ERROR); + }); +}); + +proxy.listen(8001); +``` + +```cjs const http2 = require('node:http2'); const { NGHTTP2_REFUSED_STREAM } = http2.constants; const net = require('node:net'); @@ -3173,7 +3669,32 @@ proxy.listen(8001); An HTTP/2 CONNECT client: -```js +```mjs +import { connect, constants } from 'node:http2'; + +const client = connect('http://localhost:8001'); + +// Must not specify the ':path' and ':scheme' headers +// for CONNECT requests or an error will be thrown. +const req = client.request({ + ':method': 'CONNECT', + ':authority': 'localhost:8000', +}); + +req.on('response', (headers) => { + console.log(headers[constants.HTTP2_HEADER_STATUS]); +}); +let data = ''; +req.setEncoding('utf8'); +req.on('data', (chunk) => data += chunk); +req.on('end', () => { + console.log(`The server says: ${data}`); + client.close(); +}); +req.end('Jane'); +``` + +```cjs const http2 = require('node:http2'); const client = http2.connect('http://localhost:8001'); @@ -3207,7 +3728,13 @@ method as a tunnel for other communication protocols (such as WebSockets). The use of the Extended CONNECT Protocol is enabled by HTTP/2 servers by using the `enableConnectProtocol` setting: -```js +```mjs +import { createServer } from 'node:http2'; +const settings = { enableConnectProtocol: true }; +const server = createServer({ settings }); +``` + +```cjs const http2 = require('node:http2'); const settings = { enableConnectProtocol: true }; const server = http2.createServer({ settings }); @@ -3217,7 +3744,18 @@ Once the client receives the `SETTINGS` frame from the server indicating that the extended CONNECT may be used, it may send `CONNECT` requests that use the `':protocol'` HTTP/2 pseudo-header: -```js +```mjs +import { connect } from 'node:http2'; +const client = connect('http://localhost:8080'); +client.on('remoteSettings', (settings) => { + if (settings.enableConnectProtocol) { + const req = client.request({ ':method': 'CONNECT', ':protocol': 'foo' }); + // ... + } +}); +``` + +```cjs const http2 = require('node:http2'); const client = http2.connect('http://localhost:8080'); client.on('remoteSettings', (settings) => { @@ -3240,7 +3778,17 @@ different implementation. The following example creates an HTTP/2 server using the compatibility API: -```js +```mjs +import { createServer } from 'node:http2'; +const server = createServer((req, res) => { + res.setHeader('Content-Type', 'text/html'); + res.setHeader('X-Foo', 'bar'); + res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8' }); + res.end('ok'); +}); +``` + +```cjs const http2 = require('node:http2'); const server = http2.createServer((req, res) => { res.setHeader('Content-Type', 'text/html'); @@ -3269,7 +3817,31 @@ features of HTTP/2. The following example creates a server that supports both protocols: -```js +```mjs +import { createSecureServer } from 'node:http2'; +import { readFileSync } from 'node:fs'; + +const cert = readFileSync('./cert.pem'); +const key = readFileSync('./key.pem'); + +const server = createSecureServer( + { cert, key, allowHTTP1: true }, + onRequest, +).listen(8000); + +function onRequest(req, res) { + // Detects if it is a HTTPS request or HTTP/2 + const { socket: { alpnProtocol } } = req.httpVersion === '2.0' ? + req.stream.session : req; + res.writeHead(200, { 'content-type': 'application/json' }); + res.end(JSON.stringify({ + alpnProtocol, + httpVersion: req.httpVersion, + })); +} +``` + +```cjs const { createSecureServer } = require('node:http2'); const { readFileSync } = require('node:fs'); @@ -3983,7 +4555,16 @@ more information. All other interactions will be routed directly to the socket. -```js +```mjs +import { createServer } from 'node:http2'; +const server = createServer((req, res) => { + const ip = req.socket.remoteAddress; + const port = req.socket.remotePort; + res.end(`Your IP address is ${ip} and your source port is ${port}.`); +}).listen(3000); +``` + +```cjs const http2 = require('node:http2'); const server = http2.createServer((req, res) => { const ip = req.socket.remoteAddress; @@ -4194,7 +4775,22 @@ will result in a [`TypeError`][] being thrown. The [Performance Observer][] API can be used to collect basic performance metrics for each `Http2Session` and `Http2Stream` instance. -```js +```mjs +import { PerformanceObserver } from 'node:perf_hooks'; + +const obs = new PerformanceObserver((items) => { + const entry = items.getEntries()[0]; + console.log(entry.entryType); // prints 'http2' + if (entry.name === 'Http2Session') { + // Entry contains statistics about the Http2Session + } else if (entry.name === 'Http2Stream') { + // Entry contains statistics about the Http2Stream + } +}); +obs.observe({ entryTypes: ['http2'] }); +``` + +```cjs const { PerformanceObserver } = require('node:perf_hooks'); const obs = new PerformanceObserver((items) => {