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