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.

Files changed (51) hide show
  1. package/NC.rar +0 -0
  2. package/README.md +23 -3
  3. package/lib/auth.js +573 -0
  4. package/lib/compression.js +119 -0
  5. package/lib/config.js +443 -0
  6. package/lib/core.js +699 -0
  7. package/lib/cors.js +207 -0
  8. package/lib/ext.js +96 -0
  9. package/lib/handler.js +165 -0
  10. package/lib/headers.js +187 -0
  11. package/lib/index.js +11 -0
  12. package/lib/methods.js +126 -0
  13. package/lib/request.js +751 -0
  14. package/lib/response.js +797 -0
  15. package/lib/route.js +517 -0
  16. package/lib/security.js +83 -0
  17. package/lib/server.js +603 -0
  18. package/lib/streams.js +61 -0
  19. package/lib/toolkit.js +258 -0
  20. package/lib/transmit.js +381 -0
  21. package/lib/validation.js +250 -0
  22. package/package-lock1.json +13 -0
  23. package/package.json +21 -3
  24. package/package1.json +24 -0
  25. package/package2.json +24 -0
  26. package/test/.hidden +1 -0
  27. package/test/auth.js +2020 -0
  28. package/test/common.js +27 -0
  29. package/test/core.js +2082 -0
  30. package/test/cors.js +647 -0
  31. package/test/file/image.jpg +0 -0
  32. package/test/file/image.png +0 -0
  33. package/test/file/image.png.gz +0 -0
  34. package/test/file/note.txt +1 -0
  35. package/test/handler.js +659 -0
  36. package/test/headers.js +537 -0
  37. package/test/index.js +25 -0
  38. package/test/methods.js +795 -0
  39. package/test/payload.js +849 -0
  40. package/test/request.js +2378 -0
  41. package/test/response.js +1568 -0
  42. package/test/route.js +967 -0
  43. package/test/security.js +97 -0
  44. package/test/server.js +3132 -0
  45. package/test/state.js +215 -0
  46. package/test/templates/invalid.html +3 -0
  47. package/test/templates/plugin/test.html +1 -0
  48. package/test/templates/test.html +3 -0
  49. package/test/toolkit.js +641 -0
  50. package/test/transmit.js +2121 -0
  51. package/test/validation.js +1831 -0
