@fastify/static 5.0.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.
Files changed (46) hide show
  1. package/.gitattributes +5 -0
  2. package/.github/dependabot.yml +13 -0
  3. package/.github/stale.yml +21 -0
  4. package/.github/workflows/ci.yml +46 -0
  5. package/LICENSE +21 -0
  6. package/README.md +440 -0
  7. package/example/public/images/sample.jpg +0 -0
  8. package/example/public/index.css +9 -0
  9. package/example/public/index.html +11 -0
  10. package/example/public/index.js +8 -0
  11. package/example/public2/test.css +4 -0
  12. package/example/public2/test.html +8 -0
  13. package/example/server-compress.js +15 -0
  14. package/example/server-dir-list.js +49 -0
  15. package/example/server.js +13 -0
  16. package/index.d.ts +98 -0
  17. package/index.js +509 -0
  18. package/lib/dirList.js +199 -0
  19. package/package.json +71 -0
  20. package/test/dir-list.test.js +675 -0
  21. package/test/static/.example +1 -0
  22. package/test/static/a .md +1 -0
  23. package/test/static/deep/path/for/test/index.html +5 -0
  24. package/test/static/deep/path/for/test/purpose/foo.html +5 -0
  25. package/test/static/foo.html +3 -0
  26. package/test/static/foobar.html +3 -0
  27. package/test/static/index.css +0 -0
  28. package/test/static/index.html +5 -0
  29. package/test/static/shallow/sample.jpg +0 -0
  30. package/test/static-pre-compressed/all-three.html +5 -0
  31. package/test/static-pre-compressed/all-three.html.br +0 -0
  32. package/test/static-pre-compressed/all-three.html.gz +0 -0
  33. package/test/static-pre-compressed/dir/index.html +5 -0
  34. package/test/static-pre-compressed/dir/index.html.br +0 -0
  35. package/test/static-pre-compressed/empty/.gitkeep +0 -0
  36. package/test/static-pre-compressed/gzip-only.html +3 -0
  37. package/test/static-pre-compressed/gzip-only.html.gz +0 -0
  38. package/test/static-pre-compressed/index.html +5 -0
  39. package/test/static-pre-compressed/index.html.br +0 -0
  40. package/test/static-pre-compressed/sample.jpg +0 -0
  41. package/test/static-pre-compressed/sample.jpg.br +0 -0
  42. package/test/static-pre-compressed/uncompressed.html +3 -0
  43. package/test/static.test.js +3482 -0
  44. package/test/static2/bar.html +3 -0
  45. package/test/static2/index.html +3 -0
  46. package/test/types/index.ts +105 -0
