From 783aa8acc5a400072bba25813e6ea43f1710e02a Mon Sep 17 00:00:00 2001 From: Chris Nienhuis Date: Mon, 10 Aug 2020 21:16:01 -0500 Subject: [PATCH] add isInjected property to request object (#4117) Implement `isInjected` read-only request property to indicate request created by `server.inject()`. --- API.md | 6 ++++++ lib/request.js | 8 +++++++- lib/server.js | 3 ++- lib/transmit.js | 3 +-- test/core.js | 53 ++++++++++++++++++++++++++++++++++++++++++++++++- 5 files changed, 68 insertions(+), 5 deletions(-) diff --git a/API.md b/API.md index d25fb3385..12cbe3753 100755 --- a/API.md +++ b/API.md @@ -4581,6 +4581,12 @@ Request information: Note that the `request.info` object is not meant to be modified. +#### `request.isInjected` + +Access: read only. + +`true` if the request was created via [`server.inject()`](#server.inject()), and `false` otherwise. + #### `request.logs` Access: read only. diff --git a/lib/request.js b/lib/request.js index 553674d18..6f425d561 100755 --- a/lib/request.js +++ b/lib/request.js @@ -15,7 +15,7 @@ const Transmit = require('./transmit'); const internals = { events: Podium.validate(['finish', { name: 'peek', spread: true }, 'disconnect']), - reserved: ['server', 'url', 'query', 'path', 'method', 'mime', 'setUrl', 'setMethod', 'headers', 'id', 'app', 'plugins', 'route', 'auth', 'pre', 'preResponses', 'info', 'orig', 'params', 'paramsArray', 'payload', 'state', 'jsonp', 'response', 'raw', 'domain', 'log', 'logs', 'generateResponse'] + reserved: ['server', 'url', 'query', 'path', 'method', 'mime', 'setUrl', 'setMethod', 'headers', 'id', 'app', 'plugins', 'route', 'auth', 'pre', 'preResponses', 'info', 'isInjected', 'orig', 'params', 'paramsArray', 'payload', 'state', 'jsonp', 'response', 'raw', 'domain', 'log', 'logs', 'generateResponse'] }; @@ -29,6 +29,7 @@ exports = module.exports = internals.Request = class { this._eventContext = { request: this }; this._events = null; // Assigned an emitter when request.events is accessed this._expectContinue = !!options.expectContinue; + this._isInjected = !!options.isInjected; this._isPayloadPending = !!(req.headers['content-length'] || req.headers['transfer-encoding']); // Changes to false when incoming payload fully processed this._isReplied = false; // true when response processing started this._route = this._core.router.specials.notFound.route; // Used prior to routing (only settings are used, not the handler) @@ -101,6 +102,11 @@ exports = module.exports = internals.Request = class { return this._events; } + get isInjected() { + + return this._isInjected; + } + get url() { if (this._urlError) { diff --git a/lib/server.js b/lib/server.js index b9a910181..8011c446e 100755 --- a/lib/server.js +++ b/lib/server.js @@ -339,7 +339,8 @@ internals.Server = class { auth: options.auth, allowInternals: options.allowInternals, app: options.app, - plugins: options.plugins + plugins: options.plugins, + isInjected: true }); const res = await Shot.inject(needle, settings); diff --git a/lib/transmit.js b/lib/transmit.js index ffaa8fca1..55c629c2c 100755 --- a/lib/transmit.js +++ b/lib/transmit.js @@ -6,7 +6,6 @@ const Ammo = require('@hapi/ammo'); const Boom = require('@hapi/boom'); const Bounce = require('@hapi/bounce'); const Hoek = require('@hapi/hoek'); -const Shot = require('@hapi/shot'); const Teamwork = require('@hapi/teamwork'); const Config = require('./config'); @@ -94,7 +93,7 @@ internals.transmit = function (response) { // Connection: close - const isInjection = Shot.isInjection(request.raw.req); + const isInjection = request.isInjected; if (!(isInjection || request._core.started) || request._isPayloadPending && !request.raw.req._readableState.ended) { diff --git a/test/core.js b/test/core.js index f25ebef87..b569eaf5e 100755 --- a/test/core.js +++ b/test/core.js @@ -1116,7 +1116,7 @@ describe('Core', () => { expect(options.auth.artifacts).to.exist(); }); - it('sets isInjected', async () => { + it('sets `request.auth.isInjected = true` when `auth` option is defined', async () => { const server = Hapi.server(); server.route({ method: 'GET', path: '/', handler: (request) => request.auth.isInjected }); @@ -1134,6 +1134,57 @@ describe('Core', () => { expect(res.result).to.be.true(); }); + it('sets `request.isInjected = true` for requests created via `server.inject`', async () => { + + const server = Hapi.server(); + server.route({ method: 'GET', path: '/', handler: (request) => request.isInjected }); + + const options = { + url: '/' + }; + + const res = await server.inject(options); + expect(res.statusCode).to.equal(200); + expect(res.result).to.be.true(); + }); + + it('`request.isInjected` access is read-only', async () => { + + const server = Hapi.server(); + server.route({ method: 'GET', path: '/', handler: (request) => { + + const illegalAssignment = () => { + + request.isInjected = false; + }; + + expect(illegalAssignment).to.throw('Cannot set property isInjected of [object Object] which has only a getter'); + + return request.isInjected; + } }); + + const options = { + url: '/' + }; + + const res = await server.inject(options); + expect(res.statusCode).to.equal(200); + expect(res.result).to.be.true(); + }); + + it('sets `request.isInjected = false` for normal request', async () => { + + const server = Hapi.server(); + server.route({ method: 'GET', path: '/', handler: (request) => request.isInjected }); + + await server.start(); + + const { payload } = await Wreck.get(`http://localhost:${server.info.port}/`); + expect(payload.toString()).to.equal('false'); + + await server.stop(); + }); + it('sets app settings', async () => { const server = Hapi.server();