package/test/core.js ADDED
@@ -0,0 +1,2082 @@
1
+ 'use strict';
2
+
3
+ const ChildProcess = require('child_process');
4
+ const Fs = require('fs');
5
+ const Http = require('http');
6
+ const Https = require('https');
7
+ const Net = require('net');
8
+ const Os = require('os');
9
+ const Path = require('path');
10
+ const Stream = require('stream');
11
+ const TLS = require('tls');
12
+
13
+ const Boom = require('@hapi/boom');
14
+ const CatboxMemory = require('@hapi/catbox-memory');
15
+ const Code = require('@hapi/code');
16
+ const Handlebars = require('handlebars');
17
+ const Hapi = require('..');
18
+ const Hoek = require('@hapi/hoek');
19
+ const Inert = require('@hapi/inert');
20
+ const Lab = require('@hapi/lab');
21
+ const Vision = require('@hapi/vision');
22
+ const Wreck = require('@hapi/wreck');
23
+
24
+ const Common = require('./common');
25
+
26
+
27
+ const internals = {};
28
+
29
+
30
+ const { describe, it, before } = exports.lab = Lab.script();
31
+ const expect = Code.expect;
32
+
33
+
34
+ describe('Core', () => {
35
+
36
+ before(Common.setDefaultDnsOrder);
37
+
38
+ it('sets app settings defaults', () => {
39
+
40
+ const server = Hapi.server();
41
+ expect(server.settings.app).to.equal({});
42
+ });
43
+
44
+ it('sets app settings', () => {
45
+
46
+ const server = Hapi.server({ app: { message: 'test defaults' } });
47
+ expect(server.settings.app.message).to.equal('test defaults');
48
+ });
49
+
50
+ it('overrides mime settings', () => {
51
+
52
+ const options = {
53
+ mime: {
54
+ override: {
55
+ 'node/module': {
56
+ source: 'steve',
57
+ compressible: false,
58
+ extensions: ['node', 'module', 'npm'],
59
+ type: 'node/module'
60
+ }
61
+ }
62
+ }
63
+ };
64
+
65
+ const server = Hapi.server(options);
66
+ expect(server.mime.path('file.npm').type).to.equal('node/module');
67
+ expect(server.mime.path('file.npm').source).to.equal('steve');
68
+ });
69
+
70
+ it('allows null port and host', () => {
71
+
72
+ expect(() => {
73
+
74
+ Hapi.server({ host: null, port: null });
75
+ }).to.not.throw();
76
+ });
77
+
78
+ it('does not throw when given a default authentication strategy', () => {
79
+
80
+ expect(() => {
81
+
82
+ Hapi.server({ routes: { auth: 'test' } });
83
+ }).not.to.throw();
84
+ });
85
+
86
+ it('throws when disabling autoListen and providing a port', () => {
87
+
88
+ expect(() => {
89
+
90
+ Hapi.server({ port: 80, autoListen: false });
91
+ }).to.throw('Cannot specify port when autoListen is false');
92
+ });
93
+
94
+ it('throws when disabling autoListen and providing special host', () => {
95
+
96
+ expect(() => {
97
+
98
+ Hapi.server({ port: '/a/b/hapi-server.socket', autoListen: false });
99
+ }).to.throw('Cannot specify port when autoListen is false');
100
+ });
101
+
102
+ it('defaults address to 0.0.0.0 or :: when no host is provided', async () => {
103
+
104
+ const server = Hapi.server();
105
+ await server.start();
106
+
107
+ let expectedBoundAddress = '0.0.0.0';
108
+ if (Net.isIPv6(server.listener.address().address)) {
109
+ expectedBoundAddress = '::';
110
+ }
111
+
112
+ expect(server.info.address).to.equal(expectedBoundAddress);
113
+ await server.stop();
114
+ });
115
+
116
+ it('uses address when present instead of host', async () => {
117
+
118
+ const server = Hapi.server({ host: 'no.such.domain.hapi', address: 'localhost' });
119
+ await server.start();
120
+ expect(server.info.host).to.equal('no.such.domain.hapi');
121
+ expect(server.info.address).to.equal('127.0.0.1');
122
+ await server.stop();
123
+ });
124
+
125
+ it('uses uri when present instead of host and port', async () => {
126
+
127
+ const server = Hapi.server({ host: 'no.such.domain.hapi', address: 'localhost', uri: 'http://uri.example.com:8080' });
128
+ expect(server.info.uri).to.equal('http://uri.example.com:8080');
129
+ await server.start();
130
+ expect(server.info.host).to.equal('no.such.domain.hapi');
131
+ expect(server.info.address).to.equal('127.0.0.1');
132
+ expect(server.info.uri).to.equal('http://uri.example.com:8080');
133
+ await server.stop();
134
+ });
135
+
136
+ it('throws on uri ending with /', () => {
137
+
138
+ expect(() => {
139
+
140
+ Hapi.server({ uri: 'http://uri.example.com:8080/' });
141
+ }).to.throw(/Invalid server options/);
142
+ });
143
+
144
+ it('creates a server listening on a unix domain socket', { skip: process.platform === 'win32' }, async () => {
145
+
146
+ const port = Path.join(__dirname, 'hapi-server.socket');
147
+
148
+ if (Fs.existsSync(port)) {
149
+ Fs.unlinkSync(port);
150
+ }
151
+
152
+ const server = Hapi.server({ port });
153
+
154
+ expect(server.type).to.equal('socket');
155
+
156
+ await server.start();
157
+ const absSocketPath = Path.resolve(port);
158
+ expect(server.info.port).to.equal(absSocketPath);
159
+ await server.stop();
160
+
161
+ if (Fs.existsSync(port)) {
162
+ Fs.unlinkSync(port);
163
+ }
164
+ });
165
+
166
+ it('creates a server listening on a windows named pipe', async () => {
167
+
168
+ const port = '\\\\.\\pipe\\6653e55f-26ec-4268-a4f2-882f4089315c';
169
+ const server = Hapi.server({ port });
170
+
171
+ expect(server.type).to.equal('socket');
172
+
173
+ await server.start();
174
+ expect(server.info.port).to.equal(port);
175
+ await server.stop();
176
+ });
177
+
178
+ it('creates an https server when passed tls options', () => {
179
+
180
+ const tlsOptions = {
181
+ key: '-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEA0UqyXDCqWDKpoNQQK/fdr0OkG4gW6DUafxdufH9GmkX/zoKz\ng/SFLrPipzSGINKWtyMvo7mPjXqqVgE10LDI3VFV8IR6fnART+AF8CW5HMBPGt/s\nfQW4W4puvBHkBxWSW1EvbecgNEIS9hTGvHXkFzm4xJ2e9DHp2xoVAjREC73B7JbF\nhc5ZGGchKw+CFmAiNysU0DmBgQcac0eg2pWoT+YGmTeQj6sRXO67n2xy/hA1DuN6\nA4WBK3wM3O4BnTG0dNbWUEbe7yAbV5gEyq57GhJIeYxRvveVDaX90LoAqM4cUH06\n6rciON0UbDHV2LP/JaH5jzBjUyCnKLLo5snlbwIDAQABAoIBAQDJm7YC3pJJUcxb\nc8x8PlHbUkJUjxzZ5MW4Zb71yLkfRYzsxrTcyQA+g+QzA4KtPY8XrZpnkgm51M8e\n+B16AcIMiBxMC6HgCF503i16LyyJiKrrDYfGy2rTK6AOJQHO3TXWJ3eT3BAGpxuS\n12K2Cq6EvQLCy79iJm7Ks+5G6EggMZPfCVdEhffRm2Epl4T7LpIAqWiUDcDfS05n\nNNfAGxxvALPn+D+kzcSF6hpmCVrFVTf9ouhvnr+0DpIIVPwSK/REAF3Ux5SQvFuL\njPmh3bGwfRtcC5d21QNrHdoBVSN2UBLmbHUpBUcOBI8FyivAWJhRfKnhTvXMFG8L\nwaXB51IZAoGBAP/E3uz6zCyN7l2j09wmbyNOi1AKvr1WSmuBJveITouwblnRSdvc\nsYm4YYE0Vb94AG4n7JIfZLKtTN0xvnCo8tYjrdwMJyGfEfMGCQQ9MpOBXAkVVZvP\ne2k4zHNNsfvSc38UNSt7K0HkVuH5BkRBQeskcsyMeu0qK4wQwdtiCoBDAoGBANF7\nFMppYxSW4ir7Jvkh0P8bP/Z7AtaSmkX7iMmUYT+gMFB5EKqFTQjNQgSJxS/uHVDE\nSC5co8WGHnRk7YH2Pp+Ty1fHfXNWyoOOzNEWvg6CFeMHW2o+/qZd4Z5Fep6qCLaa\nFvzWWC2S5YslEaaP8DQ74aAX4o+/TECrxi0z2lllAoGAdRB6qCSyRsI/k4Rkd6Lv\nw00z3lLMsoRIU6QtXaZ5rN335Awyrfr5F3vYxPZbOOOH7uM/GDJeOJmxUJxv+cia\nPQDflpPJZU4VPRJKFjKcb38JzO6C3Gm+po5kpXGuQQA19LgfDeO2DNaiHZOJFrx3\nm1R3Zr/1k491lwokcHETNVkCgYBPLjrZl6Q/8BhlLrG4kbOx+dbfj/euq5NsyHsX\n1uI7bo1Una5TBjfsD8nYdUr3pwWltcui2pl83Ak+7bdo3G8nWnIOJ/WfVzsNJzj7\n/6CvUzR6sBk5u739nJbfgFutBZBtlSkDQPHrqA7j3Ysibl3ZIJlULjMRKrnj6Ans\npCDwkQKBgQCM7gu3p7veYwCZaxqDMz5/GGFUB1My7sK0hcT7/oH61yw3O8pOekee\nuctI1R3NOudn1cs5TAy/aypgLDYTUGQTiBRILeMiZnOrvQQB9cEf7TFgDoRNCcDs\nV/ZWiegVB/WY7H0BkCekuq5bHwjgtJTpvHGqQ9YD7RhE8RSYOhdQ/Q==\n-----END RSA PRIVATE KEY-----\n',
182
+ cert: '-----BEGIN CERTIFICATE-----\nMIIDBjCCAe4CCQDvLNml6smHlTANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJV\nUzETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0\ncyBQdHkgTHRkMB4XDTE0MDEyNTIxMjIxOFoXDTE1MDEyNTIxMjIxOFowRTELMAkG\nA1UEBhMCVVMxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0\nIFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB\nANFKslwwqlgyqaDUECv33a9DpBuIFug1Gn8Xbnx/RppF/86Cs4P0hS6z4qc0hiDS\nlrcjL6O5j416qlYBNdCwyN1RVfCEen5wEU/gBfAluRzATxrf7H0FuFuKbrwR5AcV\nkltRL23nIDRCEvYUxrx15Bc5uMSdnvQx6dsaFQI0RAu9weyWxYXOWRhnISsPghZg\nIjcrFNA5gYEHGnNHoNqVqE/mBpk3kI+rEVzuu59scv4QNQ7jegOFgSt8DNzuAZ0x\ntHTW1lBG3u8gG1eYBMquexoSSHmMUb73lQ2l/dC6AKjOHFB9Ouq3IjjdFGwx1diz\n/yWh+Y8wY1Mgpyiy6ObJ5W8CAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAoSc6Skb4\ng1e0ZqPKXBV2qbx7hlqIyYpubCl1rDiEdVzqYYZEwmst36fJRRrVaFuAM/1DYAmT\nWMhU+yTfA+vCS4tql9b9zUhPw/IDHpBDWyR01spoZFBF/hE1MGNpCSXXsAbmCiVf\naxrIgR2DNketbDxkQx671KwF1+1JOMo9ffXp+OhuRo5NaGIxhTsZ+f/MA4y084Aj\nDI39av50sTRTWWShlN+J7PtdQVA5SZD97oYbeUeL7gI18kAJww9eUdmT0nEjcwKs\nxsQT1fyKbo7AlZBY4KSlUMuGnn0VnAsB9b+LxtXlDfnjyM8bVQx1uAfRo0DO8p/5\n3J5DTjAU55deBQ==\n-----END CERTIFICATE-----\n'
183
+ };
184
+
185
+ const server = Hapi.server({ tls: tlsOptions });
186
+ expect(server.listener instanceof Https.Server).to.equal(true);
187
+ });
188
+
189
+ it('uses a provided listener', async () => {
190
+
191
+ const listener = Http.createServer();
192
+ const server = Hapi.server({ listener });
193
+ server.route({ method: 'GET', path: '/', handler: () => 'ok' });
194
+
195
+ await server.start();
196
+ const { payload } = await Wreck.get('http://localhost:' + server.info.port + '/');
197
+ expect(payload.toString()).to.equal('ok');
198
+ await server.stop();
199
+ });
200
+
201
+ it('uses a provided listener (TLS)', async () => {
202
+
203
+ const listener = Http.createServer();
204
+ const server = Hapi.server({ listener, tls: true });
205
+ server.route({ method: 'GET', path: '/', handler: () => 'ok' });
206
+
207
+ await server.start();
208
+ expect(server.info.protocol).to.equal('https');
209
+ await server.stop();
210
+ });
211
+
212
+ it('uses a provided listener with manual listen', async () => {
213
+
214
+ const listener = Http.createServer();
215
+ const server = Hapi.server({ listener, autoListen: false });
216
+ server.route({ method: 'GET', path: '/', handler: () => 'ok' });
217
+
218
+ const listen = () => {
219
+
220
+ return new Promise((resolve) => listener.listen(0, 'localhost', resolve));
221
+ };
222
+
223
+ await listen();
224
+ await server.start();
225
+ const { payload } = await Wreck.get('http://localhost:' + server.info.port + '/');
226
+ expect(payload.toString()).to.equal('ok');
227
+ await server.stop();
228
+ });
229
+
230
+ it('sets info.uri with default localhost when no hostname', () => {
231
+
232
+ const orig = Os.hostname;
233
+ Os.hostname = function () {
234
+
235
+ Os.hostname = orig;
236
+ return '';
237
+ };
238
+
239
+ const server = Hapi.server({ port: 80 });
240
+ expect(server.info.uri).to.equal('http://localhost:80');
241
+ });
242
+
243
+ it('sets info.uri without port when 0', () => {
244
+
245
+ const server = Hapi.server({ host: 'example.com' });
246
+ expect(server.info.uri).to.equal('http://example.com');
247
+ });
248
+
249
+ it('closes connection on socket timeout', async () => {
250
+
251
+ const server = Hapi.server({ routes: { timeout: { socket: 50 }, payload: { timeout: 45 } } });
252
+ server.route({
253
+ method: 'GET', path: '/', options: {
254
+ handler: async (request) => {
255
+
256
+ await Hoek.wait(70);
257
+ return 'too late';
258
+ }
259
+ }
260
+ });
261
+
262
+ await server.start();
263
+ try {
264
+ await Wreck.request('GET', 'http://localhost:' + server.info.port + '/');
265
+ }
266
+ catch (err) {
267
+ expect(err.message).to.equal('Client request error: socket hang up');
268
+ }
269
+
270
+ await server.stop();
271
+ });
272
+
273
+ it('disables node socket timeout', async () => {
274
+
275
+ const server = Hapi.server({ routes: { timeout: { socket: false } } });
276
+ server.route({ method: 'GET', path: '/', handler: () => null });
277
+
278
+ await server.start();
279
+
280
+ let timeout;
281
+ const orig = Net.Socket.prototype.setTimeout;
282
+ Net.Socket.prototype.setTimeout = function (...args) {
283
+
284
+ timeout = 'gotcha';
285
+ Net.Socket.prototype.setTimeout = orig;
286
+ return orig.apply(this, args);
287
+ };
288
+
289
+ const res = await Wreck.request('GET', 'http://localhost:' + server.info.port + '/');
290
+ await Wreck.read(res);
291
+ expect(timeout).to.equal('gotcha');
292
+ await server.stop();
293
+ });
294
+
295
+ it('throws on invalid config', () => {
296
+
297
+ expect(() => {
298
+
299
+ Hapi.server({ something: false });
300
+ }).to.throw(/Invalid server options/);
301
+ });
302
+
303
+ it('combines configuration from server and defaults (cors)', () => {
304
+
305
+ const server = Hapi.server({ routes: { cors: { origin: ['example.com'] } } });
306
+ expect(server.settings.routes.cors.origin).to.equal(['example.com']);
307
+ });
308
+
309
+ it('combines configuration from server and defaults (security)', () => {
310
+
311
+ const server = Hapi.server({ routes: { security: { hsts: 2, xss: false } } });
312
+ expect(server.settings.routes.security.hsts).to.equal(2);
313
+ expect(server.settings.routes.security.xss).to.be.false();
314
+ expect(server.settings.routes.security.xframe).to.equal('deny');
315
+ expect(server.settings.routes.security.referrer).to.equal(false);
316
+ });
317
+
318
+ describe('_debug()', () => {
319
+
320
+ it('outputs 500 on ext exception', async () => {
321
+
322
+ const server = Hapi.server();
323
+
324
+ const ext = async (request) => {
325
+
326
+ await Hoek.wait(0);
327
+ const not = null;
328
+ not.here;
329
+ };
330
+
331
+ server.ext('onPreHandler', ext);
332
+ server.route({ method: 'GET', path: '/', handler: () => null });
333
+ const log = server.events.once({ name: 'request', channels: 'error' });
334
+
335
+ const orig = console.error;
336
+ console.error = function (...args) {
337
+
338
+ console.error = orig;
339
+ expect(args[0]).to.equal('Debug:');
340
+ expect(args[1]).to.equal('internal, implementation, error');
341
+ };
342
+
343
+ const res = await server.inject('/');
344
+ expect(res.statusCode).to.equal(500);
345
+
346
+ const [, event] = await log;
347
+ expect(event.error.message).to.include(['Cannot read prop', 'null', 'here']);
348
+ });
349
+ });
350
+
351
+ describe('_createCache()', () => {
352
+
353
+ it('provisions cache using engine instance', async () => {
354
+
355
+ // Config provision
356
+
357
+ const engine = new CatboxMemory();
358
+ const server = Hapi.server({ cache: { engine, name: 'test1' } });
359
+ expect(server._core.caches.get('test1').client.connection).to.shallow.equal(engine);
360
+
361
+ // Active provision
362
+
363
+ await server.cache.provision({ engine, name: 'test2' });
364
+ expect(server._core.caches.get('test2').client.connection).to.shallow.equal(engine);
365
+
366
+ // Active provision but indirect constructor
367
+
368
+ const Provider = function (options) {
369
+
370
+ this.settings = options;
371
+ };
372
+
373
+ const ref = {};
374
+ await server.cache.provision({ provider: { constructor: Provider, options: { ref } }, name: 'test3' });
375
+ expect(server._core.caches.get('test3').client.connection.settings.ref).to.shallow.equal(ref);
376
+ });
377
+ });
378
+
379
+ describe('start()', () => {
380
+
381
+ it('starts and stops', async () => {
382
+
383
+ const server = Hapi.server();
384
+
385
+ let started = 0;
386
+ let stopped = 0;
387
+
388
+ server.events.on('start', () => {
389
+
390
+ ++started;
391
+ });
392
+
393
+ server.events.on('stop', () => {
394
+
395
+ ++stopped;
396
+ });
397
+
398
+ await server.start();
399
+ expect(server._core.started).to.equal(true);
400
+
401
+ await server.stop();
402
+ expect(server._core.started).to.equal(false);
403
+ expect(started).to.equal(1);
404
+ expect(stopped).to.equal(1);
405
+ });
406
+
407
+ it('initializes, starts, and stops', async () => {
408
+
409
+ const server = Hapi.server();
410
+
411
+ let started = 0;
412
+ let stopped = 0;
413
+
414
+ server.events.on('start', () => {
415
+
416
+ ++started;
417
+ });
418
+
419
+ server.events.on('stop', () => {
420
+
421
+ ++stopped;
422
+ });
423
+
424
+ await server.initialize();
425
+ await server.start();
426
+ expect(server._core.started).to.equal(true);
427
+
428
+ await server.stop();
429
+ expect(server._core.started).to.equal(false);
430
+ expect(started).to.equal(1);
431
+ expect(stopped).to.equal(1);
432
+ });
433
+
434
+ it('does not re-initialize the server', async () => {
435
+
436
+ const server = Hapi.server();
437
+ await server.initialize();
438
+ await server.initialize();
439
+ });
440
+
441
+ it('returns connection start error', async () => {
442
+
443
+ const server1 = Hapi.server();
444
+ await server1.start();
445
+ const port = server1.info.port;
446
+
447
+ const server2 = Hapi.server({ port });
448
+ await expect(server2.start()).to.reject(/EADDRINUSE/);
449
+
450
+ await server1.stop();
451
+ });
452
+
453
+ it('returns onPostStart error', async () => {
454
+
455
+ const server = Hapi.server();
456
+
457
+ const postStart = function (srv) {
458
+
459
+ throw new Error('boom');
460
+ };
461
+
462
+ server.ext('onPostStart', postStart);
463
+
464
+ await expect(server.start()).to.reject('boom');
465
+ await server.stop();
466
+ expect(server.info.started).to.equal(0);
467
+ });
468
+
469
+ it('errors on bad cache start', async () => {
470
+
471
+ const cache = {
472
+ engine: {
473
+ start: function () {
474
+
475
+ throw new Error('oops');
476
+ },
477
+ stop: function () { }
478
+ }
479
+ };
480
+
481
+ const server = Hapi.server({ cache });
482
+ await expect(server.start()).to.reject('oops');
483
+ });
484
+
485
+ it('fails to start server when registration incomplete', async () => {
486
+
487
+ const plugin = {
488
+ name: 'plugin',
489
+ register: Hoek.ignore
490
+ };
491
+
492
+ const server = Hapi.server();
493
+ server.register(plugin);
494
+ await expect(server.start()).to.reject('Cannot start server before plugins finished registration');
495
+ });
496
+
497
+ it('fails to initialize server when not stopped', async () => {
498
+
499
+ const plugin = function () { };
500
+ plugin.attributes = { name: 'plugin' };
501
+
502
+ const server = Hapi.server();
503
+ await server.start();
504
+ await expect(server.initialize()).to.reject('Cannot initialize server while it is in started phase');
505
+ await server.stop();
506
+ });
507
+
508
+ it('fails to start server when starting', async () => {
509
+
510
+ const plugin = function () { };
511
+ plugin.attributes = { name: 'plugin' };
512
+
513
+ const server = Hapi.server();
514
+ const starting = server.start();
515
+ await expect(server.start()).to.reject('Cannot start server while it is in initializing phase');
516
+ await starting;
517
+ await server.stop();
518
+ });
519
+ });
520
+
521
+ describe('stop()', () => {
522
+
523
+ it('stops the cache', async () => {
524
+
525
+ const server = Hapi.server();
526
+ const cache = server.cache({ segment: 'test', expiresIn: 1000 });
527
+ await server.initialize();
528
+
529
+ await cache.set('a', 'going in', 0);
530
+ const value = await cache.get('a');
531
+ expect(value).to.equal('going in');
532
+ await server.stop();
533
+ await expect(cache.get('a')).to.reject();
534
+ });
535
+
536
+ it('returns an extension error (onPreStop)', async () => {
537
+
538
+ const server = Hapi.server();
539
+ const preStop = function (srv) {
540
+
541
+ throw new Error('failed cleanup');
542
+ };
543
+
544
+ server.ext('onPreStop', preStop);
545
+
546
+ await server.start();
547
+ await expect(server.stop()).to.reject('failed cleanup');
548
+ });
549
+
550
+ it('returns an extension error (onPostStop)', async () => {
551
+
552
+ const server = Hapi.server();
553
+
554
+ const postStop = function (srv) {
555
+
556
+ throw new Error('failed cleanup');
557
+ };
558
+
559
+ server.ext('onPostStop', postStop);
560
+
561
+ await server.start();
562
+ await expect(server.stop()).to.reject('failed cleanup');
563
+ });
564
+
565
+ it('returns an extension timeout (onPreStop)', async () => {
566
+
567
+ const server = Hapi.server();
568
+ const preStop = function (srv) {
569
+
570
+ return Hoek.block();
571
+ };
572
+
573
+ server.ext('onPreStop', preStop, { timeout: 100 });
574
+
575
+ await server.start();
576
+ await expect(server.stop()).to.reject('onPreStop timed out');
577
+ });
578
+
579
+ it('errors when stopping a stopping server', async () => {
580
+
581
+ const server = Hapi.server();
582
+
583
+ const stopping = server.stop();
584
+ await expect(server.stop()).to.reject('Cannot stop server while in stopping phase');
585
+ await stopping;
586
+ });
587
+
588
+ it('errors on bad cache stop', async () => {
589
+
590
+ const cache = {
591
+ engine: {
592
+ start: function () { },
593
+ stop: function () {
594
+
595
+ throw new Error('oops');
596
+ }
597
+ }
598
+ };
599
+
600
+ const server = Hapi.server({ cache });
601
+ await server.start();
602
+ await expect(server.stop()).to.reject('oops');
603
+ });
604
+ });
605
+
606
+ describe('_init()', () => {
607
+
608
+ it('clears connections on close (HTTP)', async () => {
609
+
610
+ const server = Hapi.server();
611
+
612
+ let count = 0;
613
+ server.route({
614
+ method: 'GET',
615
+ path: '/',
616
+ handler: (request, h) => {
617
+
618
+ ++count;
619
+ return h.abandon;
620
+ }
621
+ });
622
+
623
+ await server.start();
624
+ const promise = Wreck.request('GET', `http://localhost:${server.info.port}/`, { rejectUnauthorized: false });
625
+
626
+ await Hoek.wait(50);
627
+ const count1 = await internals.countConnections(server);
628
+ expect(count1).to.equal(1);
629
+ expect(server._core.sockets.size).to.equal(1);
630
+ expect(count).to.equal(1);
631
+
632
+ promise.req.abort();
633
+ await expect(promise).to.reject();
634
+
635
+ await Hoek.wait(50);
636
+ const count2 = await internals.countConnections(server);
637
+ expect(count2).to.equal(0);
638
+ expect(server._core.sockets.size).to.equal(0);
639
+ expect(count).to.equal(1);
640
+ await server.stop();
641
+ });
642
+
643
+ it('clears connections on close (HTTPS)', async () => {
644
+
645
+ const tlsOptions = {
646
+ key: '-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEA0UqyXDCqWDKpoNQQK/fdr0OkG4gW6DUafxdufH9GmkX/zoKz\ng/SFLrPipzSGINKWtyMvo7mPjXqqVgE10LDI3VFV8IR6fnART+AF8CW5HMBPGt/s\nfQW4W4puvBHkBxWSW1EvbecgNEIS9hTGvHXkFzm4xJ2e9DHp2xoVAjREC73B7JbF\nhc5ZGGchKw+CFmAiNysU0DmBgQcac0eg2pWoT+YGmTeQj6sRXO67n2xy/hA1DuN6\nA4WBK3wM3O4BnTG0dNbWUEbe7yAbV5gEyq57GhJIeYxRvveVDaX90LoAqM4cUH06\n6rciON0UbDHV2LP/JaH5jzBjUyCnKLLo5snlbwIDAQABAoIBAQDJm7YC3pJJUcxb\nc8x8PlHbUkJUjxzZ5MW4Zb71yLkfRYzsxrTcyQA+g+QzA4KtPY8XrZpnkgm51M8e\n+B16AcIMiBxMC6HgCF503i16LyyJiKrrDYfGy2rTK6AOJQHO3TXWJ3eT3BAGpxuS\n12K2Cq6EvQLCy79iJm7Ks+5G6EggMZPfCVdEhffRm2Epl4T7LpIAqWiUDcDfS05n\nNNfAGxxvALPn+D+kzcSF6hpmCVrFVTf9ouhvnr+0DpIIVPwSK/REAF3Ux5SQvFuL\njPmh3bGwfRtcC5d21QNrHdoBVSN2UBLmbHUpBUcOBI8FyivAWJhRfKnhTvXMFG8L\nwaXB51IZAoGBAP/E3uz6zCyN7l2j09wmbyNOi1AKvr1WSmuBJveITouwblnRSdvc\nsYm4YYE0Vb94AG4n7JIfZLKtTN0xvnCo8tYjrdwMJyGfEfMGCQQ9MpOBXAkVVZvP\ne2k4zHNNsfvSc38UNSt7K0HkVuH5BkRBQeskcsyMeu0qK4wQwdtiCoBDAoGBANF7\nFMppYxSW4ir7Jvkh0P8bP/Z7AtaSmkX7iMmUYT+gMFB5EKqFTQjNQgSJxS/uHVDE\nSC5co8WGHnRk7YH2Pp+Ty1fHfXNWyoOOzNEWvg6CFeMHW2o+/qZd4Z5Fep6qCLaa\nFvzWWC2S5YslEaaP8DQ74aAX4o+/TECrxi0z2lllAoGAdRB6qCSyRsI/k4Rkd6Lv\nw00z3lLMsoRIU6QtXaZ5rN335Awyrfr5F3vYxPZbOOOH7uM/GDJeOJmxUJxv+cia\nPQDflpPJZU4VPRJKFjKcb38JzO6C3Gm+po5kpXGuQQA19LgfDeO2DNaiHZOJFrx3\nm1R3Zr/1k491lwokcHETNVkCgYBPLjrZl6Q/8BhlLrG4kbOx+dbfj/euq5NsyHsX\n1uI7bo1Una5TBjfsD8nYdUr3pwWltcui2pl83Ak+7bdo3G8nWnIOJ/WfVzsNJzj7\n/6CvUzR6sBk5u739nJbfgFutBZBtlSkDQPHrqA7j3Ysibl3ZIJlULjMRKrnj6Ans\npCDwkQKBgQCM7gu3p7veYwCZaxqDMz5/GGFUB1My7sK0hcT7/oH61yw3O8pOekee\nuctI1R3NOudn1cs5TAy/aypgLDYTUGQTiBRILeMiZnOrvQQB9cEf7TFgDoRNCcDs\nV/ZWiegVB/WY7H0BkCekuq5bHwjgtJTpvHGqQ9YD7RhE8RSYOhdQ/Q==\n-----END RSA PRIVATE KEY-----\n',
647
+ cert: '-----BEGIN CERTIFICATE-----\nMIIDBjCCAe4CCQDvLNml6smHlTANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJV\nUzETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0\ncyBQdHkgTHRkMB4XDTE0MDEyNTIxMjIxOFoXDTE1MDEyNTIxMjIxOFowRTELMAkG\nA1UEBhMCVVMxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0\nIFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB\nANFKslwwqlgyqaDUECv33a9DpBuIFug1Gn8Xbnx/RppF/86Cs4P0hS6z4qc0hiDS\nlrcjL6O5j416qlYBNdCwyN1RVfCEen5wEU/gBfAluRzATxrf7H0FuFuKbrwR5AcV\nkltRL23nIDRCEvYUxrx15Bc5uMSdnvQx6dsaFQI0RAu9weyWxYXOWRhnISsPghZg\nIjcrFNA5gYEHGnNHoNqVqE/mBpk3kI+rEVzuu59scv4QNQ7jegOFgSt8DNzuAZ0x\ntHTW1lBG3u8gG1eYBMquexoSSHmMUb73lQ2l/dC6AKjOHFB9Ouq3IjjdFGwx1diz\n/yWh+Y8wY1Mgpyiy6ObJ5W8CAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAoSc6Skb4\ng1e0ZqPKXBV2qbx7hlqIyYpubCl1rDiEdVzqYYZEwmst36fJRRrVaFuAM/1DYAmT\nWMhU+yTfA+vCS4tql9b9zUhPw/IDHpBDWyR01spoZFBF/hE1MGNpCSXXsAbmCiVf\naxrIgR2DNketbDxkQx671KwF1+1JOMo9ffXp+OhuRo5NaGIxhTsZ+f/MA4y084Aj\nDI39av50sTRTWWShlN+J7PtdQVA5SZD97oYbeUeL7gI18kAJww9eUdmT0nEjcwKs\nxsQT1fyKbo7AlZBY4KSlUMuGnn0VnAsB9b+LxtXlDfnjyM8bVQx1uAfRo0DO8p/5\n3J5DTjAU55deBQ==\n-----END CERTIFICATE-----\n'
648
+ };
649
+
650
+ const server = Hapi.server({ tls: tlsOptions });
651
+
652
+ let count = 0;
653
+ server.route({
654
+ method: 'GET',
655
+ path: '/',
656
+ handler: (request, h) => {
657
+
658
+ ++count;
659
+ return h.abandon;
660
+ }
661
+ });
662
+
663
+ await server.start();
664
+ const promise = Wreck.request('GET', `https://localhost:${server.info.port}/`, { rejectUnauthorized: false });
665
+
666
+ await Hoek.wait(100);
667
+ const count1 = await internals.countConnections(server);
668
+ expect(count1).to.equal(1);
669
+ expect(server._core.sockets.size).to.equal(1);
670
+ expect(count).to.equal(1);
671
+
672
+ promise.req.abort();
673
+ await expect(promise).to.reject();
674
+
675
+ await Hoek.wait(50);
676
+ const count2 = await internals.countConnections(server);
677
+ expect(count2).to.equal(0);
678
+ expect(server._core.sockets.size).to.equal(0);
679
+ expect(count).to.equal(1);
680
+ await server.stop();
681
+ });
682
+ });
683
+
684
+ describe('_start()', () => {
685
+
686
+ it('starts connection', async () => {
687
+
688
+ const server = Hapi.server();
689
+ await server.start();
690
+ let expectedBoundAddress = '0.0.0.0';
691
+ if (Net.isIPv6(server.listener.address().address)) {
692
+ expectedBoundAddress = '::';
693
+ }
694
+
695
+ expect(server.info.host).to.equal(Os.hostname());
696
+ expect(server.info.address).to.equal(expectedBoundAddress);
697
+ expect(server.info.port).to.be.a.number().and.above(1);
698
+ await server.stop();
699
+ });
700
+
701
+ it('starts connection (tls)', async () => {
702
+
703
+ const tlsOptions = {
704
+ key: '-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEA0UqyXDCqWDKpoNQQK/fdr0OkG4gW6DUafxdufH9GmkX/zoKz\ng/SFLrPipzSGINKWtyMvo7mPjXqqVgE10LDI3VFV8IR6fnART+AF8CW5HMBPGt/s\nfQW4W4puvBHkBxWSW1EvbecgNEIS9hTGvHXkFzm4xJ2e9DHp2xoVAjREC73B7JbF\nhc5ZGGchKw+CFmAiNysU0DmBgQcac0eg2pWoT+YGmTeQj6sRXO67n2xy/hA1DuN6\nA4WBK3wM3O4BnTG0dNbWUEbe7yAbV5gEyq57GhJIeYxRvveVDaX90LoAqM4cUH06\n6rciON0UbDHV2LP/JaH5jzBjUyCnKLLo5snlbwIDAQABAoIBAQDJm7YC3pJJUcxb\nc8x8PlHbUkJUjxzZ5MW4Zb71yLkfRYzsxrTcyQA+g+QzA4KtPY8XrZpnkgm51M8e\n+B16AcIMiBxMC6HgCF503i16LyyJiKrrDYfGy2rTK6AOJQHO3TXWJ3eT3BAGpxuS\n12K2Cq6EvQLCy79iJm7Ks+5G6EggMZPfCVdEhffRm2Epl4T7LpIAqWiUDcDfS05n\nNNfAGxxvALPn+D+kzcSF6hpmCVrFVTf9ouhvnr+0DpIIVPwSK/REAF3Ux5SQvFuL\njPmh3bGwfRtcC5d21QNrHdoBVSN2UBLmbHUpBUcOBI8FyivAWJhRfKnhTvXMFG8L\nwaXB51IZAoGBAP/E3uz6zCyN7l2j09wmbyNOi1AKvr1WSmuBJveITouwblnRSdvc\nsYm4YYE0Vb94AG4n7JIfZLKtTN0xvnCo8tYjrdwMJyGfEfMGCQQ9MpOBXAkVVZvP\ne2k4zHNNsfvSc38UNSt7K0HkVuH5BkRBQeskcsyMeu0qK4wQwdtiCoBDAoGBANF7\nFMppYxSW4ir7Jvkh0P8bP/Z7AtaSmkX7iMmUYT+gMFB5EKqFTQjNQgSJxS/uHVDE\nSC5co8WGHnRk7YH2Pp+Ty1fHfXNWyoOOzNEWvg6CFeMHW2o+/qZd4Z5Fep6qCLaa\nFvzWWC2S5YslEaaP8DQ74aAX4o+/TECrxi0z2lllAoGAdRB6qCSyRsI/k4Rkd6Lv\nw00z3lLMsoRIU6QtXaZ5rN335Awyrfr5F3vYxPZbOOOH7uM/GDJeOJmxUJxv+cia\nPQDflpPJZU4VPRJKFjKcb38JzO6C3Gm+po5kpXGuQQA19LgfDeO2DNaiHZOJFrx3\nm1R3Zr/1k491lwokcHETNVkCgYBPLjrZl6Q/8BhlLrG4kbOx+dbfj/euq5NsyHsX\n1uI7bo1Una5TBjfsD8nYdUr3pwWltcui2pl83Ak+7bdo3G8nWnIOJ/WfVzsNJzj7\n/6CvUzR6sBk5u739nJbfgFutBZBtlSkDQPHrqA7j3Ysibl3ZIJlULjMRKrnj6Ans\npCDwkQKBgQCM7gu3p7veYwCZaxqDMz5/GGFUB1My7sK0hcT7/oH61yw3O8pOekee\nuctI1R3NOudn1cs5TAy/aypgLDYTUGQTiBRILeMiZnOrvQQB9cEf7TFgDoRNCcDs\nV/ZWiegVB/WY7H0BkCekuq5bHwjgtJTpvHGqQ9YD7RhE8RSYOhdQ/Q==\n-----END RSA PRIVATE KEY-----\n',
705
+ cert: '-----BEGIN CERTIFICATE-----\nMIIDBjCCAe4CCQDvLNml6smHlTANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJV\nUzETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0\ncyBQdHkgTHRkMB4XDTE0MDEyNTIxMjIxOFoXDTE1MDEyNTIxMjIxOFowRTELMAkG\nA1UEBhMCVVMxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0\nIFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB\nANFKslwwqlgyqaDUECv33a9DpBuIFug1Gn8Xbnx/RppF/86Cs4P0hS6z4qc0hiDS\nlrcjL6O5j416qlYBNdCwyN1RVfCEen5wEU/gBfAluRzATxrf7H0FuFuKbrwR5AcV\nkltRL23nIDRCEvYUxrx15Bc5uMSdnvQx6dsaFQI0RAu9weyWxYXOWRhnISsPghZg\nIjcrFNA5gYEHGnNHoNqVqE/mBpk3kI+rEVzuu59scv4QNQ7jegOFgSt8DNzuAZ0x\ntHTW1lBG3u8gG1eYBMquexoSSHmMUb73lQ2l/dC6AKjOHFB9Ouq3IjjdFGwx1diz\n/yWh+Y8wY1Mgpyiy6ObJ5W8CAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAoSc6Skb4\ng1e0ZqPKXBV2qbx7hlqIyYpubCl1rDiEdVzqYYZEwmst36fJRRrVaFuAM/1DYAmT\nWMhU+yTfA+vCS4tql9b9zUhPw/IDHpBDWyR01spoZFBF/hE1MGNpCSXXsAbmCiVf\naxrIgR2DNketbDxkQx671KwF1+1JOMo9ffXp+OhuRo5NaGIxhTsZ+f/MA4y084Aj\nDI39av50sTRTWWShlN+J7PtdQVA5SZD97oYbeUeL7gI18kAJww9eUdmT0nEjcwKs\nxsQT1fyKbo7AlZBY4KSlUMuGnn0VnAsB9b+LxtXlDfnjyM8bVQx1uAfRo0DO8p/5\n3J5DTjAU55deBQ==\n-----END CERTIFICATE-----\n'
706
+ };
707
+
708
+ const server = Hapi.server({ host: '0.0.0.0', port: 0, tls: tlsOptions });
709
+ await server.start();
710
+ expect(server.info.host).to.equal('0.0.0.0');
711
+ expect(server.info.port).to.not.equal(0);
712
+ await server.stop();
713
+ });
714
+
715
+ it('sets info with defaults when missing hostname and address', () => {
716
+
717
+ const hostname = Os.hostname;
718
+ Os.hostname = function () {
719
+
720
+ Os.hostname = hostname;
721
+ return '';
722
+ };
723
+
724
+ const server = Hapi.server({ port: '8000' });
725
+ expect(server.info.host).to.equal('localhost');
726
+ expect(server.info.uri).to.equal('http://localhost:8000');
727
+ });
728
+
729
+ it('ignored repeated calls', async () => {
730
+
731
+ const server = Hapi.server();
732
+ await server.start();
733
+ await server.start();
734
+ await server.stop();
735
+ });
736
+ });
737
+
738
+ describe('_stop()', () => {
739
+
740
+ it('waits to stop until all connections are closed (HTTP)', async () => {
741
+
742
+ const server = Hapi.server();
743
+ await server.start();
744
+
745
+ const socket1 = await internals.socket(server);
746
+ const socket2 = await internals.socket(server);
747
+
748
+ await Hoek.wait(50);
749
+ const count1 = await internals.countConnections(server);
750
+ expect(count1).to.equal(2);
751
+ expect(server._core.sockets.size).to.equal(2);
752
+
753
+ const stop = server.stop();
754
+ socket1.end();
755
+ socket2.end();
756
+
757
+ await stop;
758
+ await Hoek.wait(10);
759
+
760
+ const count2 = await internals.countConnections(server);
761
+ expect(count2).to.equal(0);
762
+ expect(server._core.sockets.size).to.equal(0);
763
+ });
764
+
765
+ it('waits to stop until all connections are closed (HTTPS)', async () => {
766
+
767
+ const tlsOptions = {
768
+ key: '-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEA0UqyXDCqWDKpoNQQK/fdr0OkG4gW6DUafxdufH9GmkX/zoKz\ng/SFLrPipzSGINKWtyMvo7mPjXqqVgE10LDI3VFV8IR6fnART+AF8CW5HMBPGt/s\nfQW4W4puvBHkBxWSW1EvbecgNEIS9hTGvHXkFzm4xJ2e9DHp2xoVAjREC73B7JbF\nhc5ZGGchKw+CFmAiNysU0DmBgQcac0eg2pWoT+YGmTeQj6sRXO67n2xy/hA1DuN6\nA4WBK3wM3O4BnTG0dNbWUEbe7yAbV5gEyq57GhJIeYxRvveVDaX90LoAqM4cUH06\n6rciON0UbDHV2LP/JaH5jzBjUyCnKLLo5snlbwIDAQABAoIBAQDJm7YC3pJJUcxb\nc8x8PlHbUkJUjxzZ5MW4Zb71yLkfRYzsxrTcyQA+g+QzA4KtPY8XrZpnkgm51M8e\n+B16AcIMiBxMC6HgCF503i16LyyJiKrrDYfGy2rTK6AOJQHO3TXWJ3eT3BAGpxuS\n12K2Cq6EvQLCy79iJm7Ks+5G6EggMZPfCVdEhffRm2Epl4T7LpIAqWiUDcDfS05n\nNNfAGxxvALPn+D+kzcSF6hpmCVrFVTf9ouhvnr+0DpIIVPwSK/REAF3Ux5SQvFuL\njPmh3bGwfRtcC5d21QNrHdoBVSN2UBLmbHUpBUcOBI8FyivAWJhRfKnhTvXMFG8L\nwaXB51IZAoGBAP/E3uz6zCyN7l2j09wmbyNOi1AKvr1WSmuBJveITouwblnRSdvc\nsYm4YYE0Vb94AG4n7JIfZLKtTN0xvnCo8tYjrdwMJyGfEfMGCQQ9MpOBXAkVVZvP\ne2k4zHNNsfvSc38UNSt7K0HkVuH5BkRBQeskcsyMeu0qK4wQwdtiCoBDAoGBANF7\nFMppYxSW4ir7Jvkh0P8bP/Z7AtaSmkX7iMmUYT+gMFB5EKqFTQjNQgSJxS/uHVDE\nSC5co8WGHnRk7YH2Pp+Ty1fHfXNWyoOOzNEWvg6CFeMHW2o+/qZd4Z5Fep6qCLaa\nFvzWWC2S5YslEaaP8DQ74aAX4o+/TECrxi0z2lllAoGAdRB6qCSyRsI/k4Rkd6Lv\nw00z3lLMsoRIU6QtXaZ5rN335Awyrfr5F3vYxPZbOOOH7uM/GDJeOJmxUJxv+cia\nPQDflpPJZU4VPRJKFjKcb38JzO6C3Gm+po5kpXGuQQA19LgfDeO2DNaiHZOJFrx3\nm1R3Zr/1k491lwokcHETNVkCgYBPLjrZl6Q/8BhlLrG4kbOx+dbfj/euq5NsyHsX\n1uI7bo1Una5TBjfsD8nYdUr3pwWltcui2pl83Ak+7bdo3G8nWnIOJ/WfVzsNJzj7\n/6CvUzR6sBk5u739nJbfgFutBZBtlSkDQPHrqA7j3Ysibl3ZIJlULjMRKrnj6Ans\npCDwkQKBgQCM7gu3p7veYwCZaxqDMz5/GGFUB1My7sK0hcT7/oH61yw3O8pOekee\nuctI1R3NOudn1cs5TAy/aypgLDYTUGQTiBRILeMiZnOrvQQB9cEf7TFgDoRNCcDs\nV/ZWiegVB/WY7H0BkCekuq5bHwjgtJTpvHGqQ9YD7RhE8RSYOhdQ/Q==\n-----END RSA PRIVATE KEY-----\n',
769
+ cert: '-----BEGIN CERTIFICATE-----\nMIIDBjCCAe4CCQDvLNml6smHlTANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJV\nUzETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0\ncyBQdHkgTHRkMB4XDTE0MDEyNTIxMjIxOFoXDTE1MDEyNTIxMjIxOFowRTELMAkG\nA1UEBhMCVVMxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0\nIFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB\nANFKslwwqlgyqaDUECv33a9DpBuIFug1Gn8Xbnx/RppF/86Cs4P0hS6z4qc0hiDS\nlrcjL6O5j416qlYBNdCwyN1RVfCEen5wEU/gBfAluRzATxrf7H0FuFuKbrwR5AcV\nkltRL23nIDRCEvYUxrx15Bc5uMSdnvQx6dsaFQI0RAu9weyWxYXOWRhnISsPghZg\nIjcrFNA5gYEHGnNHoNqVqE/mBpk3kI+rEVzuu59scv4QNQ7jegOFgSt8DNzuAZ0x\ntHTW1lBG3u8gG1eYBMquexoSSHmMUb73lQ2l/dC6AKjOHFB9Ouq3IjjdFGwx1diz\n/yWh+Y8wY1Mgpyiy6ObJ5W8CAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAoSc6Skb4\ng1e0ZqPKXBV2qbx7hlqIyYpubCl1rDiEdVzqYYZEwmst36fJRRrVaFuAM/1DYAmT\nWMhU+yTfA+vCS4tql9b9zUhPw/IDHpBDWyR01spoZFBF/hE1MGNpCSXXsAbmCiVf\naxrIgR2DNketbDxkQx671KwF1+1JOMo9ffXp+OhuRo5NaGIxhTsZ+f/MA4y084Aj\nDI39av50sTRTWWShlN+J7PtdQVA5SZD97oYbeUeL7gI18kAJww9eUdmT0nEjcwKs\nxsQT1fyKbo7AlZBY4KSlUMuGnn0VnAsB9b+LxtXlDfnjyM8bVQx1uAfRo0DO8p/5\n3J5DTjAU55deBQ==\n-----END CERTIFICATE-----\n'
770
+ };
771
+
772
+ const server = Hapi.server({ tls: tlsOptions });
773
+ await server.start();
774
+
775
+ const socket1 = await internals.socket(server, 'tls');
776
+ const socket2 = await internals.socket(server, 'tls');
777
+
778
+ await Hoek.wait(50);
779
+ const count1 = await internals.countConnections(server);
780
+ expect(count1).to.equal(2);
781
+ expect(server._core.sockets.size).to.equal(2);
782
+
783
+ const stop = server.stop();
784
+ socket1.end();
785
+ socket2.end();
786
+
787
+ await stop;
788
+ await Hoek.wait(10);
789
+
790
+ const count2 = await internals.countConnections(server);
791
+ expect(count2).to.equal(0);
792
+ expect(server._core.sockets.size).to.equal(0);
793
+ });
794
+
795
+ it('immediately destroys unhandled connections', async () => {
796
+
797
+ const server = Hapi.server();
798
+ await server.start();
799
+
800
+ await internals.socket(server);
801
+ await internals.socket(server);
802
+
803
+ await Hoek.wait(50);
804
+ const count1 = await internals.countConnections(server);
805
+ expect(count1).to.equal(2);
806
+
807
+ const timer = new Hoek.Bench();
808
+ await server.stop({ timeout: 100 });
809
+ expect(timer.elapsed()).to.be.at.most(110);
810
+ });
811
+
812
+ it('waits to destroy handled connections until after the timeout', async () => {
813
+
814
+ const server = Hapi.server();
815
+ server.route({ method: 'GET', path: '/', handler: (request, h) => h.abandon });
816
+ await server.start();
817
+
818
+ const socket = await internals.socket(server);
819
+ socket.write('GET / HTTP/1.0\nHost: test\n\n');
820
+ await Hoek.wait(10);
821
+
822
+ const count1 = await internals.countConnections(server);
823
+ expect(count1).to.equal(1);
824
+
825
+ const timer = new Hoek.Bench();
826
+ await server.stop({ timeout: 20 });
827
+ expect(timer.elapsed()).to.be.at.least(19);
828
+ });
829
+
830
+ it('waits to destroy connections if they close by themselves', async () => {
831
+
832
+ const server = Hapi.server();
833
+ server.route({ method: 'GET', path: '/', handler: (request, h) => h.abandon });
834
+ await server.start();
835
+
836
+ const socket = await internals.socket(server);
837
+ socket.write('GET / HTTP/1.0\nHost: test\n\n');
838
+ await Hoek.wait(10);
839
+
840
+ const count1 = await internals.countConnections(server);
841
+ expect(count1).to.equal(1);
842
+
843
+ setTimeout(() => socket.end(), 100);
844
+
845
+ const timer = new Hoek.Bench();
846
+ await server.stop({ timeout: 400 });
847
+ expect(timer.elapsed()).to.be.below(300);
848
+ });
849
+
850
+ it('immediately destroys idle keep-alive connections', { retry: true }, async () => {
851
+
852
+ const server = Hapi.server();
853
+ server.route({ method: 'GET', path: '/', handler: () => null });
854
+
855
+ await server.start();
856
+
857
+ const socket = await internals.socket(server);
858
+ socket.write('GET / HTTP/1.1\nHost: test\nConnection: Keep-Alive\n\n\n');
859
+ await new Promise((resolve) => socket.on('data', resolve));
860
+
861
+ const count = await internals.countConnections(server);
862
+ expect(count).to.equal(1);
863
+
864
+ const timer = new Hoek.Bench();
865
+ await server.stop({ timeout: 20 });
866
+ expect(timer.elapsed()).to.be.at.most(20);
867
+ });
868
+
869
+ it('waits to stop until connections close by themselves when cleanStop is disabled', async () => {
870
+
871
+ const server = Hapi.server({ operations: { cleanStop: false } });
872
+ server.route({ method: 'GET', path: '/', handler: (request, h) => h.abandon });
873
+ await server.start();
874
+
875
+ const socket = await internals.socket(server);
876
+ socket.write('GET / HTTP/1.0\nHost: test\n\n');
877
+ await Hoek.wait(10);
878
+
879
+ const count1 = await internals.countConnections(server);
880
+ expect(count1).to.equal(1);
881
+
882
+ setTimeout(() => socket.end(), 100);
883
+
884
+ const stop = server.stop();
885
+
886
+ await Hoek.wait(50);
887
+
888
+ const count2 = await internals.countConnections(server);
889
+ expect(count2).to.equal(1);
890
+
891
+ await Hoek.wait(200);
892
+
893
+ const count3 = await internals.countConnections(server);
894
+ expect(count3).to.equal(0);
895
+
896
+ await stop;
897
+ });
898
+
899
+ it('refuses to handle new incoming requests on persistent connections', async () => {
900
+
901
+ const server = Hapi.server();
902
+ server.route({ method: 'GET', path: '/', handler: () => 'ok' });
903
+ await server.start();
904
+
905
+ const agent = new Http.Agent({ keepAlive: true, maxSockets: 1 });
906
+ const first = Wreck.get('http://localhost:' + server.info.port + '/', { agent });
907
+ const second = Wreck.get('http://localhost:' + server.info.port + '/', { agent });
908
+
909
+ const { res, payload } = await first;
910
+ const stop = server.stop();
911
+
912
+ await expect(second).to.reject();
913
+ await stop;
914
+
915
+ expect(res.headers.connection).to.equal('keep-alive');
916
+ expect(payload.toString()).to.equal('ok');
917
+ expect(server._core.started).to.equal(false);
918
+ });
919
+
920
+ it('finishes in-progress requests and ends connection', async () => {
921
+
922
+ let stop;
923
+ const handler = async (request) => {
924
+
925
+ stop = server.stop({ timeout: 200 });
926
+ await Hoek.wait(0);
927
+ return 'ok';
928
+ };
929
+
930
+ const server = Hapi.server();
931
+ server.route({ method: 'GET', path: '/', handler });
932
+ await server.start();
933
+
934
+ const agent = new Http.Agent({ keepAlive: true, maxSockets: 1 });
935
+
936
+ const first = Wreck.get('http://localhost:' + server.info.port + '/', { agent });
937
+ const second = Wreck.get('http://localhost:' + server.info.port + '/404', { agent });
938
+
939
+ const { res, payload } = await first;
940
+ expect(res.headers.connection).to.equal('close');
941
+ expect(payload.toString()).to.equal('ok');
942
+
943
+ await expect(second).to.reject();
944
+ await expect(stop).to.not.reject();
945
+ });
946
+
947
+ it('does not close longpoll HTTPS requests before response (if within timeout)', async () => {
948
+
949
+ const tlsOptions = {
950
+ key: '-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEA0UqyXDCqWDKpoNQQK/fdr0OkG4gW6DUafxdufH9GmkX/zoKz\ng/SFLrPipzSGINKWtyMvo7mPjXqqVgE10LDI3VFV8IR6fnART+AF8CW5HMBPGt/s\nfQW4W4puvBHkBxWSW1EvbecgNEIS9hTGvHXkFzm4xJ2e9DHp2xoVAjREC73B7JbF\nhc5ZGGchKw+CFmAiNysU0DmBgQcac0eg2pWoT+YGmTeQj6sRXO67n2xy/hA1DuN6\nA4WBK3wM3O4BnTG0dNbWUEbe7yAbV5gEyq57GhJIeYxRvveVDaX90LoAqM4cUH06\n6rciON0UbDHV2LP/JaH5jzBjUyCnKLLo5snlbwIDAQABAoIBAQDJm7YC3pJJUcxb\nc8x8PlHbUkJUjxzZ5MW4Zb71yLkfRYzsxrTcyQA+g+QzA4KtPY8XrZpnkgm51M8e\n+B16AcIMiBxMC6HgCF503i16LyyJiKrrDYfGy2rTK6AOJQHO3TXWJ3eT3BAGpxuS\n12K2Cq6EvQLCy79iJm7Ks+5G6EggMZPfCVdEhffRm2Epl4T7LpIAqWiUDcDfS05n\nNNfAGxxvALPn+D+kzcSF6hpmCVrFVTf9ouhvnr+0DpIIVPwSK/REAF3Ux5SQvFuL\njPmh3bGwfRtcC5d21QNrHdoBVSN2UBLmbHUpBUcOBI8FyivAWJhRfKnhTvXMFG8L\nwaXB51IZAoGBAP/E3uz6zCyN7l2j09wmbyNOi1AKvr1WSmuBJveITouwblnRSdvc\nsYm4YYE0Vb94AG4n7JIfZLKtTN0xvnCo8tYjrdwMJyGfEfMGCQQ9MpOBXAkVVZvP\ne2k4zHNNsfvSc38UNSt7K0HkVuH5BkRBQeskcsyMeu0qK4wQwdtiCoBDAoGBANF7\nFMppYxSW4ir7Jvkh0P8bP/Z7AtaSmkX7iMmUYT+gMFB5EKqFTQjNQgSJxS/uHVDE\nSC5co8WGHnRk7YH2Pp+Ty1fHfXNWyoOOzNEWvg6CFeMHW2o+/qZd4Z5Fep6qCLaa\nFvzWWC2S5YslEaaP8DQ74aAX4o+/TECrxi0z2lllAoGAdRB6qCSyRsI/k4Rkd6Lv\nw00z3lLMsoRIU6QtXaZ5rN335Awyrfr5F3vYxPZbOOOH7uM/GDJeOJmxUJxv+cia\nPQDflpPJZU4VPRJKFjKcb38JzO6C3Gm+po5kpXGuQQA19LgfDeO2DNaiHZOJFrx3\nm1R3Zr/1k491lwokcHETNVkCgYBPLjrZl6Q/8BhlLrG4kbOx+dbfj/euq5NsyHsX\n1uI7bo1Una5TBjfsD8nYdUr3pwWltcui2pl83Ak+7bdo3G8nWnIOJ/WfVzsNJzj7\n/6CvUzR6sBk5u739nJbfgFutBZBtlSkDQPHrqA7j3Ysibl3ZIJlULjMRKrnj6Ans\npCDwkQKBgQCM7gu3p7veYwCZaxqDMz5/GGFUB1My7sK0hcT7/oH61yw3O8pOekee\nuctI1R3NOudn1cs5TAy/aypgLDYTUGQTiBRILeMiZnOrvQQB9cEf7TFgDoRNCcDs\nV/ZWiegVB/WY7H0BkCekuq5bHwjgtJTpvHGqQ9YD7RhE8RSYOhdQ/Q==\n-----END RSA PRIVATE KEY-----\n',
951
+ cert: '-----BEGIN CERTIFICATE-----\nMIIDBjCCAe4CCQDvLNml6smHlTANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJV\nUzETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0\ncyBQdHkgTHRkMB4XDTE0MDEyNTIxMjIxOFoXDTE1MDEyNTIxMjIxOFowRTELMAkG\nA1UEBhMCVVMxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0\nIFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB\nANFKslwwqlgyqaDUECv33a9DpBuIFug1Gn8Xbnx/RppF/86Cs4P0hS6z4qc0hiDS\nlrcjL6O5j416qlYBNdCwyN1RVfCEen5wEU/gBfAluRzATxrf7H0FuFuKbrwR5AcV\nkltRL23nIDRCEvYUxrx15Bc5uMSdnvQx6dsaFQI0RAu9weyWxYXOWRhnISsPghZg\nIjcrFNA5gYEHGnNHoNqVqE/mBpk3kI+rEVzuu59scv4QNQ7jegOFgSt8DNzuAZ0x\ntHTW1lBG3u8gG1eYBMquexoSSHmMUb73lQ2l/dC6AKjOHFB9Ouq3IjjdFGwx1diz\n/yWh+Y8wY1Mgpyiy6ObJ5W8CAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAoSc6Skb4\ng1e0ZqPKXBV2qbx7hlqIyYpubCl1rDiEdVzqYYZEwmst36fJRRrVaFuAM/1DYAmT\nWMhU+yTfA+vCS4tql9b9zUhPw/IDHpBDWyR01spoZFBF/hE1MGNpCSXXsAbmCiVf\naxrIgR2DNketbDxkQx671KwF1+1JOMo9ffXp+OhuRo5NaGIxhTsZ+f/MA4y084Aj\nDI39av50sTRTWWShlN+J7PtdQVA5SZD97oYbeUeL7gI18kAJww9eUdmT0nEjcwKs\nxsQT1fyKbo7AlZBY4KSlUMuGnn0VnAsB9b+LxtXlDfnjyM8bVQx1uAfRo0DO8p/5\n3J5DTjAU55deBQ==\n-----END CERTIFICATE-----\n'
952
+ };
953
+
954
+ const server = Hapi.server({ tls: tlsOptions });
955
+
956
+ let stop;
957
+ const handler = async (request) => {
958
+
959
+ stop = server.stop({ timeout: 200 });
960
+ await Hoek.wait(150);
961
+ return 'ok';
962
+ };
963
+
964
+ server.route({ method: 'GET', path: '/', handler });
965
+ await server.start();
966
+
967
+ const agent = new Https.Agent({ keepAlive: true, maxSockets: 1, rejectUnauthorized: false });
968
+ const { res, payload } = await Wreck.get('https://localhost:' + server.info.port + '/', { agent });
969
+ expect(res.headers.connection).to.equal('close');
970
+ expect(payload.toString()).to.equal('ok');
971
+
972
+ await stop;
973
+ });
974
+
975
+ it('removes connection event listeners after it stops', async () => {
976
+
977
+ const server = Hapi.server();
978
+ const initial = server.listener.listeners('connection').length;
979
+ await server.start();
980
+
981
+ expect(server.listener.listeners('connection').length).to.be.greaterThan(initial);
982
+
983
+ await server.stop();
984
+ await server.start();
985
+ await server.stop();
986
+
987
+ expect(server.listener.listeners('connection').length).to.equal(initial);
988
+ });
989
+
990
+ it('ignores repeated calls', async () => {
991
+
992
+ const server = Hapi.server();
993
+ await server.stop();
994
+ await server.stop();
995
+ });
996
+
997
+ it('emits a closing event before the server\'s listener close event is emitted', async () => {
998
+
999
+ const server = Hapi.server();
1000
+ const events = [];
1001
+
1002
+ server.events.on('closing', () => events.push('closing'));
1003
+ server.events.on('stop', () => events.push('stop'));
1004
+ server._core.listener.on('close', () => events.push('close'));
1005
+
1006
+ await server.start();
1007
+ await server.stop();
1008
+
1009
+ expect(events).to.equal(['closing', 'close', 'stop']);
1010
+ });
1011
+
1012
+ it('emits a closing event before the close event when there is an active request being processed', async () => {
1013
+
1014
+ const server = Hapi.server();
1015
+ const events = [];
1016
+
1017
+ let stop;
1018
+ const handler = async () => {
1019
+
1020
+ stop = server.stop({ timeout: 200 });
1021
+ await Hoek.wait(0);
1022
+ return 'ok';
1023
+ };
1024
+
1025
+ server.route({ method: 'GET', path: '/', handler });
1026
+
1027
+ server.events.on('closing', () => events.push('closing'));
1028
+ server.events.on('stop', () => events.push('stop'));
1029
+ server._core.listener.on('close', () => events.push('close'));
1030
+
1031
+ await server.start();
1032
+
1033
+ const agent = new Http.Agent({ keepAlive: true, maxSockets: 1 });
1034
+
1035
+ // ongoing active request
1036
+ const first = Wreck.get('http://localhost:' + server.info.port + '/', { agent });
1037
+ // denied incoming request
1038
+ const second = Wreck.get('http://localhost:' + server.info.port + '/', { agent });
1039
+
1040
+ const { res, payload } = await first;
1041
+ expect(res.headers.connection).to.equal('close');
1042
+ expect(payload.toString()).to.equal('ok');
1043
+
1044
+ await expect(second).to.reject();
1045
+ await expect(stop).to.not.reject();
1046
+
1047
+ expect(events).to.equal(['closing', 'close', 'stop']);
1048
+ });
1049
+ });
1050
+
1051
+ describe('_dispatch()', () => {
1052
+
1053
+ it('rejects request due to high rss load', async () => {
1054
+
1055
+ const server = Hapi.server({ load: { sampleInterval: 5, maxRssBytes: 1 } });
1056
+
1057
+ let buffer;
1058
+ const handler = (request) => {
1059
+
1060
+ buffer = buffer || Buffer.alloc(2048);
1061
+ return 'ok';
1062
+ };
1063
+
1064
+ const log = server.events.once('log');
1065
+
1066
+ server.route({ method: 'GET', path: '/', handler });
1067
+ await server.start();
1068
+
1069
+ const res1 = await server.inject('/');
1070
+ expect(res1.statusCode).to.equal(200);
1071
+
1072
+ await Hoek.wait(10);
1073
+ const res2 = await server.inject('/');
1074
+ expect(res2.statusCode).to.equal(503);
1075
+
1076
+ const [event, tags] = await log;
1077
+ expect(event.channel).to.equal('internal');
1078
+ expect(event.data.rss > 10000).to.equal(true);
1079
+ expect(tags.load).to.be.true();
1080
+
1081
+ await server.stop();
1082
+ });
1083
+ });
1084
+
1085
+ describe('inject()', () => {
1086
+
1087
+ it('keeps the options.credentials object untouched', async () => {
1088
+
1089
+ const server = Hapi.server();
1090
+ server.route({ method: 'GET', path: '/', handler: () => null });
1091
+
1092
+ const options = {
1093
+ url: '/',
1094
+ auth: {
1095
+ credentials: { foo: 'bar' },
1096
+ strategy: 'test'
1097
+ }
1098
+ };
1099
+
1100
+ const res = await server.inject(options);
1101
+ expect(res.statusCode).to.equal(204);
1102
+ expect(options.auth.credentials).to.exist();
1103
+ });
1104
+
1105
+ it('sets credentials (with host header)', async () => {
1106
+
1107
+ const server = Hapi.server();
1108
+ server.route({ method: 'GET', path: '/', handler: () => null });
1109
+
1110
+ const options = {
1111
+ url: '/',
1112
+ auth: {
1113
+ credentials: { foo: 'bar' },
1114
+ strategy: 'test'
1115
+ },
1116
+ headers: {
1117
+ host: 'something'
1118
+ }
1119
+ };
1120
+
1121
+ const res = await server.inject(options);
1122
+ expect(res.statusCode).to.equal(204);
1123
+ expect(options.auth.credentials).to.exist();
1124
+ });
1125
+
1126
+ it('sets credentials (with authority)', async () => {
1127
+
1128
+ const server = Hapi.server();
1129
+ server.route({ method: 'GET', path: '/', handler: (request) => request.headers.host });
1130
+
1131
+ const options = {
1132
+ url: '/',
1133
+ authority: 'something',
1134
+ auth: {
1135
+ credentials: { foo: 'bar' },
1136
+ strategy: 'test'
1137
+ }
1138
+ };
1139
+
1140
+ const res = await server.inject(options);
1141
+ expect(res.statusCode).to.equal(200);
1142
+ expect(res.result).to.equal('something');
1143
+ expect(options.auth.credentials).to.exist();
1144
+ });
1145
+
1146
+ it('sets authority', async () => {
1147
+
1148
+ const server = Hapi.server();
1149
+ server.route({ method: 'GET', path: '/', handler: (request) => request.headers.host });
1150
+
1151
+ const options = {
1152
+ url: '/',
1153
+ authority: 'something'
1154
+ };
1155
+
1156
+ const res = await server.inject(options);
1157
+ expect(res.statusCode).to.equal(200);
1158
+ expect(res.result).to.equal('something');
1159
+ });
1160
+
1161
+ it('passes the options.artifacts object', async () => {
1162
+
1163
+ const server = Hapi.server();
1164
+ server.route({ method: 'GET', path: '/', handler: (request) => request.auth.artifacts });
1165
+
1166
+ const options = {
1167
+ url: '/',
1168
+ auth: {
1169
+ credentials: { foo: 'bar' },
1170
+ artifacts: { bar: 'baz' },
1171
+ strategy: 'test'
1172
+ }
1173
+ };
1174
+
1175
+ const res = await server.inject(options);
1176
+ expect(res.statusCode).to.equal(200);
1177
+ expect(res.result.bar).to.equal('baz');
1178
+ expect(options.auth.artifacts).to.exist();
1179
+ });
1180
+
1181
+ it('sets `request.auth.isInjected = true` when `auth` option is defined', async () => {
1182
+
1183
+ const server = Hapi.server();
1184
+ server.route({ method: 'GET', path: '/', handler: (request) => request.auth.isInjected });
1185
+
1186
+ const options = {
1187
+ url: '/',
1188
+ auth: {
1189
+ credentials: { foo: 'bar' },
1190
+ strategy: 'test'
1191
+ }
1192
+ };
1193
+
1194
+ const res = await server.inject(options);
1195
+ expect(res.statusCode).to.equal(200);
1196
+ expect(res.result).to.be.true();
1197
+ });
1198
+
1199
+ it('sets `request.isInjected = true` for requests created via `server.inject`', async () => {
1200
+
1201
+ const server = Hapi.server();
1202
+ server.route({ method: 'GET', path: '/', handler: (request) => request.isInjected });
1203
+
1204
+ const options = {
1205
+ url: '/'
1206
+ };
1207
+
1208
+ const res = await server.inject(options);
1209
+ expect(res.statusCode).to.equal(200);
1210
+ expect(res.result).to.be.true();
1211
+ });
1212
+
1213
+ it('`request.isInjected` access is read-only', async () => {
1214
+
1215
+ const server = Hapi.server();
1216
+ server.route({ method: 'GET', path: '/', handler: (request) => {
1217
+
1218
+ const illegalAssignment = () => {
1219
+
1220
+ request.isInjected = false;
1221
+ };
1222
+
1223
+ expect(illegalAssignment).to.throw('Cannot set property isInjected of [object Object] which has only a getter');
1224
+
1225
+ return request.isInjected;
1226
+ } });
1227
+
1228
+ const options = {
1229
+ url: '/'
1230
+ };
1231
+
1232
+ const res = await server.inject(options);
1233
+ expect(res.statusCode).to.equal(200);
1234
+ expect(res.result).to.be.true();
1235
+ });
1236
+
1237
+ it('sets `request.isInjected = false` for normal request', async () => {
1238
+
1239
+ const server = Hapi.server();
1240
+ server.route({ method: 'GET', path: '/', handler: (request) => request.isInjected });
1241
+
1242
+ await server.start();
1243
+
1244
+ const { payload } = await Wreck.get(`http://localhost:${server.info.port}/`);
1245
+ expect(payload.toString()).to.equal('false');
1246
+
1247
+ await server.stop();
1248
+ });
1249
+
1250
+ it('sets app settings', async () => {
1251
+
1252
+ const server = Hapi.server();
1253
+ server.route({ method: 'GET', path: '/', handler: (request) => request.app.x });
1254
+
1255
+ const options = {
1256
+ url: '/',
1257
+ authority: 'x', // For coverage
1258
+ app: {
1259
+ x: 123
1260
+ }
1261
+ };
1262
+
1263
+ const res = await server.inject(options);
1264
+ expect(res.statusCode).to.equal(200);
1265
+ expect(res.result).to.equal(123);
1266
+ });
1267
+
1268
+ it('sets plugins settings', async () => {
1269
+
1270
+ const server = Hapi.server();
1271
+ server.route({ method: 'GET', path: '/', handler: (request) => request.plugins.x.y });
1272
+
1273
+ const options = {
1274
+ url: '/',
1275
+ authority: 'x', // For coverage
1276
+ plugins: {
1277
+ x: {
1278
+ y: 123
1279
+ }
1280
+ }
1281
+ };
1282
+
1283
+ const res = await server.inject(options);
1284
+ expect(res.statusCode).to.equal(200);
1285
+ expect(res.result).to.equal(123);
1286
+ });
1287
+
1288
+ it('returns the request object', async () => {
1289
+
1290
+ const handler = (request) => {
1291
+
1292
+ request.app.key = 'value';
1293
+ return null;
1294
+ };
1295
+
1296
+ const server = Hapi.server();
1297
+ server.route({ method: 'GET', path: '/', handler });
1298
+
1299
+ const res = await server.inject('/');
1300
+ expect(res.statusCode).to.equal(204);
1301
+ expect(res.request.app.key).to.equal('value');
1302
+ });
1303
+
1304
+ it('returns the request object for POST', async () => {
1305
+
1306
+ const payload = { foo: true };
1307
+ const handler = (request) => {
1308
+
1309
+ return request.payload;
1310
+ };
1311
+
1312
+ const server = Hapi.server();
1313
+ server.route({ method: 'POST', path: '/', handler });
1314
+
1315
+ const res = await server.inject({ method: 'POST', url: '/', payload });
1316
+ expect(res.statusCode).to.equal(200);
1317
+ expect(JSON.parse(res.payload)).to.equal(payload);
1318
+ });
1319
+
1320
+ it('returns the request string for POST', async () => {
1321
+
1322
+ const payload = JSON.stringify({ foo: true });
1323
+ const handler = (request) => {
1324
+
1325
+ return request.payload;
1326
+ };
1327
+
1328
+ const server = Hapi.server();
1329
+ server.route({ method: 'POST', path: '/', handler });
1330
+
1331
+ const res = await server.inject({ method: 'POST', url: '/', payload });
1332
+ expect(res.statusCode).to.equal(200);
1333
+ expect(res.payload).to.equal(payload);
1334
+ });
1335
+
1336
+ it('returns the request stream for POST', async () => {
1337
+
1338
+ const param = { foo: true };
1339
+ const payload = new Stream.Readable();
1340
+ payload.push(JSON.stringify(param));
1341
+ payload.push(null);
1342
+
1343
+ const handler = (request) => {
1344
+
1345
+ return request.payload;
1346
+ };
1347
+
1348
+ const server = Hapi.server();
1349
+ server.route({ method: 'POST', path: '/', handler });
1350
+
1351
+ const res = await server.inject({ method: 'POST', url: '/', payload });
1352
+ expect(res.statusCode).to.equal(200);
1353
+ expect(JSON.parse(res.payload)).to.equal(param);
1354
+ });
1355
+
1356
+ it('can set a client remoteAddress', async () => {
1357
+
1358
+ const server = Hapi.server();
1359
+ server.route({ method: 'GET', path: '/', handler: (request) => request.info.remoteAddress });
1360
+
1361
+ const res = await server.inject({ url: '/', remoteAddress: '1.2.3.4' });
1362
+ expect(res.statusCode).to.equal(200);
1363
+ expect(res.payload).to.equal('1.2.3.4');
1364
+ });
1365
+
1366
+ it('sets a default remoteAddress of 127.0.0.1', async () => {
1367
+
1368
+ const server = Hapi.server();
1369
+ server.route({ method: 'GET', path: '/', handler: (request) => request.info.remoteAddress });
1370
+
1371
+ const res = await server.inject('/');
1372
+ expect(res.statusCode).to.equal(200);
1373
+ expect(res.payload).to.equal('127.0.0.1');
1374
+ });
1375
+
1376
+ it('sets correct host header', async () => {
1377
+
1378
+ const server = Hapi.server({ host: 'example.com', port: 2080 });
1379
+ server.route({
1380
+ method: 'GET',
1381
+ path: '/',
1382
+ handler: (request) => request.headers.host
1383
+ });
1384
+
1385
+ const res = await server.inject('/');
1386
+ expect(res.result).to.equal('example.com:2080');
1387
+ });
1388
+ });
1389
+
1390
+ describe('table()', () => {
1391
+
1392
+ it('returns an array of the current routes', () => {
1393
+
1394
+ const server = Hapi.server();
1395
+
1396
+ server.route({ path: '/test/', method: 'get', handler: () => null });
1397
+ server.route({ path: '/test/{p}/end', method: 'get', handler: () => null });
1398
+
1399
+ const routes = server.table();
1400
+ expect(routes.length).to.equal(2);
1401
+ expect(routes[0].path).to.equal('/test/');
1402
+ });
1403
+
1404
+ it('combines global and vhost routes', () => {
1405
+
1406
+ const server = Hapi.server();
1407
+
1408
+ server.route({ path: '/test/', method: 'get', handler: () => null });
1409
+ server.route({ path: '/test/', vhost: 'one.example.com', method: 'get', handler: () => null });
1410
+ server.route({ path: '/test/', vhost: 'two.example.com', method: 'get', handler: () => null });
1411
+ server.route({ path: '/test/{p}/end', method: 'get', handler: () => null });
1412
+
1413
+ const routes = server.table();
1414
+ expect(routes.length).to.equal(4);
1415
+ });
1416
+
1417
+ it('combines global and vhost routes and filters based on host', () => {
1418
+
1419
+ const server = Hapi.server();
1420
+
1421
+ server.route({ path: '/test/', method: 'get', handler: () => null });
1422
+ server.route({ path: '/test/', vhost: 'one.example.com', method: 'get', handler: () => null });
1423
+ server.route({ path: '/test/', vhost: 'two.example.com', method: 'get', handler: () => null });
1424
+ server.route({ path: '/test/{p}/end', method: 'get', handler: () => null });
1425
+
1426
+ const routes = server.table('one.example.com');
1427
+ expect(routes.length).to.equal(3);
1428
+ });
1429
+
1430
+ it('accepts a list of hosts', () => {
1431
+
1432
+ const server = Hapi.server();
1433
+
1434
+ server.route({ path: '/test/', method: 'get', handler: () => null });
1435
+ server.route({ path: '/test/', vhost: 'one.example.com', method: 'get', handler: () => null });
1436
+ server.route({ path: '/test/', vhost: 'two.example.com', method: 'get', handler: () => null });
1437
+ server.route({ path: '/test/{p}/end', method: 'get', handler: () => null });
1438
+
1439
+ const routes = server.table(['one.example.com', 'two.example.com']);
1440
+ expect(routes.length).to.equal(4);
1441
+ });
1442
+
1443
+ it('ignores unknown host', () => {
1444
+
1445
+ const server = Hapi.server();
1446
+
1447
+ server.route({ path: '/test/', method: 'get', handler: () => null });
1448
+ server.route({ path: '/test/', vhost: 'one.example.com', method: 'get', handler: () => null });
1449
+ server.route({ path: '/test/', vhost: 'two.example.com', method: 'get', handler: () => null });
1450
+ server.route({ path: '/test/{p}/end', method: 'get', handler: () => null });
1451
+
1452
+ const routes = server.table('three.example.com');
1453
+ expect(routes.length).to.equal(2);
1454
+ });
1455
+ });
1456
+
1457
+ describe('ext()', () => {
1458
+
1459
+ it('supports adding an array of methods', async () => {
1460
+
1461
+ const server = Hapi.server();
1462
+ server.ext('onPreHandler', [
1463
+ (request, h) => {
1464
+
1465
+ request.app.x = '1';
1466
+ return h.continue;
1467
+ },
1468
+ (request, h) => {
1469
+
1470
+ request.app.x += '2';
1471
+ return h.continue;
1472
+ }
1473
+ ]);
1474
+
1475
+ server.route({ method: 'GET', path: '/', handler: (request) => request.app.x });
1476
+
1477
+ const res = await server.inject('/');
1478
+ expect(res.result).to.equal('12');
1479
+ });
1480
+
1481
+ it('sets bind via options', async () => {
1482
+
1483
+ const server = Hapi.server();
1484
+ const preHandler = function (request, h) {
1485
+
1486
+ request.app.x = this.y;
1487
+ return h.continue;
1488
+ };
1489
+
1490
+ server.ext('onPreHandler', preHandler, { bind: { y: 42 } });
1491
+
1492
+ server.route({ method: 'GET', path: '/', handler: (request) => request.app.x });
1493
+
1494
+ const res = await server.inject('/');
1495
+ expect(res.result).to.equal(42);
1496
+ });
1497
+
1498
+ it('uses server views for ext added via server', async () => {
1499
+
1500
+ const server = Hapi.server();
1501
+ await server.register(Vision);
1502
+
1503
+ server.views({
1504
+ engines: { html: Handlebars },
1505
+ path: __dirname + '/templates'
1506
+ });
1507
+
1508
+ const preHandler = (request, h) => {
1509
+
1510
+ return h.view('test').takeover();
1511
+ };
1512
+
1513
+ server.ext('onPreHandler', preHandler);
1514
+
1515
+ const test = {
1516
+ name: 'test',
1517
+
1518
+ register: function (plugin, options) {
1519
+
1520
+ plugin.views({
1521
+ engines: { html: Handlebars },
1522
+ path: './no_such_directory_found'
1523
+ });
1524
+
1525
+ plugin.route({ path: '/view', method: 'GET', handler: () => null });
1526
+ }
1527
+ };
1528
+
1529
+ await server.register(test);
1530
+ const res = await server.inject('/view');
1531
+ expect(res.statusCode).to.equal(200);
1532
+ });
1533
+
1534
+ it('supports toolkit decorators on empty result', async () => {
1535
+
1536
+ const server = Hapi.server();
1537
+ const onRequest = (request, h) => {
1538
+
1539
+ return h.response().redirect('/elsewhere').takeover();
1540
+ };
1541
+
1542
+ server.ext('onRequest', onRequest);
1543
+
1544
+ const res = await server.inject('/');
1545
+ expect(res.statusCode).to.equal(302);
1546
+ expect(res.headers.location).to.equal('/elsewhere');
1547
+ });
1548
+
1549
+ it('supports direct toolkit decorators', async () => {
1550
+
1551
+ const server = Hapi.server();
1552
+ const onRequest = (request, h) => {
1553
+
1554
+ return h.redirect('/elsewhere').takeover();
1555
+ };
1556
+
1557
+ server.ext('onRequest', onRequest);
1558
+
1559
+ const res = await server.inject('/');
1560
+ expect(res.statusCode).to.equal(302);
1561
+ expect(res.headers.location).to.equal('/elsewhere');
1562
+ });
1563
+
1564
+ it('skips extensions once takeover is called', async () => {
1565
+
1566
+ const server = Hapi.server();
1567
+
1568
+ const preResponse1 = (request, h) => {
1569
+
1570
+ return h.response(1).takeover();
1571
+ };
1572
+
1573
+ server.ext('onPreResponse', preResponse1);
1574
+
1575
+ let called = false;
1576
+ const preResponse2 = (request) => {
1577
+
1578
+ called = true;
1579
+ return 2;
1580
+ };
1581
+
1582
+ server.ext('onPreResponse', preResponse2);
1583
+
1584
+ server.route({ method: 'GET', path: '/', handler: () => 0 });
1585
+
1586
+ const res = await server.inject({ method: 'GET', url: '/' });
1587
+ expect(res.result).to.equal(1);
1588
+ expect(called).to.be.false();
1589
+ });
1590
+
1591
+ it('executes all extensions with return values', async () => {
1592
+
1593
+ const server = Hapi.server();
1594
+ server.ext('onPreResponse', () => 1);
1595
+
1596
+ let called = false;
1597
+ const preResponse2 = (request) => {
1598
+
1599
+ called = true;
1600
+ return 2;
1601
+ };
1602
+
1603
+ server.ext('onPreResponse', preResponse2);
1604
+ server.route({ method: 'GET', path: '/', handler: () => 0 });
1605
+
1606
+ const res = await server.inject({ method: 'GET', url: '/' });
1607
+ expect(res.result).to.equal(2);
1608
+ expect(called).to.be.true();
1609
+ });
1610
+
1611
+ describe('onRequest', () => {
1612
+
1613
+ it('replies with custom response', async () => {
1614
+
1615
+ const server = Hapi.server();
1616
+ const onRequest = (request) => {
1617
+
1618
+ throw Boom.badRequest('boom');
1619
+ };
1620
+
1621
+ server.ext('onRequest', onRequest);
1622
+
1623
+ const res = await server.inject('/');
1624
+ expect(res.statusCode).to.equal(400);
1625
+ expect(res.result.message).to.equal('boom');
1626
+ });
1627
+
1628
+ it('replies with a view', async () => {
1629
+
1630
+ const server = Hapi.server();
1631
+ await server.register(Vision);
1632
+
1633
+ server.views({
1634
+ engines: { 'html': Handlebars },
1635
+ path: __dirname + '/templates'
1636
+ });
1637
+
1638
+ const onRequest = (request, h) => {
1639
+
1640
+ return h.view('test', { message: 'hola!' }).takeover();
1641
+ };
1642
+
1643
+ server.ext('onRequest', onRequest);
1644
+
1645
+ server.route({ method: 'GET', path: '/', handler: () => 'ok' });
1646
+
1647
+ const res = await server.inject('/');
1648
+ expect(res.result).to.match(/<div>\r?\n <h1>hola!<\/h1>\r?\n<\/div>\r?\n/);
1649
+ });
1650
+ });
1651
+
1652
+ describe('onPreResponse', () => {
1653
+
1654
+ it('replies with custom response', async () => {
1655
+
1656
+ const server = Hapi.server();
1657
+
1658
+ const preRequest = (request, h) => {
1659
+
1660
+ if (typeof request.response.source === 'string') {
1661
+ throw Boom.badRequest('boom');
1662
+ }
1663
+
1664
+ return h.continue;
1665
+ };
1666
+
1667
+ server.ext('onPreResponse', preRequest);
1668
+
1669
+ server.route({
1670
+ method: 'GET',
1671
+ path: '/text',
1672
+ handler: () => 'ok'
1673
+ });
1674
+
1675
+ server.route({
1676
+ method: 'GET',
1677
+ path: '/obj',
1678
+ handler: () => ({ status: 'ok' })
1679
+ });
1680
+
1681
+ const res1 = await server.inject({ method: 'GET', url: '/text' });
1682
+ expect(res1.result.message).to.equal('boom');
1683
+
1684
+ const res2 = await server.inject({ method: 'GET', url: '/obj' });
1685
+ expect(res2.result.status).to.equal('ok');
1686
+ });
1687
+
1688
+ it('intercepts 404 responses', async () => {
1689
+
1690
+ const server = Hapi.server();
1691
+
1692
+ const preResponse = (request, h) => {
1693
+
1694
+ return h.response(request.response.output.statusCode).takeover();
1695
+ };
1696
+
1697
+ server.ext('onPreResponse', preResponse);
1698
+
1699
+ const res = await server.inject({ method: 'GET', url: '/missing' });
1700
+ expect(res.statusCode).to.equal(200);
1701
+ expect(res.result).to.equal(404);
1702
+ });
1703
+
1704
+ it('intercepts 404 when using directory handler and file is missing', async () => {
1705
+
1706
+ const server = Hapi.server();
1707
+ await server.register(Inert);
1708
+
1709
+ const preResponse = (request) => {
1710
+
1711
+ const response = request.response;
1712
+ return { isBoom: response.isBoom };
1713
+ };
1714
+
1715
+ server.ext('onPreResponse', preResponse);
1716
+
1717
+ server.route({ method: 'GET', path: '/{path*}', handler: { directory: { path: './somewhere', listing: false, index: true } } });
1718
+
1719
+ const res = await server.inject('/missing');
1720
+ expect(res.statusCode).to.equal(200);
1721
+ expect(res.result.isBoom).to.equal(true);
1722
+ });
1723
+
1724
+ it('intercepts 404 when using file handler and file is missing', async () => {
1725
+
1726
+ const server = Hapi.server();
1727
+ await server.register(Inert);
1728
+
1729
+ const preResponse = (request) => {
1730
+
1731
+ const response = request.response;
1732
+ return { isBoom: response.isBoom };
1733
+ };
1734
+
1735
+ server.ext('onPreResponse', preResponse);
1736
+
1737
+ server.route({ method: 'GET', path: '/{path*}', handler: { file: './somewhere/something.txt' } });
1738
+
1739
+ const res = await server.inject('/missing');
1740
+ expect(res.statusCode).to.equal(200);
1741
+ expect(res.result.isBoom).to.equal(true);
1742
+ });
1743
+
1744
+ it('cleans unused file stream when response is overridden', { skip: !Common.hasLsof }, async () => {
1745
+
1746
+ const server = Hapi.server();
1747
+ await server.register(Inert);
1748
+
1749
+ const preResponse = (request) => {
1750
+
1751
+ return { something: 'else' };
1752
+ };
1753
+
1754
+ server.ext('onPreResponse', preResponse);
1755
+
1756
+ server.route({ method: 'GET', path: '/{path*}', handler: { directory: { path: './' } } });
1757
+
1758
+ const res = await server.inject('/package.json');
1759
+ expect(res.statusCode).to.equal(200);
1760
+ expect(res.result.something).to.equal('else');
1761
+
1762
+ await new Promise((resolve) => {
1763
+
1764
+ const cmd = ChildProcess.spawn('lsof', ['-p', process.pid]);
1765
+ let lsof = '';
1766
+
1767
+ cmd.stdout.on('data', (buffer) => {
1768
+
1769
+ lsof += buffer.toString();
1770
+ });
1771
+
1772
+ cmd.stdout.on('end', () => {
1773
+
1774
+ let count = 0;
1775
+ const lines = lsof.split('\n');
1776
+ for (let i = 0; i < lines.length; ++i) {
1777
+ count += !!lines[i].match(/package.json/);
1778
+ }
1779
+
1780
+ expect(count).to.equal(0);
1781
+ resolve();
1782
+ });
1783
+
1784
+ cmd.stdin.end();
1785
+ });
1786
+ });
1787
+
1788
+ it('executes multiple extensions', async () => {
1789
+
1790
+ const server = Hapi.server();
1791
+
1792
+ const preResponse1 = (request, h) => {
1793
+
1794
+ request.response.source = request.response.source + '1';
1795
+ return h.continue;
1796
+ };
1797
+
1798
+ server.ext('onPreResponse', preResponse1);
1799
+
1800
+ const preResponse2 = (request, h) => {
1801
+
1802
+ request.response.source = request.response.source + '2';
1803
+ return h.continue;
1804
+ };
1805
+
1806
+ server.ext('onPreResponse', preResponse2);
1807
+ server.route({ method: 'GET', path: '/', handler: () => '0' });
1808
+
1809
+ const res = await server.inject({ method: 'GET', url: '/' });
1810
+ expect(res.result).to.equal('012');
1811
+ });
1812
+ });
1813
+ });
1814
+
1815
+ describe('route()', () => {
1816
+
1817
+ it('emits route event', async () => {
1818
+
1819
+ const server = Hapi.server();
1820
+ const log = server.events.once('route');
1821
+
1822
+ server.route({
1823
+ method: 'GET',
1824
+ path: '/',
1825
+ handler: () => null
1826
+ });
1827
+
1828
+ const [route] = await log;
1829
+ expect(route.path).to.equal('/');
1830
+ });
1831
+
1832
+ it('overrides the default notFound handler', async () => {
1833
+
1834
+ const server = Hapi.server();
1835
+ server.route({ method: '*', path: '/{p*}', handler: () => 'found' });
1836
+ const res = await server.inject({ method: 'GET', url: '/page' });
1837
+ expect(res.statusCode).to.equal(200);
1838
+ expect(res.result).to.equal('found');
1839
+ });
1840
+
1841
+ it('responds to HEAD requests for a GET route', async () => {
1842
+
1843
+ const handler = (request, h) => {
1844
+
1845
+ return h.response('ok').etag('test').code(205);
1846
+ };
1847
+
1848
+ const server = Hapi.server();
1849
+ server.route({ method: 'GET', path: '/', handler });
1850
+ const res1 = await server.inject({ method: 'GET', url: '/' });
1851
+
1852
+ expect(res1.statusCode).to.equal(205);
1853
+ expect(res1.headers['content-type']).to.equal('text/html; charset=utf-8');
1854
+ expect(res1.headers['content-length']).to.equal(2);
1855
+ expect(res1.headers.etag).to.equal('"test"');
1856
+ expect(res1.result).to.equal('ok');
1857
+
1858
+ const res2 = await server.inject({ method: 'HEAD', url: '/' });
1859
+ expect(res2.statusCode).to.equal(res1.statusCode);
1860
+ expect(res2.headers['content-type']).to.equal(res1.headers['content-type']);
1861
+ expect(res2.headers['content-length']).to.equal(res1.headers['content-length']);
1862
+ expect(res2.headers.etag).to.equal(res1.headers.etag);
1863
+ expect(res2.result).to.not.exist();
1864
+ });
1865
+
1866
+ it('returns 404 on HEAD requests for non-GET routes', async () => {
1867
+
1868
+ const server = Hapi.server();
1869
+ server.route({ method: 'POST', path: '/', handler: () => 'ok' });
1870
+
1871
+ const res1 = await server.inject({ method: 'HEAD', url: '/' });
1872
+ expect(res1.statusCode).to.equal(404);
1873
+ expect(res1.result).to.not.exist();
1874
+
1875
+ const res2 = await server.inject({ method: 'HEAD', url: '/not-there' });
1876
+
1877
+ expect(res2.statusCode).to.equal(404);
1878
+ expect(res2.result).to.not.exist();
1879
+ });
1880
+
1881
+ it('returns 500 on HEAD requests for failed responses', async () => {
1882
+
1883
+ const preResponse = (request, h) => {
1884
+
1885
+ request.response._processors.marshal = function (response, callback) {
1886
+
1887
+ process.nextTick(callback, new Error('boom!'));
1888
+ };
1889
+
1890
+ return h.continue;
1891
+ };
1892
+
1893
+ const server = Hapi.server();
1894
+ server.route({ method: 'GET', path: '/', handler: () => 'ok' });
1895
+ server.ext('onPreResponse', preResponse);
1896
+
1897
+ const res1 = await server.inject({ method: 'GET', url: '/' });
1898
+ expect(res1.statusCode).to.equal(500);
1899
+ expect(res1.result).to.exist();
1900
+
1901
+ const res2 = await server.inject({ method: 'HEAD', url: '/' });
1902
+ expect(res2.statusCode).to.equal(res1.statusCode);
1903
+ expect(res2.headers['content-type']).to.equal(res1.headers['content-type']);
1904
+ expect(res2.headers['content-length']).to.equal(res1.headers['content-length']);
1905
+ expect(res2.result).to.not.exist();
1906
+ });
1907
+
1908
+ it('allows methods array', async () => {
1909
+
1910
+ const server = Hapi.server();
1911
+ const config = { method: ['GET', 'PUT', 'POST', 'DELETE'], path: '/', handler: (request) => request.route.method };
1912
+ server.route(config);
1913
+ expect(config.method).to.equal(['GET', 'PUT', 'POST', 'DELETE']); // Ensure config is cloned
1914
+
1915
+ const res1 = await server.inject({ method: 'HEAD', url: '/' });
1916
+ expect(res1.statusCode).to.equal(200);
1917
+
1918
+ const res2 = await server.inject({ method: 'GET', url: '/' });
1919
+ expect(res2.statusCode).to.equal(200);
1920
+ expect(res2.payload).to.equal('get');
1921
+
1922
+ const res3 = await server.inject({ method: 'PUT', url: '/' });
1923
+ expect(res3.statusCode).to.equal(200);
1924
+ expect(res3.payload).to.equal('put');
1925
+
1926
+ const res4 = await server.inject({ method: 'POST', url: '/' });
1927
+ expect(res4.statusCode).to.equal(200);
1928
+ expect(res4.payload).to.equal('post');
1929
+
1930
+ const res5 = await server.inject({ method: 'DELETE', url: '/' });
1931
+ expect(res5.statusCode).to.equal(200);
1932
+ expect(res5.payload).to.equal('delete');
1933
+ });
1934
+
1935
+ it('adds routes using single and array methods', () => {
1936
+
1937
+ const server = Hapi.server();
1938
+ server.route([
1939
+ {
1940
+ method: 'GET',
1941
+ path: '/api/products',
1942
+ handler: () => null
1943
+ },
1944
+ {
1945
+ method: 'GET',
1946
+ path: '/api/products/{id}',
1947
+ handler: () => null
1948
+ },
1949
+ {
1950
+ method: 'POST',
1951
+ path: '/api/products',
1952
+ handler: () => null
1953
+ },
1954
+ {
1955
+ method: ['PUT', 'PATCH'],
1956
+ path: '/api/products/{id}',
1957
+ handler: () => null
1958
+ },
1959
+ {
1960
+ method: 'DELETE',
1961
+ path: '/api/products/{id}',
1962
+ handler: () => null
1963
+ }
1964
+ ]);
1965
+
1966
+ const table = server.table();
1967
+ const paths = table.map((route) => {
1968
+
1969
+ const obj = {
1970
+ method: route.method,
1971
+ path: route.path
1972
+ };
1973
+ return obj;
1974
+ });
1975
+
1976
+ expect(table).to.have.length(6);
1977
+ expect(paths).to.only.include([
1978
+ { method: 'get', path: '/api/products' },
1979
+ { method: 'get', path: '/api/products/{id}' },
1980
+ { method: 'post', path: '/api/products' },
1981
+ { method: 'put', path: '/api/products/{id}' },
1982
+ { method: 'patch', path: '/api/products/{id}' },
1983
+ { method: 'delete', path: '/api/products/{id}' }
1984
+ ]);
1985
+ });
1986
+
1987
+ it('throws on methods array with id', () => {
1988
+
1989
+ const server = Hapi.server();
1990
+
1991
+ expect(() => {
1992
+
1993
+ server.route({
1994
+ method: ['GET', 'PUT', 'POST', 'DELETE'],
1995
+ path: '/',
1996
+ options: {
1997
+ id: 'abc',
1998
+ handler: (request) => request.route.method
1999
+ }
2000
+ });
2001
+ }).to.throw('Route id abc for path / conflicts with existing path /');
2002
+ });
2003
+ });
2004
+
2005
+ describe('_defaultRoutes()', () => {
2006
+
2007
+ it('returns 404 when making a request to a route that does not exist', async () => {
2008
+
2009
+ const server = Hapi.server();
2010
+ const res = await server.inject({ method: 'GET', url: '/nope' });
2011
+ expect(res.statusCode).to.equal(404);
2012
+ });
2013
+
2014
+ it('returns 400 on bad request', async () => {
2015
+
2016
+ const server = Hapi.server();
2017
+ server.route({ method: 'GET', path: '/a/{p}', handler: () => null });
2018
+ const res = await server.inject('/a/%');
2019
+ expect(res.statusCode).to.equal(400);
2020
+ });
2021
+ });
2022
+
2023
+ describe('load', () => {
2024
+
2025
+ it('measures loop delay', async () => {
2026
+
2027
+ const server = Hapi.server({ load: { sampleInterval: 4 } });
2028
+
2029
+ const handler = (request) => {
2030
+
2031
+ const start = Date.now();
2032
+ while (Date.now() - start < 5) { }
2033
+ return 'ok';
2034
+ };
2035
+
2036
+ server.route({ method: 'GET', path: '/', handler });
2037
+ await server.start();
2038
+
2039
+ await server.inject('/');
2040
+ expect(server.load.eventLoopDelay).to.be.below(7);
2041
+
2042
+ await Hoek.wait(0);
2043
+
2044
+ await server.inject('/');
2045
+ expect(server.load.eventLoopDelay).to.be.above(0);
2046
+
2047
+ await Hoek.wait(0);
2048
+
2049
+ await server.inject('/');
2050
+ expect(server.load.eventLoopDelay).to.be.above(0);
2051
+ expect(server.load.heapUsed).to.be.above(1024 * 1024);
2052
+ expect(server.load.rss).to.be.above(1024 * 1024);
2053
+ await server.stop();
2054
+ });
2055
+ });
2056
+ });
2057
+
2058
+
2059
+ internals.countConnections = function (server) {
2060
+
2061
+ return new Promise((resolve, reject) => {
2062
+
2063
+ server.listener.getConnections((err, count) => {
2064
+
2065
+ return (err ? reject(err) : resolve(count));
2066
+ });
2067
+ });
2068
+ };
2069
+
2070
+
2071
+ internals.socket = function (server, mode) {
2072
+
2073
+ const socket = new Net.Socket();
2074
+ socket.on('error', Hoek.ignore);
2075
+
2076
+ if (mode === 'tls') {
2077
+ socket.connect(server.info.port, '127.0.0.1');
2078
+ return new Promise((resolve) => TLS.connect({ socket, rejectUnauthorized: false }, () => resolve(socket)));
2079
+ }
2080
+
2081
+ return new Promise((resolve) => socket.connect(server.info.port, '127.0.0.1', () => resolve(socket)));
2082
+ };