@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/cors.js
ADDED
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const Boom = require('boom');
|
|
4
|
+
const Hoek = require('hoek');
|
|
5
|
+
|
|
6
|
+
let Route = null; // Delayed load due to circular dependency
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
const internals = {};
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
exports.route = function (options) {
|
|
13
|
+
|
|
14
|
+
if (!options) {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const settings = Hoek.clone(options);
|
|
19
|
+
settings._headers = settings.headers.concat(settings.additionalHeaders);
|
|
20
|
+
settings._headersString = settings._headers.join(',');
|
|
21
|
+
for (let i = 0; i < settings._headers.length; ++i) {
|
|
22
|
+
settings._headers[i] = settings._headers[i].toLowerCase();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (settings._headers.indexOf('origin') === -1) {
|
|
26
|
+
settings._headers.push('origin');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
settings._exposedHeaders = settings.exposedHeaders.concat(settings.additionalExposedHeaders).join(',');
|
|
30
|
+
|
|
31
|
+
if (settings.origin === 'ignore') {
|
|
32
|
+
settings._origin = false;
|
|
33
|
+
}
|
|
34
|
+
else if (settings.origin.indexOf('*') !== -1) {
|
|
35
|
+
Hoek.assert(settings.origin.length === 1, 'Cannot specify cors.origin * together with other values');
|
|
36
|
+
settings._origin = true;
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
settings._origin = {
|
|
40
|
+
qualified: [],
|
|
41
|
+
wildcards: []
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
for (const origin of settings.origin) {
|
|
45
|
+
if (origin.indexOf('*') !== -1) {
|
|
46
|
+
settings._origin.wildcards.push(new RegExp('^' + Hoek.escapeRegex(origin).replace(/\\\*/g, '.*').replace(/\\\?/g, '.') + '$'));
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
settings._origin.qualified.push(origin);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return settings;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
exports.options = function (route, server) {
|
|
59
|
+
|
|
60
|
+
if (route.method === 'options' ||
|
|
61
|
+
!route.settings.cors) {
|
|
62
|
+
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
exports.handler(server);
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
exports.handler = function (server) {
|
|
71
|
+
|
|
72
|
+
Route = Route || require('./route');
|
|
73
|
+
|
|
74
|
+
if (server._core.router.specials.options) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const definition = {
|
|
79
|
+
method: '_special',
|
|
80
|
+
path: '/{p*}',
|
|
81
|
+
handler: internals.handler,
|
|
82
|
+
options: {
|
|
83
|
+
cors: false
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const route = new Route(definition, server, { special: true });
|
|
88
|
+
server._core.router.special('options', route);
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
internals.handler = function (request, h) {
|
|
93
|
+
|
|
94
|
+
// Validate CORS preflight request
|
|
95
|
+
|
|
96
|
+
const method = request.headers['access-control-request-method'];
|
|
97
|
+
if (!method) {
|
|
98
|
+
throw Boom.notFound('CORS error: Missing Access-Control-Request-Method header');
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Lookup route
|
|
102
|
+
|
|
103
|
+
const route = request.server.match(method, request.path, request.info.hostname);
|
|
104
|
+
if (!route) {
|
|
105
|
+
throw Boom.notFound();
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const settings = route.settings.cors;
|
|
109
|
+
if (!settings) {
|
|
110
|
+
return { message: 'CORS is disabled for this route' };
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Validate Origin header
|
|
114
|
+
|
|
115
|
+
const origin = request.headers.origin;
|
|
116
|
+
|
|
117
|
+
if (!origin &&
|
|
118
|
+
settings._origin !== false) {
|
|
119
|
+
|
|
120
|
+
throw Boom.notFound('CORS error: Missing Origin header');
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (!exports.matchOrigin(origin, settings)) {
|
|
124
|
+
return { message: 'CORS error: Origin not allowed' };
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Validate allowed headers
|
|
128
|
+
|
|
129
|
+
let headers = request.headers['access-control-request-headers'];
|
|
130
|
+
if (headers) {
|
|
131
|
+
headers = headers.toLowerCase().split(/\s*,\s*/);
|
|
132
|
+
if (Hoek.intersect(headers, settings._headers).length !== headers.length) {
|
|
133
|
+
return { message: 'CORS error: Some headers are not allowed' };
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Reply with the route CORS headers
|
|
138
|
+
|
|
139
|
+
const response = h.response();
|
|
140
|
+
response._header('access-control-allow-origin', settings._origin ? origin : '*');
|
|
141
|
+
response._header('access-control-allow-methods', method);
|
|
142
|
+
response._header('access-control-allow-headers', settings._headersString);
|
|
143
|
+
response._header('access-control-max-age', settings.maxAge);
|
|
144
|
+
|
|
145
|
+
if (settings.credentials) {
|
|
146
|
+
response._header('access-control-allow-credentials', 'true');
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (settings._exposedHeaders) {
|
|
150
|
+
response._header('access-control-expose-headers', settings._exposedHeaders);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return response;
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
exports.headers = function (request) {
|
|
158
|
+
|
|
159
|
+
const settings = request.route.settings.cors;
|
|
160
|
+
const response = request.response;
|
|
161
|
+
|
|
162
|
+
if (settings._origin !== false) {
|
|
163
|
+
response.vary('origin');
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if ((request.info.cors && !request.info.cors.isOriginMatch) || // After route lookup
|
|
167
|
+
!exports.matchOrigin(request.headers.origin, request.route.settings.cors)) { // Response from onRequest
|
|
168
|
+
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
response._header('access-control-allow-origin', settings._origin ? request.headers.origin : '*');
|
|
173
|
+
|
|
174
|
+
if (settings.credentials) {
|
|
175
|
+
response._header('access-control-allow-credentials', 'true');
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (settings._exposedHeaders) {
|
|
179
|
+
response._header('access-control-expose-headers', settings._exposedHeaders, { append: true });
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
exports.matchOrigin = function (origin, settings) {
|
|
185
|
+
|
|
186
|
+
if (settings._origin === true ||
|
|
187
|
+
settings._origin === false) {
|
|
188
|
+
|
|
189
|
+
return true;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
if (!origin) {
|
|
193
|
+
return false;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (settings._origin.qualified.indexOf(origin) !== -1) {
|
|
197
|
+
return true;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
for (const wildcard of settings._origin.wildcards) {
|
|
201
|
+
if (origin.match(wildcard)) {
|
|
202
|
+
return true;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
return false;
|
|
207
|
+
};
|
package/lib/ext.js
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const Hoek = require('hoek');
|
|
4
|
+
const Topo = require('topo');
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
const internals = {};
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
exports = module.exports = internals.Ext = class {
|
|
11
|
+
|
|
12
|
+
constructor(type, core) {
|
|
13
|
+
|
|
14
|
+
this._topo = new Topo();
|
|
15
|
+
this._core = core;
|
|
16
|
+
this._routes = [];
|
|
17
|
+
|
|
18
|
+
this.type = type;
|
|
19
|
+
this.nodes = null;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
add(event) {
|
|
23
|
+
|
|
24
|
+
const methods = [].concat(event.method);
|
|
25
|
+
const options = event.options;
|
|
26
|
+
|
|
27
|
+
for (const method of methods) {
|
|
28
|
+
const settings = {
|
|
29
|
+
before: options.before,
|
|
30
|
+
after: options.after,
|
|
31
|
+
group: event.realm.plugin,
|
|
32
|
+
sort: this._core.extensionsSeq++
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const node = {
|
|
36
|
+
func: method, // Request: function (request, h), Server: function (server)
|
|
37
|
+
bind: options.bind,
|
|
38
|
+
server: event.server, // Server event
|
|
39
|
+
realm: event.realm
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
this._topo.add(node, settings);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
this.nodes = this._topo.nodes;
|
|
46
|
+
|
|
47
|
+
// Notify routes
|
|
48
|
+
|
|
49
|
+
for (const route of this._routes) {
|
|
50
|
+
route.rebuild(event);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
merge(others) {
|
|
55
|
+
|
|
56
|
+
const merge = [];
|
|
57
|
+
for (const other of others) {
|
|
58
|
+
merge.push(other._topo);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
this._topo.merge(merge);
|
|
62
|
+
this.nodes = (this._topo.nodes.length ? this._topo.nodes : null);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
subscribe(route) {
|
|
66
|
+
|
|
67
|
+
this._routes.push(route);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
static combine(route, type) {
|
|
71
|
+
|
|
72
|
+
const ext = new internals.Ext(type, route._core);
|
|
73
|
+
|
|
74
|
+
const events = route.settings.ext[type];
|
|
75
|
+
if (events) {
|
|
76
|
+
for (let event of events) {
|
|
77
|
+
event = Object.assign({}, event); // Shallow cloned
|
|
78
|
+
Hoek.assert(!event.options.sandbox, 'Cannot specify sandbox option for route extension');
|
|
79
|
+
event.realm = route.realm;
|
|
80
|
+
ext.add(event);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const server = route._core.extensions.route[type];
|
|
85
|
+
const realm = route.realm._extensions[type];
|
|
86
|
+
|
|
87
|
+
ext.merge([server, realm]);
|
|
88
|
+
|
|
89
|
+
server.subscribe(route);
|
|
90
|
+
realm.subscribe(route);
|
|
91
|
+
|
|
92
|
+
return ext;
|
|
93
|
+
}
|
|
94
|
+
};
|
package/lib/handler.js
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const Hoek = require('hoek');
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
const internals = {};
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
exports.execute = async function (request) {
|
|
10
|
+
|
|
11
|
+
// Prerequisites
|
|
12
|
+
|
|
13
|
+
if (request._route._prerequisites) {
|
|
14
|
+
for (const set of request._route._prerequisites) { // Serial execution of each set
|
|
15
|
+
const pres = [];
|
|
16
|
+
for (const item of set) {
|
|
17
|
+
pres.push(internals.handler(request, item.method, item));
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const responses = await Promise.all(pres); // Parallel execution within sets
|
|
21
|
+
for (const response of responses) {
|
|
22
|
+
if (response !== undefined) {
|
|
23
|
+
return response;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Handler
|
|
30
|
+
|
|
31
|
+
const result = await internals.handler(request, request.route.settings.handler);
|
|
32
|
+
if (result._takeover ||
|
|
33
|
+
typeof result === 'symbol') {
|
|
34
|
+
|
|
35
|
+
return result;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
request._setResponse(result);
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
internals.handler = async function (request, method, pre) {
|
|
43
|
+
|
|
44
|
+
const bind = request.route.settings.bind;
|
|
45
|
+
const realm = request.route.realm;
|
|
46
|
+
let response = await request._core.toolkit.execute(method, request, { bind, realm, continue: 'null' });
|
|
47
|
+
|
|
48
|
+
// Handler
|
|
49
|
+
|
|
50
|
+
if (!pre) {
|
|
51
|
+
if (response.isBoom) {
|
|
52
|
+
request._log(['handler', 'error'], response);
|
|
53
|
+
throw response;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return response;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Pre
|
|
60
|
+
|
|
61
|
+
if (response.isBoom) {
|
|
62
|
+
response.assign = pre.assign;
|
|
63
|
+
response = await request._core.toolkit.failAction(request, pre.failAction, response, { tags: ['pre', 'error'], retain: true });
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (typeof response === 'symbol') {
|
|
67
|
+
return response;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (pre.assign) {
|
|
71
|
+
request.pre[pre.assign] = (response.isBoom ? response : response.source);
|
|
72
|
+
request.preResponses[pre.assign] = response;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (response._takeover) {
|
|
76
|
+
return response;
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
exports.defaults = function (method, handler, core) {
|
|
82
|
+
|
|
83
|
+
let defaults = null;
|
|
84
|
+
|
|
85
|
+
if (typeof handler === 'object') {
|
|
86
|
+
const type = Object.keys(handler)[0];
|
|
87
|
+
const serverHandler = core._decorations.handler[type];
|
|
88
|
+
|
|
89
|
+
Hoek.assert(serverHandler, 'Unknown handler:', type);
|
|
90
|
+
|
|
91
|
+
if (serverHandler.defaults) {
|
|
92
|
+
defaults = (typeof serverHandler.defaults === 'function' ? serverHandler.defaults(method) : serverHandler.defaults);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return defaults || {};
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
exports.configure = function (handler, route) {
|
|
101
|
+
|
|
102
|
+
if (typeof handler === 'object') {
|
|
103
|
+
const type = Object.keys(handler)[0];
|
|
104
|
+
const serverHandler = route._core._decorations.handler[type];
|
|
105
|
+
|
|
106
|
+
Hoek.assert(serverHandler, 'Unknown handler:', type);
|
|
107
|
+
|
|
108
|
+
return serverHandler(route.public, handler[type]);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return handler;
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
exports.prerequisitesConfig = function (config) {
|
|
116
|
+
|
|
117
|
+
if (!config) {
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/*
|
|
122
|
+
[
|
|
123
|
+
[
|
|
124
|
+
function (request, h) { },
|
|
125
|
+
{
|
|
126
|
+
method: function (request, h) { }
|
|
127
|
+
assign: key1
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
method: function (request, h) { },
|
|
131
|
+
assign: key2
|
|
132
|
+
}
|
|
133
|
+
],
|
|
134
|
+
{
|
|
135
|
+
method: function (request, h) { },
|
|
136
|
+
assign: key3
|
|
137
|
+
}
|
|
138
|
+
]
|
|
139
|
+
*/
|
|
140
|
+
|
|
141
|
+
const prerequisites = [];
|
|
142
|
+
|
|
143
|
+
for (let pres of config) {
|
|
144
|
+
pres = [].concat(pres);
|
|
145
|
+
|
|
146
|
+
const set = [];
|
|
147
|
+
for (let pre of pres) {
|
|
148
|
+
if (typeof pre !== 'object') {
|
|
149
|
+
pre = { method: pre };
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const item = {
|
|
153
|
+
method: pre.method,
|
|
154
|
+
assign: pre.assign,
|
|
155
|
+
failAction: pre.failAction || 'error'
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
set.push(item);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
prerequisites.push(set);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return prerequisites.length ? prerequisites : null;
|
|
165
|
+
};
|
package/lib/headers.js
ADDED
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
const Stream = require('stream');
|
|
5
|
+
|
|
6
|
+
const Boom = require('boom');
|
|
7
|
+
|
|
8
|
+
const Response = require('./response');
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
const internals = {};
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
exports.cache = function (request) {
|
|
15
|
+
|
|
16
|
+
const response = request.response;
|
|
17
|
+
if (response.headers['cache-control']) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const settings = request.route.settings.cache;
|
|
22
|
+
const policy = settings && request._route._cache && (settings._statuses.has(response.statusCode) || (response.statusCode === 304 && settings._statuses.has(200)));
|
|
23
|
+
|
|
24
|
+
if (policy ||
|
|
25
|
+
response.settings.ttl) {
|
|
26
|
+
|
|
27
|
+
const ttl = (response.settings.ttl !== null ? response.settings.ttl : request._route._cache.ttl());
|
|
28
|
+
const privacy = (request.auth.isAuthenticated || response.headers['set-cookie'] ? 'private' : settings.privacy || 'default');
|
|
29
|
+
response._header('cache-control', 'max-age=' + Math.floor(ttl / 1000) + ', must-revalidate' + (privacy !== 'default' ? ', ' + privacy : ''));
|
|
30
|
+
}
|
|
31
|
+
else if (settings) {
|
|
32
|
+
response._header('cache-control', settings.otherwise);
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
exports.content = async function (request) {
|
|
38
|
+
|
|
39
|
+
const response = request.response;
|
|
40
|
+
if (response._isPayloadSupported() ||
|
|
41
|
+
request.method === 'head') {
|
|
42
|
+
|
|
43
|
+
await response._marshal();
|
|
44
|
+
|
|
45
|
+
if (request.jsonp &&
|
|
46
|
+
response._payload.jsonp) {
|
|
47
|
+
|
|
48
|
+
response._header('content-type', 'text/javascript' + (response.settings.charset ? '; charset=' + response.settings.charset : ''));
|
|
49
|
+
response._header('x-content-type-options', 'nosniff');
|
|
50
|
+
response._payload.jsonp(request.jsonp);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (response._payload.size &&
|
|
54
|
+
typeof response._payload.size === 'function') {
|
|
55
|
+
|
|
56
|
+
response._header('content-length', response._payload.size(), { override: false });
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (!response._isPayloadSupported()) {
|
|
60
|
+
response._close(request); // Close unused file streams
|
|
61
|
+
response._payload = new internals.Empty(); // Set empty stream
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
exports.type(request, true);
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
|
|
68
|
+
// Set empty stream
|
|
69
|
+
|
|
70
|
+
response._close(request); // Close unused file streams
|
|
71
|
+
response._payload = new internals.Empty();
|
|
72
|
+
delete response.headers['content-length'];
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
exports.state = async function (request) {
|
|
78
|
+
|
|
79
|
+
const response = request.response;
|
|
80
|
+
const states = [];
|
|
81
|
+
|
|
82
|
+
for (const stateName in request._states) {
|
|
83
|
+
states.push(request._states[stateName]);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
try {
|
|
87
|
+
for (const name in request._core.states.cookies) {
|
|
88
|
+
const autoValue = request._core.states.cookies[name].autoValue;
|
|
89
|
+
if (!autoValue || name in request._states || name in request.state) {
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (typeof autoValue !== 'function') {
|
|
94
|
+
states.push({ name, value: autoValue });
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const value = await autoValue(request);
|
|
99
|
+
states.push({ name, value });
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (!states.length) {
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
let header = await request._core.states.format(states);
|
|
107
|
+
const existing = response.headers['set-cookie'];
|
|
108
|
+
if (existing) {
|
|
109
|
+
header = (Array.isArray(existing) ? existing : [existing]).concat(header);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
response._header('set-cookie', header);
|
|
113
|
+
}
|
|
114
|
+
catch (err) {
|
|
115
|
+
const error = Boom.boomify(err);
|
|
116
|
+
request._log(['state', 'response', 'error'], error);
|
|
117
|
+
request._states = {}; // Clear broken state
|
|
118
|
+
throw error;
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
exports.type = function (request, postMarshal) {
|
|
124
|
+
|
|
125
|
+
const response = request.response;
|
|
126
|
+
let type = response.headers['content-type'];
|
|
127
|
+
if (!type) {
|
|
128
|
+
if (response._contentType) {
|
|
129
|
+
const charset = (response.settings.charset && response._contentType !== 'application/octet-stream' ? '; charset=' + response.settings.charset : '');
|
|
130
|
+
response.type(response._contentType + charset);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
type = type.trim();
|
|
135
|
+
if ((!response._contentType || !postMarshal) &&
|
|
136
|
+
response.settings.charset &&
|
|
137
|
+
type.match(/^(?:text\/)|(?:application\/(?:json)|(?:javascript))/)) {
|
|
138
|
+
|
|
139
|
+
if (!type.match(/; *charset=/)) {
|
|
140
|
+
const semi = (type[type.length - 1] === ';');
|
|
141
|
+
response.type(type + (semi ? ' ' : '; ') + 'charset=' + (response.settings.charset));
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
exports.entity = function (request) {
|
|
149
|
+
|
|
150
|
+
if (!request._entity) {
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const response = request.response;
|
|
155
|
+
|
|
156
|
+
if (request._entity.etag &&
|
|
157
|
+
!response.headers.etag) {
|
|
158
|
+
|
|
159
|
+
response.etag(request._entity.etag, { vary: request._entity.vary });
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (request._entity.modified &&
|
|
163
|
+
!response.headers['last-modified']) {
|
|
164
|
+
|
|
165
|
+
response.header('last-modified', request._entity.modified);
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
exports.unmodified = function (request) {
|
|
171
|
+
|
|
172
|
+
const response = request.response;
|
|
173
|
+
if (response.statusCode === 304) {
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const entity = {
|
|
178
|
+
etag: response.headers.etag,
|
|
179
|
+
vary: response.settings.varyEtag,
|
|
180
|
+
modified: response.headers['last-modified']
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
if (Response.unmodified(request, entity)) {
|
|
184
|
+
response.code(304);
|
|
185
|
+
}
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
internals.Empty = class extends Stream.Readable {
|
|
190
|
+
|
|
191
|
+
_read(/* size */) {
|
|
192
|
+
|
|
193
|
+
this.push(null);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
writeToStream(stream) {
|
|
197
|
+
|
|
198
|
+
stream.end();
|
|
199
|
+
}
|
|
200
|
+
};
|