@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/toolkit.js
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const Boom = require('boom');
|
|
4
|
+
const Bounce = require('bounce');
|
|
5
|
+
const Hoek = require('hoek');
|
|
6
|
+
|
|
7
|
+
const Response = require('./response');
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
const internals = {
|
|
11
|
+
reserved: ['abandon', 'authenticated', 'close', 'context', 'continue', 'entity', 'redirect', 'realm', 'request', 'response', 'state', 'unauthenticated', 'unstate']
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
exports = module.exports = internals.Manager = class {
|
|
16
|
+
|
|
17
|
+
constructor() {
|
|
18
|
+
|
|
19
|
+
this.abandon = Symbol('abandon');
|
|
20
|
+
this.close = Symbol('close');
|
|
21
|
+
this.continue = Symbol('continue');
|
|
22
|
+
this.reserved = internals.reserved;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async execute(method, request, options) {
|
|
26
|
+
|
|
27
|
+
const h = new internals.Toolkit(request, this, options);
|
|
28
|
+
const bind = options.bind || null;
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
var response = await (options.args ? method.call(bind, request, h, ...options.args) : method.call(bind, request, h));
|
|
32
|
+
}
|
|
33
|
+
catch (err) {
|
|
34
|
+
if (Bounce.isSystem(err)) {
|
|
35
|
+
response = Boom.badImplementation(err);
|
|
36
|
+
}
|
|
37
|
+
else if (!Bounce.isError(err)) {
|
|
38
|
+
response = Boom.badImplementation('Cannot throw non-error object', err);
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
response = Boom.boomify(err);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Process response
|
|
46
|
+
|
|
47
|
+
if (response === undefined) {
|
|
48
|
+
response = Boom.badImplementation(`${method.name} method did not return a value, a promise, or throw an error`);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (options.continue &&
|
|
52
|
+
response === this.continue) {
|
|
53
|
+
|
|
54
|
+
if (options.continue === 'undefined') {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// 'null'
|
|
59
|
+
|
|
60
|
+
response = null;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (options.auth &&
|
|
64
|
+
response instanceof internals.Auth) {
|
|
65
|
+
|
|
66
|
+
return response;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (typeof response !== 'symbol') {
|
|
70
|
+
response = Response.wrap(response, request);
|
|
71
|
+
if (!response.isBoom) {
|
|
72
|
+
response = await response._prepare();
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return response;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
failAction(request, failAction, err, options) {
|
|
80
|
+
|
|
81
|
+
const retain = options.retain ? err : undefined;
|
|
82
|
+
if (failAction === 'ignore') {
|
|
83
|
+
return retain;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (failAction === 'log') {
|
|
87
|
+
request._log(options.tags, err);
|
|
88
|
+
return retain;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (failAction === 'error') {
|
|
92
|
+
throw err;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return this.execute(failAction, request, { realm: request.route.realm, args: [options.details || err] });
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
/*
|
|
101
|
+
const handler = function (request, h) {
|
|
102
|
+
|
|
103
|
+
result / h.response(result) -> result // Not allowed before handler
|
|
104
|
+
h.response(result).takeover() -> result (respond)
|
|
105
|
+
h.continue -> null // Defaults to null only in handler and pre, not allowed in auth
|
|
106
|
+
|
|
107
|
+
throw error / h.response(error) -> error (respond) // failAction override in pre
|
|
108
|
+
<undefined> -> badImplementation (respond)
|
|
109
|
+
|
|
110
|
+
// Auth only (scheme.payload and scheme.response use the same interface as pre-handler extension methods)
|
|
111
|
+
|
|
112
|
+
h.unauthenticated(error, data) -> error (respond) + data
|
|
113
|
+
h.authenticated(data ) -> (continue) + data
|
|
114
|
+
};
|
|
115
|
+
*/
|
|
116
|
+
|
|
117
|
+
internals.Toolkit = class {
|
|
118
|
+
|
|
119
|
+
constructor(request, manager, options) {
|
|
120
|
+
|
|
121
|
+
this.abandon = manager.abandon;
|
|
122
|
+
this.close = manager.close;
|
|
123
|
+
this.continue = manager.continue;
|
|
124
|
+
this.context = options.bind;
|
|
125
|
+
this.realm = options.realm;
|
|
126
|
+
this.request = request;
|
|
127
|
+
|
|
128
|
+
if (options.auth) {
|
|
129
|
+
this.authenticated = internals.authenticated;
|
|
130
|
+
this.unauthenticated = internals.unauthenticated;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
for (const method of request._core.decorations.toolkit) {
|
|
134
|
+
this[method] = request._core._decorations.toolkit[method];
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
response(result) {
|
|
139
|
+
|
|
140
|
+
Hoek.assert(!result || typeof result !== 'object' || typeof result.then !== 'function', 'Cannot wrap a promise');
|
|
141
|
+
Hoek.assert(result instanceof Error === false, 'Cannot wrap an error');
|
|
142
|
+
Hoek.assert(typeof result !== 'symbol', 'Cannot wrap a symbol');
|
|
143
|
+
|
|
144
|
+
return Response.wrap(result, this.request);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
redirect(location) {
|
|
148
|
+
|
|
149
|
+
return this.response('').redirect(location);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
entity(options) {
|
|
153
|
+
|
|
154
|
+
Hoek.assert(options, 'Entity method missing required options');
|
|
155
|
+
Hoek.assert(options.etag || options.modified, 'Entity methods missing required options key');
|
|
156
|
+
|
|
157
|
+
this.request._entity = options;
|
|
158
|
+
|
|
159
|
+
const entity = Response.entity(options.etag, options);
|
|
160
|
+
if (Response.unmodified(this.request, entity)) {
|
|
161
|
+
return this.response().code(304).takeover();
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
state(name, value, options) {
|
|
166
|
+
|
|
167
|
+
this.request._setState(name, value, options);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
unstate(name, options) {
|
|
171
|
+
|
|
172
|
+
this.request._clearState(name, options);
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
internals.authenticated = function (data) {
|
|
178
|
+
|
|
179
|
+
Hoek.assert(data && data.credentials, 'Authentication data missing credentials information');
|
|
180
|
+
|
|
181
|
+
return new internals.Auth(null, data);
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
internals.unauthenticated = function (error, data) {
|
|
186
|
+
|
|
187
|
+
Hoek.assert(!data || data.credentials, 'Authentication data missing credentials information');
|
|
188
|
+
|
|
189
|
+
return new internals.Auth(error, data);
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
internals.Auth = class {
|
|
194
|
+
|
|
195
|
+
constructor(error, data) {
|
|
196
|
+
|
|
197
|
+
this.isAuth = true;
|
|
198
|
+
this.error = error;
|
|
199
|
+
this.data = data;
|
|
200
|
+
}
|
|
201
|
+
};
|
package/lib/transmit.js
ADDED
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const Http = require('http');
|
|
4
|
+
|
|
5
|
+
const Ammo = require('ammo');
|
|
6
|
+
const Boom = require('boom');
|
|
7
|
+
const Bounce = require('bounce');
|
|
8
|
+
const Hoek = require('hoek');
|
|
9
|
+
const Shot = require('shot');
|
|
10
|
+
const Teamwork = require('teamwork');
|
|
11
|
+
|
|
12
|
+
const Config = require('./config');
|
|
13
|
+
const Response = require('./response');
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
const internals = {};
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
exports.send = async function (request) {
|
|
20
|
+
|
|
21
|
+
const response = request.response;
|
|
22
|
+
if (response.isBoom) {
|
|
23
|
+
return internals.fail(request, response);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
await internals.marshal(request);
|
|
28
|
+
await internals.transmit(response);
|
|
29
|
+
}
|
|
30
|
+
catch (err) {
|
|
31
|
+
Bounce.rethrow(err, 'system');
|
|
32
|
+
request._setResponse(err);
|
|
33
|
+
return internals.fail(request, err);
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
internals.marshal = async function (request) {
|
|
39
|
+
|
|
40
|
+
for (const func of request._route._marshalCycle) {
|
|
41
|
+
await func(request);
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
internals.fail = async function (request, boom) {
|
|
47
|
+
|
|
48
|
+
const response = internals.error(boom, request);
|
|
49
|
+
request.response = response; // Not using request._setResponse() to avoid double log
|
|
50
|
+
|
|
51
|
+
try {
|
|
52
|
+
await internals.marshal(request);
|
|
53
|
+
}
|
|
54
|
+
catch (err) {
|
|
55
|
+
Bounce.rethrow(err, 'system');
|
|
56
|
+
|
|
57
|
+
// Failed to marshal an error - replace with minimal representation of original error
|
|
58
|
+
|
|
59
|
+
const minimal = {
|
|
60
|
+
statusCode: response.statusCode,
|
|
61
|
+
error: Http.STATUS_CODES[response.statusCode],
|
|
62
|
+
message: boom.message
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
response._payload = new Response.Payload(JSON.stringify(minimal), {});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return internals.transmit(response);
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
internals.error = function (boom, request) {
|
|
73
|
+
|
|
74
|
+
const error = boom.output;
|
|
75
|
+
const response = new Response(error.payload, request);
|
|
76
|
+
response._error = boom;
|
|
77
|
+
response.code(error.statusCode);
|
|
78
|
+
response.headers = Hoek.clone(error.headers); // Prevent source from being modified
|
|
79
|
+
return response;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
internals.transmit = function (response) {
|
|
84
|
+
|
|
85
|
+
const request = response.request;
|
|
86
|
+
const length = internals.length(response);
|
|
87
|
+
|
|
88
|
+
// Pipes
|
|
89
|
+
|
|
90
|
+
const encoding = request._core.compression.encoding(response, length);
|
|
91
|
+
const ranger = (encoding ? null : internals.range(response, length));
|
|
92
|
+
const compressor = internals.encoding(response, encoding);
|
|
93
|
+
|
|
94
|
+
// Connection: close
|
|
95
|
+
|
|
96
|
+
const isInjection = Shot.isInjection(request.raw.req);
|
|
97
|
+
if (!(isInjection || request._core.started) ||
|
|
98
|
+
(request._isPayloadPending && !request.raw.req._readableState.ended)) {
|
|
99
|
+
|
|
100
|
+
response._header('connection', 'close');
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Write headers
|
|
104
|
+
|
|
105
|
+
internals.writeHead(response);
|
|
106
|
+
|
|
107
|
+
// Injection
|
|
108
|
+
|
|
109
|
+
if (isInjection) {
|
|
110
|
+
request.raw.res[Config.symbol] = { request };
|
|
111
|
+
|
|
112
|
+
if (response.variety === 'plain') {
|
|
113
|
+
request.raw.res[Config.symbol].result = response._isPayloadSupported() ? response.source : null;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Finalize response stream
|
|
118
|
+
|
|
119
|
+
const stream = internals.chain([response._payload, response._tap(), compressor, ranger]);
|
|
120
|
+
return internals.pipe(request, stream);
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
internals.length = function (response) {
|
|
125
|
+
|
|
126
|
+
const request = response.request;
|
|
127
|
+
|
|
128
|
+
const header = response.headers['content-length'];
|
|
129
|
+
if (header === undefined) {
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
let length = header;
|
|
134
|
+
if (typeof length === 'string') {
|
|
135
|
+
length = parseInt(header, 10);
|
|
136
|
+
if (!isFinite(length)) {
|
|
137
|
+
delete response.headers['content-length'];
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Empty response
|
|
143
|
+
|
|
144
|
+
if (length === 0 &&
|
|
145
|
+
!response._statusCode &&
|
|
146
|
+
response.statusCode === 200 &&
|
|
147
|
+
request.route.settings.response.emptyStatusCode === 204) {
|
|
148
|
+
|
|
149
|
+
response.code(204);
|
|
150
|
+
delete response.headers['content-length'];
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return length;
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
internals.range = function (response, length) {
|
|
158
|
+
|
|
159
|
+
const request = response.request;
|
|
160
|
+
|
|
161
|
+
if (!length ||
|
|
162
|
+
!request.route.settings.response.ranges ||
|
|
163
|
+
request.method !== 'get' ||
|
|
164
|
+
response.statusCode !== 200) {
|
|
165
|
+
|
|
166
|
+
return null;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
response._header('accept-ranges', 'bytes');
|
|
170
|
+
|
|
171
|
+
if (!request.headers.range) {
|
|
172
|
+
return null;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Check If-Range
|
|
176
|
+
|
|
177
|
+
if (request.headers['if-range'] &&
|
|
178
|
+
request.headers['if-range'] !== response.headers.etag) { // Ignoring last-modified date (weak)
|
|
179
|
+
|
|
180
|
+
return null;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Parse header
|
|
184
|
+
|
|
185
|
+
const ranges = Ammo.header(request.headers.range, length);
|
|
186
|
+
if (!ranges) {
|
|
187
|
+
const error = Boom.rangeNotSatisfiable();
|
|
188
|
+
error.output.headers['content-range'] = 'bytes */' + length;
|
|
189
|
+
throw error;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Prepare transform
|
|
193
|
+
|
|
194
|
+
if (ranges.length !== 1) { // Ignore requests for multiple ranges
|
|
195
|
+
return null;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const range = ranges[0];
|
|
199
|
+
response.code(206);
|
|
200
|
+
response.bytes(range.to - range.from + 1);
|
|
201
|
+
response._header('content-range', 'bytes ' + range.from + '-' + range.to + '/' + length);
|
|
202
|
+
|
|
203
|
+
return new Ammo.Stream(range);
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
internals.encoding = function (response, encoding) {
|
|
208
|
+
|
|
209
|
+
const request = response.request;
|
|
210
|
+
|
|
211
|
+
const header = response.headers['content-encoding'] || encoding;
|
|
212
|
+
if (header &&
|
|
213
|
+
response.headers.etag &&
|
|
214
|
+
response.settings.varyEtag) {
|
|
215
|
+
|
|
216
|
+
response.headers.etag = response.headers.etag.slice(0, -1) + '-' + header + '"';
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
if (!encoding ||
|
|
220
|
+
response.statusCode === 206 ||
|
|
221
|
+
!response._isPayloadSupported()) {
|
|
222
|
+
|
|
223
|
+
return null;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
delete response.headers['content-length'];
|
|
227
|
+
response._header('content-encoding', encoding);
|
|
228
|
+
const compressor = request._core.compression.encoder(request, encoding);
|
|
229
|
+
if (response.variety === 'stream' &&
|
|
230
|
+
typeof response._payload.setCompressor === 'function') {
|
|
231
|
+
|
|
232
|
+
response._payload.setCompressor(compressor);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
return compressor;
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
internals.pipe = function (request, stream) {
|
|
240
|
+
|
|
241
|
+
const team = new Teamwork();
|
|
242
|
+
|
|
243
|
+
// Write payload
|
|
244
|
+
|
|
245
|
+
const env = { stream, request, team };
|
|
246
|
+
|
|
247
|
+
const aborted = internals.end.bind(null, env, 'aborted');
|
|
248
|
+
const close = internals.end.bind(null, env, 'close');
|
|
249
|
+
const end = internals.end.bind(null, env, null);
|
|
250
|
+
|
|
251
|
+
request.raw.req.on('aborted', aborted);
|
|
252
|
+
request.raw.req.on('close', close);
|
|
253
|
+
|
|
254
|
+
request.raw.res.on('close', close);
|
|
255
|
+
request.raw.res.on('error', end);
|
|
256
|
+
request.raw.res.on('finish', end);
|
|
257
|
+
|
|
258
|
+
if (stream.writeToStream) {
|
|
259
|
+
stream.writeToStream(request.raw.res);
|
|
260
|
+
}
|
|
261
|
+
else {
|
|
262
|
+
stream.on('error', end);
|
|
263
|
+
stream.pipe(request.raw.res);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
return team.work;
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
internals.end = function (env, event, err) {
|
|
271
|
+
|
|
272
|
+
const { request, stream, team } = env;
|
|
273
|
+
|
|
274
|
+
if (!team) { // Used instead of cleaning up emitter listeners
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
env.team = null;
|
|
279
|
+
|
|
280
|
+
if (request.raw.res.finished) {
|
|
281
|
+
if (event !== 'aborted') {
|
|
282
|
+
request.info.responded = Date.now();
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
team.attend();
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
if (err) {
|
|
290
|
+
request.raw.res.destroy();
|
|
291
|
+
Response.drain(stream);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
err = err || new Boom(`Request ${event}`, { statusCode: request.route.settings.response.disconnectStatusCode });
|
|
295
|
+
const error = internals.error(Boom.boomify(err), request);
|
|
296
|
+
request._setResponse(error);
|
|
297
|
+
|
|
298
|
+
if (request.raw.res[Config.symbol]) {
|
|
299
|
+
request.raw.res.statusCode = error.statusCode;
|
|
300
|
+
request.raw.res[Config.symbol].result = error.source; // Force injected response to error
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
if (event) {
|
|
304
|
+
request._log(['response', 'error', event]);
|
|
305
|
+
}
|
|
306
|
+
else {
|
|
307
|
+
request._log(['response', 'error'], err);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
request.raw.res.end(); // Triggers injection promise resolve
|
|
311
|
+
team.attend();
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
internals.writeHead = function (response) {
|
|
316
|
+
|
|
317
|
+
const res = response.request.raw.res;
|
|
318
|
+
const headers = Object.keys(response.headers);
|
|
319
|
+
let i = 0;
|
|
320
|
+
|
|
321
|
+
try {
|
|
322
|
+
for (; i < headers.length; ++i) {
|
|
323
|
+
const header = headers[i];
|
|
324
|
+
const value = response.headers[header];
|
|
325
|
+
if (value !== undefined) {
|
|
326
|
+
res.setHeader(header, value);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
catch (err) {
|
|
331
|
+
for (--i; i >= 0; --i) {
|
|
332
|
+
res.removeHeader(headers[i]); // Undo headers
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
throw Boom.boomify(err);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
if (response.settings.message) {
|
|
339
|
+
res.statusMessage = response.settings.message;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
try {
|
|
343
|
+
res.writeHead(response.statusCode);
|
|
344
|
+
}
|
|
345
|
+
catch (err) {
|
|
346
|
+
throw Boom.boomify(err);
|
|
347
|
+
}
|
|
348
|
+
};
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
internals.chain = function (sources) {
|
|
352
|
+
|
|
353
|
+
let from = sources[0];
|
|
354
|
+
for (let i = 1; i < sources.length; ++i) {
|
|
355
|
+
const to = sources[i];
|
|
356
|
+
if (to) {
|
|
357
|
+
from.on('error', internals.errorPipe.bind(from, to));
|
|
358
|
+
from = from.pipe(to);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
return from;
|
|
363
|
+
};
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
internals.errorPipe = function (to, err) {
|
|
367
|
+
|
|
368
|
+
to.emit('error', err);
|
|
369
|
+
};
|