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