@@ -0,0 +1,3482 @@
1
+ 'use strict'
2
+
3
+ /* eslint n/no-deprecated-api: "off" */
4
+
5
+ const path = require('path')
6
+ const fs = require('fs')
7
+ const url = require('url')
8
+ const http = require('http')
9
+ const t = require('tap')
10
+ const simple = require('simple-get')
11
+ const Fastify = require('fastify')
12
+ const { kErrorHandler } = require('fastify/lib/symbols')
13
+ const compress = require('fastify-compress')
14
+ const concat = require('concat-stream')
15
+ const pino = require('pino')
16
+ const proxyquire = require('proxyquire')
17
+
18
+ const fastifyStatic = require('../')
19
+
20
+ const indexContent = fs
21
+ .readFileSync('./test/static/index.html')
22
+ .toString('utf8')
23
+ const index2Content = fs
24
+ .readFileSync('./test/static2/index.html')
25
+ .toString('utf8')
26
+ const foobarContent = fs
27
+ .readFileSync('./test/static/foobar.html')
28
+ .toString('utf8')
29
+ const deepContent = fs
30
+ .readFileSync('./test/static/deep/path/for/test/purpose/foo.html')
31
+ .toString('utf8')
32
+ const innerIndex = fs
33
+ .readFileSync('./test/static/deep/path/for/test/index.html')
34
+ .toString('utf8')
35
+ const allThreeBr = fs.readFileSync(
36
+ './test/static-pre-compressed/all-three.html.br'
37
+ )
38
+ const allThreeGzip = fs.readFileSync(
39
+ './test/static-pre-compressed/all-three.html.gz'
40
+ )
41
+ const gzipOnly = fs.readFileSync(
42
+ './test/static-pre-compressed/gzip-only.html.gz'
43
+ )
44
+ const indexBr = fs.readFileSync(
45
+ './test/static-pre-compressed/index.html.br'
46
+ )
47
+ const dirIndexBr = fs.readFileSync(
48
+ './test/static-pre-compressed/dir/index.html.br'
49
+ )
50
+ const uncompressedStatic = fs
51
+ .readFileSync('./test/static-pre-compressed/uncompressed.html')
52
+ .toString('utf8')
53
+ const fooContent = fs.readFileSync('./test/static/foo.html').toString('utf8')
54
+ const barContent = fs.readFileSync('./test/static2/bar.html').toString('utf8')
55
+
56
+ const GENERIC_RESPONSE_CHECK_COUNT = 5
57
+ function genericResponseChecks (t, response) {
58
+ t.ok(/text\/(html|css)/.test(response.headers['content-type']))
59
+ t.ok(response.headers.etag)
60
+ t.ok(response.headers['last-modified'])
61
+ t.ok(response.headers.date)
62
+ t.ok(response.headers['cache-control'])
63
+ }
64
+
65
+ const GENERIC_ERROR_RESPONSE_CHECK_COUNT = 2
66
+ function genericErrorResponseChecks (t, response) {
67
+ t.equal(response.headers['content-type'], 'application/json; charset=utf-8')
68
+ t.ok(response.headers.date)
69
+ }
70
+
71
+ t.test('register /static prefixAvoidTrailingSlash', t => {
72
+ t.plan(12)
73
+
74
+ const pluginOptions = {
75
+ root: path.join(__dirname, '/static'),
76
+ prefix: '/static',
77
+ prefixAvoidTrailingSlash: true
78
+ }
79
+ const fastify = Fastify()
80
+ fastify.register(fastifyStatic, pluginOptions)
81
+
82
+ t.teardown(fastify.close.bind(fastify))
83
+
84
+ fastify.listen(0, (err) => {
85
+ t.error(err)
86
+
87
+ fastify.server.unref()
88
+
89
+ t.test('/static/index.html', (t) => {
90
+ t.plan(3 + GENERIC_RESPONSE_CHECK_COUNT)
91
+ simple.concat({
92
+ method: 'GET',
93
+ url: 'http://localhost:' + fastify.server.address().port + '/static/index.html'
94
+ }, (err, response, body) => {
95
+ t.error(err)
96
+ t.equal(response.statusCode, 200)
97
+ t.equal(body.toString(), indexContent)
98
+ genericResponseChecks(t, response)
99
+ })
100
+ })
101
+
102
+ t.test('/static/index.css', (t) => {
103
+ t.plan(2 + GENERIC_RESPONSE_CHECK_COUNT)
104
+ simple.concat({
105
+ method: 'GET',
106
+ url: 'http://localhost:' + fastify.server.address().port + '/static/index.css'
107
+ }, (err, response, body) => {
108
+ t.error(err)
109
+ t.equal(response.statusCode, 200)
110
+ genericResponseChecks(t, response)
111
+ })
112
+ })
113
+
114
+ t.test('/static/', (t) => {
115
+ t.plan(3 + GENERIC_RESPONSE_CHECK_COUNT)
116
+ simple.concat({
117
+ method: 'GET',
118
+ url: 'http://localhost:' + fastify.server.address().port + '/static/'
119
+ }, (err, response, body) => {
120
+ t.error(err)
121
+ t.equal(response.statusCode, 200)
122
+ t.equal(body.toString(), indexContent)
123
+ genericResponseChecks(t, response)
124
+ })
125
+ })
126
+
127
+ t.test('/static', (t) => {
128
+ t.plan(3 + GENERIC_RESPONSE_CHECK_COUNT)
129
+ simple.concat({
130
+ method: 'GET',
131
+ url: 'http://localhost:' + fastify.server.address().port + '/static'
132
+ }, (err, response, body) => {
133
+ t.error(err)
134
+ t.equal(response.statusCode, 200)
135
+ t.equal(body.toString(), indexContent)
136
+ genericResponseChecks(t, response)
137
+ })
138
+ })
139
+
140
+ t.test('/static/deep/path/for/test/purpose/foo.html', (t) => {
141
+ t.plan(3 + GENERIC_RESPONSE_CHECK_COUNT)
142
+ simple.concat({
143
+ method: 'GET',
144
+ url: 'http://localhost:' + fastify.server.address().port + '/static/deep/path/for/test/purpose/foo.html'
145
+ }, (err, response, body) => {
146
+ t.error(err)
147
+ t.equal(response.statusCode, 200)
148
+ t.equal(body.toString(), deepContent)
149
+ genericResponseChecks(t, response)
150
+ })
151
+ })
152
+
153
+ t.test('/static/deep/path/for/test/', (t) => {
154
+ t.plan(3 + GENERIC_RESPONSE_CHECK_COUNT)
155
+ simple.concat({
156
+ method: 'GET',
157
+ url: 'http://localhost:' + fastify.server.address().port + '/static/deep/path/for/test/'
158
+ }, (err, response, body) => {
159
+ t.error(err)
160
+ t.equal(response.statusCode, 200)
161
+ t.equal(body.toString(), innerIndex)
162
+ genericResponseChecks(t, response)
163
+ })
164
+ })
165
+
166
+ t.test('/static/this/path/for/test', (t) => {
167
+ t.plan(2 + GENERIC_ERROR_RESPONSE_CHECK_COUNT)
168
+ simple.concat({
169
+ method: 'GET',
170
+ url: 'http://localhost:' + fastify.server.address().port + '/static/this/path/for/test',
171
+ followRedirect: false
172
+ }, (err, response, body) => {
173
+ t.error(err)
174
+ t.equal(response.statusCode, 404)
175
+ genericErrorResponseChecks(t, response)
176
+ })
177
+ })
178
+
179
+ t.test('/static/this/path/doesnt/exist.html', (t) => {
180
+ t.plan(2 + GENERIC_ERROR_RESPONSE_CHECK_COUNT)
181
+ simple.concat({
182
+ method: 'GET',
183
+ url: 'http://localhost:' + fastify.server.address().port + '/static/this/path/doesnt/exist.html',
184
+ followRedirect: false
185
+ }, (err, response, body) => {
186
+ t.error(err)
187
+ t.equal(response.statusCode, 404)
188
+ genericErrorResponseChecks(t, response)
189
+ })
190
+ })
191
+
192
+ t.test('/static/../index.js', (t) => {
193
+ t.plan(2 + GENERIC_ERROR_RESPONSE_CHECK_COUNT)
194
+ simple.concat({
195
+ method: 'GET',
196
+ url: 'http://localhost:' + fastify.server.address().port + '/static/../index.js',
197
+ followRedirect: false
198
+ }, (err, response, body) => {
199
+ t.error(err)
200
+ t.equal(response.statusCode, 403)
201
+ genericErrorResponseChecks(t, response)
202
+ })
203
+ })
204
+
205
+ t.test('file not exposed outside of the plugin', (t) => {
206
+ t.plan(2)
207
+ simple.concat({
208
+ method: 'GET',
209
+ // foobar is in static
210
+ url: 'http://localhost:' + fastify.server.address().port + '/foobar.html'
211
+ }, (err, response, body) => {
212
+ t.error(err)
213
+ t.equal(response.statusCode, 404)
214
+ })
215
+ })
216
+
217
+ t.test('file not exposed outside of the plugin', t => {
218
+ t.plan(3 + GENERIC_RESPONSE_CHECK_COUNT)
219
+ simple.concat({
220
+ method: 'HEAD',
221
+ url: 'http://localhost:' + fastify.server.address().port + '/static/index.html'
222
+ }, (err, response, body) => {
223
+ t.error(err)
224
+ t.equal(response.statusCode, 200)
225
+ t.equal(body.toString(), '')
226
+ genericResponseChecks(t, response)
227
+ })
228
+ })
229
+ })
230
+ })
231
+
232
+ t.test('register /static', (t) => {
233
+ t.plan(11)
234
+
235
+ const pluginOptions = {
236
+ root: path.join(__dirname, '/static'),
237
+ prefix: '/static'
238
+ }
239
+ const fastify = Fastify()
240
+ fastify.register(fastifyStatic, pluginOptions)
241
+
242
+ t.teardown(fastify.close.bind(fastify))
243
+
244
+ fastify.listen(0, (err) => {
245
+ t.error(err)
246
+
247
+ fastify.server.unref()
248
+
249
+ t.test('/static/index.html', (t) => {
250
+ t.plan(3 + GENERIC_RESPONSE_CHECK_COUNT)
251
+ simple.concat({
252
+ method: 'GET',
253
+ url: 'http://localhost:' + fastify.server.address().port + '/static/index.html'
254
+ }, (err, response, body) => {
255
+ t.error(err)
256
+ t.equal(response.statusCode, 200)
257
+ t.equal(body.toString(), indexContent)
258
+ genericResponseChecks(t, response)
259
+ })
260
+ })
261
+
262
+ t.test('/static/index.css', (t) => {
263
+ t.plan(2 + GENERIC_RESPONSE_CHECK_COUNT)
264
+ simple.concat({
265
+ method: 'GET',
266
+ url: 'http://localhost:' + fastify.server.address().port + '/static/index.css'
267
+ }, (err, response, body) => {
268
+ t.error(err)
269
+ t.equal(response.statusCode, 200)
270
+ genericResponseChecks(t, response)
271
+ })
272
+ })
273
+
274
+ t.test('/static/', (t) => {
275
+ t.plan(3 + GENERIC_RESPONSE_CHECK_COUNT)
276
+ simple.concat({
277
+ method: 'GET',
278
+ url: 'http://localhost:' + fastify.server.address().port + '/static/'
279
+ }, (err, response, body) => {
280
+ t.error(err)
281
+ t.equal(response.statusCode, 200)
282
+ t.equal(body.toString(), indexContent)
283
+ genericResponseChecks(t, response)
284
+ })
285
+ })
286
+
287
+ t.test('/static', (t) => {
288
+ t.plan(2)
289
+ simple.concat({
290
+ method: 'GET',
291
+ url: 'http://localhost:' + fastify.server.address().port + '/static'
292
+ }, (err, response, body) => {
293
+ t.error(err)
294
+ t.equal(response.statusCode, 404)
295
+ })
296
+ })
297
+
298
+ t.test('/static/deep/path/for/test/purpose/foo.html', (t) => {
299
+ t.plan(3 + GENERIC_RESPONSE_CHECK_COUNT)
300
+ simple.concat({
301
+ method: 'GET',
302
+ url: 'http://localhost:' + fastify.server.address().port + '/static/deep/path/for/test/purpose/foo.html'
303
+ }, (err, response, body) => {
304
+ t.error(err)
305
+ t.equal(response.statusCode, 200)
306
+ t.equal(body.toString(), deepContent)
307
+ genericResponseChecks(t, response)
308
+ })
309
+ })
310
+
311
+ t.test('/static/deep/path/for/test/', (t) => {
312
+ t.plan(3 + GENERIC_RESPONSE_CHECK_COUNT)
313
+ simple.concat({
314
+ method: 'GET',
315
+ url: 'http://localhost:' + fastify.server.address().port + '/static/deep/path/for/test/'
316
+ }, (err, response, body) => {
317
+ t.error(err)
318
+ t.equal(response.statusCode, 200)
319
+ t.equal(body.toString(), innerIndex)
320
+ genericResponseChecks(t, response)
321
+ })
322
+ })
323
+
324
+ t.test('/static/this/path/for/test', (t) => {
325
+ t.plan(2 + GENERIC_ERROR_RESPONSE_CHECK_COUNT)
326
+ simple.concat({
327
+ method: 'GET',
328
+ url: 'http://localhost:' + fastify.server.address().port + '/static/this/path/for/test',
329
+ followRedirect: false
330
+ }, (err, response, body) => {
331
+ t.error(err)
332
+ t.equal(response.statusCode, 404)
333
+ genericErrorResponseChecks(t, response)
334
+ })
335
+ })
336
+
337
+ t.test('/static/this/path/doesnt/exist.html', (t) => {
338
+ t.plan(2 + GENERIC_ERROR_RESPONSE_CHECK_COUNT)
339
+ simple.concat({
340
+ method: 'GET',
341
+ url: 'http://localhost:' + fastify.server.address().port + '/static/this/path/doesnt/exist.html',
342
+ followRedirect: false
343
+ }, (err, response, body) => {
344
+ t.error(err)
345
+ t.equal(response.statusCode, 404)
346
+ genericErrorResponseChecks(t, response)
347
+ })
348
+ })
349
+
350
+ t.test('/static/../index.js', (t) => {
351
+ t.plan(2 + GENERIC_ERROR_RESPONSE_CHECK_COUNT)
352
+ simple.concat({
353
+ method: 'GET',
354
+ url: 'http://localhost:' + fastify.server.address().port + '/static/../index.js',
355
+ followRedirect: false
356
+ }, (err, response, body) => {
357
+ t.error(err)
358
+ t.equal(response.statusCode, 403)
359
+ genericErrorResponseChecks(t, response)
360
+ })
361
+ })
362
+
363
+ t.test('file not exposed outside of the plugin', (t) => {
364
+ t.plan(2)
365
+ simple.concat({
366
+ method: 'GET',
367
+ // foobar is in static
368
+ url: 'http://localhost:' + fastify.server.address().port + '/foobar.html'
369
+ }, (err, response, body) => {
370
+ t.error(err)
371
+ t.equal(response.statusCode, 404)
372
+ })
373
+ })
374
+ })
375
+ })
376
+
377
+ t.test('register /static/', t => {
378
+ t.plan(12)
379
+
380
+ const pluginOptions = {
381
+ root: path.join(__dirname, '/static'),
382
+ prefix: '/static/'
383
+ }
384
+ const fastify = Fastify()
385
+ fastify.register(fastifyStatic, pluginOptions)
386
+
387
+ t.teardown(fastify.close.bind(fastify))
388
+
389
+ fastify.listen(0, (err) => {
390
+ t.error(err)
391
+
392
+ fastify.server.unref()
393
+
394
+ t.test('/static/index.html', (t) => {
395
+ t.plan(3 + GENERIC_RESPONSE_CHECK_COUNT)
396
+ simple.concat({
397
+ method: 'GET',
398
+ url: 'http://localhost:' + fastify.server.address().port + '/static/index.html'
399
+ }, (err, response, body) => {
400
+ t.error(err)
401
+ t.equal(response.statusCode, 200)
402
+ t.equal(body.toString(), indexContent)
403
+ genericResponseChecks(t, response)
404
+ })
405
+ })
406
+
407
+ t.test('/static/index.html', t => {
408
+ t.plan(3 + GENERIC_RESPONSE_CHECK_COUNT)
409
+ simple.concat({
410
+ method: 'HEAD',
411
+ url: 'http://localhost:' + fastify.server.address().port + '/static/index.html'
412
+ }, (err, response, body) => {
413
+ t.error(err)
414
+ t.equal(response.statusCode, 200)
415
+ t.equal(body.toString(), '')
416
+ genericResponseChecks(t, response)
417
+ })
418
+ })
419
+
420
+ t.test('/static/index.css', (t) => {
421
+ t.plan(2 + GENERIC_RESPONSE_CHECK_COUNT)
422
+ simple.concat({
423
+ method: 'GET',
424
+ url: 'http://localhost:' + fastify.server.address().port + '/static/index.css'
425
+ }, (err, response, body) => {
426
+ t.error(err)
427
+ t.equal(response.statusCode, 200)
428
+ genericResponseChecks(t, response)
429
+ })
430
+ })
431
+
432
+ t.test('/static/', (t) => {
433
+ t.plan(3 + GENERIC_RESPONSE_CHECK_COUNT)
434
+ simple.concat({
435
+ method: 'GET',
436
+ url: 'http://localhost:' + fastify.server.address().port + '/static/'
437
+ }, (err, response, body) => {
438
+ t.error(err)
439
+ t.equal(response.statusCode, 200)
440
+ t.equal(body.toString(), indexContent)
441
+ genericResponseChecks(t, response)
442
+ })
443
+ })
444
+
445
+ t.test('/static', (t) => {
446
+ t.plan(2)
447
+ simple.concat({
448
+ method: 'GET',
449
+ url: 'http://localhost:' + fastify.server.address().port + '/static'
450
+ }, (err, response, body) => {
451
+ t.error(err)
452
+ t.equal(response.statusCode, 404)
453
+ })
454
+ })
455
+
456
+ t.test('/static/deep/path/for/test/purpose/foo.html', (t) => {
457
+ t.plan(3 + GENERIC_RESPONSE_CHECK_COUNT)
458
+ simple.concat({
459
+ method: 'GET',
460
+ url: 'http://localhost:' + fastify.server.address().port + '/static/deep/path/for/test/purpose/foo.html'
461
+ }, (err, response, body) => {
462
+ t.error(err)
463
+ t.equal(response.statusCode, 200)
464
+ t.equal(body.toString(), deepContent)
465
+ genericResponseChecks(t, response)
466
+ })
467
+ })
468
+
469
+ t.test('/static/deep/path/for/test/', (t) => {
470
+ t.plan(3 + GENERIC_RESPONSE_CHECK_COUNT)
471
+ simple.concat({
472
+ method: 'GET',
473
+ url: 'http://localhost:' + fastify.server.address().port + '/static/deep/path/for/test/'
474
+ }, (err, response, body) => {
475
+ t.error(err)
476
+ t.equal(response.statusCode, 200)
477
+ t.equal(body.toString(), innerIndex)
478
+ genericResponseChecks(t, response)
479
+ })
480
+ })
481
+
482
+ t.test('/static/this/path/for/test', (t) => {
483
+ t.plan(2 + GENERIC_ERROR_RESPONSE_CHECK_COUNT)
484
+ simple.concat({
485
+ method: 'GET',
486
+ url: 'http://localhost:' + fastify.server.address().port + '/static/this/path/for/test',
487
+ followRedirect: false
488
+ }, (err, response, body) => {
489
+ t.error(err)
490
+ t.equal(response.statusCode, 404)
491
+ genericErrorResponseChecks(t, response)
492
+ })
493
+ })
494
+
495
+ t.test('/static/this/path/doesnt/exist.html', (t) => {
496
+ t.plan(2 + GENERIC_ERROR_RESPONSE_CHECK_COUNT)
497
+ simple.concat({
498
+ method: 'GET',
499
+ url: 'http://localhost:' + fastify.server.address().port + '/static/this/path/doesnt/exist.html',
500
+ followRedirect: false
501
+ }, (err, response, body) => {
502
+ t.error(err)
503
+ t.equal(response.statusCode, 404)
504
+ genericErrorResponseChecks(t, response)
505
+ })
506
+ })
507
+
508
+ t.test('/static/../index.js', (t) => {
509
+ t.plan(2 + GENERIC_ERROR_RESPONSE_CHECK_COUNT)
510
+ simple.concat({
511
+ method: 'GET',
512
+ url: 'http://localhost:' + fastify.server.address().port + '/static/../index.js',
513
+ followRedirect: false
514
+ }, (err, response, body) => {
515
+ t.error(err)
516
+ t.equal(response.statusCode, 403)
517
+ genericErrorResponseChecks(t, response)
518
+ })
519
+ })
520
+
521
+ t.test('304', t => {
522
+ t.plan(5 + GENERIC_RESPONSE_CHECK_COUNT)
523
+ simple.concat({
524
+ method: 'GET',
525
+ url: 'http://localhost:' + fastify.server.address().port + '/static/index.html'
526
+ }, (err, response, body) => {
527
+ t.error(err)
528
+ const etag = response.headers.etag
529
+ t.equal(response.statusCode, 200)
530
+ t.equal(body.toString(), indexContent)
531
+ genericResponseChecks(t, response)
532
+
533
+ simple.concat({
534
+ method: 'GET',
535
+ url: 'http://localhost:' + fastify.server.address().port + '/static/index.html',
536
+ headers: {
537
+ 'if-none-match': etag
538
+ }
539
+ }, (err, response, body) => {
540
+ t.error(err)
541
+ t.equal(response.statusCode, 304)
542
+ })
543
+ })
544
+ })
545
+ })
546
+ })
547
+
548
+ t.test('register /static and /static2', (t) => {
549
+ t.plan(5)
550
+
551
+ const pluginOptions = {
552
+ root: [path.join(__dirname, '/static'), path.join(__dirname, '/static2')],
553
+ prefix: '/static'
554
+ }
555
+ const fastify = Fastify()
556
+ fastify.register(fastifyStatic, pluginOptions)
557
+
558
+ fastify.get('/foo', (req, rep) => {
559
+ rep.sendFile('foo.html')
560
+ })
561
+
562
+ fastify.get('/bar', (req, rep) => {
563
+ rep.sendFile('bar.html')
564
+ })
565
+
566
+ t.teardown(fastify.close.bind(fastify))
567
+
568
+ fastify.listen(0, (err) => {
569
+ t.error(err)
570
+
571
+ fastify.server.unref()
572
+
573
+ t.test('/static/index.html', (t) => {
574
+ t.plan(4 + GENERIC_RESPONSE_CHECK_COUNT)
575
+ simple.concat({
576
+ method: 'GET',
577
+ url: 'http://localhost:' + fastify.server.address().port + '/static/index.html'
578
+ }, (err, response, body) => {
579
+ t.error(err)
580
+ t.equal(response.statusCode, 200)
581
+ t.not(body.toString(), index2Content)
582
+ t.equal(body.toString(), indexContent)
583
+ genericResponseChecks(t, response)
584
+ })
585
+ })
586
+
587
+ t.test('/static/bar.html', (t) => {
588
+ t.plan(3 + GENERIC_RESPONSE_CHECK_COUNT)
589
+ simple.concat({
590
+ method: 'GET',
591
+ url: 'http://localhost:' + fastify.server.address().port + '/static/bar.html'
592
+ }, (err, response, body) => {
593
+ t.error(err)
594
+ t.equal(response.statusCode, 200)
595
+ t.equal(body.toString(), barContent)
596
+ genericResponseChecks(t, response)
597
+ })
598
+ })
599
+
600
+ t.test('sendFile foo.html', (t) => {
601
+ t.plan(3 + GENERIC_RESPONSE_CHECK_COUNT)
602
+ simple.concat({
603
+ method: 'GET',
604
+ url: 'http://localhost:' + fastify.server.address().port + '/foo'
605
+ }, (err, response, body) => {
606
+ t.error(err)
607
+ t.equal(response.statusCode, 200)
608
+ t.equal(body.toString(), fooContent)
609
+ genericResponseChecks(t, response)
610
+ })
611
+ })
612
+
613
+ t.test('sendFile bar.html', (t) => {
614
+ t.plan(3 + GENERIC_RESPONSE_CHECK_COUNT)
615
+ simple.concat({
616
+ method: 'GET',
617
+ url: 'http://localhost:' + fastify.server.address().port + '/bar'
618
+ }, (err, response, body) => {
619
+ t.error(err)
620
+ t.equal(response.statusCode, 200)
621
+ t.equal(body.toString(), barContent)
622
+ genericResponseChecks(t, response)
623
+ })
624
+ })
625
+ })
626
+ })
627
+
628
+ t.test('payload.filename is set', (t) => {
629
+ t.plan(3)
630
+
631
+ const pluginOptions = {
632
+ root: path.join(__dirname, '/static'),
633
+ prefix: '/static/'
634
+ }
635
+ const fastify = Fastify()
636
+ let gotFilename
637
+ fastify.register(fastifyStatic, pluginOptions)
638
+ fastify.addHook('onSend', function (req, reply, payload, next) {
639
+ gotFilename = payload.filename
640
+ next()
641
+ })
642
+
643
+ t.teardown(fastify.close.bind(fastify))
644
+
645
+ fastify.listen(0, (err) => {
646
+ t.error(err)
647
+
648
+ fastify.server.unref()
649
+
650
+ t.test('/static/index.html', (t) => {
651
+ t.plan(5 + GENERIC_RESPONSE_CHECK_COUNT)
652
+ simple.concat({
653
+ method: 'GET',
654
+ url: 'http://localhost:' + fastify.server.address().port + '/static/index.html'
655
+ }, (err, response, body) => {
656
+ t.error(err)
657
+ t.equal(response.statusCode, 200)
658
+ t.equal(body.toString(), indexContent)
659
+ t.equal(typeof gotFilename, 'string')
660
+ t.equal(gotFilename, path.join(pluginOptions.root, 'index.html'))
661
+ genericResponseChecks(t, response)
662
+ })
663
+ })
664
+
665
+ t.test('/static/this/path/doesnt/exist.html', (t) => {
666
+ t.plan(3 + GENERIC_ERROR_RESPONSE_CHECK_COUNT)
667
+ simple.concat({
668
+ method: 'GET',
669
+ url: 'http://localhost:' + fastify.server.address().port + '/static/this/path/doesnt/exist.html',
670
+ followRedirect: false
671
+ }, (err, response, body) => {
672
+ t.error(err)
673
+ t.equal(response.statusCode, 404)
674
+ t.equal(typeof gotFilename, 'undefined')
675
+ genericErrorResponseChecks(t, response)
676
+ })
677
+ })
678
+ })
679
+ })
680
+
681
+ t.test('error responses can be customized with fastify.setErrorHandler()', t => {
682
+ t.plan(2)
683
+
684
+ const pluginOptions = {
685
+ root: path.join(__dirname, '/static')
686
+ }
687
+ const fastify = Fastify()
688
+
689
+ fastify.setErrorHandler(function errorHandler (err, request, reply) {
690
+ reply.type('text/plain').send(err.status + ' Custom error message')
691
+ })
692
+
693
+ fastify.register(fastifyStatic, pluginOptions)
694
+
695
+ t.teardown(fastify.close.bind(fastify))
696
+
697
+ fastify.listen(0, err => {
698
+ t.error(err)
699
+
700
+ fastify.server.unref()
701
+
702
+ t.test('/../index.js', t => {
703
+ t.plan(4)
704
+
705
+ simple.concat({
706
+ method: 'GET',
707
+ url: 'http://localhost:' + fastify.server.address().port + '/../index.js',
708
+ followRedirect: false
709
+ }, (err, response, body) => {
710
+ t.error(err)
711
+ t.equal(response.statusCode, 403)
712
+ t.equal(response.headers['content-type'], 'text/plain')
713
+ t.equal(body.toString(), '403 Custom error message')
714
+ })
715
+ })
716
+ })
717
+ })
718
+
719
+ t.test('not found responses can be customized with fastify.setNotFoundHandler()', t => {
720
+ t.plan(2)
721
+
722
+ const pluginOptions = {
723
+ root: path.join(__dirname, '/static')
724
+ }
725
+ const fastify = Fastify()
726
+
727
+ fastify.setNotFoundHandler(function notFoundHandler (request, reply) {
728
+ reply.code(404).type('text/plain').send(request.raw.url + ' Not Found')
729
+ })
730
+
731
+ fastify.register(fastifyStatic, pluginOptions)
732
+
733
+ t.teardown(fastify.close.bind(fastify))
734
+
735
+ fastify.listen(0, err => {
736
+ t.error(err)
737
+
738
+ fastify.server.unref()
739
+
740
+ t.test('/path/does/not/exist.html', t => {
741
+ t.plan(4)
742
+
743
+ simple.concat({
744
+ method: 'GET',
745
+ url: 'http://localhost:' + fastify.server.address().port + '/path/does/not/exist.html',
746
+ followRedirect: false
747
+ }, (err, response, body) => {
748
+ t.error(err)
749
+ t.equal(response.statusCode, 404)
750
+ t.equal(response.headers['content-type'], 'text/plain')
751
+ t.equal(body.toString(), '/path/does/not/exist.html Not Found')
752
+ })
753
+ })
754
+ })
755
+ })
756
+
757
+ t.test('fastify.setNotFoundHandler() is called for dotfiles when when send is configured to ignore dotfiles', t => {
758
+ t.plan(2)
759
+
760
+ const pluginOptions = {
761
+ root: path.join(__dirname, '/static'),
762
+ send: {
763
+ dotfiles: 'ignore'
764
+ }
765
+ }
766
+ const fastify = Fastify()
767
+
768
+ fastify.setNotFoundHandler(function notFoundHandler (request, reply) {
769
+ reply.code(404).type('text/plain').send(request.raw.url + ' Not Found')
770
+ })
771
+
772
+ fastify.register(fastifyStatic, pluginOptions)
773
+
774
+ t.teardown(fastify.close.bind(fastify))
775
+
776
+ fastify.listen(0, err => {
777
+ t.error(err)
778
+
779
+ fastify.server.unref()
780
+
781
+ // Requesting files with a leading dot doesn't follow the same code path as
782
+ // other 404 errors
783
+ t.test('/path/does/not/.exist.html', t => {
784
+ t.plan(4)
785
+
786
+ simple.concat({
787
+ method: 'GET',
788
+ url: 'http://localhost:' + fastify.server.address().port + '/path/does/not/.exist.html',
789
+ followRedirect: false
790
+ }, (err, response, body) => {
791
+ t.error(err)
792
+ t.equal(response.statusCode, 404)
793
+ t.equal(response.headers['content-type'], 'text/plain')
794
+ t.equal(body.toString(), '/path/does/not/.exist.html Not Found')
795
+ })
796
+ })
797
+ })
798
+ })
799
+
800
+ t.test('serving disabled', (t) => {
801
+ t.plan(3)
802
+
803
+ const pluginOptions = {
804
+ root: path.join(__dirname, '/static'),
805
+ prefix: '/static/',
806
+ serve: false
807
+ }
808
+ const fastify = Fastify()
809
+ fastify.register(fastifyStatic, pluginOptions)
810
+
811
+ fastify.get('/foo/bar', (request, reply) => {
812
+ return reply.sendFile('index.html')
813
+ })
814
+
815
+ t.teardown(fastify.close.bind(fastify))
816
+
817
+ fastify.listen(0, (err) => {
818
+ t.error(err)
819
+
820
+ fastify.server.unref()
821
+
822
+ t.test('/static/index.html not found', (t) => {
823
+ t.plan(2)
824
+ simple.concat({
825
+ method: 'GET',
826
+ url: 'http://localhost:' + fastify.server.address().port + '/static/index.html'
827
+ }, (err, response, body) => {
828
+ t.error(err)
829
+ t.equal(response.statusCode, 404)
830
+ })
831
+ })
832
+
833
+ t.test('/static/index.html via sendFile found', (t) => {
834
+ t.plan(3 + GENERIC_RESPONSE_CHECK_COUNT)
835
+ simple.concat({
836
+ method: 'GET',
837
+ url: 'http://localhost:' + fastify.server.address().port + '/foo/bar'
838
+ }, (err, response, body) => {
839
+ t.error(err)
840
+ t.equal(response.statusCode, 200)
841
+ t.equal(body.toString(), indexContent)
842
+ genericResponseChecks(t, response)
843
+ })
844
+ })
845
+ })
846
+ })
847
+
848
+ t.test('sendFile', (t) => {
849
+ t.plan(5)
850
+
851
+ const pluginOptions = {
852
+ root: path.join(__dirname, '/static'),
853
+ prefix: '/static'
854
+ }
855
+ const fastify = Fastify()
856
+ const maxAge = Math.round(Math.random() * 10) * 10000
857
+ fastify.register(fastifyStatic, pluginOptions)
858
+
859
+ fastify.get('/foo/bar', function (req, reply) {
860
+ return reply.sendFile('/index.html')
861
+ })
862
+
863
+ fastify.get('/root/path/override/test', (request, reply) => {
864
+ return reply.sendFile(
865
+ '/foo.html',
866
+ path.join(__dirname, 'static', 'deep', 'path', 'for', 'test', 'purpose')
867
+ )
868
+ })
869
+
870
+ fastify.get('/foo/bar/options/override/test', function (req, reply) {
871
+ return reply.sendFile('/index.html', { maxAge })
872
+ })
873
+
874
+ fastify.listen(0, (err) => {
875
+ t.error(err)
876
+
877
+ fastify.server.unref()
878
+
879
+ t.test('reply.sendFile()', (t) => {
880
+ t.plan(3 + GENERIC_RESPONSE_CHECK_COUNT)
881
+ simple.concat({
882
+ method: 'GET',
883
+ url: 'http://localhost:' + fastify.server.address().port + '/foo/bar',
884
+ followRedirect: false
885
+ }, (err, response, body) => {
886
+ t.error(err)
887
+ t.equal(response.statusCode, 200)
888
+ t.equal(body.toString(), indexContent)
889
+ genericResponseChecks(t, response)
890
+ })
891
+ })
892
+
893
+ t.test('reply.sendFile() with rootPath', (t) => {
894
+ t.plan(3 + GENERIC_RESPONSE_CHECK_COUNT)
895
+ simple.concat({
896
+ method: 'GET',
897
+ url: 'http://localhost:' + fastify.server.address().port + '/root/path/override/test',
898
+ followRedirect: false
899
+ }, (err, response, body) => {
900
+ t.error(err)
901
+ t.equal(response.statusCode, 200)
902
+ t.equal(body.toString(), deepContent)
903
+ genericResponseChecks(t, response)
904
+ })
905
+ })
906
+
907
+ t.test('reply.sendFile() again without root path', (t) => {
908
+ t.plan(3 + GENERIC_RESPONSE_CHECK_COUNT)
909
+ simple.concat({
910
+ method: 'GET',
911
+ url: 'http://localhost:' + fastify.server.address().port + '/foo/bar',
912
+ followRedirect: false
913
+ }, (err, response, body) => {
914
+ t.error(err)
915
+ t.equal(response.statusCode, 200)
916
+ t.equal(body.toString(), indexContent)
917
+ genericResponseChecks(t, response)
918
+ })
919
+ })
920
+
921
+ t.test('reply.sendFile() with options', (t) => {
922
+ t.plan(4 + GENERIC_RESPONSE_CHECK_COUNT)
923
+ simple.concat({
924
+ method: 'GET',
925
+ url: 'http://localhost:' + fastify.server.address().port + '/foo/bar/options/override/test',
926
+ followRedirect: false
927
+ }, (err, response, body) => {
928
+ t.error(err)
929
+ t.equal(response.statusCode, 200)
930
+ t.equal(response.headers['cache-control'], `public, max-age=${maxAge / 1000}`)
931
+ t.equal(body.toString(), indexContent)
932
+ genericResponseChecks(t, response)
933
+ })
934
+ })
935
+ })
936
+ })
937
+
938
+ t.test('sendFile disabled', (t) => {
939
+ t.plan(2)
940
+
941
+ const pluginOptions = {
942
+ root: path.join(__dirname, '/static'),
943
+ prefix: '/static',
944
+ decorateReply: false
945
+ }
946
+ const fastify = Fastify()
947
+ fastify.register(fastifyStatic, pluginOptions)
948
+
949
+ fastify.get('/foo/bar', function (req, reply) {
950
+ if (typeof reply.sendFile === 'undefined') {
951
+ reply.send('pass')
952
+ } else {
953
+ reply.send('fail')
954
+ }
955
+ })
956
+
957
+ fastify.listen(0, (err) => {
958
+ t.error(err)
959
+
960
+ fastify.server.unref()
961
+
962
+ t.test('reply.sendFile undefined', (t) => {
963
+ t.plan(3)
964
+ simple.concat({
965
+ method: 'GET',
966
+ url: 'http://localhost:' + fastify.server.address().port + '/foo/bar',
967
+ followRedirect: false
968
+ }, (err, response, body) => {
969
+ t.error(err)
970
+ t.equal(response.statusCode, 200)
971
+ t.equal(body.toString(), 'pass')
972
+ })
973
+ })
974
+ })
975
+ })
976
+
977
+ t.test('allowedPath option', (t) => {
978
+ t.plan(3)
979
+
980
+ const pluginOptions = {
981
+ root: path.join(__dirname, '/static'),
982
+ allowedPath: (pathName) => pathName !== '/foobar.html'
983
+ }
984
+ const fastify = Fastify()
985
+ fastify.register(fastifyStatic, pluginOptions)
986
+ fastify.listen(0, (err) => {
987
+ t.error(err)
988
+
989
+ fastify.server.unref()
990
+
991
+ t.test('/foobar.html not found', (t) => {
992
+ t.plan(2 + GENERIC_ERROR_RESPONSE_CHECK_COUNT)
993
+ simple.concat({
994
+ method: 'GET',
995
+ url: 'http://localhost:' + fastify.server.address().port + '/foobar.html',
996
+ followRedirect: false
997
+ }, (err, response, body) => {
998
+ t.error(err)
999
+ t.equal(response.statusCode, 404)
1000
+ genericErrorResponseChecks(t, response)
1001
+ })
1002
+ })
1003
+
1004
+ t.test('/index.css found', (t) => {
1005
+ t.plan(2)
1006
+ simple.concat({
1007
+ method: 'GET',
1008
+ url: 'http://localhost:' + fastify.server.address().port + '/index.css',
1009
+ followRedirect: false
1010
+ }, (err, response, body) => {
1011
+ t.error(err)
1012
+ t.equal(response.statusCode, 200)
1013
+ })
1014
+ })
1015
+ })
1016
+ })
1017
+
1018
+ t.test('download', (t) => {
1019
+ t.plan(7)
1020
+
1021
+ const pluginOptions = {
1022
+ root: path.join(__dirname, '/static'),
1023
+ prefix: '/static'
1024
+ }
1025
+ const fastify = Fastify()
1026
+ fastify.register(fastifyStatic, pluginOptions)
1027
+
1028
+ fastify.get('/foo/bar', function (req, reply) {
1029
+ return reply.download('/index.html')
1030
+ })
1031
+
1032
+ fastify.get('/foo/bar/change', function (req, reply) {
1033
+ return reply.download('/index.html', 'hello-world.html')
1034
+ })
1035
+
1036
+ fastify.get('/foo/bar/override', function (req, reply) {
1037
+ return reply.download('/index.html', 'hello-world.html', {
1038
+ maxAge: '2 hours',
1039
+ immutable: true
1040
+ })
1041
+ })
1042
+
1043
+ fastify.get('/foo/bar/override/2', function (req, reply) {
1044
+ return reply.download('/index.html', { acceptRanges: false })
1045
+ })
1046
+
1047
+ fastify.get('/root/path/override/test', (request, reply) => {
1048
+ return reply.download('/foo.html', {
1049
+ root: path.join(
1050
+ __dirname,
1051
+ 'static',
1052
+ 'deep',
1053
+ 'path',
1054
+ 'for',
1055
+ 'test',
1056
+ 'purpose'
1057
+ )
1058
+ })
1059
+ })
1060
+
1061
+ fastify.get('/root/path/override/test/change', (request, reply) => {
1062
+ return reply.download('/foo.html', 'hello-world.html', {
1063
+ root: path.join(
1064
+ __dirname,
1065
+ 'static',
1066
+ 'deep',
1067
+ 'path',
1068
+ 'for',
1069
+ 'test',
1070
+ 'purpose'
1071
+ )
1072
+ })
1073
+ })
1074
+
1075
+ fastify.listen(0, (err) => {
1076
+ t.error(err)
1077
+
1078
+ fastify.server.unref()
1079
+
1080
+ t.test('reply.download()', (t) => {
1081
+ t.plan(4 + GENERIC_RESPONSE_CHECK_COUNT)
1082
+ simple.concat({
1083
+ method: 'GET',
1084
+ url: 'http://localhost:' + fastify.server.address().port + '/foo/bar',
1085
+ followRedirect: false
1086
+ }, (err, response, body) => {
1087
+ t.error(err)
1088
+ t.equal(response.statusCode, 200)
1089
+ t.equal(response.headers['content-disposition'], 'attachment; filename="index.html"')
1090
+ t.equal(body.toString(), indexContent)
1091
+ genericResponseChecks(t, response)
1092
+ })
1093
+ })
1094
+
1095
+ t.test('reply.download() with fileName', t => {
1096
+ t.plan(4 + GENERIC_RESPONSE_CHECK_COUNT)
1097
+ simple.concat({
1098
+ method: 'GET',
1099
+ url: 'http://localhost:' + fastify.server.address().port + '/foo/bar/change',
1100
+ followRedirect: false
1101
+ }, (err, response, body) => {
1102
+ t.error(err)
1103
+ t.equal(response.statusCode, 200)
1104
+ t.equal(response.headers['content-disposition'], 'attachment; filename="hello-world.html"')
1105
+ t.equal(body.toString(), indexContent)
1106
+ genericResponseChecks(t, response)
1107
+ })
1108
+ })
1109
+
1110
+ t.test('reply.download() with fileName', (t) => {
1111
+ t.plan(4 + GENERIC_RESPONSE_CHECK_COUNT)
1112
+ simple.concat({
1113
+ method: 'GET',
1114
+ url: 'http://localhost:' + fastify.server.address().port + '/root/path/override/test',
1115
+ followRedirect: false
1116
+ }, (err, response, body) => {
1117
+ t.error(err)
1118
+ t.equal(response.statusCode, 200)
1119
+ t.equal(response.headers['content-disposition'], 'attachment; filename="foo.html"')
1120
+ t.equal(body.toString(), deepContent)
1121
+ genericResponseChecks(t, response)
1122
+ })
1123
+ })
1124
+
1125
+ t.test('reply.download() with custom opts', (t) => {
1126
+ t.plan(5 + GENERIC_RESPONSE_CHECK_COUNT)
1127
+ simple.concat({
1128
+ method: 'GET',
1129
+ url: 'http://localhost:' + fastify.server.address().port + '/foo/bar/override',
1130
+ followRedirect: false
1131
+ }, (err, response, body) => {
1132
+ t.error(err)
1133
+ t.equal(response.statusCode, 200)
1134
+ t.equal(response.headers['content-disposition'], 'attachment; filename="hello-world.html"')
1135
+ t.equal(response.headers['cache-control'], 'public, max-age=7200, immutable')
1136
+ t.equal(body.toString(), indexContent)
1137
+ genericResponseChecks(t, response)
1138
+ })
1139
+ })
1140
+
1141
+ t.test('reply.download() with custom opts (2)', (t) => {
1142
+ t.plan(5 + GENERIC_RESPONSE_CHECK_COUNT)
1143
+ simple.concat({
1144
+ method: 'GET',
1145
+ url: 'http://localhost:' + fastify.server.address().port + '/foo/bar/override/2',
1146
+ followRedirect: false
1147
+ }, (err, response, body) => {
1148
+ t.error(err)
1149
+ t.equal(response.statusCode, 200)
1150
+ t.equal(response.headers['content-disposition'], 'attachment; filename="index.html"')
1151
+ t.equal(response.headers['accept-ranges'], undefined)
1152
+ t.equal(body.toString(), indexContent)
1153
+ genericResponseChecks(t, response)
1154
+ })
1155
+ })
1156
+
1157
+ t.test('reply.download() with rootPath and fileName', (t) => {
1158
+ t.plan(4 + GENERIC_RESPONSE_CHECK_COUNT)
1159
+ simple.concat({
1160
+ method: 'GET',
1161
+ url: 'http://localhost:' + fastify.server.address().port + '/root/path/override/test/change',
1162
+ followRedirect: false
1163
+ }, (err, response, body) => {
1164
+ t.error(err)
1165
+ t.equal(response.statusCode, 200)
1166
+ t.equal(response.headers['content-disposition'], 'attachment; filename="hello-world.html"')
1167
+ t.equal(body.toString(), deepContent)
1168
+ genericResponseChecks(t, response)
1169
+ })
1170
+ })
1171
+ })
1172
+ })
1173
+
1174
+ t.test('sendFile disabled', (t) => {
1175
+ t.plan(2)
1176
+
1177
+ const pluginOptions = {
1178
+ root: path.join(__dirname, '/static'),
1179
+ prefix: '/static',
1180
+ decorateReply: false
1181
+ }
1182
+ const fastify = Fastify()
1183
+ fastify.register(fastifyStatic, pluginOptions)
1184
+
1185
+ fastify.get('/foo/bar', function (req, reply) {
1186
+ if (typeof reply.sendFile === 'undefined') {
1187
+ reply.send('pass')
1188
+ } else {
1189
+ reply.send('fail')
1190
+ }
1191
+ })
1192
+
1193
+ fastify.listen(0, (err) => {
1194
+ t.error(err)
1195
+
1196
+ fastify.server.unref()
1197
+
1198
+ t.test('reply.sendFile undefined', (t) => {
1199
+ t.plan(3)
1200
+ simple.concat({
1201
+ method: 'GET',
1202
+ url: 'http://localhost:' + fastify.server.address().port + '/foo/bar',
1203
+ followRedirect: false
1204
+ }, (err, response, body) => {
1205
+ t.error(err)
1206
+ t.equal(response.statusCode, 200)
1207
+ t.equal(body.toString(), 'pass')
1208
+ })
1209
+ })
1210
+ })
1211
+ })
1212
+
1213
+ t.test('download disabled', (t) => {
1214
+ t.plan(3)
1215
+
1216
+ const pluginOptions = {
1217
+ root: path.join(__dirname, '/static'),
1218
+ prefix: '/static',
1219
+ decorateReply: false
1220
+ }
1221
+ const fastify = Fastify()
1222
+ fastify.register(fastifyStatic, pluginOptions)
1223
+
1224
+ fastify.get('/foo/bar', function (req, reply) {
1225
+ if (typeof reply.download === 'undefined') {
1226
+ t.equal(reply.download, undefined)
1227
+ reply.send('pass')
1228
+ } else {
1229
+ reply.send('fail')
1230
+ }
1231
+ })
1232
+
1233
+ fastify.listen(0, (err) => {
1234
+ t.error(err)
1235
+
1236
+ fastify.server.unref()
1237
+
1238
+ t.test('reply.sendFile undefined', (t) => {
1239
+ t.plan(3)
1240
+ simple.concat({
1241
+ method: 'GET',
1242
+ url: 'http://localhost:' + fastify.server.address().port + '/foo/bar',
1243
+ followRedirect: false
1244
+ }, (err, response, body) => {
1245
+ t.error(err)
1246
+ t.equal(response.statusCode, 200)
1247
+ t.equal(body.toString(), 'pass')
1248
+ })
1249
+ })
1250
+ })
1251
+ })
1252
+
1253
+ t.test('prefix default', (t) => {
1254
+ t.plan(1)
1255
+ const pluginOptions = { root: path.join(__dirname, 'static') }
1256
+ const fastify = Fastify({ logger: false })
1257
+ t.doesNotThrow(() => fastify.register(fastifyStatic, pluginOptions))
1258
+ })
1259
+
1260
+ t.test('root not found warning', (t) => {
1261
+ t.plan(2)
1262
+ const rootPath = path.join(__dirname, 'does-not-exist')
1263
+ const pluginOptions = { root: rootPath }
1264
+ const destination = concat((data) => {
1265
+ t.equal(JSON.parse(data).msg, `"root" path "${rootPath}" must exist`)
1266
+ })
1267
+ const logger = pino(
1268
+ {
1269
+ level: 'warn'
1270
+ },
1271
+ destination
1272
+ )
1273
+ const fastify = Fastify({ logger })
1274
+ fastify.register(fastifyStatic, pluginOptions)
1275
+ fastify.listen(0, (err) => {
1276
+ t.error(err)
1277
+ fastify.server.unref()
1278
+ destination.end()
1279
+ })
1280
+ })
1281
+
1282
+ t.test('send options', (t) => {
1283
+ t.plan(11)
1284
+ const pluginOptions = {
1285
+ root: path.join(__dirname, '/static'),
1286
+ acceptRanges: 'acceptRanges',
1287
+ cacheControl: 'cacheControl',
1288
+ dotfiles: 'dotfiles',
1289
+ etag: 'etag',
1290
+ extensions: 'extensions',
1291
+ immutable: 'immutable',
1292
+ index: 'index',
1293
+ lastModified: 'lastModified',
1294
+ maxAge: 'maxAge'
1295
+ }
1296
+ const fastify = Fastify({ logger: false })
1297
+ const fastifyStatic = require('proxyquire')('../', {
1298
+ send: function sendStub (req, pathName, options) {
1299
+ t.equal(pathName, '/index.html')
1300
+ t.equal(options.root, path.join(__dirname, '/static'))
1301
+ t.equal(options.acceptRanges, 'acceptRanges')
1302
+ t.equal(options.cacheControl, 'cacheControl')
1303
+ t.equal(options.dotfiles, 'dotfiles')
1304
+ t.equal(options.etag, 'etag')
1305
+ t.equal(options.extensions, 'extensions')
1306
+ t.equal(options.immutable, 'immutable')
1307
+ t.equal(options.index, 'index')
1308
+ t.equal(options.lastModified, 'lastModified')
1309
+ t.equal(options.maxAge, 'maxAge')
1310
+ return { on: () => { }, pipe: () => { } }
1311
+ }
1312
+ })
1313
+ fastify.register(fastifyStatic, pluginOptions)
1314
+ fastify.inject({ url: '/index.html' })
1315
+ })
1316
+
1317
+ t.test('setHeaders option', (t) => {
1318
+ t.plan(6 + GENERIC_RESPONSE_CHECK_COUNT)
1319
+
1320
+ const pluginOptions = {
1321
+ root: path.join(__dirname, 'static'),
1322
+ setHeaders: function (res, pathName) {
1323
+ t.equal(pathName, path.join(__dirname, 'static/index.html'))
1324
+ res.setHeader('X-Test-Header', 'test')
1325
+ }
1326
+ }
1327
+ const fastify = Fastify()
1328
+ fastify.register(fastifyStatic, pluginOptions)
1329
+
1330
+ t.teardown(fastify.close.bind(fastify))
1331
+
1332
+ fastify.listen(0, (err) => {
1333
+ t.error(err)
1334
+
1335
+ fastify.server.unref()
1336
+
1337
+ simple.concat({
1338
+ method: 'GET',
1339
+ url: 'http://localhost:' + fastify.server.address().port + '/index.html',
1340
+ followRedirect: false
1341
+ }, (err, response, body) => {
1342
+ t.error(err)
1343
+ t.equal(response.statusCode, 200)
1344
+ t.equal(response.headers['x-test-header'], 'test')
1345
+ t.equal(body.toString(), indexContent)
1346
+ genericResponseChecks(t, response)
1347
+ })
1348
+ })
1349
+ })
1350
+
1351
+ t.test('maxAge option', (t) => {
1352
+ t.plan(5 + GENERIC_RESPONSE_CHECK_COUNT)
1353
+
1354
+ const pluginOptions = {
1355
+ root: path.join(__dirname, 'static'),
1356
+ maxAge: 3600000
1357
+ }
1358
+ const fastify = Fastify()
1359
+ fastify.register(fastifyStatic, pluginOptions)
1360
+
1361
+ t.teardown(fastify.close.bind(fastify))
1362
+
1363
+ fastify.listen(0, (err) => {
1364
+ t.error(err)
1365
+
1366
+ fastify.server.unref()
1367
+
1368
+ simple.concat({
1369
+ method: 'GET',
1370
+ url: 'http://localhost:' + fastify.server.address().port + '/index.html',
1371
+ followRedirect: false
1372
+ }, (err, response, body) => {
1373
+ t.error(err)
1374
+ t.equal(response.statusCode, 200)
1375
+ t.equal(response.headers['cache-control'], 'public, max-age=3600')
1376
+ t.equal(body.toString(), indexContent)
1377
+ genericResponseChecks(t, response)
1378
+ })
1379
+ })
1380
+ })
1381
+
1382
+ t.test('errors', (t) => {
1383
+ t.plan(11)
1384
+
1385
+ t.test('no root', (t) => {
1386
+ t.plan(1)
1387
+ const pluginOptions = {}
1388
+ const fastify = Fastify({ logger: false })
1389
+ fastify.register(fastifyStatic, pluginOptions).ready((err) => {
1390
+ t.equal(err.constructor, Error)
1391
+ })
1392
+ })
1393
+
1394
+ t.test('root is not a string', (t) => {
1395
+ t.plan(1)
1396
+ const pluginOptions = { root: 42 }
1397
+ const fastify = Fastify({ logger: false })
1398
+ fastify.register(fastifyStatic, pluginOptions).ready((err) => {
1399
+ t.equal(err.constructor, Error)
1400
+ })
1401
+ })
1402
+
1403
+ t.test('root is not an absolute path', (t) => {
1404
+ t.plan(1)
1405
+ const pluginOptions = { root: './my/path' }
1406
+ const fastify = Fastify({ logger: false })
1407
+ fastify.register(fastifyStatic, pluginOptions).ready((err) => {
1408
+ t.equal(err.constructor, Error)
1409
+ })
1410
+ })
1411
+
1412
+ t.test('root is not a directory', (t) => {
1413
+ t.plan(1)
1414
+ const pluginOptions = { root: __filename }
1415
+ const fastify = Fastify({ logger: false })
1416
+ fastify.register(fastifyStatic, pluginOptions).ready((err) => {
1417
+ t.equal(err.constructor, Error)
1418
+ })
1419
+ })
1420
+
1421
+ t.test('root is an empty array', (t) => {
1422
+ t.plan(1)
1423
+ const pluginOptions = { root: [] }
1424
+ const fastify = Fastify({ logger: false })
1425
+ fastify.register(fastifyStatic, pluginOptions).ready((err) => {
1426
+ t.equal(err.constructor, Error)
1427
+ })
1428
+ })
1429
+
1430
+ t.test('root array does not contain strings', (t) => {
1431
+ t.plan(1)
1432
+ const pluginOptions = { root: [1] }
1433
+ const fastify = Fastify({ logger: false })
1434
+ fastify.register(fastifyStatic, pluginOptions).ready((err) => {
1435
+ t.equal(err.constructor, Error)
1436
+ })
1437
+ })
1438
+
1439
+ t.test('root array does not contain an absolute path', (t) => {
1440
+ t.plan(1)
1441
+ const pluginOptions = { root: ['./my/path'] }
1442
+ const fastify = Fastify({ logger: false })
1443
+ fastify.register(fastifyStatic, pluginOptions).ready((err) => {
1444
+ t.equal(err.constructor, Error)
1445
+ })
1446
+ })
1447
+
1448
+ t.test('root array path is not a directory', (t) => {
1449
+ t.plan(1)
1450
+ const pluginOptions = { root: [__filename] }
1451
+ const fastify = Fastify({ logger: false })
1452
+ fastify.register(fastifyStatic, pluginOptions).ready((err) => {
1453
+ t.equal(err.constructor, Error)
1454
+ })
1455
+ })
1456
+
1457
+ t.test('all root array paths must be valid', (t) => {
1458
+ t.plan(1)
1459
+ const pluginOptions = { root: [path.join(__dirname, '/static'), 1] }
1460
+ const fastify = Fastify({ logger: false })
1461
+ fastify.register(fastifyStatic, pluginOptions).ready((err) => {
1462
+ t.equal(err.constructor, Error)
1463
+ })
1464
+ })
1465
+
1466
+ t.test('duplicate root paths are not allowed', (t) => {
1467
+ t.plan(1)
1468
+ const pluginOptions = {
1469
+ root: [path.join(__dirname, '/static'), path.join(__dirname, '/static')]
1470
+ }
1471
+ const fastify = Fastify({ logger: false })
1472
+ fastify.register(fastifyStatic, pluginOptions).ready((err) => {
1473
+ t.equal(err.constructor, Error)
1474
+ })
1475
+ })
1476
+
1477
+ t.test('setHeaders is not a function', (t) => {
1478
+ t.plan(1)
1479
+ const pluginOptions = { root: __dirname, setHeaders: 'headers' }
1480
+ const fastify = Fastify({ logger: false })
1481
+ fastify.register(fastifyStatic, pluginOptions).ready((err) => {
1482
+ t.equal(err.constructor, TypeError)
1483
+ })
1484
+ })
1485
+ })
1486
+
1487
+ t.test('register no prefix', (t) => {
1488
+ t.plan(8)
1489
+
1490
+ const pluginOptions = {
1491
+ root: path.join(__dirname, '/static')
1492
+ }
1493
+ const fastify = Fastify()
1494
+ fastify.register(fastifyStatic, pluginOptions)
1495
+
1496
+ fastify.get('/', (request, reply) => {
1497
+ reply.send({ hello: 'world' })
1498
+ })
1499
+
1500
+ t.teardown(fastify.close.bind(fastify))
1501
+
1502
+ fastify.listen(0, (err) => {
1503
+ t.error(err)
1504
+
1505
+ fastify.server.unref()
1506
+
1507
+ t.test('/index.html', (t) => {
1508
+ t.plan(3 + GENERIC_RESPONSE_CHECK_COUNT)
1509
+ simple.concat({
1510
+ method: 'GET',
1511
+ url: 'http://localhost:' + fastify.server.address().port + '/index.html'
1512
+ }, (err, response, body) => {
1513
+ t.error(err)
1514
+ t.equal(response.statusCode, 200)
1515
+ t.equal(body.toString(), indexContent)
1516
+ genericResponseChecks(t, response)
1517
+ })
1518
+ })
1519
+
1520
+ t.test('/index.css', (t) => {
1521
+ t.plan(2 + GENERIC_RESPONSE_CHECK_COUNT)
1522
+ simple.concat({
1523
+ method: 'GET',
1524
+ url: 'http://localhost:' + fastify.server.address().port + '/index.css'
1525
+ }, (err, response, body) => {
1526
+ t.error(err)
1527
+ t.equal(response.statusCode, 200)
1528
+ genericResponseChecks(t, response)
1529
+ })
1530
+ })
1531
+
1532
+ t.test('/', (t) => {
1533
+ t.plan(3)
1534
+ simple.concat({
1535
+ method: 'GET',
1536
+ url: 'http://localhost:' + fastify.server.address().port
1537
+ }, (err, response, body) => {
1538
+ t.error(err)
1539
+ t.equal(response.statusCode, 200)
1540
+ t.same(JSON.parse(body), { hello: 'world' })
1541
+ })
1542
+ })
1543
+
1544
+ t.test('/deep/path/for/test/purpose/foo.html', (t) => {
1545
+ t.plan(3 + GENERIC_RESPONSE_CHECK_COUNT)
1546
+ simple.concat({
1547
+ method: 'GET',
1548
+ url: 'http://localhost:' + fastify.server.address().port + '/deep/path/for/test/purpose/foo.html'
1549
+ }, (err, response, body) => {
1550
+ t.error(err)
1551
+ t.equal(response.statusCode, 200)
1552
+ t.equal(body.toString(), deepContent)
1553
+ genericResponseChecks(t, response)
1554
+ })
1555
+ })
1556
+
1557
+ t.test('/deep/path/for/test/', (t) => {
1558
+ t.plan(3 + GENERIC_RESPONSE_CHECK_COUNT)
1559
+ simple.concat({
1560
+ method: 'GET',
1561
+ url: 'http://localhost:' + fastify.server.address().port + '/deep/path/for/test/'
1562
+ }, (err, response, body) => {
1563
+ t.error(err)
1564
+ t.equal(response.statusCode, 200)
1565
+ t.equal(body.toString(), innerIndex)
1566
+ genericResponseChecks(t, response)
1567
+ })
1568
+ })
1569
+
1570
+ t.test('/this/path/doesnt/exist.html', (t) => {
1571
+ t.plan(2 + GENERIC_ERROR_RESPONSE_CHECK_COUNT)
1572
+ simple.concat({
1573
+ method: 'GET',
1574
+ url: 'http://localhost:' + fastify.server.address().port + '/this/path/doesnt/exist.html',
1575
+ followRedirect: false
1576
+ }, (err, response, body) => {
1577
+ t.error(err)
1578
+ t.equal(response.statusCode, 404)
1579
+ genericErrorResponseChecks(t, response)
1580
+ })
1581
+ })
1582
+
1583
+ t.test('/../index.js', (t) => {
1584
+ t.plan(2 + GENERIC_ERROR_RESPONSE_CHECK_COUNT)
1585
+ simple.concat({
1586
+ method: 'GET',
1587
+ url: 'http://localhost:' + fastify.server.address().port + '/../index.js',
1588
+ followRedirect: false
1589
+ }, (err, response, body) => {
1590
+ t.error(err)
1591
+ t.equal(response.statusCode, 403)
1592
+ genericErrorResponseChecks(t, response)
1593
+ })
1594
+ })
1595
+ })
1596
+ })
1597
+
1598
+ t.test('with fastify-compress', t => {
1599
+ t.plan(3)
1600
+
1601
+ const pluginOptions = {
1602
+ root: path.join(__dirname, '/static')
1603
+ }
1604
+ const fastify = Fastify()
1605
+ fastify.register(compress, { threshold: 0 })
1606
+ fastify.register(fastifyStatic, pluginOptions)
1607
+
1608
+ t.teardown(fastify.close.bind(fastify))
1609
+
1610
+ fastify.listen(0, err => {
1611
+ t.error(err)
1612
+
1613
+ t.test('deflate', function (t) {
1614
+ t.plan(3 + GENERIC_RESPONSE_CHECK_COUNT)
1615
+
1616
+ simple.concat({
1617
+ method: 'GET',
1618
+ url: 'http://localhost:' + fastify.server.address().port + '/index.html',
1619
+ headers: {
1620
+ 'accept-encoding': ['deflate']
1621
+ }
1622
+ }, (err, response, body) => {
1623
+ t.error(err)
1624
+ t.equal(response.statusCode, 200)
1625
+ t.equal(response.headers['content-encoding'], 'deflate')
1626
+ genericResponseChecks(t, response)
1627
+ })
1628
+ })
1629
+
1630
+ t.test('gzip', function (t) {
1631
+ t.plan(3 + GENERIC_RESPONSE_CHECK_COUNT)
1632
+
1633
+ simple.concat({
1634
+ method: 'GET',
1635
+ url: 'http://localhost:' + fastify.server.address().port + '/index.html'
1636
+ }, (err, response, body) => {
1637
+ t.error(err)
1638
+ t.equal(response.statusCode, 200)
1639
+ t.equal(response.headers['content-encoding'], 'gzip')
1640
+ genericResponseChecks(t, response)
1641
+ })
1642
+ })
1643
+ })
1644
+ })
1645
+ t.test('register /static/ with schemaHide true', t => {
1646
+ t.plan(4)
1647
+
1648
+ const pluginOptions = {
1649
+ root: path.join(__dirname, '/static'),
1650
+ prefix: '/static/',
1651
+ schemaHide: true
1652
+ }
1653
+
1654
+ const fastify = Fastify()
1655
+
1656
+ fastify.addHook('onRoute', function (routeOptions) {
1657
+ t.same(routeOptions.schema, { hide: true })
1658
+ })
1659
+
1660
+ fastify.register(fastifyStatic, pluginOptions)
1661
+
1662
+ t.teardown(fastify.close.bind(fastify))
1663
+
1664
+ fastify.listen(0, (err) => {
1665
+ t.error(err)
1666
+
1667
+ fastify.server.unref()
1668
+
1669
+ t.test('/static/index.html', (t) => {
1670
+ t.plan(3 + GENERIC_RESPONSE_CHECK_COUNT)
1671
+
1672
+ simple.concat({
1673
+ method: 'GET',
1674
+ url: 'http://localhost:' + fastify.server.address().port + '/static/index.html'
1675
+ }, (err, response, body) => {
1676
+ t.error(err)
1677
+ t.equal(response.statusCode, 200)
1678
+ t.equal(body.toString(), indexContent)
1679
+ genericResponseChecks(t, response)
1680
+ })
1681
+ })
1682
+ })
1683
+ })
1684
+
1685
+ t.test('register /static/ with schemaHide false', t => {
1686
+ t.plan(4)
1687
+
1688
+ const pluginOptions = {
1689
+ root: path.join(__dirname, '/static'),
1690
+ prefix: '/static/',
1691
+ schemaHide: false
1692
+ }
1693
+
1694
+ const fastify = Fastify()
1695
+
1696
+ fastify.addHook('onRoute', function (routeOptions) {
1697
+ t.same(routeOptions.schema, { hide: false })
1698
+ })
1699
+
1700
+ fastify.register(fastifyStatic, pluginOptions)
1701
+
1702
+ t.teardown(fastify.close.bind(fastify))
1703
+
1704
+ fastify.listen(0, (err) => {
1705
+ t.error(err)
1706
+
1707
+ fastify.server.unref()
1708
+
1709
+ t.test('/static/index.html', (t) => {
1710
+ t.plan(3 + GENERIC_RESPONSE_CHECK_COUNT)
1711
+
1712
+ simple.concat({
1713
+ method: 'GET',
1714
+ url: 'http://localhost:' + fastify.server.address().port + '/static/index.html'
1715
+ }, (err, response, body) => {
1716
+ t.error(err)
1717
+ t.equal(response.statusCode, 200)
1718
+ t.equal(body.toString(), indexContent)
1719
+ genericResponseChecks(t, response)
1720
+ })
1721
+ })
1722
+ })
1723
+ })
1724
+
1725
+ t.test('register /static/ without schemaHide', t => {
1726
+ t.plan(4)
1727
+
1728
+ const pluginOptions = {
1729
+ root: path.join(__dirname, '/static'),
1730
+ prefix: '/static/'
1731
+ }
1732
+
1733
+ const fastify = Fastify()
1734
+
1735
+ fastify.addHook('onRoute', function (routeOptions) {
1736
+ t.same(routeOptions.schema, { hide: true })
1737
+ })
1738
+
1739
+ fastify.register(fastifyStatic, pluginOptions)
1740
+
1741
+ t.teardown(fastify.close.bind(fastify))
1742
+
1743
+ fastify.listen(0, (err) => {
1744
+ t.error(err)
1745
+
1746
+ fastify.server.unref()
1747
+
1748
+ t.test('/static/index.html', (t) => {
1749
+ t.plan(3 + GENERIC_RESPONSE_CHECK_COUNT)
1750
+
1751
+ simple.concat({
1752
+ method: 'GET',
1753
+ url: 'http://localhost:' + fastify.server.address().port + '/static/index.html'
1754
+ }, (err, response, body) => {
1755
+ t.error(err)
1756
+ t.equal(response.statusCode, 200)
1757
+ t.equal(body.toString(), indexContent)
1758
+ genericResponseChecks(t, response)
1759
+ })
1760
+ })
1761
+ })
1762
+ })
1763
+
1764
+ t.test('fastify with exposeHeadRoutes', t => {
1765
+ t.plan(2)
1766
+
1767
+ const pluginOptions = {
1768
+ root: path.join(__dirname, '/static'),
1769
+ wildcard: false
1770
+ }
1771
+ const fastify = Fastify({ exposeHeadRoutes: true })
1772
+ fastify.register(fastifyStatic, pluginOptions)
1773
+
1774
+ fastify.get('/*', (request, reply) => {
1775
+ reply.send({ hello: 'world' })
1776
+ })
1777
+
1778
+ t.teardown(fastify.close.bind(fastify))
1779
+
1780
+ fastify.listen(0, err => {
1781
+ t.error(err)
1782
+
1783
+ fastify.server.unref()
1784
+
1785
+ t.test('/index.html', t => {
1786
+ t.plan(3 + GENERIC_RESPONSE_CHECK_COUNT)
1787
+ simple.concat({
1788
+ method: 'HEAD',
1789
+ url: 'http://localhost:' + fastify.server.address().port + '/index.html'
1790
+ }, (err, response, body) => {
1791
+ t.error(err)
1792
+ t.equal(response.statusCode, 200)
1793
+ t.equal(body.toString(), '')
1794
+ genericResponseChecks(t, response)
1795
+ })
1796
+ })
1797
+ })
1798
+ })
1799
+
1800
+ t.test('register with wildcard false', t => {
1801
+ t.plan(9)
1802
+
1803
+ const pluginOptions = {
1804
+ root: path.join(__dirname, '/static'),
1805
+ wildcard: false
1806
+ }
1807
+ const fastify = Fastify()
1808
+ fastify.register(fastifyStatic, pluginOptions)
1809
+
1810
+ fastify.get('/*', (request, reply) => {
1811
+ reply.send({ hello: 'world' })
1812
+ })
1813
+
1814
+ t.teardown(fastify.close.bind(fastify))
1815
+
1816
+ fastify.listen(0, (err) => {
1817
+ t.error(err)
1818
+
1819
+ fastify.server.unref()
1820
+
1821
+ t.test('/index.html', t => {
1822
+ t.plan(3 + GENERIC_RESPONSE_CHECK_COUNT)
1823
+ simple.concat({
1824
+ method: 'GET',
1825
+ url: 'http://localhost:' + fastify.server.address().port + '/index.html'
1826
+ }, (err, response, body) => {
1827
+ t.error(err)
1828
+ t.equal(response.statusCode, 200)
1829
+ t.equal(body.toString(), indexContent)
1830
+ genericResponseChecks(t, response)
1831
+ })
1832
+ })
1833
+
1834
+ t.test('/index.css', (t) => {
1835
+ t.plan(2 + GENERIC_RESPONSE_CHECK_COUNT)
1836
+ simple.concat({
1837
+ method: 'GET',
1838
+ url: 'http://localhost:' + fastify.server.address().port + '/index.css'
1839
+ }, (err, response, body) => {
1840
+ t.error(err)
1841
+ t.equal(response.statusCode, 200)
1842
+ genericResponseChecks(t, response)
1843
+ })
1844
+ })
1845
+
1846
+ t.test('/', (t) => {
1847
+ t.plan(3 + GENERIC_RESPONSE_CHECK_COUNT)
1848
+ simple.concat({
1849
+ method: 'GET',
1850
+ url: 'http://localhost:' + fastify.server.address().port
1851
+ }, (err, response, body) => {
1852
+ t.error(err)
1853
+ t.equal(response.statusCode, 200)
1854
+ t.equal(body.toString(), indexContent)
1855
+ genericResponseChecks(t, response)
1856
+ })
1857
+ })
1858
+
1859
+ t.test('/not-defined', (t) => {
1860
+ t.plan(3)
1861
+ simple.concat({
1862
+ method: 'GET',
1863
+ url: 'http://localhost:' + fastify.server.address().port + '/not-defined'
1864
+ }, (err, response, body) => {
1865
+ t.error(err)
1866
+ t.equal(response.statusCode, 200)
1867
+ t.same(JSON.parse(body), { hello: 'world' })
1868
+ })
1869
+ })
1870
+
1871
+ t.test('/deep/path/for/test/purpose/foo.html', (t) => {
1872
+ t.plan(3 + GENERIC_RESPONSE_CHECK_COUNT)
1873
+ simple.concat({
1874
+ method: 'GET',
1875
+ url: 'http://localhost:' + fastify.server.address().port + '/deep/path/for/test/purpose/foo.html'
1876
+ }, (err, response, body) => {
1877
+ t.error(err)
1878
+ t.equal(response.statusCode, 200)
1879
+ t.equal(body.toString(), deepContent)
1880
+ genericResponseChecks(t, response)
1881
+ })
1882
+ })
1883
+
1884
+ t.test('/deep/path/for/test/', (t) => {
1885
+ t.plan(3 + GENERIC_RESPONSE_CHECK_COUNT)
1886
+ simple.concat({
1887
+ method: 'GET',
1888
+ url: 'http://localhost:' + fastify.server.address().port + '/deep/path/for/test/'
1889
+ }, (err, response, body) => {
1890
+ t.error(err)
1891
+ t.equal(response.statusCode, 200)
1892
+ t.equal(body.toString(), innerIndex)
1893
+ genericResponseChecks(t, response)
1894
+ })
1895
+ })
1896
+
1897
+ t.test('/../index.js', (t) => {
1898
+ t.plan(3)
1899
+ simple.concat({
1900
+ method: 'GET',
1901
+ url: 'http://localhost:' + fastify.server.address().port + '/../index.js',
1902
+ followRedirect: false
1903
+ }, (err, response, body) => {
1904
+ t.error(err)
1905
+ t.equal(response.statusCode, 200)
1906
+ t.same(JSON.parse(body), { hello: 'world' })
1907
+ })
1908
+ })
1909
+
1910
+ t.test('/index.css', t => {
1911
+ t.plan(3 + GENERIC_RESPONSE_CHECK_COUNT)
1912
+ simple.concat({
1913
+ method: 'HEAD',
1914
+ url: 'http://localhost:' + fastify.server.address().port + '/index.css'
1915
+ }, (err, response, body) => {
1916
+ t.error(err)
1917
+ t.equal(response.statusCode, 200)
1918
+ t.equal(body.toString(), '')
1919
+ genericResponseChecks(t, response)
1920
+ })
1921
+ })
1922
+ })
1923
+ })
1924
+
1925
+ t.test('register with wildcard string', (t) => {
1926
+ t.plan(1)
1927
+
1928
+ const pluginOptions = {
1929
+ root: path.join(__dirname, '/static'),
1930
+ wildcard: '**/index.html'
1931
+ }
1932
+ const fastify = Fastify()
1933
+ fastify.register(fastifyStatic, pluginOptions)
1934
+
1935
+ fastify.get('/*', (request, reply) => {
1936
+ reply.send({ hello: 'world' })
1937
+ })
1938
+
1939
+ fastify.ready(function (err) {
1940
+ t.ok(err)
1941
+ })
1942
+ })
1943
+
1944
+ t.test('register with wildcard string on multiple root paths', (t) => {
1945
+ t.plan(1)
1946
+
1947
+ const pluginOptions = {
1948
+ root: [path.join(__dirname, '/static'), path.join(__dirname, '/static2')],
1949
+ wildcard: '**/*.js'
1950
+ }
1951
+ const fastify = Fastify()
1952
+ fastify.register(fastifyStatic, pluginOptions)
1953
+
1954
+ fastify.get('/*', (request, reply) => {
1955
+ reply.send({ hello: 'world' })
1956
+ })
1957
+
1958
+ t.teardown(fastify.close.bind(fastify))
1959
+
1960
+ fastify.listen(0, (err) => {
1961
+ t.ok(err)
1962
+
1963
+ fastify.server.unref()
1964
+ })
1965
+ })
1966
+
1967
+ t.test('register with wildcard false and alternative index', t => {
1968
+ t.plan(11)
1969
+
1970
+ const pluginOptions = {
1971
+ root: path.join(__dirname, '/static'),
1972
+ wildcard: false,
1973
+ index: ['foobar.html', 'foo.html', 'index.html']
1974
+ }
1975
+ const fastify = Fastify()
1976
+ fastify.register(fastifyStatic, pluginOptions)
1977
+
1978
+ fastify.get('/*', (request, reply) => {
1979
+ reply.send({ hello: 'world' })
1980
+ })
1981
+
1982
+ t.teardown(fastify.close.bind(fastify))
1983
+
1984
+ fastify.listen(0, (err) => {
1985
+ t.error(err)
1986
+
1987
+ fastify.server.unref()
1988
+
1989
+ t.test('/index.html', (t) => {
1990
+ t.plan(3 + GENERIC_RESPONSE_CHECK_COUNT)
1991
+ simple.concat({
1992
+ method: 'GET',
1993
+ url: 'http://localhost:' + fastify.server.address().port + '/index.html'
1994
+ }, (err, response, body) => {
1995
+ t.error(err)
1996
+ t.equal(response.statusCode, 200)
1997
+ t.equal(body.toString(), indexContent)
1998
+ genericResponseChecks(t, response)
1999
+ })
2000
+ })
2001
+
2002
+ t.test('/index.html', t => {
2003
+ t.plan(3 + GENERIC_RESPONSE_CHECK_COUNT)
2004
+ simple.concat({
2005
+ method: 'HEAD',
2006
+ url: 'http://localhost:' + fastify.server.address().port + '/index.html'
2007
+ }, (err, response, body) => {
2008
+ t.error(err)
2009
+ t.equal(response.statusCode, 200)
2010
+ t.equal(body.toString(), '')
2011
+ genericResponseChecks(t, response)
2012
+ })
2013
+ })
2014
+
2015
+ t.test('/index.css', (t) => {
2016
+ t.plan(2 + GENERIC_RESPONSE_CHECK_COUNT)
2017
+ simple.concat({
2018
+ method: 'GET',
2019
+ url: 'http://localhost:' + fastify.server.address().port + '/index.css'
2020
+ }, (err, response, body) => {
2021
+ t.error(err)
2022
+ t.equal(response.statusCode, 200)
2023
+ genericResponseChecks(t, response)
2024
+ })
2025
+ })
2026
+
2027
+ t.test('/?a=b', (t) => {
2028
+ t.plan(3 + GENERIC_RESPONSE_CHECK_COUNT)
2029
+ simple.concat({
2030
+ method: 'GET',
2031
+ url: 'http://localhost:' + fastify.server.address().port
2032
+ }, (err, response, body) => {
2033
+ t.error(err)
2034
+ t.equal(response.statusCode, 200)
2035
+ t.equal(body.toString(), foobarContent)
2036
+ genericResponseChecks(t, response)
2037
+ })
2038
+ })
2039
+
2040
+ t.test('/?a=b', t => {
2041
+ t.plan(3 + GENERIC_RESPONSE_CHECK_COUNT)
2042
+ simple.concat({
2043
+ method: 'HEAD',
2044
+ url: 'http://localhost:' + fastify.server.address().port
2045
+ }, (err, response, body) => {
2046
+ t.error(err)
2047
+ t.equal(response.statusCode, 200)
2048
+ t.equal(body.toString(), '')
2049
+ genericResponseChecks(t, response)
2050
+ })
2051
+ })
2052
+
2053
+ t.test('/not-defined', (t) => {
2054
+ t.plan(3)
2055
+ simple.concat({
2056
+ method: 'GET',
2057
+ url: 'http://localhost:' + fastify.server.address().port + '/not-defined'
2058
+ }, (err, response, body) => {
2059
+ t.error(err)
2060
+ t.equal(response.statusCode, 200)
2061
+ t.same(JSON.parse(body), { hello: 'world' })
2062
+ })
2063
+ })
2064
+
2065
+ t.test('/deep/path/for/test/purpose/', (t) => {
2066
+ t.plan(3 + GENERIC_RESPONSE_CHECK_COUNT)
2067
+ simple.concat({
2068
+ method: 'GET',
2069
+ url: 'http://localhost:' + fastify.server.address().port + '/deep/path/for/test/purpose/'
2070
+ }, (err, response, body) => {
2071
+ t.error(err)
2072
+ t.equal(response.statusCode, 200)
2073
+ t.equal(body.toString(), deepContent)
2074
+ genericResponseChecks(t, response)
2075
+ })
2076
+ })
2077
+
2078
+ t.test('/deep/path/for/test/', (t) => {
2079
+ t.plan(3 + GENERIC_RESPONSE_CHECK_COUNT)
2080
+ simple.concat({
2081
+ method: 'GET',
2082
+ url: 'http://localhost:' + fastify.server.address().port + '/deep/path/for/test/'
2083
+ }, (err, response, body) => {
2084
+ t.error(err)
2085
+ t.equal(response.statusCode, 200)
2086
+ t.equal(body.toString(), innerIndex)
2087
+ genericResponseChecks(t, response)
2088
+ })
2089
+ })
2090
+
2091
+ t.test('/deep/path/for/test/', t => {
2092
+ t.plan(3 + GENERIC_RESPONSE_CHECK_COUNT)
2093
+ simple.concat({
2094
+ method: 'HEAD',
2095
+ url: 'http://localhost:' + fastify.server.address().port + '/deep/path/for/test/'
2096
+ }, (err, response, body) => {
2097
+ t.error(err)
2098
+ t.equal(response.statusCode, 200)
2099
+ t.equal(body.toString(), '')
2100
+ genericResponseChecks(t, response)
2101
+ })
2102
+ })
2103
+
2104
+ t.test('/../index.js', (t) => {
2105
+ t.plan(3)
2106
+ simple.concat({
2107
+ method: 'GET',
2108
+ url: 'http://localhost:' + fastify.server.address().port + '/../index.js',
2109
+ followRedirect: false
2110
+ }, (err, response, body) => {
2111
+ t.error(err)
2112
+ t.equal(response.statusCode, 200)
2113
+ t.same(JSON.parse(body), { hello: 'world' })
2114
+ })
2115
+ })
2116
+ })
2117
+ })
2118
+
2119
+ t.test('register /static with wildcard false and alternative index', t => {
2120
+ t.plan(11)
2121
+
2122
+ const pluginOptions = {
2123
+ root: path.join(__dirname, '/static'),
2124
+ prefix: '/static',
2125
+ wildcard: false,
2126
+ index: ['foobar.html', 'foo.html', 'index.html']
2127
+ }
2128
+ const fastify = Fastify()
2129
+ fastify.register(fastifyStatic, pluginOptions)
2130
+
2131
+ fastify.get('/*', (request, reply) => {
2132
+ reply.send({ hello: 'world' })
2133
+ })
2134
+
2135
+ t.teardown(fastify.close.bind(fastify))
2136
+
2137
+ fastify.listen(0, (err) => {
2138
+ t.error(err)
2139
+
2140
+ fastify.server.unref()
2141
+
2142
+ t.test('/static/index.html', (t) => {
2143
+ t.plan(3 + GENERIC_RESPONSE_CHECK_COUNT)
2144
+ simple.concat({
2145
+ method: 'GET',
2146
+ url: 'http://localhost:' + fastify.server.address().port + '/static/index.html'
2147
+ }, (err, response, body) => {
2148
+ t.error(err)
2149
+ t.equal(response.statusCode, 200)
2150
+ t.equal(body.toString(), indexContent)
2151
+ genericResponseChecks(t, response)
2152
+ })
2153
+ })
2154
+
2155
+ t.test('/static/index.html', t => {
2156
+ t.plan(3 + GENERIC_RESPONSE_CHECK_COUNT)
2157
+ simple.concat({
2158
+ method: 'HEAD',
2159
+ url: 'http://localhost:' + fastify.server.address().port + '/static/index.html'
2160
+ }, (err, response, body) => {
2161
+ t.error(err)
2162
+ t.equal(response.statusCode, 200)
2163
+ t.equal(body.toString(), '')
2164
+ genericResponseChecks(t, response)
2165
+ })
2166
+ })
2167
+
2168
+ t.test('/static/index.css', (t) => {
2169
+ t.plan(2 + GENERIC_RESPONSE_CHECK_COUNT)
2170
+ simple.concat({
2171
+ method: 'GET',
2172
+ url: 'http://localhost:' + fastify.server.address().port + '/static/index.css'
2173
+ }, (err, response, body) => {
2174
+ t.error(err)
2175
+ t.equal(response.statusCode, 200)
2176
+ genericResponseChecks(t, response)
2177
+ })
2178
+ })
2179
+
2180
+ t.test('/static', (t) => {
2181
+ t.plan(2)
2182
+
2183
+ // simple-get doesn't tell us about redirects so use http.request directly
2184
+ // to verify we do not get a redirect when not requested
2185
+ const testurl = 'http://localhost:' + fastify.server.address().port + '/static'
2186
+ const req = http.request(url.parse(testurl), res => {
2187
+ t.equal(res.statusCode, 200)
2188
+ let body = ''
2189
+ res.on('data', (chunk) => {
2190
+ body += chunk.toString()
2191
+ })
2192
+ res.on('end', () => {
2193
+ t.same(JSON.parse(body.toString()), { hello: 'world' })
2194
+ })
2195
+ })
2196
+ req.on('error', (err) => console.error(err))
2197
+ req.end()
2198
+ })
2199
+
2200
+ t.test('/static/', (t) => {
2201
+ t.plan(3 + GENERIC_RESPONSE_CHECK_COUNT)
2202
+ simple.concat({
2203
+ method: 'GET',
2204
+ url: 'http://localhost:' + fastify.server.address().port + '/static/'
2205
+ }, (err, response, body) => {
2206
+ t.error(err)
2207
+ t.equal(response.statusCode, 200)
2208
+ t.equal(body.toString(), foobarContent)
2209
+ genericResponseChecks(t, response)
2210
+ })
2211
+ })
2212
+
2213
+ t.test('/static/', t => {
2214
+ t.plan(3 + GENERIC_RESPONSE_CHECK_COUNT)
2215
+ simple.concat({
2216
+ method: 'HEAD',
2217
+ url: 'http://localhost:' + fastify.server.address().port + '/static/'
2218
+ }, (err, response, body) => {
2219
+ t.error(err)
2220
+ t.equal(response.statusCode, 200)
2221
+ t.equal(body.toString(), '')
2222
+ genericResponseChecks(t, response)
2223
+ })
2224
+ })
2225
+
2226
+ t.test('/static/not-defined', (t) => {
2227
+ t.plan(3)
2228
+ simple.concat({
2229
+ method: 'GET',
2230
+ url: 'http://localhost:' + fastify.server.address().port + '/static/not-defined'
2231
+ }, (err, response, body) => {
2232
+ t.error(err)
2233
+ t.equal(response.statusCode, 200)
2234
+ t.same(JSON.parse(body), { hello: 'world' })
2235
+ })
2236
+ })
2237
+
2238
+ t.test('/static/deep/path/for/test/purpose/', (t) => {
2239
+ t.plan(3 + GENERIC_RESPONSE_CHECK_COUNT)
2240
+ simple.concat({
2241
+ method: 'GET',
2242
+ url: 'http://localhost:' + fastify.server.address().port + '/static/deep/path/for/test/purpose/'
2243
+ }, (err, response, body) => {
2244
+ t.error(err)
2245
+ t.equal(response.statusCode, 200)
2246
+ t.equal(body.toString(), deepContent)
2247
+ genericResponseChecks(t, response)
2248
+ })
2249
+ })
2250
+
2251
+ t.test('/static/deep/path/for/test/', (t) => {
2252
+ t.plan(3 + GENERIC_RESPONSE_CHECK_COUNT)
2253
+ simple.concat({
2254
+ method: 'GET',
2255
+ url: 'http://localhost:' + fastify.server.address().port + '/static/deep/path/for/test/'
2256
+ }, (err, response, body) => {
2257
+ t.error(err)
2258
+ t.equal(response.statusCode, 200)
2259
+ t.equal(body.toString(), innerIndex)
2260
+ genericResponseChecks(t, response)
2261
+ })
2262
+ })
2263
+
2264
+ t.test('/static/../index.js', (t) => {
2265
+ t.plan(3)
2266
+ simple.concat({
2267
+ method: 'GET',
2268
+ url: 'http://localhost:' + fastify.server.address().port + '/static/../index.js',
2269
+ followRedirect: false
2270
+ }, (err, response, body) => {
2271
+ t.error(err)
2272
+ t.equal(response.statusCode, 200)
2273
+ t.same(JSON.parse(body), { hello: 'world' })
2274
+ })
2275
+ })
2276
+ })
2277
+ })
2278
+
2279
+ t.test('register /static with redirect true', t => {
2280
+ t.plan(7)
2281
+
2282
+ const pluginOptions = {
2283
+ root: path.join(__dirname, '/static'),
2284
+ prefix: '/static',
2285
+ redirect: true,
2286
+ index: 'index.html'
2287
+ }
2288
+
2289
+ const fastify = Fastify()
2290
+
2291
+ fastify.register(fastifyStatic, pluginOptions)
2292
+
2293
+ t.teardown(fastify.close.bind(fastify))
2294
+
2295
+ fastify.listen(3001, (err) => {
2296
+ t.error(err)
2297
+
2298
+ fastify.server.unref()
2299
+
2300
+ t.test('/static?a=b', (t) => {
2301
+ t.plan(5 + GENERIC_RESPONSE_CHECK_COUNT)
2302
+
2303
+ // simple-get doesn't tell us about redirects so use http.request directly
2304
+ const testurl = 'http://localhost:' + fastify.server.address().port + '/static?a=b'
2305
+ const req = http.request(url.parse(testurl), res => {
2306
+ t.equal(res.statusCode, 301)
2307
+ t.equal(res.headers.location, '/static/?a=b')
2308
+ })
2309
+ req.on('error', (err) => console.error(err))
2310
+ req.end()
2311
+
2312
+ simple.concat({
2313
+ method: 'GET',
2314
+ url: 'http://localhost:' + fastify.server.address().port + '/static?a=b'
2315
+ }, (err, response, body) => {
2316
+ t.error(err)
2317
+ t.equal(response.statusCode, 200)
2318
+ t.equal(body.toString(), indexContent)
2319
+ genericResponseChecks(t, response)
2320
+ })
2321
+ })
2322
+
2323
+ t.test('/static', t => {
2324
+ t.plan(2)
2325
+
2326
+ // simple-get doesn't tell us about redirects so use http.request directly
2327
+ const testurl = 'http://localhost:' + fastify.server.address().port + '/static'
2328
+ const req = http.request(url.parse(testurl), res => {
2329
+ t.equal(res.statusCode, 301)
2330
+ t.equal(res.headers.location, '/static/')
2331
+ })
2332
+ req.on('error', err => console.error(err))
2333
+ req.end()
2334
+ })
2335
+
2336
+ t.test('/static/', t => {
2337
+ t.plan(3 + GENERIC_RESPONSE_CHECK_COUNT)
2338
+
2339
+ simple.concat({
2340
+ method: 'GET',
2341
+ url: 'http://localhost:' + fastify.server.address().port + '/static/'
2342
+ }, (err, response, body) => {
2343
+ t.error(err)
2344
+ t.equal(response.statusCode, 200)
2345
+ t.equal(body.toString(), indexContent)
2346
+ genericResponseChecks(t, response)
2347
+ })
2348
+ })
2349
+
2350
+ t.test('/static/deep', (t) => {
2351
+ t.plan(2 + GENERIC_ERROR_RESPONSE_CHECK_COUNT)
2352
+
2353
+ simple.concat({
2354
+ method: 'GET',
2355
+ url: 'http://localhost:' + fastify.server.address().port + '/static/deep'
2356
+ }, (err, response, body) => {
2357
+ t.error(err)
2358
+ t.equal(response.statusCode, 404)
2359
+ genericErrorResponseChecks(t, response)
2360
+ })
2361
+ })
2362
+
2363
+ t.test('/static/deep/path/for/test?a=b', (t) => {
2364
+ t.plan(5 + GENERIC_RESPONSE_CHECK_COUNT)
2365
+
2366
+ // simple-get doesn't tell us about redirects so use http.request directly
2367
+ const testurl = 'http://localhost:' + fastify.server.address().port + '/static/deep/path/for/test?a=b'
2368
+ const req = http.request(url.parse(testurl), res => {
2369
+ t.equal(res.statusCode, 301)
2370
+ t.equal(res.headers.location, '/static/deep/path/for/test/?a=b')
2371
+ })
2372
+ req.on('error', (err) => console.error(err))
2373
+ req.end()
2374
+
2375
+ // verify the redirect with query parameters works
2376
+ simple.concat({
2377
+ method: 'GET',
2378
+ url: testurl
2379
+ }, (err, response, body) => {
2380
+ t.error(err)
2381
+ t.equal(response.statusCode, 200)
2382
+ t.equal(body.toString(), innerIndex)
2383
+ genericResponseChecks(t, response)
2384
+ })
2385
+ })
2386
+
2387
+ t.test('/static/deep/path/for/test', (t) => {
2388
+ t.plan(3 + GENERIC_RESPONSE_CHECK_COUNT)
2389
+
2390
+ simple.concat({
2391
+ method: 'GET',
2392
+ url: 'http://localhost:' + fastify.server.address().port + '/static/deep/path/for/test'
2393
+ }, (err, response, body) => {
2394
+ t.error(err)
2395
+ t.equal(response.statusCode, 200)
2396
+ t.equal(body.toString(), innerIndex)
2397
+ genericResponseChecks(t, response)
2398
+ })
2399
+ })
2400
+ })
2401
+ })
2402
+
2403
+ t.test('register /static with redirect true and wildcard false', t => {
2404
+ t.plan(8)
2405
+
2406
+ const pluginOptions = {
2407
+ root: path.join(__dirname, '/static'),
2408
+ prefix: '/static',
2409
+ redirect: true,
2410
+ wildcard: false,
2411
+ index: 'index.html'
2412
+ }
2413
+
2414
+ const fastify = Fastify()
2415
+
2416
+ fastify.register(fastifyStatic, pluginOptions)
2417
+
2418
+ t.teardown(fastify.close.bind(fastify))
2419
+
2420
+ fastify.listen(3001, err => {
2421
+ t.error(err)
2422
+
2423
+ fastify.server.unref()
2424
+
2425
+ t.test('/static?a=b', t => {
2426
+ t.plan(5 + GENERIC_RESPONSE_CHECK_COUNT)
2427
+
2428
+ // simple-get doesn't tell us about redirects so use http.request directly
2429
+ const testurl = 'http://localhost:' + fastify.server.address().port + '/static?a=b'
2430
+ const req = http.request(url.parse(testurl), res => {
2431
+ t.equal(res.statusCode, 301)
2432
+ t.equal(res.headers.location, '/static/?a=b')
2433
+ })
2434
+ req.on('error', err => console.error(err))
2435
+ req.end()
2436
+
2437
+ simple.concat({
2438
+ method: 'GET',
2439
+ url: 'http://localhost:' + fastify.server.address().port + '/static?a=b'
2440
+ }, (err, response, body) => {
2441
+ t.error(err)
2442
+ t.equal(response.statusCode, 200)
2443
+ t.equal(body.toString(), indexContent)
2444
+ genericResponseChecks(t, response)
2445
+ })
2446
+ })
2447
+
2448
+ t.test('/static/?a=b', t => {
2449
+ t.plan(3 + GENERIC_RESPONSE_CHECK_COUNT)
2450
+
2451
+ simple.concat({
2452
+ method: 'GET',
2453
+ url: 'http://localhost:' + fastify.server.address().port + '/static/?a=b'
2454
+ }, (err, response, body) => {
2455
+ t.error(err)
2456
+ t.equal(response.statusCode, 200)
2457
+ t.equal(body.toString(), indexContent)
2458
+ genericResponseChecks(t, response)
2459
+ })
2460
+ })
2461
+
2462
+ t.test('/static/?a=b', t => {
2463
+ t.plan(3 + GENERIC_RESPONSE_CHECK_COUNT)
2464
+
2465
+ simple.concat({
2466
+ method: 'HEAD',
2467
+ url: 'http://localhost:' + fastify.server.address().port + '/static/?a=b'
2468
+ }, (err, response, body) => {
2469
+ t.error(err)
2470
+ t.equal(response.statusCode, 200)
2471
+ t.equal(body.toString(), '')
2472
+ genericResponseChecks(t, response)
2473
+ })
2474
+ })
2475
+
2476
+ t.test('/static/deep', t => {
2477
+ t.plan(2 + GENERIC_ERROR_RESPONSE_CHECK_COUNT)
2478
+
2479
+ simple.concat({
2480
+ method: 'GET',
2481
+ url: 'http://localhost:' + fastify.server.address().port + '/static/deep'
2482
+ }, (err, response, body) => {
2483
+ t.error(err)
2484
+ t.equal(response.statusCode, 404)
2485
+ genericErrorResponseChecks(t, response)
2486
+ })
2487
+ })
2488
+
2489
+ t.test('/static/deep/path/for/test?a=b', t => {
2490
+ t.plan(5 + GENERIC_RESPONSE_CHECK_COUNT)
2491
+
2492
+ // simple-get doesn't tell us about redirects so use http.request directly
2493
+ const testurl = 'http://localhost:' + fastify.server.address().port + '/static/deep/path/for/test?a=b'
2494
+ const req = http.request(url.parse(testurl), res => {
2495
+ t.equal(res.statusCode, 301)
2496
+ t.equal(res.headers.location, '/static/deep/path/for/test/?a=b')
2497
+ })
2498
+ req.on('error', err => console.error(err))
2499
+ req.end()
2500
+
2501
+ // verify the redirect with query parameters works
2502
+ simple.concat({
2503
+ method: 'GET',
2504
+ url: testurl
2505
+ }, (err, response, body) => {
2506
+ t.error(err)
2507
+ t.equal(response.statusCode, 200)
2508
+ t.equal(body.toString(), innerIndex)
2509
+ genericResponseChecks(t, response)
2510
+ })
2511
+ })
2512
+
2513
+ t.test('/static/deep/path/for/test', t => {
2514
+ t.plan(3 + GENERIC_RESPONSE_CHECK_COUNT)
2515
+
2516
+ simple.concat({
2517
+ method: 'GET',
2518
+ url: 'http://localhost:' + fastify.server.address().port + '/static/deep/path/for/test'
2519
+ }, (err, response, body) => {
2520
+ t.error(err)
2521
+ t.equal(response.statusCode, 200)
2522
+ t.equal(body.toString(), innerIndex)
2523
+ genericResponseChecks(t, response)
2524
+ })
2525
+ })
2526
+
2527
+ t.test('/static/deep/path/for/test', t => {
2528
+ t.plan(3 + GENERIC_RESPONSE_CHECK_COUNT)
2529
+
2530
+ simple.concat({
2531
+ method: 'HEAD',
2532
+ url: 'http://localhost:' + fastify.server.address().port + '/static/deep/path/for/test'
2533
+ }, (err, response, body) => {
2534
+ t.error(err)
2535
+ t.equal(response.statusCode, 200)
2536
+ t.equal(body.toString(), '')
2537
+ genericResponseChecks(t, response)
2538
+ })
2539
+ })
2540
+ })
2541
+ })
2542
+
2543
+ t.test('trailing slash behavior with redirect = false', (t) => {
2544
+ t.plan(6)
2545
+
2546
+ const fastify = Fastify()
2547
+ fastify.register(fastifyStatic, {
2548
+ root: path.join(__dirname, '/static'),
2549
+ prefix: '/static',
2550
+ redirect: false
2551
+ })
2552
+ fastify.server.unref()
2553
+
2554
+ t.teardown(fastify.close.bind(fastify))
2555
+
2556
+ fastify.listen(0, (err) => {
2557
+ t.error(err)
2558
+
2559
+ const host = 'http://localhost:' + fastify.server.address().port
2560
+
2561
+ t.test('prefix with no trailing slash => 404', (t) => {
2562
+ t.plan(2)
2563
+ simple.concat({
2564
+ method: 'GET',
2565
+ url: host + '/static'
2566
+ }, (err, response) => {
2567
+ t.error(err)
2568
+ t.equal(response.statusCode, 404)
2569
+ })
2570
+ })
2571
+
2572
+ t.test('prefix with trailing trailing slash => 200', (t) => {
2573
+ t.plan(2)
2574
+ simple.concat({
2575
+ method: 'GET',
2576
+ url: host + '/static/'
2577
+ }, (err, response) => {
2578
+ t.error(err)
2579
+ t.equal(response.statusCode, 200)
2580
+ })
2581
+ })
2582
+
2583
+ t.test('deep path with no index.html or trailing slash => 404', (t) => {
2584
+ t.plan(2)
2585
+ simple.concat({
2586
+ method: 'GET',
2587
+ url: host + '/static/deep/path'
2588
+ }, (err, response) => {
2589
+ t.error(err)
2590
+ t.equal(response.statusCode, 404)
2591
+ })
2592
+ })
2593
+
2594
+ t.test('deep path with index.html but no trailing slash => 200', (t) => {
2595
+ t.plan(2)
2596
+ simple.concat({
2597
+ method: 'GET',
2598
+ url: host + '/static/deep/path/for/test'
2599
+ }, (err, response) => {
2600
+ t.error(err)
2601
+ t.equal(response.statusCode, 200)
2602
+ })
2603
+ })
2604
+
2605
+ t.test('deep path with index.html and trailing slash => 200', (t) => {
2606
+ t.plan(2)
2607
+ simple.concat({
2608
+ method: 'GET',
2609
+ url: host + '/static/deep/path/for/test/'
2610
+ }, (err, response) => {
2611
+ t.error(err)
2612
+ t.equal(response.statusCode, 200)
2613
+ })
2614
+ })
2615
+ })
2616
+ })
2617
+
2618
+ t.test('if dotfiles are properly served according to plugin options', (t) => {
2619
+ t.plan(3)
2620
+ const exampleContents = fs
2621
+ .readFileSync(path.join(__dirname, 'static', '.example'), {
2622
+ encoding: 'utf8'
2623
+ })
2624
+ .toString()
2625
+
2626
+ t.test('freely serve dotfiles', (t) => {
2627
+ t.plan(4)
2628
+ const fastify = Fastify()
2629
+
2630
+ const pluginOptions = {
2631
+ root: path.join(__dirname, 'static'),
2632
+ prefix: '/static/',
2633
+ dotfiles: 'allow'
2634
+ }
2635
+
2636
+ fastify.register(fastifyStatic, pluginOptions)
2637
+
2638
+ t.teardown(fastify.close.bind(fastify))
2639
+ fastify.listen(0, (err) => {
2640
+ t.error(err)
2641
+
2642
+ simple.concat({
2643
+ method: 'GET',
2644
+ url: 'http://localhost:' + fastify.server.address().port + '/static/.example'
2645
+ }, (err, response, body) => {
2646
+ t.error(err)
2647
+ t.equal(response.statusCode, 200)
2648
+ t.equal(body.toString(), exampleContents)
2649
+ })
2650
+ })
2651
+ })
2652
+
2653
+ t.test('ignore dotfiles', (t) => {
2654
+ t.plan(3)
2655
+ const fastify = Fastify()
2656
+
2657
+ const pluginOptions = {
2658
+ root: path.join(__dirname, 'static'),
2659
+ prefix: '/static/',
2660
+ dotfiles: 'ignore'
2661
+ }
2662
+
2663
+ fastify.register(fastifyStatic, pluginOptions)
2664
+
2665
+ t.teardown(fastify.close.bind(fastify))
2666
+ fastify.listen(0, (err) => {
2667
+ t.error(err)
2668
+
2669
+ simple.concat({
2670
+ method: 'GET',
2671
+ url: 'http://localhost:' + fastify.server.address().port + '/static/.example'
2672
+ }, (err, response, body) => {
2673
+ t.error(err)
2674
+ t.equal(response.statusCode, 404)
2675
+ })
2676
+ })
2677
+ })
2678
+
2679
+ t.test('deny requests to serve a dotfile', (t) => {
2680
+ t.plan(3)
2681
+ const fastify = Fastify()
2682
+
2683
+ const pluginOptions = {
2684
+ root: path.join(__dirname, 'static'),
2685
+ prefix: '/static/',
2686
+ dotfiles: 'deny'
2687
+ }
2688
+
2689
+ fastify.register(fastifyStatic, pluginOptions)
2690
+
2691
+ t.teardown(fastify.close.bind(fastify))
2692
+ fastify.listen(0, (err) => {
2693
+ t.error(err)
2694
+
2695
+ simple.concat({
2696
+ method: 'GET',
2697
+ url: 'http://localhost:' + fastify.server.address().port + '/static/.example'
2698
+ }, (err, response, body) => {
2699
+ t.error(err)
2700
+ t.equal(response.statusCode, 403)
2701
+ })
2702
+ })
2703
+ })
2704
+ })
2705
+
2706
+ t.test('register with failing glob handler', (t) => {
2707
+ const fastifyStatic = proxyquire.noCallThru()('../', {
2708
+ glob: function globStub (pattern, options, cb) {
2709
+ process.nextTick(function () {
2710
+ return cb(new Error('mock glob error'))
2711
+ })
2712
+ }
2713
+ })
2714
+
2715
+ const pluginOptions = {
2716
+ root: path.join(__dirname, '/static'),
2717
+ serve: true,
2718
+ wildcard: false
2719
+ }
2720
+ const fastify = Fastify()
2721
+ fastify.register(fastifyStatic, pluginOptions)
2722
+
2723
+ t.teardown(fastify.close.bind(fastify))
2724
+
2725
+ fastify.listen(0, (err) => {
2726
+ fastify.server.unref()
2727
+ t.ok(err)
2728
+ t.end()
2729
+ })
2730
+ })
2731
+
2732
+ t.test(
2733
+ 'register with rootpath that causes statSync to fail with non-ENOENT code',
2734
+ (t) => {
2735
+ const fastifyStatic = proxyquire('../', {
2736
+ fs: {
2737
+ statSync: function statSyncStub (path) {
2738
+ throw new Error({ code: 'MOCK' })
2739
+ }
2740
+ }
2741
+ })
2742
+
2743
+ const pluginOptions = {
2744
+ root: path.join(__dirname, '/static'),
2745
+ wildcard: true
2746
+ }
2747
+ const fastify = Fastify()
2748
+ fastify.register(fastifyStatic, pluginOptions)
2749
+
2750
+ t.teardown(fastify.close.bind(fastify))
2751
+ fastify.listen(0, (err) => {
2752
+ fastify.server.unref()
2753
+ t.ok(err)
2754
+ t.end()
2755
+ })
2756
+ }
2757
+ )
2758
+
2759
+ t.test('inject support', async (t) => {
2760
+ const pluginOptions = {
2761
+ root: path.join(__dirname, '/static'),
2762
+ prefix: '/static'
2763
+ }
2764
+ const fastify = Fastify()
2765
+ fastify.register(fastifyStatic, pluginOptions)
2766
+ t.teardown(fastify.close.bind(fastify))
2767
+
2768
+ const response = await fastify.inject({
2769
+ method: 'GET',
2770
+ url: '/static/index.html'
2771
+ })
2772
+ t.equal(response.statusCode, 200)
2773
+ t.equal(response.body.toString(), indexContent)
2774
+ })
2775
+
2776
+ t.test('routes should use custom errorHandler premature stream close', t => {
2777
+ t.plan(4)
2778
+
2779
+ const pluginOptions = {
2780
+ root: path.join(__dirname, '/static'),
2781
+ prefix: '/static/'
2782
+ }
2783
+
2784
+ const fastify = Fastify()
2785
+
2786
+ fastify.addHook('onRoute', function (routeOptions) {
2787
+ t.ok(routeOptions.errorHandler instanceof Function)
2788
+
2789
+ routeOptions.onRequest = (request, reply, done) => {
2790
+ const fakeError = new Error()
2791
+ fakeError.code = 'ERR_STREAM_PREMATURE_CLOSE'
2792
+ done(fakeError)
2793
+ }
2794
+ })
2795
+
2796
+ fastify.register(fastifyStatic, pluginOptions)
2797
+ t.teardown(fastify.close.bind(fastify))
2798
+
2799
+ fastify.inject(
2800
+ {
2801
+ method: 'GET',
2802
+ url: '/static/index.html'
2803
+ },
2804
+ (err, response) => {
2805
+ t.error(err)
2806
+ t.equal(response, null)
2807
+ }
2808
+ )
2809
+ })
2810
+
2811
+ t.test('routes should fallback to default errorHandler', t => {
2812
+ t.plan(4)
2813
+
2814
+ const pluginOptions = {
2815
+ root: path.join(__dirname, '/static'),
2816
+ prefix: '/static/'
2817
+ }
2818
+
2819
+ const fastify = Fastify()
2820
+
2821
+ fastify.addHook('onRoute', function (routeOptions) {
2822
+ t.ok(routeOptions.errorHandler instanceof Function)
2823
+
2824
+ routeOptions.preHandler = (request, reply, done) => {
2825
+ const fakeError = new Error()
2826
+ fakeError.code = 'SOMETHING_ELSE'
2827
+ done(fakeError)
2828
+ }
2829
+ })
2830
+
2831
+ fastify.register(fastifyStatic, pluginOptions)
2832
+ t.teardown(fastify.close.bind(fastify))
2833
+
2834
+ fastify.inject({
2835
+ method: 'GET',
2836
+ url: '/static/index.html'
2837
+ }, (err, response) => {
2838
+ t.error(err)
2839
+ t.same(JSON.parse(response.payload), {
2840
+ statusCode: 500,
2841
+ code: 'SOMETHING_ELSE',
2842
+ error: 'Internal Server Error',
2843
+ message: ''
2844
+ })
2845
+ })
2846
+ })
2847
+
2848
+ t.test('routes use default errorHandler when fastify.errorHandler is not defined', t => {
2849
+ t.plan(4)
2850
+
2851
+ const pluginOptions = {
2852
+ root: path.join(__dirname, '/static'),
2853
+ prefix: '/static/'
2854
+ }
2855
+
2856
+ const fastify = Fastify()
2857
+ fastify[kErrorHandler] = undefined // simulate old fastify version
2858
+
2859
+ fastify.addHook('onRoute', function (routeOptions) {
2860
+ t.notOk(routeOptions.errorHandler instanceof Function)
2861
+
2862
+ routeOptions.preHandler = (request, reply, done) => {
2863
+ const fakeError = new Error()
2864
+ fakeError.code = 'SOMETHING_ELSE'
2865
+ done(fakeError)
2866
+ }
2867
+ })
2868
+
2869
+ fastify.register(fastifyStatic, pluginOptions)
2870
+ t.teardown(fastify.close.bind(fastify))
2871
+
2872
+ fastify.inject({
2873
+ method: 'GET',
2874
+ url: '/static/index.html'
2875
+ }, (err, response) => {
2876
+ t.error(err)
2877
+ t.same(JSON.parse(response.payload), {
2878
+ statusCode: 500,
2879
+ code: 'SOMETHING_ELSE',
2880
+ error: 'Internal Server Error',
2881
+ message: ''
2882
+ })
2883
+ })
2884
+ })
2885
+
2886
+ t.test('precent encoded URLs in glob mode', (t) => {
2887
+ t.plan(4)
2888
+
2889
+ const fastify = Fastify({})
2890
+
2891
+ fastify.register(fastifyStatic, {
2892
+ root: path.join(__dirname, 'static'),
2893
+ prefix: '/static',
2894
+ wildcard: true
2895
+ })
2896
+
2897
+ t.teardown(fastify.close.bind(fastify))
2898
+
2899
+ fastify.listen(0, (err) => {
2900
+ t.error(err)
2901
+ fastify.server.unref()
2902
+
2903
+ simple.concat({
2904
+ method: 'GET',
2905
+ url: 'http://localhost:' + fastify.server.address().port + '/static/a .md',
2906
+ followRedirect: false
2907
+ }, (err, response, body) => {
2908
+ t.error(err)
2909
+ t.equal(response.statusCode, 200)
2910
+ t.equal(
2911
+ fs.readFileSync(path.join(__dirname, 'static', 'a .md'), 'utf-8'),
2912
+ body.toString()
2913
+ )
2914
+ })
2915
+ })
2916
+ })
2917
+
2918
+ t.test('register /static and /static2 without wildcard', t => {
2919
+ t.plan(3)
2920
+
2921
+ const pluginOptions = {
2922
+ root: [path.join(__dirname, '/static'), path.join(__dirname, '/static2')],
2923
+ wildcard: false
2924
+ }
2925
+ const fastify = Fastify()
2926
+ fastify.register(fastifyStatic, pluginOptions)
2927
+
2928
+ t.teardown(fastify.close.bind(fastify))
2929
+
2930
+ fastify.listen(0, err => {
2931
+ t.error(err)
2932
+
2933
+ fastify.server.unref()
2934
+
2935
+ t.test('/index.html', t => {
2936
+ t.plan(4 + GENERIC_RESPONSE_CHECK_COUNT)
2937
+ simple.concat({
2938
+ method: 'GET',
2939
+ url: 'http://localhost:' + fastify.server.address().port + '/index.html'
2940
+ }, (err, response, body) => {
2941
+ t.error(err)
2942
+ t.equal(response.statusCode, 200)
2943
+ t.not(body.toString(), index2Content)
2944
+ t.equal(body.toString(), indexContent)
2945
+ genericResponseChecks(t, response)
2946
+ })
2947
+ })
2948
+
2949
+ t.test('/static/bar.html', t => {
2950
+ t.plan(3 + GENERIC_RESPONSE_CHECK_COUNT)
2951
+ simple.concat({
2952
+ method: 'GET',
2953
+ url: 'http://localhost:' + fastify.server.address().port + '/bar.html'
2954
+ }, (err, response, body) => {
2955
+ t.error(err)
2956
+ t.equal(response.statusCode, 200)
2957
+ t.equal(body.toString(), barContent)
2958
+ genericResponseChecks(t, response)
2959
+ })
2960
+ })
2961
+ })
2962
+ })
2963
+
2964
+ t.test(
2965
+ 'will serve pre-compressed files with .br at the highest priority',
2966
+ async (t) => {
2967
+ const pluginOptions = {
2968
+ root: path.join(__dirname, '/static-pre-compressed'),
2969
+ prefix: '/static-pre-compressed/',
2970
+ preCompressed: true
2971
+ }
2972
+
2973
+ const fastify = Fastify()
2974
+
2975
+ fastify.register(fastifyStatic, pluginOptions)
2976
+ t.teardown(fastify.close.bind(fastify))
2977
+
2978
+ const response = await fastify.inject({
2979
+ method: 'GET',
2980
+ url: '/static-pre-compressed/all-three.html',
2981
+ headers: {
2982
+ 'accept-encoding': 'gzip, deflate, br'
2983
+ }
2984
+ })
2985
+
2986
+ genericResponseChecks(t, response)
2987
+ t.equal(response.headers['content-encoding'], 'br')
2988
+ t.equal(response.statusCode, 200)
2989
+ t.same(response.rawPayload, allThreeBr)
2990
+ t.end()
2991
+ }
2992
+ )
2993
+
2994
+ t.test(
2995
+ 'will serve pre-compressed files and fallback to .gz if .br is not on disk',
2996
+ async (t) => {
2997
+ const pluginOptions = {
2998
+ root: path.join(__dirname, '/static-pre-compressed'),
2999
+ prefix: '/static-pre-compressed/',
3000
+ preCompressed: true
3001
+ }
3002
+
3003
+ const fastify = Fastify()
3004
+
3005
+ fastify.register(fastifyStatic, pluginOptions)
3006
+ t.teardown(fastify.close.bind(fastify))
3007
+
3008
+ const response = await fastify.inject({
3009
+ method: 'GET',
3010
+ url: '/static-pre-compressed/gzip-only.html',
3011
+ headers: {
3012
+ 'accept-encoding': 'gzip, deflate, br'
3013
+ }
3014
+ })
3015
+
3016
+ genericResponseChecks(t, response)
3017
+ t.equal(response.headers['content-encoding'], 'gzip')
3018
+ t.equal(response.statusCode, 200)
3019
+ t.same(response.rawPayload, gzipOnly)
3020
+ t.end()
3021
+ }
3022
+ )
3023
+
3024
+ t.test(
3025
+ 'will serve pre-compressed files with .gzip if * directive used',
3026
+ async (t) => {
3027
+ const pluginOptions = {
3028
+ root: path.join(__dirname, '/static-pre-compressed'),
3029
+ prefix: '/static-pre-compressed/',
3030
+ preCompressed: true
3031
+ }
3032
+
3033
+ const fastify = Fastify()
3034
+
3035
+ fastify.register(fastifyStatic, pluginOptions)
3036
+ t.teardown(fastify.close.bind(fastify))
3037
+
3038
+ const response = await fastify.inject({
3039
+ method: 'GET',
3040
+ url: '/static-pre-compressed/all-three.html',
3041
+ headers: {
3042
+ 'accept-encoding': '*'
3043
+ }
3044
+ })
3045
+
3046
+ genericResponseChecks(t, response)
3047
+ t.equal(response.headers['content-encoding'], 'gzip')
3048
+ t.equal(response.statusCode, 200)
3049
+ t.same(response.rawPayload, allThreeGzip)
3050
+ t.end()
3051
+ }
3052
+ )
3053
+
3054
+ t.test(
3055
+ 'will serve pre-compressed files with .gzip if multiple * directives used',
3056
+ async (t) => {
3057
+ const pluginOptions = {
3058
+ root: path.join(__dirname, '/static-pre-compressed'),
3059
+ prefix: '/static-pre-compressed/',
3060
+ preCompressed: true
3061
+ }
3062
+
3063
+ const fastify = Fastify()
3064
+
3065
+ fastify.register(fastifyStatic, pluginOptions)
3066
+ t.teardown(fastify.close.bind(fastify))
3067
+
3068
+ const response = await fastify.inject({
3069
+ method: 'GET',
3070
+ url: '/static-pre-compressed/all-three.html',
3071
+ headers: {
3072
+ 'accept-encoding': '*, *'
3073
+ }
3074
+ })
3075
+
3076
+ genericResponseChecks(t, response)
3077
+ t.equal(response.headers['content-encoding'], 'gzip')
3078
+ t.equal(response.statusCode, 200)
3079
+ t.same(response.rawPayload, allThreeGzip)
3080
+ t.end()
3081
+ }
3082
+ )
3083
+
3084
+ t.test(
3085
+ 'will serve uncompressed files if there are no compressed variants on disk',
3086
+ async (t) => {
3087
+ const pluginOptions = {
3088
+ root: path.join(__dirname, '/static-pre-compressed'),
3089
+ prefix: '/static-pre-compressed/',
3090
+ preCompressed: true
3091
+ }
3092
+
3093
+ const fastify = Fastify()
3094
+
3095
+ fastify.register(fastifyStatic, pluginOptions)
3096
+ t.teardown(fastify.close.bind(fastify))
3097
+
3098
+ const response = await fastify.inject({
3099
+ method: 'GET',
3100
+ url: '/static-pre-compressed/uncompressed.html',
3101
+ headers: {
3102
+ 'accept-encoding': 'gzip, deflate, br'
3103
+ }
3104
+ })
3105
+
3106
+ genericResponseChecks(t, response)
3107
+ t.equal(response.headers['content-encoding'], undefined)
3108
+ t.equal(response.statusCode, 200)
3109
+ t.equal(response.body, uncompressedStatic)
3110
+ t.end()
3111
+ }
3112
+ )
3113
+
3114
+ t.test(
3115
+ 'will serve pre-compressed files with .br at the highest priority (with wildcard: false)',
3116
+ async (t) => {
3117
+ const pluginOptions = {
3118
+ root: path.join(__dirname, '/static-pre-compressed'),
3119
+ prefix: '/static-pre-compressed/',
3120
+ preCompressed: true,
3121
+ wildcard: false
3122
+ }
3123
+
3124
+ const fastify = Fastify()
3125
+
3126
+ fastify.register(fastifyStatic, pluginOptions)
3127
+ t.teardown(fastify.close.bind(fastify))
3128
+
3129
+ const response = await fastify.inject({
3130
+ method: 'GET',
3131
+ url: '/static-pre-compressed/all-three.html',
3132
+ headers: {
3133
+ 'accept-encoding': 'gzip, deflate, br'
3134
+ }
3135
+ })
3136
+
3137
+ genericResponseChecks(t, response)
3138
+ t.equal(response.headers['content-encoding'], 'br')
3139
+ t.equal(response.statusCode, 200)
3140
+ t.same(response.rawPayload, allThreeBr)
3141
+ t.end()
3142
+ }
3143
+ )
3144
+
3145
+ t.test(
3146
+ 'will serve pre-compressed files and fallback to .gz if .br is not on disk (with wildcard: false)',
3147
+ async (t) => {
3148
+ const pluginOptions = {
3149
+ root: path.join(__dirname, '/static-pre-compressed'),
3150
+ prefix: '/static-pre-compressed/',
3151
+ preCompressed: true,
3152
+ wildcard: false
3153
+ }
3154
+
3155
+ const fastify = Fastify()
3156
+
3157
+ fastify.register(fastifyStatic, pluginOptions)
3158
+ t.teardown(fastify.close.bind(fastify))
3159
+
3160
+ const response = await fastify.inject({
3161
+ method: 'GET',
3162
+ url: '/static-pre-compressed/gzip-only.html',
3163
+ headers: {
3164
+ 'accept-encoding': 'gzip, deflate, br'
3165
+ }
3166
+ })
3167
+
3168
+ genericResponseChecks(t, response)
3169
+ t.equal(response.headers['content-encoding'], 'gzip')
3170
+ t.equal(response.statusCode, 200)
3171
+ t.same(response.rawPayload, gzipOnly)
3172
+ t.end()
3173
+ }
3174
+ )
3175
+
3176
+ t.test(
3177
+ 'will serve pre-compressed files with .gzip if * directive used (with wildcard: false)',
3178
+ async (t) => {
3179
+ const pluginOptions = {
3180
+ root: path.join(__dirname, '/static-pre-compressed'),
3181
+ prefix: '/static-pre-compressed/',
3182
+ preCompressed: true,
3183
+ wildcard: false
3184
+ }
3185
+
3186
+ const fastify = Fastify()
3187
+
3188
+ fastify.register(fastifyStatic, pluginOptions)
3189
+ t.teardown(fastify.close.bind(fastify))
3190
+
3191
+ const response = await fastify.inject({
3192
+ method: 'GET',
3193
+ url: '/static-pre-compressed/all-three.html',
3194
+ headers: {
3195
+ 'accept-encoding': '*'
3196
+ }
3197
+ })
3198
+
3199
+ genericResponseChecks(t, response)
3200
+ t.equal(response.headers['content-encoding'], 'gzip')
3201
+ t.equal(response.statusCode, 200)
3202
+ t.same(response.rawPayload, allThreeGzip)
3203
+ t.end()
3204
+ }
3205
+ )
3206
+
3207
+ t.test(
3208
+ 'will serve pre-compressed files with .gzip if multiple * directives used (with wildcard: false)',
3209
+ async (t) => {
3210
+ const pluginOptions = {
3211
+ root: path.join(__dirname, '/static-pre-compressed'),
3212
+ prefix: '/static-pre-compressed/',
3213
+ preCompressed: true,
3214
+ wildcard: false
3215
+ }
3216
+
3217
+ const fastify = Fastify()
3218
+
3219
+ fastify.register(fastifyStatic, pluginOptions)
3220
+ t.teardown(fastify.close.bind(fastify))
3221
+
3222
+ const response = await fastify.inject({
3223
+ method: 'GET',
3224
+ url: '/static-pre-compressed/all-three.html',
3225
+ headers: {
3226
+ 'accept-encoding': '*, *'
3227
+ }
3228
+ })
3229
+
3230
+ genericResponseChecks(t, response)
3231
+ t.equal(response.headers['content-encoding'], 'gzip')
3232
+ t.equal(response.statusCode, 200)
3233
+ t.same(response.rawPayload, allThreeGzip)
3234
+ t.end()
3235
+ }
3236
+ )
3237
+
3238
+ t.test(
3239
+ 'will serve uncompressed files if there are no compressed variants on disk (with wildcard: false)',
3240
+ async (t) => {
3241
+ const pluginOptions = {
3242
+ root: path.join(__dirname, '/static-pre-compressed'),
3243
+ prefix: '/static-pre-compressed/',
3244
+ preCompressed: true,
3245
+ wildcard: false
3246
+ }
3247
+
3248
+ const fastify = Fastify()
3249
+
3250
+ fastify.register(fastifyStatic, pluginOptions)
3251
+ t.teardown(fastify.close.bind(fastify))
3252
+
3253
+ const response = await fastify.inject({
3254
+ method: 'GET',
3255
+ url: '/static-pre-compressed/uncompressed.html',
3256
+ headers: {
3257
+ 'accept-encoding': 'gzip, deflate, br'
3258
+ }
3259
+ })
3260
+
3261
+ genericResponseChecks(t, response)
3262
+ t.equal(response.headers['content-encoding'], undefined)
3263
+ t.equal(response.statusCode, 200)
3264
+ t.equal(response.body, uncompressedStatic)
3265
+ t.end()
3266
+ }
3267
+ )
3268
+
3269
+ t.test(
3270
+ 'will serve uncompressed files the accept-encoding header is missing',
3271
+ async (t) => {
3272
+ const pluginOptions = {
3273
+ root: path.join(__dirname, '/static-pre-compressed'),
3274
+ prefix: '/static-pre-compressed/',
3275
+ preCompressed: true
3276
+ }
3277
+
3278
+ const fastify = Fastify()
3279
+
3280
+ fastify.register(fastifyStatic, pluginOptions)
3281
+ t.teardown(fastify.close.bind(fastify))
3282
+
3283
+ const response = await fastify.inject({
3284
+ method: 'GET',
3285
+ url: '/static-pre-compressed/uncompressed.html'
3286
+ })
3287
+
3288
+ genericResponseChecks(t, response)
3289
+ t.equal(response.headers['content-encoding'], undefined)
3290
+ t.equal(response.statusCode, 200)
3291
+ t.equal(response.body, uncompressedStatic)
3292
+ t.end()
3293
+ }
3294
+ )
3295
+
3296
+ t.test(
3297
+ 'will serve precompressed index',
3298
+ async (t) => {
3299
+ const pluginOptions = {
3300
+ root: path.join(__dirname, '/static-pre-compressed'),
3301
+ prefix: '/static-pre-compressed/',
3302
+ preCompressed: true
3303
+ }
3304
+
3305
+ const fastify = Fastify()
3306
+
3307
+ fastify.register(fastifyStatic, pluginOptions)
3308
+ t.teardown(fastify.close.bind(fastify))
3309
+
3310
+ const response = await fastify.inject({
3311
+ method: 'GET',
3312
+ url: '/static-pre-compressed/',
3313
+ headers: {
3314
+ 'accept-encoding': 'gzip, deflate, br'
3315
+ }
3316
+ })
3317
+
3318
+ genericResponseChecks(t, response)
3319
+ t.equal(response.headers['content-encoding'], 'br')
3320
+ t.equal(response.statusCode, 200)
3321
+ t.same(response.rawPayload, indexBr)
3322
+ t.end()
3323
+ }
3324
+ )
3325
+
3326
+ t.test(
3327
+ 'will serve preCompressed index without trailing slash',
3328
+ async (t) => {
3329
+ const pluginOptions = {
3330
+ root: path.join(__dirname, '/static-pre-compressed'),
3331
+ prefix: '/static-pre-compressed/',
3332
+ preCompressed: true,
3333
+ redirect: false
3334
+ }
3335
+
3336
+ const fastify = Fastify()
3337
+
3338
+ fastify.register(fastifyStatic, pluginOptions)
3339
+ t.teardown(fastify.close.bind(fastify))
3340
+
3341
+ const response = await fastify.inject({
3342
+ method: 'GET',
3343
+ url: '/static-pre-compressed/dir',
3344
+ headers: {
3345
+ 'accept-encoding': 'gzip, deflate, br'
3346
+ }
3347
+ })
3348
+
3349
+ genericResponseChecks(t, response)
3350
+ t.equal(response.headers['content-encoding'], 'br')
3351
+ t.equal(response.statusCode, 200)
3352
+ t.same(response.rawPayload, dirIndexBr)
3353
+ t.end()
3354
+ }
3355
+ )
3356
+
3357
+ t.test(
3358
+ 'will serve precompressed index with alternative index option',
3359
+ async (t) => {
3360
+ const pluginOptions = {
3361
+ root: path.join(__dirname, '/static-pre-compressed'),
3362
+ prefix: '/static-pre-compressed/',
3363
+ preCompressed: true,
3364
+ index: ['all-three.html']
3365
+ }
3366
+
3367
+ const fastify = Fastify()
3368
+
3369
+ fastify.register(fastifyStatic, pluginOptions)
3370
+ t.teardown(fastify.close.bind(fastify))
3371
+
3372
+ const response = await fastify.inject({
3373
+ method: 'GET',
3374
+ url: '/static-pre-compressed/',
3375
+ headers: {
3376
+ 'accept-encoding': 'gzip, deflate, br'
3377
+ }
3378
+ })
3379
+
3380
+ genericResponseChecks(t, response)
3381
+ t.equal(response.headers['content-encoding'], 'br')
3382
+ t.equal(response.statusCode, 200)
3383
+ t.same(response.rawPayload, allThreeBr)
3384
+ t.end()
3385
+ }
3386
+ )
3387
+
3388
+ t.test(
3389
+ 'will serve precompressed file without content-type charset',
3390
+ async (t) => {
3391
+ const pluginOptions = {
3392
+ root: path.join(__dirname, '/static-pre-compressed'),
3393
+ prefix: '/static-pre-compressed/',
3394
+ preCompressed: true
3395
+ }
3396
+
3397
+ const fastify = Fastify()
3398
+
3399
+ fastify.register(fastifyStatic, pluginOptions)
3400
+ t.teardown(fastify.close.bind(fastify))
3401
+
3402
+ const response = await fastify.inject({
3403
+ method: 'GET',
3404
+ url: '/static-pre-compressed/sample.jpg',
3405
+ headers: {
3406
+ 'accept-encoding': 'gzip, deflate, br'
3407
+ }
3408
+ })
3409
+
3410
+ t.equal(response.headers['content-type'], 'image/jpeg')
3411
+ t.equal(response.headers['content-encoding'], 'br')
3412
+ t.equal(response.statusCode, 200)
3413
+ t.end()
3414
+ }
3415
+ )
3416
+
3417
+ t.test(
3418
+ 'nonexistent index with precompressed option',
3419
+ async (t) => {
3420
+ const pluginOptions = {
3421
+ root: path.join(__dirname, '/static-pre-compressed'),
3422
+ prefix: '/static-pre-compressed/',
3423
+ preCompressed: true
3424
+ }
3425
+
3426
+ const fastify = Fastify()
3427
+
3428
+ fastify.register(fastifyStatic, pluginOptions)
3429
+ t.teardown(fastify.close.bind(fastify))
3430
+
3431
+ const response = await fastify.inject({
3432
+ method: 'GET',
3433
+ url: '/static-pre-compressed/empty/',
3434
+ headers: {
3435
+ 'accept-encoding': 'gzip, deflate, br'
3436
+ }
3437
+ })
3438
+
3439
+ t.equal(response.statusCode, 404)
3440
+ genericErrorResponseChecks(t, response)
3441
+ t.end()
3442
+ }
3443
+ )
3444
+
3445
+ t.test('should not redirect to protocol-relative locations', (t) => {
3446
+ const urls = [
3447
+ ['//^/..', '/', 301],
3448
+ ['//^/.', null, 404], // it is NOT recognized as a directory by pillarjs/send
3449
+ ['//:/..', '/', 301],
3450
+ ['/\\\\a//google.com/%2e%2e%2f%2e%2e', '/a//google.com/%2e%2e%2f%2e%2e/', 301],
3451
+ ['//a//youtube.com/%2e%2e%2f%2e%2e', '/a//youtube.com/%2e%2e%2f%2e%2e/', 301],
3452
+ ['/^', null, 404], // it is NOT recognized as a directory by pillarjs/send
3453
+ ['//google.com/%2e%2e', '/', 301],
3454
+ ['//users/%2e%2e', '/', 301],
3455
+ ['//users', null, 404],
3456
+ ['///deep/path//for//test//index.html', null, 200]
3457
+ ]
3458
+
3459
+ t.plan(1 + urls.length * 2)
3460
+ const fastify = Fastify()
3461
+ fastify.register(fastifyStatic, {
3462
+ root: path.join(__dirname, '/static'),
3463
+ redirect: true
3464
+ })
3465
+ t.teardown(fastify.close.bind(fastify))
3466
+ fastify.listen(0, (err, address) => {
3467
+ t.error(err)
3468
+ urls.forEach(([testUrl, expected, status]) => {
3469
+ const req = http.request(url.parse(address + testUrl), res => {
3470
+ t.equal(res.statusCode, status, `status ${testUrl}`)
3471
+
3472
+ if (expected) {
3473
+ t.equal(res.headers.location, expected)
3474
+ } else {
3475
+ t.notOk(res.headers.location)
3476
+ }
3477
+ })
3478
+ req.on('error', t.error)
3479
+ req.end()
3480
+ })
3481
+ })
3482
+ })