@depup/hapi 18.1.0-depup.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.npmignore +4 -0
- package/LICENSE +26 -0
- package/README.md +47 -0
- package/lib/auth.js +545 -0
- package/lib/compression.js +107 -0
- package/lib/config.js +436 -0
- package/lib/core.js +680 -0
- package/lib/cors.js +207 -0
- package/lib/ext.js +94 -0
- package/lib/handler.js +165 -0
- package/lib/headers.js +200 -0
- package/lib/index.js +11 -0
- package/lib/methods.js +123 -0
- package/lib/request.js +623 -0
- package/lib/response.js +730 -0
- package/lib/route.js +519 -0
- package/lib/security.js +84 -0
- package/lib/server.js +557 -0
- package/lib/streams.js +37 -0
- package/lib/toolkit.js +201 -0
- package/lib/transmit.js +369 -0
- package/lib/validation.js +198 -0
- package/npm-shrinkwrap.json +2456 -0
- package/package.json +132 -0
package/lib/request.js
ADDED
|
@@ -0,0 +1,623 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const Url = require('url');
|
|
4
|
+
|
|
5
|
+
const Boom = require('boom');
|
|
6
|
+
const Bounce = require('bounce');
|
|
7
|
+
const Hoek = require('hoek');
|
|
8
|
+
const Podium = require('podium');
|
|
9
|
+
|
|
10
|
+
const Cors = require('./cors');
|
|
11
|
+
const Response = require('./response');
|
|
12
|
+
const Transmit = require('./transmit');
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
const internals = {
|
|
16
|
+
events: Podium.validate(['finish', { name: 'peek', spread: true }, 'disconnect']),
|
|
17
|
+
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']
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
exports = module.exports = internals.Request = class {
|
|
22
|
+
|
|
23
|
+
constructor(server, req, res, options) {
|
|
24
|
+
|
|
25
|
+
this._allowInternals = !!options.allowInternals;
|
|
26
|
+
this._core = server._core;
|
|
27
|
+
this._entity = null; // Entity information set via h.entity()
|
|
28
|
+
this._eventContext = { request: this };
|
|
29
|
+
this._events = null; // Assigned an emitter when request.events is accessed
|
|
30
|
+
this._expectContinue = !!options.expectContinue;
|
|
31
|
+
this._isPayloadPending = !!(req.headers['content-length'] || req.headers['transfer-encoding']); // Changes to false when incoming payload fully processed
|
|
32
|
+
this._isReplied = false; // true when response processing started
|
|
33
|
+
this._route = this._core.router.specials.notFound.route; // Used prior to routing (only settings are used, not the handler)
|
|
34
|
+
this._serverTimeoutId = null;
|
|
35
|
+
this._states = {};
|
|
36
|
+
this._urlError = null;
|
|
37
|
+
|
|
38
|
+
this.app = (options.app ? Object.assign({}, options.app) : {}); // Place for application-specific state without conflicts with hapi, should not be used by plugins (shallow cloned)
|
|
39
|
+
this.headers = req.headers;
|
|
40
|
+
this.info = internals.info(this._core, req);
|
|
41
|
+
this.jsonp = null;
|
|
42
|
+
this.logs = [];
|
|
43
|
+
this.method = req.method.toLowerCase();
|
|
44
|
+
this.mime = null;
|
|
45
|
+
this.orig = {};
|
|
46
|
+
this.params = null;
|
|
47
|
+
this.paramsArray = null; // Array of path parameters in path order
|
|
48
|
+
this.path = null;
|
|
49
|
+
this.payload = null;
|
|
50
|
+
this.plugins = (options.plugins ? Object.assign({}, options.plugins) : {}); // Place for plugins to store state without conflicts with hapi, should be namespaced using plugin name (shallow cloned)
|
|
51
|
+
this.pre = {}; // Pre raw values
|
|
52
|
+
this.preResponses = {}; // Pre response values
|
|
53
|
+
this.raw = { req, res };
|
|
54
|
+
this.response = null;
|
|
55
|
+
this.route = this._route.public;
|
|
56
|
+
this.query = null;
|
|
57
|
+
this.server = server;
|
|
58
|
+
this.state = null;
|
|
59
|
+
this.url = null;
|
|
60
|
+
|
|
61
|
+
this.auth = {
|
|
62
|
+
isAuthenticated: false,
|
|
63
|
+
isAuthorized: false,
|
|
64
|
+
credentials: options.auth ? options.auth.credentials : null, // Special keys: 'app', 'user', 'scope'
|
|
65
|
+
artifacts: options.auth && options.auth.artifacts || null, // Scheme-specific artifacts
|
|
66
|
+
strategy: options.auth ? options.auth.strategy : null,
|
|
67
|
+
mode: null,
|
|
68
|
+
error: null
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
if (options.auth) {
|
|
72
|
+
this.auth.isInjected = true;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Parse request url
|
|
76
|
+
|
|
77
|
+
this._initializeUrl();
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
static generate(server, req, res, options) {
|
|
81
|
+
|
|
82
|
+
const request = new server._core.Request(server, req, res, options);
|
|
83
|
+
|
|
84
|
+
// Decorate
|
|
85
|
+
|
|
86
|
+
if (server._core._decorations.requestApply) {
|
|
87
|
+
for (const property in server._core._decorations.requestApply) {
|
|
88
|
+
const assignment = server._core._decorations.requestApply[property];
|
|
89
|
+
request[property] = assignment(request);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
request._listen();
|
|
94
|
+
return request;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
get events() {
|
|
98
|
+
|
|
99
|
+
if (!this._events) {
|
|
100
|
+
this._events = new Podium(internals.events);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return this._events;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
_initializeUrl() {
|
|
107
|
+
|
|
108
|
+
try {
|
|
109
|
+
this._setUrl(this.raw.req.url, this._core.settings.router.stripTrailingSlash);
|
|
110
|
+
}
|
|
111
|
+
catch (err) {
|
|
112
|
+
this.path = this.raw.req.url;
|
|
113
|
+
this.query = {};
|
|
114
|
+
|
|
115
|
+
this._urlError = Boom.boomify(err, { statusCode: 400, override: false });
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
setUrl(url, stripTrailingSlash) {
|
|
120
|
+
|
|
121
|
+
Hoek.assert(this.params === null, 'Cannot change request URL after routing');
|
|
122
|
+
|
|
123
|
+
if (url instanceof Url.URL) {
|
|
124
|
+
url = url.href;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
Hoek.assert(typeof url === 'string', 'Url must be a string or URL object');
|
|
128
|
+
|
|
129
|
+
this._setUrl(url, stripTrailingSlash);
|
|
130
|
+
this._urlError = null;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
_setUrl(url, stripTrailingSlash) {
|
|
134
|
+
|
|
135
|
+
const base = (url[0] === '/' ? `${this._core.info.protocol}://${this.info.host || `${this._core.info.host}:${this._core.info.port}`}` : undefined);
|
|
136
|
+
|
|
137
|
+
url = new Url.URL(url, base);
|
|
138
|
+
|
|
139
|
+
// Apply path modifications
|
|
140
|
+
|
|
141
|
+
let path = this._core.router.normalize(url.pathname); // pathname excludes query
|
|
142
|
+
|
|
143
|
+
if (stripTrailingSlash &&
|
|
144
|
+
path.length > 1 &&
|
|
145
|
+
path[path.length - 1] === '/') {
|
|
146
|
+
|
|
147
|
+
path = path.slice(0, -1);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
url.pathname = path;
|
|
151
|
+
|
|
152
|
+
// Parse query (must be done before this.url is set in case query parsing throws)
|
|
153
|
+
|
|
154
|
+
this.query = this._parseQuery(url.searchParams);
|
|
155
|
+
|
|
156
|
+
// Store request properties
|
|
157
|
+
|
|
158
|
+
this.url = url;
|
|
159
|
+
this.path = path;
|
|
160
|
+
|
|
161
|
+
this.info.hostname = url.hostname;
|
|
162
|
+
this.info.host = url.host;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
_parseQuery(searchParams) {
|
|
166
|
+
|
|
167
|
+
// Flatten map
|
|
168
|
+
|
|
169
|
+
let query = Object.create(null);
|
|
170
|
+
for (let [key, value] of searchParams) {
|
|
171
|
+
const entry = query[key];
|
|
172
|
+
if (entry !== undefined) {
|
|
173
|
+
value = [].concat(entry, value);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
query[key] = value;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Custom parser
|
|
180
|
+
|
|
181
|
+
const parser = this._core.settings.query.parser;
|
|
182
|
+
if (parser) {
|
|
183
|
+
query = parser(query);
|
|
184
|
+
if (!query ||
|
|
185
|
+
typeof query !== 'object') {
|
|
186
|
+
|
|
187
|
+
throw Boom.badImplementation('Parsed query must be an object');
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
return query;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
setMethod(method) {
|
|
195
|
+
|
|
196
|
+
Hoek.assert(this.params === null, 'Cannot change request method after routing');
|
|
197
|
+
Hoek.assert(method && typeof method === 'string', 'Missing method');
|
|
198
|
+
|
|
199
|
+
this.method = method.toLowerCase();
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
active() {
|
|
203
|
+
|
|
204
|
+
return !!this._eventContext.request;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
async _execute() {
|
|
208
|
+
|
|
209
|
+
this.info.acceptEncoding = this._core.compression.accept(this);
|
|
210
|
+
|
|
211
|
+
try {
|
|
212
|
+
await this._onRequest();
|
|
213
|
+
}
|
|
214
|
+
catch (err) {
|
|
215
|
+
Bounce.rethrow(err, 'system');
|
|
216
|
+
return this._reply(err);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
this._lookup();
|
|
220
|
+
this._setTimeouts();
|
|
221
|
+
await this._lifecycle();
|
|
222
|
+
this._reply();
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
async _onRequest() {
|
|
226
|
+
|
|
227
|
+
// onRequest (can change request method and url)
|
|
228
|
+
|
|
229
|
+
if (this._core.extensions.route.onRequest.nodes) {
|
|
230
|
+
const response = await this._invoke(this._core.extensions.route.onRequest);
|
|
231
|
+
if (response) {
|
|
232
|
+
if (!internals.skip(response)) {
|
|
233
|
+
throw Boom.badImplementation('onRequest extension methods must return an error, a takeover response, or a continue signal');
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
throw response;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Validate path
|
|
241
|
+
|
|
242
|
+
if (this._urlError) {
|
|
243
|
+
throw this._urlError;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
_listen() {
|
|
248
|
+
|
|
249
|
+
if (this._isPayloadPending) {
|
|
250
|
+
this.raw.req.on('end', internals.event.bind(this.raw.req, this._eventContext, 'end'));
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
this.raw.req.on('close', internals.event.bind(this.raw.req, this._eventContext, 'close'));
|
|
254
|
+
this.raw.req.on('error', internals.event.bind(this.raw.req, this._eventContext, 'error'));
|
|
255
|
+
this.raw.req.on('aborted', internals.event.bind(this.raw.req, this._eventContext, 'abort'));
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
_lookup() {
|
|
259
|
+
|
|
260
|
+
const match = this._core.router.route(this.method, this.path, this.info.hostname);
|
|
261
|
+
if (!match.route.settings.isInternal ||
|
|
262
|
+
this._allowInternals) {
|
|
263
|
+
|
|
264
|
+
this._route = match.route;
|
|
265
|
+
this.route = this._route.public;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
this.params = match.params || {};
|
|
269
|
+
this.paramsArray = match.paramsArray || [];
|
|
270
|
+
|
|
271
|
+
if (this.route.settings.cors) {
|
|
272
|
+
this.info.cors = {
|
|
273
|
+
isOriginMatch: Cors.matchOrigin(this.headers.origin, this.route.settings.cors)
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
_setTimeouts() {
|
|
279
|
+
|
|
280
|
+
if (this.raw.req.socket &&
|
|
281
|
+
this.route.settings.timeout.socket !== undefined) {
|
|
282
|
+
|
|
283
|
+
this.raw.req.socket.setTimeout(this.route.settings.timeout.socket || 0); // Value can be false or positive
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
let serverTimeout = this.route.settings.timeout.server;
|
|
287
|
+
if (!serverTimeout) {
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
const elapsed = Date.now() - this.info.received;
|
|
292
|
+
serverTimeout = Math.floor(serverTimeout - elapsed); // Calculate the timeout from when the request was constructed
|
|
293
|
+
|
|
294
|
+
if (serverTimeout <= 0) {
|
|
295
|
+
internals.timeoutReply(this, serverTimeout);
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
this._serverTimeoutId = setTimeout(internals.timeoutReply, serverTimeout, this, serverTimeout);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
async _lifecycle() {
|
|
303
|
+
|
|
304
|
+
for (const func of this._route._cycle) {
|
|
305
|
+
if (this._isReplied ||
|
|
306
|
+
!this._eventContext.request) {
|
|
307
|
+
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
try {
|
|
312
|
+
var response = await (typeof func === 'function' ? func(this) : this._invoke(func));
|
|
313
|
+
}
|
|
314
|
+
catch (err) {
|
|
315
|
+
Bounce.rethrow(err, 'system');
|
|
316
|
+
response = Response.wrap(err, this);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
if (!response ||
|
|
320
|
+
response === this._core.toolkit.continue) { // Continue
|
|
321
|
+
|
|
322
|
+
continue;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
if (!internals.skip(response)) {
|
|
326
|
+
response = Boom.badImplementation('Lifecycle methods called before the handler can only return an error, a takeover response, or a continue signal');
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
this._setResponse(response);
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
async _invoke(event) {
|
|
335
|
+
|
|
336
|
+
for (const ext of event.nodes) {
|
|
337
|
+
const bind = (ext.bind || ext.realm.settings.bind);
|
|
338
|
+
const realm = ext.realm;
|
|
339
|
+
const response = await this._core.toolkit.execute(ext.func, this, { bind, realm });
|
|
340
|
+
|
|
341
|
+
if (response === this._core.toolkit.continue) {
|
|
342
|
+
continue;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
if (internals.skip(response) ||
|
|
346
|
+
this.response === null) {
|
|
347
|
+
|
|
348
|
+
return response;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
this._setResponse(response);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
async _reply(exit) {
|
|
356
|
+
|
|
357
|
+
if (this._isReplied) { // Prevent any future responses to this request
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
this._isReplied = true;
|
|
362
|
+
|
|
363
|
+
if (this._serverTimeoutId) {
|
|
364
|
+
clearTimeout(this._serverTimeoutId);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
if (!this._eventContext.request) {
|
|
368
|
+
this._finalize();
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
if (exit) { // Can be a valid response or error (if returned from an ext, already handled because this.response is also set)
|
|
373
|
+
this._setResponse(Response.wrap(exit, this)); // Wrap to ensure any object thrown is always a valid Boom or Response object
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
if (typeof this.response === 'symbol') { // close or abandon
|
|
377
|
+
this._abort();
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
await this._postCycle();
|
|
382
|
+
|
|
383
|
+
if (!this._eventContext.request ||
|
|
384
|
+
typeof this.response === 'symbol') { // close or abandon
|
|
385
|
+
|
|
386
|
+
this._abort();
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
await Transmit.send(this);
|
|
391
|
+
this._finalize();
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
async _postCycle() {
|
|
395
|
+
|
|
396
|
+
for (const func of this._route._postCycle) {
|
|
397
|
+
if (!this._eventContext.request) {
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
try {
|
|
402
|
+
var response = await (typeof func === 'function' ? func(this) : this._invoke(func));
|
|
403
|
+
}
|
|
404
|
+
catch (err) {
|
|
405
|
+
Bounce.rethrow(err, 'system');
|
|
406
|
+
response = Response.wrap(err, this);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
if (response &&
|
|
410
|
+
response !== this._core.toolkit.continue) { // Continue
|
|
411
|
+
|
|
412
|
+
this._setResponse(response);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
_abort() {
|
|
418
|
+
|
|
419
|
+
if (this.response === this._core.toolkit.close) {
|
|
420
|
+
this.raw.res.end(); // End the response in case it wasn't already closed
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
this._finalize();
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
_finalize() {
|
|
427
|
+
|
|
428
|
+
if (this.response &&
|
|
429
|
+
this.response.statusCode === 500 &&
|
|
430
|
+
this.response._error) {
|
|
431
|
+
|
|
432
|
+
const tags = this.response._error.isDeveloperError ? ['internal', 'implementation', 'error'] : ['internal', 'error'];
|
|
433
|
+
this._log(tags, this.response._error, 'error');
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
// Cleanup
|
|
437
|
+
|
|
438
|
+
this._eventContext.request = null; // Disable req events
|
|
439
|
+
|
|
440
|
+
if (this.response &&
|
|
441
|
+
this.response._close) {
|
|
442
|
+
|
|
443
|
+
this.response._close(this);
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
this.info.completed = Date.now();
|
|
447
|
+
this._core.events.emit('response', this);
|
|
448
|
+
this._core.queue.release();
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
_setResponse(response) {
|
|
452
|
+
|
|
453
|
+
if (this.response &&
|
|
454
|
+
!this.response.isBoom &&
|
|
455
|
+
this.response !== response &&
|
|
456
|
+
(response.isBoom || this.response.source !== response.source)) {
|
|
457
|
+
|
|
458
|
+
this.response._close(this);
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
if (this.info.completed) {
|
|
462
|
+
if (response._close) {
|
|
463
|
+
response._close(this);
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
return;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
this.response = response;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
_setState(name, value, options) {
|
|
473
|
+
|
|
474
|
+
const state = { name, value };
|
|
475
|
+
if (options) {
|
|
476
|
+
Hoek.assert(!options.autoValue, 'Cannot set autoValue directly in a response');
|
|
477
|
+
state.options = Hoek.clone(options);
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
this._states[name] = state;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
_clearState(name, options = {}) {
|
|
484
|
+
|
|
485
|
+
const state = { name };
|
|
486
|
+
|
|
487
|
+
state.options = Hoek.clone(options);
|
|
488
|
+
state.options.ttl = 0;
|
|
489
|
+
|
|
490
|
+
this._states[name] = state;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
_tap() {
|
|
494
|
+
|
|
495
|
+
if (!this._events) {
|
|
496
|
+
return null;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
return (this._events.hasListeners('finish') || this._events.hasListeners('peek') ? new Response.Peek(this._events) : null);
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
log(tags, data) {
|
|
503
|
+
|
|
504
|
+
return this._log(tags, data, 'app');
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
_log(tags, data, channel = 'internal') {
|
|
508
|
+
|
|
509
|
+
if (!this._core.events.hasListeners('request') &&
|
|
510
|
+
!this.route.settings.log.collect) {
|
|
511
|
+
|
|
512
|
+
return;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
if (!Array.isArray(tags)) {
|
|
516
|
+
tags = [tags];
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
const timestamp = Date.now();
|
|
520
|
+
const field = (data instanceof Error ? 'error' : 'data');
|
|
521
|
+
|
|
522
|
+
let event = [this, { request: this.info.id, timestamp, tags, [field]: data, channel }];
|
|
523
|
+
if (typeof data === 'function') {
|
|
524
|
+
event = () => [this, { request: this.info.id, timestamp, tags, data: data(), channel }];
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
if (this.route.settings.log.collect) {
|
|
528
|
+
if (typeof data === 'function') {
|
|
529
|
+
event = event();
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
this.logs.push(event[1]);
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
this._core.events.emit({ name: 'request', channel, tags }, event);
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
generateResponse(source, options) {
|
|
539
|
+
|
|
540
|
+
return new Response(source, this, options);
|
|
541
|
+
}
|
|
542
|
+
};
|
|
543
|
+
|
|
544
|
+
|
|
545
|
+
internals.Request.reserved = internals.reserved;
|
|
546
|
+
|
|
547
|
+
|
|
548
|
+
internals.info = function (core, req) {
|
|
549
|
+
|
|
550
|
+
const host = req.headers.host ? req.headers.host.trim() : '';
|
|
551
|
+
const received = Date.now();
|
|
552
|
+
|
|
553
|
+
const info = {
|
|
554
|
+
received,
|
|
555
|
+
remoteAddress: req.connection.remoteAddress,
|
|
556
|
+
remotePort: req.connection.remotePort || '',
|
|
557
|
+
referrer: req.headers.referrer || req.headers.referer || '',
|
|
558
|
+
host,
|
|
559
|
+
hostname: host.split(':')[0],
|
|
560
|
+
id: `${received}:${core.info.id}:${core.requestCounter.value++}`,
|
|
561
|
+
|
|
562
|
+
// Assigned later
|
|
563
|
+
|
|
564
|
+
acceptEncoding: null,
|
|
565
|
+
cors: null,
|
|
566
|
+
responded: 0,
|
|
567
|
+
completed: 0
|
|
568
|
+
};
|
|
569
|
+
|
|
570
|
+
if (core.requestCounter.value > core.requestCounter.max) {
|
|
571
|
+
core.requestCounter.value = core.requestCounter.min;
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
return info;
|
|
575
|
+
};
|
|
576
|
+
|
|
577
|
+
|
|
578
|
+
internals.event = function ({ request }, event, err) {
|
|
579
|
+
|
|
580
|
+
if (!request) {
|
|
581
|
+
return;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
request._isPayloadPending = false;
|
|
585
|
+
|
|
586
|
+
if (event === 'close' &&
|
|
587
|
+
request.raw.res.finished) {
|
|
588
|
+
|
|
589
|
+
return;
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
if (event === 'end') {
|
|
593
|
+
return;
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
request._log(err ? ['request', 'error'] : ['request', 'error', event], err);
|
|
597
|
+
|
|
598
|
+
if (event === 'error') {
|
|
599
|
+
return;
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
request._eventContext.request = null;
|
|
603
|
+
|
|
604
|
+
if (event === 'abort' &&
|
|
605
|
+
request._events) {
|
|
606
|
+
|
|
607
|
+
request._events.emit('disconnect');
|
|
608
|
+
}
|
|
609
|
+
};
|
|
610
|
+
|
|
611
|
+
|
|
612
|
+
internals.timeoutReply = function (request, timeout) {
|
|
613
|
+
|
|
614
|
+
const elapsed = Date.now() - request.info.received;
|
|
615
|
+
request._log(['request', 'server', 'timeout', 'error'], { timeout, elapsed });
|
|
616
|
+
request._reply(Boom.serverUnavailable());
|
|
617
|
+
};
|
|
618
|
+
|
|
619
|
+
|
|
620
|
+
internals.skip = function (response) {
|
|
621
|
+
|
|
622
|
+
return (response.isBoom || response._takeover || typeof response === 'symbol');
|
|
623
|
+
};
|