@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/response.js
ADDED
|
@@ -0,0 +1,730 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const Stream = require('stream');
|
|
4
|
+
|
|
5
|
+
const Boom = require('boom');
|
|
6
|
+
const Bounce = require('bounce');
|
|
7
|
+
const Hoek = require('hoek');
|
|
8
|
+
const Podium = require('podium');
|
|
9
|
+
|
|
10
|
+
const Streams = require('./streams');
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
const internals = {
|
|
14
|
+
events: Podium.validate(['finish', { name: 'peek', spread: true }]),
|
|
15
|
+
hopByHop: {
|
|
16
|
+
connection: true,
|
|
17
|
+
'keep-alive': true,
|
|
18
|
+
'proxy-authenticate': true,
|
|
19
|
+
'proxy-authorization': true,
|
|
20
|
+
'te': true,
|
|
21
|
+
'trailers': true,
|
|
22
|
+
'transfer-encoding': true,
|
|
23
|
+
'upgrade': true
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
exports = module.exports = internals.Response = class {
|
|
29
|
+
|
|
30
|
+
constructor(source, request, options = {}) {
|
|
31
|
+
|
|
32
|
+
this.app = {};
|
|
33
|
+
this.headers = {}; // Incomplete as some headers are stored in flags
|
|
34
|
+
this.plugins = {};
|
|
35
|
+
this.request = request;
|
|
36
|
+
this.source = null;
|
|
37
|
+
this.statusCode = null;
|
|
38
|
+
this.variety = null;
|
|
39
|
+
|
|
40
|
+
this.settings = {
|
|
41
|
+
encoding: 'utf8',
|
|
42
|
+
charset: 'utf-8', // '-' required by IANA
|
|
43
|
+
ttl: null,
|
|
44
|
+
stringify: null, // JSON.stringify options
|
|
45
|
+
passThrough: true,
|
|
46
|
+
varyEtag: false,
|
|
47
|
+
message: null
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
this._events = null;
|
|
51
|
+
this._payload = null; // Readable stream
|
|
52
|
+
this._error = null; // The boom object when created from an error (used for logging)
|
|
53
|
+
this._contentEncoding = null; // Set during transmit
|
|
54
|
+
this._contentType = null; // Used if no explicit content-type is set and type is known
|
|
55
|
+
this._takeover = false;
|
|
56
|
+
this._statusCode = false; // true when code() called
|
|
57
|
+
|
|
58
|
+
this._processors = {
|
|
59
|
+
marshal: options.marshal,
|
|
60
|
+
prepare: options.prepare,
|
|
61
|
+
close: options.close
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
this.temporary = null;
|
|
65
|
+
this.permanent = null;
|
|
66
|
+
this.rewritable = null;
|
|
67
|
+
|
|
68
|
+
this._setSource(source, options.variety);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
static wrap(result, request) {
|
|
72
|
+
|
|
73
|
+
if (result instanceof internals.Response ||
|
|
74
|
+
typeof result === 'symbol') {
|
|
75
|
+
|
|
76
|
+
return result;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (result instanceof Error) {
|
|
80
|
+
return Boom.boomify(result);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return new internals.Response(result, request);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
_setSource(source, variety) {
|
|
87
|
+
|
|
88
|
+
// Method must not set any headers or other properties as source can change later
|
|
89
|
+
|
|
90
|
+
this.variety = variety || 'plain';
|
|
91
|
+
|
|
92
|
+
if (source === null ||
|
|
93
|
+
source === undefined) {
|
|
94
|
+
|
|
95
|
+
source = null;
|
|
96
|
+
}
|
|
97
|
+
else if (Buffer.isBuffer(source)) {
|
|
98
|
+
this.variety = 'buffer';
|
|
99
|
+
this._contentType = 'application/octet-stream';
|
|
100
|
+
}
|
|
101
|
+
else if (source instanceof Stream) {
|
|
102
|
+
this.variety = 'stream';
|
|
103
|
+
this._contentType = 'application/octet-stream';
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
this.source = source;
|
|
107
|
+
|
|
108
|
+
if (this.variety === 'plain' &&
|
|
109
|
+
this.source !== null) {
|
|
110
|
+
|
|
111
|
+
this._contentType = (typeof this.source === 'string' ? 'text/html' : 'application/json');
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
get events() {
|
|
116
|
+
|
|
117
|
+
if (!this._events) {
|
|
118
|
+
this._events = new Podium(internals.events);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return this._events;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
code(statusCode) {
|
|
125
|
+
|
|
126
|
+
Hoek.assert(Number.isSafeInteger(statusCode), 'Status code must be an integer');
|
|
127
|
+
|
|
128
|
+
this.statusCode = statusCode;
|
|
129
|
+
this._statusCode = true;
|
|
130
|
+
|
|
131
|
+
return this;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
message(httpMessage) {
|
|
135
|
+
|
|
136
|
+
this.settings.message = httpMessage;
|
|
137
|
+
return this;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
header(key, value, options) {
|
|
141
|
+
|
|
142
|
+
key = key.toLowerCase();
|
|
143
|
+
if (key === 'vary') {
|
|
144
|
+
return this.vary(value);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return this._header(key, value, options);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
_header(key, value, options = {}) {
|
|
151
|
+
|
|
152
|
+
const append = options.append || false;
|
|
153
|
+
const separator = options.separator || ',';
|
|
154
|
+
const override = options.override !== false;
|
|
155
|
+
const duplicate = options.duplicate !== false;
|
|
156
|
+
|
|
157
|
+
if ((!append && override) ||
|
|
158
|
+
!this.headers[key]) {
|
|
159
|
+
|
|
160
|
+
this.headers[key] = value;
|
|
161
|
+
}
|
|
162
|
+
else if (override) {
|
|
163
|
+
if (key === 'set-cookie') {
|
|
164
|
+
this.headers[key] = [].concat(this.headers[key], value);
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
const existing = this.headers[key];
|
|
168
|
+
if (!duplicate) {
|
|
169
|
+
const values = existing.split(separator);
|
|
170
|
+
for (const v of values) {
|
|
171
|
+
if (v === value) {
|
|
172
|
+
return this;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
this.headers[key] = existing + separator + value;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return this;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
vary(value) {
|
|
185
|
+
|
|
186
|
+
if (value === '*') {
|
|
187
|
+
this.headers.vary = '*';
|
|
188
|
+
}
|
|
189
|
+
else if (!this.headers.vary) {
|
|
190
|
+
this.headers.vary = value;
|
|
191
|
+
}
|
|
192
|
+
else if (this.headers.vary !== '*') {
|
|
193
|
+
this._header('vary', value, { append: true, duplicate: false });
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return this;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
etag(tag, options) {
|
|
200
|
+
|
|
201
|
+
const entity = internals.Response.entity(tag, options);
|
|
202
|
+
this._header('etag', entity.etag);
|
|
203
|
+
this.settings.varyEtag = entity.vary;
|
|
204
|
+
return this;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
static entity(tag, options = {}) {
|
|
208
|
+
|
|
209
|
+
Hoek.assert(tag !== '*', 'ETag cannot be *');
|
|
210
|
+
|
|
211
|
+
return {
|
|
212
|
+
etag: (options.weak ? 'W/' : '') + '"' + tag + '"',
|
|
213
|
+
vary: (options.vary !== false && !options.weak), // vary defaults to true
|
|
214
|
+
modified: options.modified
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
static unmodified(request, options) {
|
|
219
|
+
|
|
220
|
+
if (request.method !== 'get' &&
|
|
221
|
+
request.method !== 'head') {
|
|
222
|
+
|
|
223
|
+
return false;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Strong verifier
|
|
227
|
+
|
|
228
|
+
if (options.etag &&
|
|
229
|
+
request.headers['if-none-match']) {
|
|
230
|
+
|
|
231
|
+
const ifNoneMatch = request.headers['if-none-match'].split(/\s*,\s*/);
|
|
232
|
+
for (const etag of ifNoneMatch) {
|
|
233
|
+
if (etag === options.etag) {
|
|
234
|
+
return true;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
if (options.vary) {
|
|
238
|
+
const etagBase = options.etag.slice(0, -1);
|
|
239
|
+
const encoders = request._core.compression.encodings;
|
|
240
|
+
for (const encoder of encoders) {
|
|
241
|
+
if (etag === etagBase + `-${encoder}"`) {
|
|
242
|
+
return true;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
return false;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Weak verifier
|
|
252
|
+
|
|
253
|
+
if (!options.modified) {
|
|
254
|
+
return false;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
const ifModifiedSinceHeader = request.headers['if-modified-since'];
|
|
258
|
+
if (!ifModifiedSinceHeader) {
|
|
259
|
+
return false;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
const ifModifiedSince = internals.parseDate(ifModifiedSinceHeader);
|
|
263
|
+
if (!ifModifiedSince) {
|
|
264
|
+
return false;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
const lastModified = internals.parseDate(options.modified);
|
|
268
|
+
if (!lastModified) {
|
|
269
|
+
return false;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
return ifModifiedSince >= lastModified;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
type(type) {
|
|
276
|
+
|
|
277
|
+
this._header('content-type', type);
|
|
278
|
+
return this;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
bytes(bytes) {
|
|
282
|
+
|
|
283
|
+
this._header('content-length', bytes);
|
|
284
|
+
return this;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
location(uri) {
|
|
288
|
+
|
|
289
|
+
this._header('location', uri);
|
|
290
|
+
return this;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
created(location) {
|
|
294
|
+
|
|
295
|
+
Hoek.assert(this.request.method === 'post' ||
|
|
296
|
+
this.request.method === 'put' ||
|
|
297
|
+
this.request.method === 'patch', 'Cannot return 201 status codes for ' + this.request.method.toUpperCase());
|
|
298
|
+
|
|
299
|
+
this.statusCode = 201;
|
|
300
|
+
this.location(location);
|
|
301
|
+
return this;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
replacer(method) {
|
|
305
|
+
|
|
306
|
+
this.settings.stringify = this.settings.stringify || {};
|
|
307
|
+
this.settings.stringify.replacer = method;
|
|
308
|
+
return this;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
spaces(count) {
|
|
312
|
+
|
|
313
|
+
this.settings.stringify = this.settings.stringify || {};
|
|
314
|
+
this.settings.stringify.space = count;
|
|
315
|
+
return this;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
suffix(suffix) {
|
|
319
|
+
|
|
320
|
+
this.settings.stringify = this.settings.stringify || {};
|
|
321
|
+
this.settings.stringify.suffix = suffix;
|
|
322
|
+
return this;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
escape(escape) {
|
|
326
|
+
|
|
327
|
+
this.settings.stringify = this.settings.stringify || {};
|
|
328
|
+
this.settings.stringify.escape = escape;
|
|
329
|
+
return this;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
passThrough(enabled) {
|
|
333
|
+
|
|
334
|
+
this.settings.passThrough = (enabled !== false); // Defaults to true
|
|
335
|
+
return this;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
redirect(location) {
|
|
339
|
+
|
|
340
|
+
this.statusCode = 302;
|
|
341
|
+
this.location(location);
|
|
342
|
+
this.temporary = this._temporary;
|
|
343
|
+
this.permanent = this._permanent;
|
|
344
|
+
this.rewritable = this._rewritable;
|
|
345
|
+
return this;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
_temporary(isTemporary) {
|
|
349
|
+
|
|
350
|
+
this._setTemporary(isTemporary !== false); // Defaults to true
|
|
351
|
+
return this;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
_permanent(isPermanent) {
|
|
355
|
+
|
|
356
|
+
this._setTemporary(isPermanent === false); // Defaults to true
|
|
357
|
+
return this;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
_rewritable(isRewritable) {
|
|
361
|
+
|
|
362
|
+
this._setRewritable(isRewritable !== false); // Defaults to true
|
|
363
|
+
return this;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
_isTemporary() {
|
|
367
|
+
|
|
368
|
+
return this.statusCode === 302 || this.statusCode === 307;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
_isRewritable() {
|
|
372
|
+
|
|
373
|
+
return this.statusCode === 301 || this.statusCode === 302;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
_setTemporary(isTemporary) {
|
|
377
|
+
|
|
378
|
+
if (isTemporary) {
|
|
379
|
+
if (this._isRewritable()) {
|
|
380
|
+
this.statusCode = 302;
|
|
381
|
+
}
|
|
382
|
+
else {
|
|
383
|
+
this.statusCode = 307;
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
else {
|
|
387
|
+
if (this._isRewritable()) {
|
|
388
|
+
this.statusCode = 301;
|
|
389
|
+
}
|
|
390
|
+
else {
|
|
391
|
+
this.statusCode = 308;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
_setRewritable(isRewritable) {
|
|
397
|
+
|
|
398
|
+
if (isRewritable) {
|
|
399
|
+
if (this._isTemporary()) {
|
|
400
|
+
this.statusCode = 302;
|
|
401
|
+
}
|
|
402
|
+
else {
|
|
403
|
+
this.statusCode = 301;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
else {
|
|
407
|
+
if (this._isTemporary()) {
|
|
408
|
+
this.statusCode = 307;
|
|
409
|
+
}
|
|
410
|
+
else {
|
|
411
|
+
this.statusCode = 308;
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
encoding(encoding) {
|
|
417
|
+
|
|
418
|
+
this.settings.encoding = encoding;
|
|
419
|
+
return this;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
charset(charset) {
|
|
423
|
+
|
|
424
|
+
this.settings.charset = charset || null;
|
|
425
|
+
return this;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
ttl(ttl) {
|
|
429
|
+
|
|
430
|
+
this.settings.ttl = ttl;
|
|
431
|
+
return this;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
state(name, value, options) {
|
|
435
|
+
|
|
436
|
+
this.request._setState(name, value, options);
|
|
437
|
+
return this;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
unstate(name, options) {
|
|
441
|
+
|
|
442
|
+
this.request._clearState(name, options);
|
|
443
|
+
return this;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
takeover() {
|
|
447
|
+
|
|
448
|
+
this._takeover = true;
|
|
449
|
+
return this;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
_prepare() {
|
|
453
|
+
|
|
454
|
+
this._passThrough();
|
|
455
|
+
|
|
456
|
+
if (!this._processors.prepare) {
|
|
457
|
+
return this;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
try {
|
|
461
|
+
return this._processors.prepare(this);
|
|
462
|
+
}
|
|
463
|
+
catch (err) {
|
|
464
|
+
throw Boom.boomify(err);
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
_passThrough() {
|
|
469
|
+
|
|
470
|
+
if (this.variety === 'stream' &&
|
|
471
|
+
this.settings.passThrough) {
|
|
472
|
+
|
|
473
|
+
if (this.source.statusCode &&
|
|
474
|
+
!this.statusCode) {
|
|
475
|
+
|
|
476
|
+
this.statusCode = this.source.statusCode; // Stream is an HTTP response
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
if (this.source.headers) {
|
|
480
|
+
let headerKeys = Object.keys(this.source.headers);
|
|
481
|
+
|
|
482
|
+
if (headerKeys.length) {
|
|
483
|
+
const localHeaders = this.headers;
|
|
484
|
+
this.headers = {};
|
|
485
|
+
|
|
486
|
+
const connection = this.source.headers.connection;
|
|
487
|
+
const byHop = {};
|
|
488
|
+
if (connection) {
|
|
489
|
+
connection.split(/\s*,\s*/).forEach((header) => {
|
|
490
|
+
|
|
491
|
+
byHop[header] = true;
|
|
492
|
+
});
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
for (const key of headerKeys) {
|
|
496
|
+
const lower = key.toLowerCase();
|
|
497
|
+
if (!internals.hopByHop[lower] &&
|
|
498
|
+
!byHop[lower]) {
|
|
499
|
+
|
|
500
|
+
this.header(lower, Hoek.clone(this.source.headers[key])); // Clone arrays
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
headerKeys = Object.keys(localHeaders);
|
|
505
|
+
for (const key of headerKeys) {
|
|
506
|
+
this.header(key, localHeaders[key], { append: key === 'set-cookie' });
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
this.statusCode = this.statusCode || 200;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
async _marshal() {
|
|
516
|
+
|
|
517
|
+
let source = this.source;
|
|
518
|
+
|
|
519
|
+
// Processor marshal
|
|
520
|
+
|
|
521
|
+
if (this._processors.marshal) {
|
|
522
|
+
try {
|
|
523
|
+
source = await this._processors.marshal(this);
|
|
524
|
+
}
|
|
525
|
+
catch (err) {
|
|
526
|
+
throw Boom.boomify(err);
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
// Stream source
|
|
531
|
+
|
|
532
|
+
if (source instanceof Stream) {
|
|
533
|
+
if (typeof source._read !== 'function') {
|
|
534
|
+
throw Boom.badImplementation('Stream must have a readable interface');
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
if (source._readableState.objectMode) {
|
|
538
|
+
throw Boom.badImplementation('Cannot reply with stream in object mode');
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
this._payload = source;
|
|
542
|
+
return;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
// Plain source (non string or null)
|
|
546
|
+
|
|
547
|
+
const jsonify = (this.variety === 'plain' && source !== null && typeof source !== 'string');
|
|
548
|
+
|
|
549
|
+
if (!jsonify &&
|
|
550
|
+
this.settings.stringify) {
|
|
551
|
+
|
|
552
|
+
throw Boom.badImplementation('Cannot set formatting options on non object response');
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
let payload = source;
|
|
556
|
+
|
|
557
|
+
if (jsonify) {
|
|
558
|
+
const options = this.settings.stringify || {};
|
|
559
|
+
const space = options.space || this.request.route.settings.json.space;
|
|
560
|
+
const replacer = options.replacer || this.request.route.settings.json.replacer;
|
|
561
|
+
const suffix = options.suffix || this.request.route.settings.json.suffix || '';
|
|
562
|
+
const escape = this.request.route.settings.json.escape || false;
|
|
563
|
+
|
|
564
|
+
try {
|
|
565
|
+
if (replacer || space) {
|
|
566
|
+
payload = JSON.stringify(payload, replacer, space);
|
|
567
|
+
}
|
|
568
|
+
else {
|
|
569
|
+
payload = JSON.stringify(payload);
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
catch (err) {
|
|
573
|
+
throw Boom.boomify(err);
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
if (suffix) {
|
|
577
|
+
payload = payload + suffix;
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
if (escape) {
|
|
581
|
+
payload = Hoek.escapeJson(payload);
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
this._payload = new internals.Response.Payload(payload, this.settings);
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
_tap() {
|
|
589
|
+
|
|
590
|
+
if (!this._events) {
|
|
591
|
+
return null;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
return (this._events.hasListeners('finish') || this._events.hasListeners('peek') ? new internals.Response.Peek(this._events) : null);
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
_close(request) {
|
|
598
|
+
|
|
599
|
+
if (this._processors.close) {
|
|
600
|
+
try {
|
|
601
|
+
this._processors.close(this);
|
|
602
|
+
}
|
|
603
|
+
catch (err) {
|
|
604
|
+
Bounce.rethrow(err, 'system');
|
|
605
|
+
request._log(['response', 'cleanup', 'error'], err);
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
const stream = this._payload || this.source;
|
|
610
|
+
if (stream instanceof Stream) {
|
|
611
|
+
internals.Response.drain(stream);
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
_isPayloadSupported() {
|
|
616
|
+
|
|
617
|
+
return (this.request.method !== 'head' && this.statusCode !== 304 && this.statusCode !== 204);
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
static drain(stream) {
|
|
621
|
+
|
|
622
|
+
if (stream.unpipe) {
|
|
623
|
+
stream.unpipe();
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
if (stream.close) {
|
|
627
|
+
stream.close();
|
|
628
|
+
}
|
|
629
|
+
else if (stream.destroy) {
|
|
630
|
+
stream.destroy();
|
|
631
|
+
}
|
|
632
|
+
else {
|
|
633
|
+
Streams.drain(stream);
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
};
|
|
637
|
+
|
|
638
|
+
|
|
639
|
+
internals.parseDate = function (string) {
|
|
640
|
+
|
|
641
|
+
try {
|
|
642
|
+
return Date.parse(string);
|
|
643
|
+
}
|
|
644
|
+
catch (errIgnore) { }
|
|
645
|
+
};
|
|
646
|
+
|
|
647
|
+
|
|
648
|
+
internals.Response.Payload = class extends Stream.Readable {
|
|
649
|
+
|
|
650
|
+
constructor(payload, options) {
|
|
651
|
+
|
|
652
|
+
super();
|
|
653
|
+
|
|
654
|
+
this._data = payload;
|
|
655
|
+
this._prefix = null;
|
|
656
|
+
this._suffix = null;
|
|
657
|
+
this._sizeOffset = 0;
|
|
658
|
+
this._encoding = options.encoding;
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
_read(size) {
|
|
662
|
+
|
|
663
|
+
if (this._prefix) {
|
|
664
|
+
this.push(this._prefix, this._encoding);
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
if (this._data) {
|
|
668
|
+
this.push(this._data, this._encoding);
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
if (this._suffix) {
|
|
672
|
+
this.push(this._suffix, this._encoding);
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
this.push(null);
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
size() {
|
|
679
|
+
|
|
680
|
+
if (!this._data) {
|
|
681
|
+
return this._sizeOffset;
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
return (Buffer.isBuffer(this._data) ? this._data.length : Buffer.byteLength(this._data, this._encoding)) + this._sizeOffset;
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
jsonp(variable) {
|
|
688
|
+
|
|
689
|
+
this._sizeOffset = this._sizeOffset + variable.length + 7;
|
|
690
|
+
this._prefix = '/**/' + variable + '('; // '/**/' prefix prevents CVE-2014-4671 security exploit
|
|
691
|
+
this._data = (this._data === null || Buffer.isBuffer(this._data)) ? this._data : this._data.replace(/\u2028/g, '\\u2028').replace(/\u2029/g, '\\u2029');
|
|
692
|
+
this._suffix = ');';
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
writeToStream(stream) {
|
|
696
|
+
|
|
697
|
+
if (this._prefix) {
|
|
698
|
+
stream.write(this._prefix, this._encoding);
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
if (this._data) {
|
|
702
|
+
stream.write(this._data, this._encoding);
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
if (this._suffix) {
|
|
706
|
+
stream.write(this._suffix, this._encoding);
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
stream.end();
|
|
710
|
+
}
|
|
711
|
+
};
|
|
712
|
+
|
|
713
|
+
|
|
714
|
+
internals.Response.Peek = class extends Stream.Transform {
|
|
715
|
+
|
|
716
|
+
constructor(podium) {
|
|
717
|
+
|
|
718
|
+
super();
|
|
719
|
+
|
|
720
|
+
this._podium = podium;
|
|
721
|
+
this.on('finish', () => podium.emit('finish'));
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
_transform(chunk, encoding, callback) {
|
|
725
|
+
|
|
726
|
+
this._podium.emit('peek', [chunk, encoding]);
|
|
727
|
+
this.push(chunk, encoding);
|
|
728
|
+
callback();
|
|
729
|
+
}
|
|
730
|
+
};
|