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/server.js
ADDED
|
@@ -0,0 +1,603 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const Hoek = require('@hapi/hoek');
|
|
4
|
+
const Shot = require('@hapi/shot');
|
|
5
|
+
const Somever = require('@hapi/somever');
|
|
6
|
+
const Teamwork = require('@hapi/teamwork');
|
|
7
|
+
|
|
8
|
+
const Config = require('./config');
|
|
9
|
+
const Core = require('./core');
|
|
10
|
+
const Cors = require('./cors');
|
|
11
|
+
const Ext = require('./ext');
|
|
12
|
+
const Package = require('../package.json');
|
|
13
|
+
const Route = require('./route');
|
|
14
|
+
const Toolkit = require('./toolkit');
|
|
15
|
+
const Validation = require('./validation');
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
const internals = {};
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
exports = module.exports = function (options) {
|
|
22
|
+
|
|
23
|
+
const core = new Core(options);
|
|
24
|
+
return new internals.Server(core);
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
internals.Server = class {
|
|
29
|
+
|
|
30
|
+
constructor(core, name, parent) {
|
|
31
|
+
|
|
32
|
+
this._core = core;
|
|
33
|
+
|
|
34
|
+
// Public interface
|
|
35
|
+
|
|
36
|
+
this.app = core.app;
|
|
37
|
+
this.auth = core.auth.public(this);
|
|
38
|
+
this.decorations = core.decorations.public;
|
|
39
|
+
this.cache = internals.cache(this);
|
|
40
|
+
this.events = core.events;
|
|
41
|
+
this.info = core.info;
|
|
42
|
+
this.listener = core.listener;
|
|
43
|
+
this.load = core.heavy.load;
|
|
44
|
+
this.methods = core.methods.methods;
|
|
45
|
+
this.mime = core.mime;
|
|
46
|
+
this.plugins = core.plugins;
|
|
47
|
+
this.registrations = core.registrations;
|
|
48
|
+
this.settings = core.settings;
|
|
49
|
+
this.states = core.states;
|
|
50
|
+
this.type = core.type;
|
|
51
|
+
this.version = Package.version;
|
|
52
|
+
|
|
53
|
+
this.realm = {
|
|
54
|
+
_extensions: {
|
|
55
|
+
onPreAuth: new Ext('onPreAuth', core),
|
|
56
|
+
onCredentials: new Ext('onCredentials', core),
|
|
57
|
+
onPostAuth: new Ext('onPostAuth', core),
|
|
58
|
+
onPreHandler: new Ext('onPreHandler', core),
|
|
59
|
+
onPostHandler: new Ext('onPostHandler', core),
|
|
60
|
+
onPreResponse: new Ext('onPreResponse', core),
|
|
61
|
+
onPostResponse: new Ext('onPostResponse', core)
|
|
62
|
+
},
|
|
63
|
+
modifiers: {
|
|
64
|
+
route: {}
|
|
65
|
+
},
|
|
66
|
+
parent: parent ? parent.realm : null,
|
|
67
|
+
plugin: name,
|
|
68
|
+
pluginOptions: {},
|
|
69
|
+
plugins: {},
|
|
70
|
+
_rules: null,
|
|
71
|
+
settings: {
|
|
72
|
+
bind: undefined,
|
|
73
|
+
files: {
|
|
74
|
+
relativeTo: undefined
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
validator: null
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
// Decorations
|
|
81
|
+
|
|
82
|
+
for (const [property, method] of core.decorations.server.entries()) {
|
|
83
|
+
this[property] = method;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
core.registerServer(this);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
_clone(name) {
|
|
90
|
+
|
|
91
|
+
return new internals.Server(this._core, name, this);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
bind(context) {
|
|
95
|
+
|
|
96
|
+
Hoek.assert(typeof context === 'object', 'bind must be an object');
|
|
97
|
+
this.realm.settings.bind = context;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
control(server) {
|
|
101
|
+
|
|
102
|
+
Hoek.assert(server instanceof internals.Server, 'Can only control Server objects');
|
|
103
|
+
|
|
104
|
+
this._core.controlled = this._core.controlled || [];
|
|
105
|
+
this._core.controlled.push(server);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
decoder(encoding, decoder) {
|
|
109
|
+
|
|
110
|
+
return this._core.compression.addDecoder(encoding, decoder);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
decorate(type, property, method, options = {}) {
|
|
114
|
+
|
|
115
|
+
Hoek.assert(this._core.decorations.public[type], 'Unknown decoration type:', type);
|
|
116
|
+
Hoek.assert(property, 'Missing decoration property name');
|
|
117
|
+
Hoek.assert(typeof property === 'string' || typeof property === 'symbol', 'Decoration property must be a string or a symbol');
|
|
118
|
+
|
|
119
|
+
const propertyName = property.toString();
|
|
120
|
+
Hoek.assert(propertyName[0] !== '_', 'Property name cannot begin with an underscore:', propertyName);
|
|
121
|
+
|
|
122
|
+
const existing = this._core.decorations[type].get(property);
|
|
123
|
+
if (options.extend) {
|
|
124
|
+
Hoek.assert(type !== 'handler', 'Cannot extent handler decoration:', propertyName);
|
|
125
|
+
Hoek.assert(existing, `Cannot extend missing ${type} decoration: ${propertyName}`);
|
|
126
|
+
Hoek.assert(typeof method === 'function', `Extended ${type} decoration method must be a function: ${propertyName}`);
|
|
127
|
+
|
|
128
|
+
method = method(existing);
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
Hoek.assert(existing === undefined, `${type[0].toUpperCase() + type.slice(1)} decoration already defined: ${propertyName}`);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (type === 'handler') {
|
|
135
|
+
|
|
136
|
+
// Handler
|
|
137
|
+
|
|
138
|
+
Hoek.assert(typeof method === 'function', 'Handler must be a function:', propertyName);
|
|
139
|
+
Hoek.assert(!method.defaults || typeof method.defaults === 'object' || typeof method.defaults === 'function', 'Handler defaults property must be an object or function');
|
|
140
|
+
Hoek.assert(!options.extend, 'Cannot extend handler decoration:', propertyName);
|
|
141
|
+
}
|
|
142
|
+
else if (type === 'request') {
|
|
143
|
+
|
|
144
|
+
// Request
|
|
145
|
+
|
|
146
|
+
Hoek.assert(!this._core.Request.reserved.includes(property), 'Cannot override built-in request interface decoration:', propertyName);
|
|
147
|
+
|
|
148
|
+
if (options.apply) {
|
|
149
|
+
this._core.decorations.requestApply = this._core.decorations.requestApply || new Map();
|
|
150
|
+
this._core.decorations.requestApply.set(property, method);
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
this._core.Request.prototype[property] = method;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
else if (type === 'response') {
|
|
157
|
+
|
|
158
|
+
// Response
|
|
159
|
+
|
|
160
|
+
Hoek.assert(!this._core.Response.reserved.includes(property), 'Cannot override built-in response interface decoration:', propertyName);
|
|
161
|
+
this._core.Response.prototype[property] = method;
|
|
162
|
+
}
|
|
163
|
+
else if (type === 'toolkit') {
|
|
164
|
+
|
|
165
|
+
// Toolkit
|
|
166
|
+
|
|
167
|
+
Hoek.assert(!Toolkit.reserved.includes(property), 'Cannot override built-in toolkit decoration:', propertyName);
|
|
168
|
+
this._core.toolkit.decorate(property, method);
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
|
|
172
|
+
// Server
|
|
173
|
+
|
|
174
|
+
if (typeof property === 'string') {
|
|
175
|
+
Hoek.assert(!Object.getOwnPropertyNames(internals.Server.prototype).includes(property), 'Cannot override the built-in server interface method:', propertyName);
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
Hoek.assert(!Object.getOwnPropertySymbols(internals.Server.prototype).includes(property), 'Cannot override the built-in server interface method:', propertyName);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
this._core.instances.forEach((server) => {
|
|
182
|
+
|
|
183
|
+
server[property] = method;
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
this._core.decorations[type].set(property, method);
|
|
188
|
+
this._core.decorations.public[type].push(property);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
dependency(dependencies, after) {
|
|
192
|
+
|
|
193
|
+
Hoek.assert(this.realm.plugin, 'Cannot call dependency() outside of a plugin');
|
|
194
|
+
Hoek.assert(!after || typeof after === 'function', 'Invalid after method');
|
|
195
|
+
|
|
196
|
+
// Normalize to { plugin: version }
|
|
197
|
+
|
|
198
|
+
if (typeof dependencies === 'string') {
|
|
199
|
+
dependencies = { [dependencies]: '*' };
|
|
200
|
+
}
|
|
201
|
+
else if (Array.isArray(dependencies)) {
|
|
202
|
+
const map = {};
|
|
203
|
+
for (const dependency of dependencies) {
|
|
204
|
+
map[dependency] = '*';
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
dependencies = map;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
this._core.dependencies.push({ plugin: this.realm.plugin, deps: dependencies });
|
|
211
|
+
|
|
212
|
+
if (after) {
|
|
213
|
+
this.ext('onPreStart', after, { after: Object.keys(dependencies) });
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
encoder(encoding, encoder) {
|
|
218
|
+
|
|
219
|
+
return this._core.compression.addEncoder(encoding, encoder);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
event(event) {
|
|
223
|
+
|
|
224
|
+
this._core.events.registerEvent(event);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
expose(key, value, options = {}) {
|
|
228
|
+
|
|
229
|
+
Hoek.assert(this.realm.plugin, 'Cannot call expose() outside of a plugin');
|
|
230
|
+
|
|
231
|
+
let plugin = this.realm.plugin;
|
|
232
|
+
if (plugin[0] === '@' &&
|
|
233
|
+
options.scope !== true) {
|
|
234
|
+
|
|
235
|
+
plugin = plugin.replace(/^@([^/]+)\//, ($0, $1) => {
|
|
236
|
+
|
|
237
|
+
return !options.scope ? '' : `${$1}__`;
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
this._core.plugins[plugin] = this._core.plugins[plugin] || {};
|
|
242
|
+
|
|
243
|
+
if (typeof key === 'string') {
|
|
244
|
+
this._core.plugins[plugin][key] = value;
|
|
245
|
+
}
|
|
246
|
+
else {
|
|
247
|
+
Hoek.merge(this._core.plugins[plugin], key);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
ext(events, method, options) { // (event, method, options) -OR- (events)
|
|
252
|
+
|
|
253
|
+
let promise;
|
|
254
|
+
if (typeof events === 'string') {
|
|
255
|
+
if (!method) {
|
|
256
|
+
const team = new Teamwork.Team();
|
|
257
|
+
method = (request, h) => {
|
|
258
|
+
|
|
259
|
+
team.attend(request);
|
|
260
|
+
return h.continue;
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
promise = team.work;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
events = { type: events, method, options };
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
events = Config.apply('exts', events);
|
|
270
|
+
for (const event of events) {
|
|
271
|
+
this._ext(event);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
return promise;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
_ext(event) {
|
|
278
|
+
|
|
279
|
+
event = Object.assign({}, event); // Shallow cloned
|
|
280
|
+
event.realm = this.realm;
|
|
281
|
+
const type = event.type;
|
|
282
|
+
|
|
283
|
+
if (!this._core.extensions.server[type]) {
|
|
284
|
+
|
|
285
|
+
// Realm route extensions
|
|
286
|
+
|
|
287
|
+
if (event.options.sandbox === 'plugin') {
|
|
288
|
+
Hoek.assert(this.realm._extensions[type], 'Unknown event type', type);
|
|
289
|
+
return this.realm._extensions[type].add(event);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// Connection route extensions
|
|
293
|
+
|
|
294
|
+
Hoek.assert(this._core.extensions.route[type], 'Unknown event type', type);
|
|
295
|
+
return this._core.extensions.route[type].add(event);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// Server extensions
|
|
299
|
+
|
|
300
|
+
Hoek.assert(!event.options.sandbox, 'Cannot specify sandbox option for server extension');
|
|
301
|
+
Hoek.assert(type !== 'onPreStart' || this._core.phase === 'stopped', 'Cannot add onPreStart (after) extension after the server was initialized');
|
|
302
|
+
|
|
303
|
+
event.server = this;
|
|
304
|
+
this._core.extensions.server[type].add(event);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
async inject(options) {
|
|
308
|
+
|
|
309
|
+
let settings = options;
|
|
310
|
+
if (typeof settings === 'string') {
|
|
311
|
+
settings = { url: settings };
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
if (!settings.authority ||
|
|
315
|
+
settings.auth ||
|
|
316
|
+
settings.app ||
|
|
317
|
+
settings.plugins ||
|
|
318
|
+
settings.allowInternals !== undefined) { // Can be false
|
|
319
|
+
|
|
320
|
+
settings = Object.assign({}, settings); // options can be reused (shallow cloned)
|
|
321
|
+
delete settings.auth;
|
|
322
|
+
delete settings.app;
|
|
323
|
+
delete settings.plugins;
|
|
324
|
+
delete settings.allowInternals;
|
|
325
|
+
|
|
326
|
+
settings.authority = settings.authority || this._core.info.host + ':' + this._core.info.port;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
Hoek.assert(!options.credentials, 'options.credentials no longer supported (use options.auth)');
|
|
330
|
+
|
|
331
|
+
if (options.auth) {
|
|
332
|
+
Hoek.assert(typeof options.auth === 'object', 'options.auth must be an object');
|
|
333
|
+
Hoek.assert(options.auth.credentials, 'options.auth.credentials is missing');
|
|
334
|
+
Hoek.assert(options.auth.strategy, 'options.auth.strategy is missing');
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
const needle = this._core._dispatch({
|
|
338
|
+
auth: options.auth,
|
|
339
|
+
allowInternals: options.allowInternals,
|
|
340
|
+
app: options.app,
|
|
341
|
+
plugins: options.plugins,
|
|
342
|
+
isInjected: true
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
const res = await Shot.inject(needle, settings);
|
|
346
|
+
const custom = res.raw.res[Config.symbol];
|
|
347
|
+
if (custom) {
|
|
348
|
+
delete res.raw.res[Config.symbol];
|
|
349
|
+
|
|
350
|
+
res.request = custom.request;
|
|
351
|
+
|
|
352
|
+
if (custom.error) {
|
|
353
|
+
throw custom.error;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
if (custom.result !== undefined) {
|
|
357
|
+
res.result = custom.result;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
if (res.result === undefined) {
|
|
362
|
+
res.result = res.payload;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
return res;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
log(tags, data) {
|
|
369
|
+
|
|
370
|
+
return this._core.log(tags, data);
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
lookup(id) {
|
|
374
|
+
|
|
375
|
+
Hoek.assert(id && typeof id === 'string', 'Invalid route id:', id);
|
|
376
|
+
|
|
377
|
+
const record = this._core.router.ids.get(id);
|
|
378
|
+
if (!record) {
|
|
379
|
+
return null;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
return record.route.public;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
match(method, path, host) {
|
|
386
|
+
|
|
387
|
+
Hoek.assert(method && typeof method === 'string', 'Invalid method:', method);
|
|
388
|
+
Hoek.assert(path && typeof path === 'string' && path[0] === '/', 'Invalid path:', path);
|
|
389
|
+
Hoek.assert(!host || typeof host === 'string', 'Invalid host:', host);
|
|
390
|
+
|
|
391
|
+
const match = this._core.router.route(method.toLowerCase(), path, host);
|
|
392
|
+
Hoek.assert(match !== this._core.router.specials.badRequest, 'Invalid path:', path);
|
|
393
|
+
if (match === this._core.router.specials.notFound) {
|
|
394
|
+
return null;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
return match.route.public;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
method(name, method, options = {}) {
|
|
401
|
+
|
|
402
|
+
return this._core.methods.add(name, method, options, this.realm);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
path(relativeTo) {
|
|
406
|
+
|
|
407
|
+
Hoek.assert(relativeTo && typeof relativeTo === 'string', 'relativeTo must be a non-empty string');
|
|
408
|
+
this.realm.settings.files.relativeTo = relativeTo;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
async register(plugins, options = {}) {
|
|
412
|
+
|
|
413
|
+
if (this.realm.modifiers.route.prefix ||
|
|
414
|
+
this.realm.modifiers.route.vhost) {
|
|
415
|
+
|
|
416
|
+
options = Hoek.clone(options);
|
|
417
|
+
options.routes = options.routes || {};
|
|
418
|
+
|
|
419
|
+
options.routes.prefix = (this.realm.modifiers.route.prefix || '') + (options.routes.prefix || '') || undefined;
|
|
420
|
+
options.routes.vhost = this.realm.modifiers.route.vhost || options.routes.vhost;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
options = Config.apply('register', options);
|
|
424
|
+
|
|
425
|
+
++this._core.registring;
|
|
426
|
+
|
|
427
|
+
try {
|
|
428
|
+
const items = [].concat(plugins);
|
|
429
|
+
for (let item of items) {
|
|
430
|
+
|
|
431
|
+
/*
|
|
432
|
+
{ register, ...attributes }
|
|
433
|
+
{ plugin: { register, ...attributes }, options, once, routes }
|
|
434
|
+
{ plugin: { plugin: { register, ...attributes } }, options, once, routes } // Required module
|
|
435
|
+
*/
|
|
436
|
+
|
|
437
|
+
if (!item.plugin) {
|
|
438
|
+
item = {
|
|
439
|
+
plugin: item
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
else if (!item.plugin.register) {
|
|
443
|
+
item = {
|
|
444
|
+
options: item.options,
|
|
445
|
+
once: item.once,
|
|
446
|
+
routes: item.routes,
|
|
447
|
+
plugin: item.plugin.plugin
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
else if (typeof item === 'function') {
|
|
451
|
+
item = Object.assign({}, item); // Shallow cloned
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
item = Config.apply('plugin', item);
|
|
455
|
+
|
|
456
|
+
const name = item.plugin.name || item.plugin.pkg.name;
|
|
457
|
+
const clone = this._clone(name);
|
|
458
|
+
|
|
459
|
+
clone.realm.modifiers.route.prefix = item.routes.prefix || options.routes.prefix;
|
|
460
|
+
clone.realm.modifiers.route.vhost = item.routes.vhost || options.routes.vhost;
|
|
461
|
+
clone.realm.pluginOptions = item.options || {};
|
|
462
|
+
|
|
463
|
+
// Validate requirements
|
|
464
|
+
|
|
465
|
+
const requirements = item.plugin.requirements;
|
|
466
|
+
Hoek.assert(!requirements.node || Somever.match(process.version, requirements.node), 'Plugin', name, 'requires node version', requirements.node, 'but found', process.version);
|
|
467
|
+
Hoek.assert(!requirements.hapi || Somever.match(this.version, requirements.hapi), 'Plugin', name, 'requires hapi version', requirements.hapi, 'but found', this.version);
|
|
468
|
+
|
|
469
|
+
// Protect against multiple registrations
|
|
470
|
+
|
|
471
|
+
if (this._core.registrations[name]) {
|
|
472
|
+
if (item.plugin.once ||
|
|
473
|
+
item.once ||
|
|
474
|
+
options.once) {
|
|
475
|
+
|
|
476
|
+
continue;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
Hoek.assert(item.plugin.multiple, 'Plugin', name, 'already registered');
|
|
480
|
+
}
|
|
481
|
+
else {
|
|
482
|
+
this._core.registrations[name] = {
|
|
483
|
+
version: item.plugin.version || item.plugin.pkg.version,
|
|
484
|
+
name,
|
|
485
|
+
options: item.options
|
|
486
|
+
};
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
if (item.plugin.dependencies) {
|
|
490
|
+
clone.dependency(item.plugin.dependencies);
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
// Register
|
|
494
|
+
|
|
495
|
+
await item.plugin.register(clone, item.options || {});
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
finally {
|
|
499
|
+
--this._core.registring;
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
route(options) {
|
|
504
|
+
|
|
505
|
+
Hoek.assert(typeof options === 'object', 'Invalid route options');
|
|
506
|
+
|
|
507
|
+
options = [].concat(options);
|
|
508
|
+
for (const config of options) {
|
|
509
|
+
if (Array.isArray(config.method)) {
|
|
510
|
+
for (const method of config.method) {
|
|
511
|
+
const settings = Object.assign({}, config); // Shallow cloned
|
|
512
|
+
settings.method = method;
|
|
513
|
+
this._addRoute(settings, this);
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
else {
|
|
517
|
+
this._addRoute(config, this);
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
_addRoute(config, server) {
|
|
523
|
+
|
|
524
|
+
const route = new Route(config, server); // Do no use config beyond this point, use route members
|
|
525
|
+
const vhosts = [].concat(route.settings.vhost || '*');
|
|
526
|
+
|
|
527
|
+
for (const vhost of vhosts) {
|
|
528
|
+
const record = this._core.router.add({ method: route.method, path: route.path, vhost, analysis: route._analysis, id: route.settings.id }, route);
|
|
529
|
+
route.fingerprint = record.fingerprint;
|
|
530
|
+
route.params = record.params;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
this.events.emit('route', route.public);
|
|
534
|
+
Cors.options(route.public, server);
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
rules(processor, options = {}) {
|
|
538
|
+
|
|
539
|
+
Hoek.assert(!this.realm._rules, 'Server realm rules already defined');
|
|
540
|
+
|
|
541
|
+
const settings = Config.apply('rules', options);
|
|
542
|
+
if (settings.validate) {
|
|
543
|
+
const schema = settings.validate.schema;
|
|
544
|
+
settings.validate.schema = Validation.compile(schema, null, this.realm, this._core);
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
this.realm._rules = { processor, settings };
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
state(name, options) {
|
|
551
|
+
|
|
552
|
+
this.states.add(name, options);
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
table(host) {
|
|
556
|
+
|
|
557
|
+
return this._core.router.table(host);
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
validator(validator) {
|
|
561
|
+
|
|
562
|
+
Hoek.assert(!this.realm.validator, 'Validator already set');
|
|
563
|
+
|
|
564
|
+
this.realm.validator = Validation.validator(validator);
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
start() {
|
|
568
|
+
|
|
569
|
+
return this._core._start();
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
initialize() {
|
|
573
|
+
|
|
574
|
+
return this._core._initialize();
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
stop(options) {
|
|
578
|
+
|
|
579
|
+
return this._core._stop(options);
|
|
580
|
+
}
|
|
581
|
+
};
|
|
582
|
+
|
|
583
|
+
|
|
584
|
+
internals.cache = (plugin) => {
|
|
585
|
+
|
|
586
|
+
const policy = function (options, _segment) {
|
|
587
|
+
|
|
588
|
+
return this._core._cachePolicy(options, _segment, plugin.realm);
|
|
589
|
+
};
|
|
590
|
+
|
|
591
|
+
policy.provision = async (opts) => {
|
|
592
|
+
|
|
593
|
+
const clients = plugin._core._createCache(opts);
|
|
594
|
+
|
|
595
|
+
// Start cache
|
|
596
|
+
|
|
597
|
+
if (['initialized', 'starting', 'started'].includes(plugin._core.phase)) {
|
|
598
|
+
await Promise.all(clients.map((client) => client.start()));
|
|
599
|
+
}
|
|
600
|
+
};
|
|
601
|
+
|
|
602
|
+
return policy;
|
|
603
|
+
};
|
package/lib/streams.js
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const Boom = require('@hapi/boom');
|
|
4
|
+
const Teamwork = require('@hapi/teamwork');
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
const internals = {
|
|
8
|
+
team: Symbol('team')
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
exports.isStream = function (stream) {
|
|
13
|
+
|
|
14
|
+
if (!stream ||
|
|
15
|
+
typeof stream !== 'object' ||
|
|
16
|
+
typeof stream.pipe !== 'function') {
|
|
17
|
+
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (typeof stream._read !== 'function') {
|
|
22
|
+
throw Boom.badImplementation('Stream must have a readable interface');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (stream._readableState.objectMode) {
|
|
26
|
+
throw Boom.badImplementation('Cannot reply with stream in object mode');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return true;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
exports.drain = function (stream) {
|
|
34
|
+
|
|
35
|
+
const team = new Teamwork.Team();
|
|
36
|
+
stream[internals.team] = team;
|
|
37
|
+
|
|
38
|
+
stream.on('readable', internals.read);
|
|
39
|
+
stream.on('error', internals.end);
|
|
40
|
+
stream.on('end', internals.end);
|
|
41
|
+
stream.on('close', internals.end);
|
|
42
|
+
|
|
43
|
+
return team.work;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
internals.read = function () {
|
|
48
|
+
|
|
49
|
+
while (this.read()) { }
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
internals.end = function () {
|
|
54
|
+
|
|
55
|
+
this.removeListener('readable', internals.read);
|
|
56
|
+
this.removeListener('error', internals.end);
|
|
57
|
+
this.removeListener('end', internals.end);
|
|
58
|
+
this.removeListener('close', internals.end);
|
|
59
|
+
|
|
60
|
+
this[internals.team].attend();
|
|
61
|
+
};
|