From 668044141282fab2c666f4bfa61807bbf50efdb4 Mon Sep 17 00:00:00 2001 From: Yoann MALLEMANCHE Date: Thu, 2 Mar 2023 15:18:03 +0100 Subject: [PATCH 01/10] feat: add diagnostic channel for server, route, request creation and response sent --- API.md | 23 +++++++++ lib/channels.js | 10 ++++ lib/core.js | 3 ++ lib/request.js | 3 ++ lib/server.js | 6 +++ test/channels.js | 128 +++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 173 insertions(+) create mode 100644 lib/channels.js create mode 100644 test/channels.js diff --git a/API.md b/API.md index ff749d6c0..967418884 100755 --- a/API.md +++ b/API.md @@ -5030,3 +5030,26 @@ const plugin = { } }; ``` + +## Diagnostic Channels + +see : https://nodejs.org/docs/latest-v16.x/api/diagnostics_channel.html + +### `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 }) => {})`. diff --git a/lib/channels.js b/lib/channels.js new file mode 100644 index 000000000..e04fe4a82 --- /dev/null +++ b/lib/channels.js @@ -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') +}; diff --git a/lib/core.js b/lib/core.js index 6ab2f9bdd..47e29f1cd 100755 --- a/lib/core.js +++ b/lib/core.js @@ -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'); @@ -510,6 +511,8 @@ exports = module.exports = internals.Core = class { const request = Request.generate(this.root, req, res, options); + Channels.onRequestChannel.publish(request); + // Track socket request processing state if (this.settings.operations.cleanStop && diff --git a/lib/request.js b/lib/request.js index 6efaa1511..35420ebb8 100755 --- a/lib/request.js +++ b/lib/request.js @@ -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'); @@ -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 }); } diff --git a/lib/server.js b/lib/server.js index d2a5d5bf7..6e338c2c8 100755 --- a/lib/server.js +++ b/lib/server.js @@ -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'); @@ -83,6 +84,8 @@ internals.Server = class { } core.registerServer(this); + + Channels.onServerChannel.publish(this); } _clone(name) { @@ -532,6 +535,9 @@ internals.Server = class { } this.events.emit('route', route.public); + + Channels.onRouteChannel.publish(route.public); + Cors.options(route.public, server); } diff --git a/test/channels.js b/test/channels.js new file mode 100644 index 000000000..785bf8567 --- /dev/null +++ b/test/channels.js @@ -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); + }); + }); +}); From 2606bb5aacc8783f969a4ab746c95a2a8dfd0759 Mon Sep 17 00:00:00 2001 From: Yoann MALLEMANCHE Date: Thu, 2 Mar 2023 20:51:45 +0100 Subject: [PATCH 02/10] style: Apply suggestions from code review Co-authored-by: Colin Ihrig --- test/channels.js | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/test/channels.js b/test/channels.js index 785bf8567..fdbeece32 100644 --- a/test/channels.js +++ b/test/channels.js @@ -3,12 +3,12 @@ const DC = require('diagnostics_channel'); const Code = require('@hapi/code'); -const Lab = require('@hapi/lab'); +const Lab = require('@hapi/lab'); const Hapi = require('..'); const { describe, it } = exports.lab = Lab.script(); -const expect = Code.expect; +const expect = Code.expect; describe('DiagnosticChannel', () => { @@ -75,17 +75,15 @@ describe('DiagnosticChannel', () => { it('response should be exposed on creation through the channel hapi.onResponse', async () => { const server = Hapi.server(); + let responseExposed; server.route({ method: 'GET', path: '/', handler: () => 'ok' }); - let responseExposed; - const eventPromise = new Promise((resolve) => { channel.subscribe((res) => { responseExposed = res; - resolve(); }); }); @@ -104,24 +102,21 @@ describe('DiagnosticChannel', () => { it('request should be exposed on creation through the channel hapi.onRequest', async () => { const server = Hapi.server(); + let requestExposed; 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); }); }); From 890062d92a22df1ac7ac3e515f0de430e1337d17 Mon Sep 17 00:00:00 2001 From: Yoann MALLEMANCHE Date: Thu, 2 Mar 2023 20:52:29 +0100 Subject: [PATCH 03/10] style: remove empty line --- test/channels.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/channels.js b/test/channels.js index fdbeece32..103ebd0d3 100644 --- a/test/channels.js +++ b/test/channels.js @@ -43,7 +43,6 @@ describe('DiagnosticChannel', () => { it('route should be exposed on creation through the channel hapi.onRoute', async () => { const server = Hapi.server(); - let route; await new Promise((resolve) => { @@ -51,7 +50,6 @@ describe('DiagnosticChannel', () => { channel.subscribe((rte) => { route = rte; - resolve(); }); From e046e2678301604d8636c7ea0459dfa86558795f Mon Sep 17 00:00:00 2001 From: Yoann MALLEMANCHE Date: Thu, 2 Mar 2023 20:54:13 +0100 Subject: [PATCH 04/10] doc: update documentation to add exemple and description to diagnostic_channels --- API.md | 44 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/API.md b/API.md index 967418884..9b9737266 100755 --- a/API.md +++ b/API.md @@ -5033,23 +5033,65 @@ const plugin = { ## Diagnostic Channels -see : https://nodejs.org/docs/latest-v16.x/api/diagnostics_channel.html +Diagnostic Channels allows Hapi to report events to other modules. This is useful for APM vendors to collect data from Hapi for diagnostics purposes. + +See [the official documentation](https://nodejs.org/docs/latest/api/diagnostics_channel.html) for more information. ### `hapi.onServer` This event is sent when a new server is created. The [`server`](#server) object is passed as the message. +```js +const DC = require('diagnostics_channel'); +const channel = DC.channel('hapi.onServer'); + +channel.subscribe((server) => { + // Do something with the server + console.log(server.version); +}); +``` + ### `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) => {})`. +```js +const DC = require('diagnostics_channel'); +const channel = DC.channel('hapi.onRoute'); + +channel.subscribe((route) => { + // Do something with the route + console.log(route.path); +}); +``` + ### `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) => {})`. +```js +const DC = require('diagnostics_channel'); +const channel = DC.channel('hapi.onRequest'); + +channel.subscribe((request) => { + // Do something with the request + console.log(request.info.id); +}); +``` + ### `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 }) => {})`. + +```js +const DC = require('diagnostics_channel'); +const channel = DC.channel('hapi.onResponse'); + +channel.subscribe((response) => { + // Do something with the response + console.log(response.statusCode); +}); +``` From cf9c82dbf8253204550b5c1045c71211ab65df15 Mon Sep 17 00:00:00 2001 From: Yoann MALLEMANCHE Date: Sat, 11 Mar 2023 16:28:00 +0100 Subject: [PATCH 05/10] fix: apply change asked in review --- API.md | 7 +++++- lib/channels.js | 13 ++++++----- lib/core.js | 2 +- lib/request.js | 2 +- lib/server.js | 4 ++-- test/channels.js | 58 ++++++++++++++++-------------------------------- 6 files changed, 36 insertions(+), 50 deletions(-) diff --git a/API.md b/API.md index 9b9737266..d2bc3c38e 100755 --- a/API.md +++ b/API.md @@ -5033,7 +5033,8 @@ const plugin = { ## Diagnostic Channels -Diagnostic Channels allows Hapi to report events to other modules. This is useful for APM vendors to collect data from Hapi for diagnostics purposes. +Diagnostic Channels allows Hapi to report events to other modules. This is useful for APM vendors to collect data from Hapi for diagnostics purposes. +This not intended to be used in your own server or plugin, use [extension methods](#server.ext()) or [events](#server.events) instead. See [the official documentation](https://nodejs.org/docs/latest/api/diagnostics_channel.html) for more information. @@ -5046,6 +5047,7 @@ const DC = require('diagnostics_channel'); const channel = DC.channel('hapi.onServer'); channel.subscribe((server) => { + // Do something with the server console.log(server.version); }); @@ -5061,6 +5063,7 @@ const DC = require('diagnostics_channel'); const channel = DC.channel('hapi.onRoute'); channel.subscribe((route) => { + // Do something with the route console.log(route.path); }); @@ -5076,6 +5079,7 @@ const DC = require('diagnostics_channel'); const channel = DC.channel('hapi.onRequest'); channel.subscribe((request) => { + // Do something with the request console.log(request.info.id); }); @@ -5091,6 +5095,7 @@ const DC = require('diagnostics_channel'); const channel = DC.channel('hapi.onResponse'); channel.subscribe((response) => { + // Do something with the response console.log(response.statusCode); }); diff --git a/lib/channels.js b/lib/channels.js index e04fe4a82..f3a6c912f 100644 --- a/lib/channels.js +++ b/lib/channels.js @@ -2,9 +2,10 @@ 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') -}; +exports.server = DC.channel('hapi.onServer'); + +exports.route = DC.channel('hapi.onRoute'); + +exports.response = DC.channel('hapi.onResponse'); + +exports.request = DC.channel('hapi.onRequest'); diff --git a/lib/core.js b/lib/core.js index 47e29f1cd..ca6c3e609 100755 --- a/lib/core.js +++ b/lib/core.js @@ -511,7 +511,7 @@ exports = module.exports = internals.Core = class { const request = Request.generate(this.root, req, res, options); - Channels.onRequestChannel.publish(request); + Channels.request.publish(request); // Track socket request processing state diff --git a/lib/request.js b/lib/request.js index 35420ebb8..fe50a1a32 100755 --- a/lib/request.js +++ b/lib/request.js @@ -511,7 +511,7 @@ exports = module.exports = internals.Request = class { this._core.events.emit('response', this); - Channels.onResponseChannel.publish(this.response); + Channels.response.publish(this.response); if (this._route._extensions.onPostResponse.nodes) { this._invoke(this._route._extensions.onPostResponse, { ignoreResponse: true }); diff --git a/lib/server.js b/lib/server.js index 6e338c2c8..f1c666de6 100755 --- a/lib/server.js +++ b/lib/server.js @@ -85,7 +85,7 @@ internals.Server = class { core.registerServer(this); - Channels.onServerChannel.publish(this); + Channels.server.publish(this); } _clone(name) { @@ -536,7 +536,7 @@ internals.Server = class { this.events.emit('route', route.public); - Channels.onRouteChannel.publish(route.public); + Channels.route.publish(route.public); Cors.options(route.public, server); } diff --git a/test/channels.js b/test/channels.js index 103ebd0d3..20ebfba24 100644 --- a/test/channels.js +++ b/test/channels.js @@ -12,46 +12,36 @@ const expect = Code.expect; describe('DiagnosticChannel', () => { - describe('onServerChannel', () => { + describe('hapi.onServer', () => { 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) => { + const exposedServer = await new Promise((resolve) => { - channel.subscribe((srv) => { - - exposedServer = srv; - resolve(); - }); + channel.subscribe(resolve); server = Hapi.server(); }); - expect(exposedServer).to.equal(server); + expect(exposedServer).to.shallow.equal(server); }); }); - describe('onRouteChannel', () => { + describe('hapi.onRoute', () => { 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) => { + const exposedRoute = await new Promise((resolve) => { - channel.subscribe((rte) => { - - route = rte; - resolve(); - }); + channel.subscribe(resolve); server.route({ method: 'GET', @@ -61,61 +51,51 @@ describe('DiagnosticChannel', () => { }); }); - expect(route).to.be.an.object(); - expect(route.settings.app.x).to.equal('o'); + expect(exposedRoute).to.be.an.object(); + expect(exposedRoute.settings.app.x).to.equal('o'); }); }); - describe('onResponseChannel', () => { + describe('hapi.onResponse', () => { const channel = DC.channel('hapi.onResponse'); it('response should be exposed on creation through the channel hapi.onResponse', async () => { const server = Hapi.server(); - let responseExposed; server.route({ method: 'GET', path: '/', handler: () => 'ok' }); - const eventPromise = new Promise((resolve) => { + const event = new Promise((resolve) => { - channel.subscribe((res) => { - - responseExposed = res; - resolve(); - }); + channel.subscribe(resolve); }); const response = await server.inject('/'); - await eventPromise; + const responseExposed = await event; - expect(response.request.response).to.equal(responseExposed); + expect(response.request.response).to.shallow.equal(responseExposed); }); }); - describe('onRequestChannel', () => { + describe('hapi.onRequest', () => { const channel = DC.channel('hapi.onRequest'); it('request should be exposed on creation through the channel hapi.onRequest', async () => { const server = Hapi.server(); - let requestExposed; server.route({ method: 'GET', path: '/', handler: () => 'ok' }); - const eventPromise = new Promise((resolve) => { + const event = new Promise((resolve) => { - channel.subscribe((req) => { - - requestExposed = req; - resolve(); - }); + channel.subscribe(resolve); }); const response = await server.inject('/'); - await eventPromise; - expect(response.request).to.equal(requestExposed); + const requestExposed = await event; + expect(response.request).to.shallow.equal(requestExposed); }); }); }); From 5eb9907495ada104ef7bd691c1d0407e573bc7ec Mon Sep 17 00:00:00 2001 From: Yoann MALLEMANCHE Date: Wed, 15 Mar 2023 13:43:48 +0100 Subject: [PATCH 06/10] fix: move channels event before user-facing hooks --- lib/request.js | 4 ++-- lib/server.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/request.js b/lib/request.js index fe50a1a32..ef96717c7 100755 --- a/lib/request.js +++ b/lib/request.js @@ -509,10 +509,10 @@ exports = module.exports = internals.Request = class { this.info.completed = Date.now(); - this._core.events.emit('response', this); - Channels.response.publish(this.response); + this._core.events.emit('response', this); + if (this._route._extensions.onPostResponse.nodes) { this._invoke(this._route._extensions.onPostResponse, { ignoreResponse: true }); } diff --git a/lib/server.js b/lib/server.js index f1c666de6..37571d79d 100755 --- a/lib/server.js +++ b/lib/server.js @@ -534,10 +534,10 @@ internals.Server = class { route.params = record.params; } - this.events.emit('route', route.public); - Channels.route.publish(route.public); + this.events.emit('route', route.public); + Cors.options(route.public, server); } From 35ea6ef1c984b0908fc852344eb35c9615a4b516 Mon Sep 17 00:00:00 2001 From: Yoann MALLEMANCHE Date: Wed, 15 Mar 2023 13:46:54 +0100 Subject: [PATCH 07/10] test: add test insuring request is not routed on hapi.onRequest --- test/channels.js | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/test/channels.js b/test/channels.js index 20ebfba24..b1d6368e4 100644 --- a/test/channels.js +++ b/test/channels.js @@ -4,6 +4,7 @@ const DC = require('diagnostics_channel'); const Code = require('@hapi/code'); const Lab = require('@hapi/lab'); +const Hoek = require('@hapi/hoek'); const Hapi = require('..'); @@ -73,7 +74,6 @@ describe('DiagnosticChannel', () => { const response = await server.inject('/'); const responseExposed = await event; - expect(response.request.response).to.shallow.equal(responseExposed); }); }); @@ -97,5 +97,30 @@ describe('DiagnosticChannel', () => { const requestExposed = await event; expect(response.request).to.shallow.equal(requestExposed); }); + + it('request should not have been routed when hapi.onRequest is triggered', async () => { + + const server = Hapi.server(); + + server.route({ method: 'GET', path: '/test/{p}', handler: () => 'ok' }); + + server.ext('onRequest', async (request, h) => { + + await Hoek.wait(10); + return h.continue; + }); + + const event = new Promise((resolve) => { + + channel.subscribe(resolve); + }); + + const request = server.inject('/test/foo'); + const requestExposed = await event; + expect(requestExposed.params).to.be.null(); + + const response = await request; + expect(response.request).to.shallow.equal(requestExposed); + }); }); }); From f21651d6770064272468cf83da888b9b4da54ad0 Mon Sep 17 00:00:00 2001 From: Yoann MALLEMANCHE Date: Sun, 19 Mar 2023 18:23:35 +0100 Subject: [PATCH 08/10] feat: add hapi.onRequestLifecycle and hapi.onError diagnostic channels --- API.md | 31 +++++++++++++++++++++ lib/channels.js | 4 +++ lib/request.js | 4 +++ test/channels.js | 71 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 110 insertions(+) diff --git a/API.md b/API.md index d2bc3c38e..1ed4002cf 100755 --- a/API.md +++ b/API.md @@ -5100,3 +5100,34 @@ channel.subscribe((response) => { console.log(response.statusCode); }); ``` + +### `hapi.onRequestLifecycle` + +This event is sent after the response is matched to a route. The [`request`](#request) object is passed as the message. + +```js +const DC = require('diagnostics_channel'); +const channel = DC.channel('hapi.onRequestLifecycle'); + +channel.subscribe((request) => { + + // Do something with the request + console.log(request.route); +}); +``` + +### `hapi.onError` + +This event is sent after the response is sent back to the client. An object containing the [`request`](#request) object and the error is passed as the message. +Similar to `server.events.on({ name: 'request', channels: 'error' }, (request, { error }) => {})`. + +```js +const DC = require('diagnostics_channel'); +const channel = DC.channel('hapi.onError'); + +channel.subscribe(({ request, error }) => { + + // Do something with the request or the error + console.error(error); +}); +``` diff --git a/lib/channels.js b/lib/channels.js index f3a6c912f..05c80e343 100644 --- a/lib/channels.js +++ b/lib/channels.js @@ -9,3 +9,7 @@ exports.route = DC.channel('hapi.onRoute'); exports.response = DC.channel('hapi.onResponse'); exports.request = DC.channel('hapi.onRequest'); + +exports.requestLifecycle = DC.channel('hapi.onRequestLifecycle'); + +exports.error = DC.channel('hapi.onError'); diff --git a/lib/request.js b/lib/request.js index ef96717c7..52ebebe3d 100755 --- a/lib/request.js +++ b/lib/request.js @@ -362,6 +362,8 @@ exports = module.exports = internals.Request = class { async _lifecycle() { + Channels.requestLifecycle.publish(this); + for (const func of this._route._cycle) { if (this._isReplied) { return; @@ -498,6 +500,8 @@ exports = module.exports = internals.Request = class { if (this.response.statusCode === 500 && this.response._error) { + Channels.error.publish({ request: this, error: this.response._error }); + const tags = this.response._error.isDeveloperError ? ['internal', 'implementation', 'error'] : ['internal', 'error']; this._log(tags, this.response._error, 'error'); } diff --git a/test/channels.js b/test/channels.js index b1d6368e4..a80c2835c 100644 --- a/test/channels.js +++ b/test/channels.js @@ -123,4 +123,75 @@ describe('DiagnosticChannel', () => { expect(response.request).to.shallow.equal(requestExposed); }); }); + + describe('hapi.onRequestLifecycle', () => { + + const channel = DC.channel('hapi.onRequestLifecycle'); + + it('request should be exposed after routing through the channel hapi.onRequestLifecycle', async () => { + + const server = Hapi.server(); + + server.route({ + method: 'POST', + path: '/test/{p}', + options: { app: { x: 'o' } }, + handler: () => 'ok' + }); + + server.ext('onPreAuth', async (request, h) => { + + await Hoek.wait(10); + return h.continue; + }); + + const event = new Promise((resolve) => { + + channel.subscribe(resolve); + }); + + const request = server.inject({ method: 'POST', url: '/test/foo', payload: '{"a":"b"}' }); + const requestExposed = await event; + expect(requestExposed.params).to.be.an.object(); + expect(requestExposed.params.p).to.equal('foo'); + expect(requestExposed.route).to.be.an.object(); + expect(requestExposed.route.settings.app.x).to.equal('o'); + expect(requestExposed.payload).to.be.undefined(); + + const response = await request; + expect(response.request).to.shallow.equal(requestExposed); + expect(response.request.payload).to.be.an.object(); + expect(response.request.payload.a).to.equal('b'); + }); + }); + + describe('hapi.onError', () => { + + const channel = DC.channel('hapi.onError'); + + it('should expose the request as well as the error object when an error happens during the request lifetime', async () => { + + const server = Hapi.server(); + + server.route({ + method: 'GET', + path: '/', + handler: () => { + + throw new Error('some error message') + } + }); + + const event = new Promise((resolve) => { + + channel.subscribe(resolve); + }); + + const response = await server.inject('/'); + const { request: requestExposed, error: errorExposed } = await event; + expect(response.request).to.shallow.equal(requestExposed); + expect(errorExposed).to.be.an.instanceof(Error); + expect(errorExposed.message).to.equal('some error message'); + }); + }); }); From a246101e862652c1cd0629ef2254434be4aa1d35 Mon Sep 17 00:00:00 2001 From: Yoann MALLEMANCHE Date: Sun, 19 Mar 2023 18:32:37 +0100 Subject: [PATCH 09/10] doc: change hapi.onError channel description --- API.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/API.md b/API.md index 1ed4002cf..da9942beb 100755 --- a/API.md +++ b/API.md @@ -5118,7 +5118,7 @@ channel.subscribe((request) => { ### `hapi.onError` -This event is sent after the response is sent back to the client. An object containing the [`request`](#request) object and the error is passed as the message. +This event is sent when a request responded with a 500 status code. An object containing the [`request`](#request) object and the error is passed as the message. Similar to `server.events.on({ name: 'request', channels: 'error' }, (request, { error }) => {})`. ```js From 34f3f35fedc5afe8a17ae83249bef51e62ee7cd3 Mon Sep 17 00:00:00 2001 From: Yoann MALLEMANCHE Date: Sun, 19 Mar 2023 18:56:02 +0100 Subject: [PATCH 10/10] style: missing semi --- test/channels.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/channels.js b/test/channels.js index a80c2835c..fd47ad483 100644 --- a/test/channels.js +++ b/test/channels.js @@ -178,7 +178,7 @@ describe('DiagnosticChannel', () => { path: '/', handler: () => { - throw new Error('some error message') + throw new Error('some error message'); } });