Skip to content
23 changes: 23 additions & 0 deletions API.md
Original file line number Diff line number Diff line change
Expand Up @@ -5030,3 +5030,26 @@ const plugin = {
}
};
```

## Diagnostic Channels

see : https://nodejs.org/docs/latest-v16.x/api/diagnostics_channel.html
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of linking to the Node docs, can you provide a small example. If you still want to link to the Node docs, I would recommend linking to latest so we don't have to keep updating the link.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed the URL, added a bit of explaining and examples, do you feel like it's enough ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm good with it. Let's give other folks a chance to weigh in.


### `hapi.onServer`

This event is sent when a new server is created. The [`server`](#server) object is passed as the message.

### `hapi.onRoute`

This event is sent when a new route is added to the server. The [`route`](#request.route) object is passed as the message.
Similar to `server.events.on('route', (route) => {})`.

### `hapi.onRequest`

This event is sent when a request is generated by the framework. The [`request`](#request) object is passed as the message.
Similar to `server.events.on('request', (request) => {})`.

### `hapi.onResponse`

This event is sent after the response is sent back to the client. The [`response`](#request.response) object is passed as the message.
Similar to `server.events.on('response', ({ response }) => {})`.
10 changes: 10 additions & 0 deletions lib/channels.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
'use strict';

const DC = require('diagnostics_channel');

module.exports = {
onServerChannel: DC.channel('hapi.onServer'),
onRouteChannel: DC.channel('hapi.onRoute'),
onResponseChannel: DC.channel('hapi.onResponse'),
onRequestChannel: DC.channel('hapi.onRequest')
};
3 changes: 3 additions & 0 deletions lib/core.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ const Podium = require('@hapi/podium');
const Statehood = require('@hapi/statehood');

const Auth = require('./auth');
const Channels = require('./channels');
const Compression = require('./compression');
const Config = require('./config');
const Cors = require('./cors');
Expand Down Expand Up @@ -510,6 +511,8 @@ exports = module.exports = internals.Core = class {

const request = Request.generate(this.root, req, res, options);

Channels.onRequestChannel.publish(request);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would probably move the request._execute() info assignment to the request constructor to make this more representative of what the onRequest ext sees.

Or maybe move the publish to the _execute() method? (though that would mean it doesn't see the ones that are denied due to load).

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this assignment is in the request._execute(), perhaps it is to avoid executing it in case of heavy load ?

I would vote to keep the publish() here given we need another event when a request is routed. I could add an event early in the request._lifecycle() method ? Something like hapi.onRequestLifecycle ?


// Track socket request processing state

if (this.settings.operations.cleanStop &&
Expand Down
3 changes: 3 additions & 0 deletions lib/request.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const Bounce = require('@hapi/bounce');
const Hoek = require('@hapi/hoek');
const Podium = require('@hapi/podium');

const Channels = require('./channels');
const Cors = require('./cors');
const Toolkit = require('./toolkit');
const Transmit = require('./transmit');
Expand Down Expand Up @@ -510,6 +511,8 @@ exports = module.exports = internals.Request = class {

this._core.events.emit('response', this);

Channels.onResponseChannel.publish(this.response);

if (this._route._extensions.onPostResponse.nodes) {
this._invoke(this._route._extensions.onPostResponse, { ignoreResponse: true });
}
Expand Down
6 changes: 6 additions & 0 deletions lib/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const Hoek = require('@hapi/hoek');
const Shot = require('@hapi/shot');
const Teamwork = require('@hapi/teamwork');

const Channels = require('./channels');
const Config = require('./config');
const Core = require('./core');
const Cors = require('./cors');
Expand Down Expand Up @@ -83,6 +84,8 @@ internals.Server = class {
}

core.registerServer(this);

Channels.onServerChannel.publish(this);
}

_clone(name) {
Expand Down Expand Up @@ -532,6 +535,9 @@ internals.Server = class {
}

this.events.emit('route', route.public);

Channels.onRouteChannel.publish(route.public);

Cors.options(route.public, server);
}

Expand Down
128 changes: 128 additions & 0 deletions test/channels.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
'use strict';

const DC = require('diagnostics_channel');

const Code = require('@hapi/code');
const Lab = require('@hapi/lab');

const Hapi = require('..');

const { describe, it } = exports.lab = Lab.script();
const expect = Code.expect;

describe('DiagnosticChannel', () => {

describe('onServerChannel', () => {

const channel = DC.channel('hapi.onServer');

it('server should be exposed on creation through the channel hapi.onServer', async () => {

let exposedServer;
let server;

await new Promise((resolve) => {

channel.subscribe((srv) => {

exposedServer = srv;
resolve();
});

server = Hapi.server();
});

expect(exposedServer).to.equal(server);
});
});

describe('onRouteChannel', () => {

const channel = DC.channel('hapi.onRoute');

it('route should be exposed on creation through the channel hapi.onRoute', async () => {

const server = Hapi.server();

let route;

await new Promise((resolve) => {

channel.subscribe((rte) => {

route = rte;

resolve();
});

server.route({
method: 'GET',
path: '/',
options: { app: { x: 'o' } },
handler: () => 'ok'
});
});

expect(route).to.be.an.object();
expect(route.settings.app.x).to.equal('o');
});
});

describe('onResponseChannel', () => {

const channel = DC.channel('hapi.onResponse');

it('response should be exposed on creation through the channel hapi.onResponse', async () => {

const server = Hapi.server();

server.route({ method: 'GET', path: '/', handler: () => 'ok' });

let responseExposed;

const eventPromise = new Promise((resolve) => {

channel.subscribe((res) => {

responseExposed = res;

resolve();
});
});

const response = await server.inject('/');
await eventPromise;

expect(response.request.response).to.equal(responseExposed);
});
});

describe('onRequestChannel', () => {

const channel = DC.channel('hapi.onRequest');

it('request should be exposed on creation through the channel hapi.onRequest', async () => {

const server = Hapi.server();

server.route({ method: 'GET', path: '/', handler: () => 'ok' });

let requestExposed;

const eventPromise = new Promise((resolve) => {

channel.subscribe((req) => {

requestExposed = req;

resolve();
});
});

const response = await server.inject('/');
await eventPromise;

expect(response.request).to.equal(requestExposed);
});
});
});