From 247cb11beee9e8b7eeaec22e9dfcc6d5fd321c57 Mon Sep 17 00:00:00 2001 From: theanarkh Date: Sun, 26 Jun 2022 21:23:03 +0800 Subject: [PATCH] http: add diagnostics channel for http client --- doc/api/diagnostics_channel.md | 69 +++++++++++++++++++ lib/_http_client.js | 15 ++++ .../parallel/test-diagnostics-channel-http.js | 63 +++++++++++++++++ 3 files changed, 147 insertions(+) create mode 100644 test/parallel/test-diagnostics-channel-http.js diff --git a/doc/api/diagnostics_channel.md b/doc/api/diagnostics_channel.md index eff642f96743b4..e6ccc3586e5c54 100644 --- a/doc/api/diagnostics_channel.md +++ b/doc/api/diagnostics_channel.md @@ -307,6 +307,75 @@ channel.subscribe(onMessage); channel.unsubscribe(onMessage); ``` +### Built-in Channel + +* `http.client.request.start`: Emited when start a request. +* `http.client.response.finish`: Emited when receive a Response. +* `http.server.request.start`: Emited when server receive a Request. +* `http.server.response.finish`: Emited when server send a Response. + +## Example + +```mjs +import http from 'node:http'; +import dc from 'node:diagnostics_channel'; + +const onClientRequestStart = dc.channel('http.client.request.start'); +const onClientResponseFinish = dc.channel('http.client.response.finish'); + +onClientRequestStart.subscribe(({ request }) => { + +}); + +onClientResponseFinish.subscribe(({ request, response }) => { + +}); + +const server = http.createServer((req, res) => { + res.end('done'); +}); + +server.listen(() => { + const { port } = server.address(); + http.get(`http://localhost:${port}`, (res) => { + res.resume(); + res.on('end', () => { + server.close(); + }); + }); +}); +``` + +```cjs +const http = require('http'); +const dc = require('diagnostics_channel'); + +const onClientRequestStart = dc.channel('http.client.request.start'); +const onClientResponseFinish = dc.channel('http.client.response.finish'); + +onClientRequestStart.subscribe(({ request }) => { + +}); + +onClientResponseFinish.subscribe(({ request, response }) => { + +}); + +const server = http.createServer((req, res) => { + res.end('done'); +}); + +server.listen(() => { + const { port } = server.address(); + http.get(`http://localhost:${port}`, (res) => { + res.resume(); + res.on('end', () => { + server.close(); + }); + }); +}); +``` + [`'uncaughtException'`]: process.md#event-uncaughtexception [`channel.subscribe(onMessage)`]: #channelsubscribeonmessage [`diagnostics_channel.channel(name)`]: #diagnostics_channelchannelname diff --git a/lib/_http_client.js b/lib/_http_client.js index a06b3904247d73..86cb1634cdbe7f 100644 --- a/lib/_http_client.js +++ b/lib/_http_client.js @@ -90,6 +90,10 @@ const { const kClientRequestStatistics = Symbol('ClientRequestStatistics'); +const dc = require('diagnostics_channel'); +const onClientRequestStartChannel = dc.channel('http.client.request.start'); +const onClientResponseFinishChannel = dc.channel('http.client.response.finish'); + const { addAbortSignal, finished } = require('stream'); let debug = require('internal/util/debuglog').debuglog('http', (fn) => { @@ -367,6 +371,11 @@ ClientRequest.prototype._finish = function _finish() { }, }); } + if (onClientRequestStartChannel.hasSubscribers) { + onClientRequestStartChannel.publish({ + request: this, + }); + } }; ClientRequest.prototype._implicitHeader = function _implicitHeader() { @@ -645,6 +654,12 @@ function parserOnIncomingClient(res, shouldKeepAlive) { }, }); } + if (onClientResponseFinishChannel.hasSubscribers) { + onClientResponseFinishChannel.publish({ + request: req, + response: res, + }); + } req.res = res; res.req = req; diff --git a/test/parallel/test-diagnostics-channel-http.js b/test/parallel/test-diagnostics-channel-http.js new file mode 100644 index 00000000000000..4de08d9253c10a --- /dev/null +++ b/test/parallel/test-diagnostics-channel-http.js @@ -0,0 +1,63 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const http = require('http'); +const net = require('net'); +const dc = require('diagnostics_channel'); + +const onClientRequestStart = dc.channel('http.client.request.start'); +const onClientResponseFinish = dc.channel('http.client.response.finish'); +const onServerRequestStart = dc.channel('http.server.request.start'); +const onServerResponseFinish = dc.channel('http.server.response.finish'); + +const isHTTPServer = (server) => server instanceof http.Server; +const isIncomingMessage = (object) => object instanceof http.IncomingMessage; +const isOutgoingMessage = (object) => object instanceof http.OutgoingMessage; +const isNetSocket = (socket) => socket instanceof net.Socket; + +onClientRequestStart.subscribe(common.mustCall(({ request }) => { + assert.strictEqual(isOutgoingMessage(request), true); +})); + +onClientResponseFinish.subscribe(common.mustCall(({ request, response }) => { + assert.strictEqual(isOutgoingMessage(request), true); + assert.strictEqual(isIncomingMessage(response), true); +})); + +onServerRequestStart.subscribe(common.mustCall(({ + request, + response, + socket, + server, +}) => { + assert.strictEqual(isIncomingMessage(request), true); + assert.strictEqual(isOutgoingMessage(response), true); + assert.strictEqual(isNetSocket(socket), true); + assert.strictEqual(isHTTPServer(server), true); +})); + +onServerResponseFinish.subscribe(common.mustCall(({ + request, + response, + socket, + server, +}) => { + assert.strictEqual(isIncomingMessage(request), true); + assert.strictEqual(isOutgoingMessage(response), true); + assert.strictEqual(isNetSocket(socket), true); + assert.strictEqual(isHTTPServer(server), true); +})); + +const server = http.createServer(common.mustCall((req, res) => { + res.end('done'); +})); + +server.listen(() => { + const { port } = server.address(); + http.get(`http://localhost:${port}`, (res) => { + res.resume(); + res.on('end', () => { + server.close(); + }); + }); +});