fastify 3.26.0 → 4.0.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -4
- package/build/build-error-serializer.js +27 -0
- package/build/build-validation.js +49 -35
- package/docs/Guides/Ecosystem.md +2 -1
- package/docs/Guides/Prototype-Poisoning.md +3 -3
- package/docs/Migration-Guide-V4.md +12 -0
- package/docs/Reference/ContentTypeParser.md +8 -1
- package/docs/Reference/Errors.md +51 -6
- package/docs/Reference/Hooks.md +4 -7
- package/docs/Reference/LTS.md +5 -4
- package/docs/Reference/Reply.md +23 -22
- package/docs/Reference/Request.md +1 -3
- package/docs/Reference/Routes.md +17 -10
- package/docs/Reference/Server.md +98 -63
- package/docs/Reference/TypeScript.md +11 -13
- package/docs/Reference/Validation-and-Serialization.md +32 -54
- package/docs/Type-Providers.md +257 -0
- package/examples/hooks.js +1 -1
- package/examples/simple-stream.js +18 -0
- package/fastify.d.ts +36 -22
- package/fastify.js +72 -53
- package/lib/configValidator.js +902 -1023
- package/lib/contentTypeParser.js +6 -16
- package/lib/context.js +36 -10
- package/lib/decorate.js +5 -3
- package/lib/error-handler.js +158 -0
- package/lib/error-serializer.js +257 -0
- package/lib/errors.js +49 -10
- package/lib/fourOhFour.js +31 -20
- package/lib/handleRequest.js +10 -13
- package/lib/hooks.js +14 -9
- package/lib/noop-set.js +10 -0
- package/lib/pluginOverride.js +0 -3
- package/lib/pluginUtils.js +3 -2
- package/lib/reply.js +44 -163
- package/lib/request.js +13 -10
- package/lib/route.js +158 -139
- package/lib/schema-controller.js +3 -3
- package/lib/schemas.js +27 -1
- package/lib/server.js +219 -116
- package/lib/symbols.js +6 -4
- package/lib/validation.js +2 -1
- package/lib/warnings.js +2 -12
- package/lib/wrapThenable.js +4 -11
- package/package.json +40 -45
- package/test/404s.test.js +265 -108
- package/test/500s.test.js +2 -2
- package/test/async-await.test.js +15 -71
- package/test/close.test.js +39 -1
- package/test/content-parser.test.js +32 -0
- package/test/context-config.test.js +56 -4
- package/test/custom-http-server.test.js +14 -7
- package/test/custom-parser-async.test.js +0 -65
- package/test/custom-parser.test.js +54 -121
- package/test/decorator.test.js +1 -3
- package/test/delete.test.js +5 -5
- package/test/encapsulated-error-handler.test.js +50 -0
- package/test/esm/index.test.js +0 -14
- package/test/fastify-instance.test.js +4 -4
- package/test/fluent-schema.test.js +4 -4
- package/test/get.test.js +3 -3
- package/test/helper.js +18 -3
- package/test/hooks-async.test.js +14 -47
- package/test/hooks.on-ready.test.js +9 -4
- package/test/hooks.test.js +58 -99
- package/test/http2/closing.test.js +5 -11
- package/test/http2/unknown-http-method.test.js +3 -9
- package/test/https/custom-https-server.test.js +12 -6
- package/test/inject.test.js +1 -1
- package/test/input-validation.js +2 -2
- package/test/internals/all.test.js +2 -2
- package/test/internals/contentTypeParser.test.js +4 -4
- package/test/internals/handleRequest.test.js +9 -46
- package/test/internals/initialConfig.test.js +33 -12
- package/test/internals/logger.test.js +1 -1
- package/test/internals/reply.test.js +245 -3
- package/test/internals/request.test.js +13 -7
- package/test/internals/server.test.js +88 -0
- package/test/listen.test.js +84 -1
- package/test/logger.test.js +98 -58
- package/test/maxRequestsPerSocket.test.js +8 -6
- package/test/middleware.test.js +2 -25
- package/test/noop-set.test.js +19 -0
- package/test/nullable-validation.test.js +51 -14
- package/test/plugin.test.js +31 -5
- package/test/pretty-print.test.js +22 -10
- package/test/reply-error.test.js +123 -12
- package/test/request-error.test.js +2 -5
- package/test/route-hooks.test.js +17 -17
- package/test/route-prefix.test.js +2 -1
- package/test/route.test.js +216 -20
- package/test/router-options.test.js +1 -1
- package/test/schema-examples.test.js +11 -5
- package/test/schema-feature.test.js +24 -19
- package/test/schema-serialization.test.js +50 -9
- package/test/schema-special-usage.test.js +14 -81
- package/test/schema-validation.test.js +9 -9
- package/test/skip-reply-send.test.js +8 -8
- package/test/stream.test.js +23 -12
- package/test/throw.test.js +8 -5
- package/test/trust-proxy.test.js +1 -1
- package/test/type-provider.test.js +20 -0
- package/test/types/fastify.test-d.ts +12 -18
- package/test/types/hooks.test-d.ts +7 -3
- package/test/types/import.js +2 -0
- package/test/types/import.ts +1 -0
- package/test/types/instance.test-d.ts +61 -15
- package/test/types/logger.test-d.ts +44 -15
- package/test/types/route.test-d.ts +8 -2
- package/test/types/schema.test-d.ts +2 -39
- package/test/types/type-provider.test-d.ts +417 -0
- package/test/validation-error-handling.test.js +9 -9
- package/test/versioned-routes.test.js +29 -17
- package/test/wrapThenable.test.js +7 -6
- package/types/.eslintrc.json +1 -1
- package/types/content-type-parser.d.ts +17 -8
- package/types/hooks.d.ts +107 -60
- package/types/instance.d.ts +137 -105
- package/types/logger.d.ts +18 -104
- package/types/plugin.d.ts +10 -4
- package/types/register.d.ts +1 -1
- package/types/reply.d.ts +16 -11
- package/types/request.d.ts +10 -5
- package/types/route.d.ts +42 -31
- package/types/schema.d.ts +15 -1
- package/types/type-provider.d.ts +99 -0
- package/types/utils.d.ts +1 -1
- package/lib/schema-compilers.js +0 -12
- package/test/emit-warning.test.js +0 -166
|
@@ -15,6 +15,21 @@ const {
|
|
|
15
15
|
kReplyIsError,
|
|
16
16
|
kReplySerializerDefault
|
|
17
17
|
} = require('../../lib/symbols')
|
|
18
|
+
const fs = require('fs')
|
|
19
|
+
const path = require('path')
|
|
20
|
+
const warning = require('../../lib/warnings')
|
|
21
|
+
|
|
22
|
+
const doGet = function (url) {
|
|
23
|
+
return new Promise((resolve, reject) => {
|
|
24
|
+
sget({ method: 'GET', url, followRedirects: false }, (err, response, body) => {
|
|
25
|
+
if (err) {
|
|
26
|
+
reject(err)
|
|
27
|
+
} else {
|
|
28
|
+
resolve({ response, body })
|
|
29
|
+
}
|
|
30
|
+
})
|
|
31
|
+
})
|
|
32
|
+
}
|
|
18
33
|
|
|
19
34
|
test('Once called, Reply should return an object with methods', t => {
|
|
20
35
|
t.plan(13)
|
|
@@ -37,7 +52,7 @@ test('Once called, Reply should return an object with methods', t => {
|
|
|
37
52
|
t.equal(reply.request, request)
|
|
38
53
|
})
|
|
39
54
|
|
|
40
|
-
test('reply.send will logStream error and destroy the stream',
|
|
55
|
+
test('reply.send will logStream error and destroy the stream', t => {
|
|
41
56
|
t.plan(1)
|
|
42
57
|
let destroyCalled
|
|
43
58
|
const payload = new EventEmitter()
|
|
@@ -452,8 +467,6 @@ test('stream with content type should not send application/octet-stream', t => {
|
|
|
452
467
|
t.plan(4)
|
|
453
468
|
|
|
454
469
|
const fastify = require('../..')()
|
|
455
|
-
const fs = require('fs')
|
|
456
|
-
const path = require('path')
|
|
457
470
|
|
|
458
471
|
const streamPath = path.join(__dirname, '..', '..', 'package.json')
|
|
459
472
|
const stream = fs.createReadStream(streamPath)
|
|
@@ -477,6 +490,32 @@ test('stream with content type should not send application/octet-stream', t => {
|
|
|
477
490
|
})
|
|
478
491
|
})
|
|
479
492
|
|
|
493
|
+
test('stream without content type should not send application/octet-stream', t => {
|
|
494
|
+
t.plan(4)
|
|
495
|
+
|
|
496
|
+
const fastify = require('../..')()
|
|
497
|
+
|
|
498
|
+
const stream = fs.createReadStream(__filename)
|
|
499
|
+
const buf = fs.readFileSync(__filename)
|
|
500
|
+
|
|
501
|
+
fastify.get('/', function (req, reply) {
|
|
502
|
+
reply.send(stream)
|
|
503
|
+
})
|
|
504
|
+
|
|
505
|
+
fastify.listen(0, err => {
|
|
506
|
+
t.error(err)
|
|
507
|
+
fastify.server.unref()
|
|
508
|
+
sget({
|
|
509
|
+
method: 'GET',
|
|
510
|
+
url: 'http://localhost:' + fastify.server.address().port
|
|
511
|
+
}, (err, response, body) => {
|
|
512
|
+
t.error(err)
|
|
513
|
+
t.equal(response.headers['content-type'], undefined)
|
|
514
|
+
t.same(body, buf)
|
|
515
|
+
})
|
|
516
|
+
})
|
|
517
|
+
})
|
|
518
|
+
|
|
480
519
|
test('stream using reply.raw.writeHead should return customize headers', t => {
|
|
481
520
|
t.plan(6)
|
|
482
521
|
|
|
@@ -1301,6 +1340,34 @@ test('reply.header setting multiple cookies as multiple Set-Cookie headers', t =
|
|
|
1301
1340
|
})
|
|
1302
1341
|
})
|
|
1303
1342
|
|
|
1343
|
+
test('should emit deprecation warning when trying to modify the reply.sent property', t => {
|
|
1344
|
+
t.plan(4)
|
|
1345
|
+
const fastify = require('../..')()
|
|
1346
|
+
|
|
1347
|
+
const deprecationCode = 'FSTDEP010'
|
|
1348
|
+
warning.emitted.delete(deprecationCode)
|
|
1349
|
+
|
|
1350
|
+
process.removeAllListeners('warning')
|
|
1351
|
+
process.on('warning', onWarning)
|
|
1352
|
+
function onWarning (warning) {
|
|
1353
|
+
t.equal(warning.name, 'FastifyDeprecation')
|
|
1354
|
+
t.equal(warning.code, deprecationCode)
|
|
1355
|
+
}
|
|
1356
|
+
|
|
1357
|
+
fastify.get('/', (req, reply) => {
|
|
1358
|
+
reply.sent = true
|
|
1359
|
+
|
|
1360
|
+
reply.raw.end()
|
|
1361
|
+
})
|
|
1362
|
+
|
|
1363
|
+
fastify.inject('/', (err, res) => {
|
|
1364
|
+
t.error(err)
|
|
1365
|
+
t.pass()
|
|
1366
|
+
|
|
1367
|
+
process.removeListener('warning', onWarning)
|
|
1368
|
+
})
|
|
1369
|
+
})
|
|
1370
|
+
|
|
1304
1371
|
test('should throw error when passing falsy value to reply.sent', t => {
|
|
1305
1372
|
t.plan(4)
|
|
1306
1373
|
const fastify = require('../..')()
|
|
@@ -1329,6 +1396,7 @@ test('should throw error when attempting to set reply.sent more than once', t =>
|
|
|
1329
1396
|
reply.sent = true
|
|
1330
1397
|
try {
|
|
1331
1398
|
reply.sent = true
|
|
1399
|
+
t.fail('must throw')
|
|
1332
1400
|
} catch (err) {
|
|
1333
1401
|
t.equal(err.code, 'FST_ERR_REP_ALREADY_SENT')
|
|
1334
1402
|
t.equal(err.message, 'Reply was already sent.')
|
|
@@ -1342,6 +1410,23 @@ test('should throw error when attempting to set reply.sent more than once', t =>
|
|
|
1342
1410
|
})
|
|
1343
1411
|
})
|
|
1344
1412
|
|
|
1413
|
+
test('should not throw error when attempting to set reply.sent if the underlining request was sent', t => {
|
|
1414
|
+
t.plan(3)
|
|
1415
|
+
const fastify = require('../..')()
|
|
1416
|
+
|
|
1417
|
+
fastify.get('/', function (req, reply) {
|
|
1418
|
+
reply.raw.end()
|
|
1419
|
+
t.doesNotThrow(() => {
|
|
1420
|
+
reply.sent = true
|
|
1421
|
+
})
|
|
1422
|
+
})
|
|
1423
|
+
|
|
1424
|
+
fastify.inject('/', (err, res) => {
|
|
1425
|
+
t.error(err)
|
|
1426
|
+
t.pass()
|
|
1427
|
+
})
|
|
1428
|
+
})
|
|
1429
|
+
|
|
1345
1430
|
test('reply.getResponseTime() should return 0 before the timer is initialised on the reply by setting up response listeners', t => {
|
|
1346
1431
|
t.plan(1)
|
|
1347
1432
|
const response = { statusCode: 200 }
|
|
@@ -1727,3 +1812,160 @@ test('reply.then', t => {
|
|
|
1727
1812
|
response.destroy(_err)
|
|
1728
1813
|
})
|
|
1729
1814
|
})
|
|
1815
|
+
|
|
1816
|
+
test('reply.sent should read from response.writableEnded if it is defined', t => {
|
|
1817
|
+
t.plan(1)
|
|
1818
|
+
|
|
1819
|
+
const reply = new Reply({ writableEnded: true }, {}, {})
|
|
1820
|
+
|
|
1821
|
+
t.equal(reply.sent, true)
|
|
1822
|
+
})
|
|
1823
|
+
|
|
1824
|
+
test('redirect to an invalid URL should not crash the server', async t => {
|
|
1825
|
+
const fastify = require('../..')()
|
|
1826
|
+
fastify.route({
|
|
1827
|
+
method: 'GET',
|
|
1828
|
+
url: '/redirect',
|
|
1829
|
+
handler: (req, reply) => {
|
|
1830
|
+
reply.log.warn = function mockWarn (obj, message) {
|
|
1831
|
+
t.equal(message, 'Invalid character in header content ["location"]')
|
|
1832
|
+
}
|
|
1833
|
+
|
|
1834
|
+
switch (req.query.useCase) {
|
|
1835
|
+
case '1':
|
|
1836
|
+
reply.redirect('/?key=a’b')
|
|
1837
|
+
break
|
|
1838
|
+
|
|
1839
|
+
case '2':
|
|
1840
|
+
reply.redirect(encodeURI('/?key=a’b'))
|
|
1841
|
+
break
|
|
1842
|
+
|
|
1843
|
+
default:
|
|
1844
|
+
reply.redirect('/?key=ab')
|
|
1845
|
+
break
|
|
1846
|
+
}
|
|
1847
|
+
}
|
|
1848
|
+
})
|
|
1849
|
+
|
|
1850
|
+
await fastify.listen(0)
|
|
1851
|
+
|
|
1852
|
+
{
|
|
1853
|
+
const { response, body } = await doGet(`http://localhost:${fastify.server.address().port}/redirect?useCase=1`)
|
|
1854
|
+
t.equal(response.statusCode, 500)
|
|
1855
|
+
t.same(JSON.parse(body), {
|
|
1856
|
+
statusCode: 500,
|
|
1857
|
+
code: 'ERR_INVALID_CHAR',
|
|
1858
|
+
error: 'Internal Server Error',
|
|
1859
|
+
message: 'Invalid character in header content ["location"]'
|
|
1860
|
+
})
|
|
1861
|
+
}
|
|
1862
|
+
{
|
|
1863
|
+
const { response } = await doGet(`http://localhost:${fastify.server.address().port}/redirect?useCase=2`)
|
|
1864
|
+
t.equal(response.statusCode, 302)
|
|
1865
|
+
t.equal(response.headers.location, '/?key=a%E2%80%99b')
|
|
1866
|
+
}
|
|
1867
|
+
|
|
1868
|
+
{
|
|
1869
|
+
const { response } = await doGet(`http://localhost:${fastify.server.address().port}/redirect?useCase=3`)
|
|
1870
|
+
t.equal(response.statusCode, 302)
|
|
1871
|
+
t.equal(response.headers.location, '/?key=ab')
|
|
1872
|
+
}
|
|
1873
|
+
|
|
1874
|
+
await fastify.close()
|
|
1875
|
+
})
|
|
1876
|
+
|
|
1877
|
+
test('invalid response headers should not crash the server', async t => {
|
|
1878
|
+
const fastify = require('../..')()
|
|
1879
|
+
fastify.route({
|
|
1880
|
+
method: 'GET',
|
|
1881
|
+
url: '/bad-headers',
|
|
1882
|
+
handler: (req, reply) => {
|
|
1883
|
+
reply.log.warn = function mockWarn (obj, message) {
|
|
1884
|
+
t.equal(message, 'Invalid character in header content ["smile-encoded"]', 'only the first invalid header is logged')
|
|
1885
|
+
}
|
|
1886
|
+
|
|
1887
|
+
reply.header('foo', '$')
|
|
1888
|
+
reply.header('smile-encoded', '\uD83D\uDE00')
|
|
1889
|
+
reply.header('smile', '😄')
|
|
1890
|
+
reply.header('bar', 'ƒ∂å')
|
|
1891
|
+
|
|
1892
|
+
reply.send({})
|
|
1893
|
+
}
|
|
1894
|
+
})
|
|
1895
|
+
|
|
1896
|
+
await fastify.listen(0)
|
|
1897
|
+
|
|
1898
|
+
const { response, body } = await doGet(`http://localhost:${fastify.server.address().port}/bad-headers`)
|
|
1899
|
+
t.equal(response.statusCode, 500)
|
|
1900
|
+
t.same(JSON.parse(body), {
|
|
1901
|
+
statusCode: 500,
|
|
1902
|
+
code: 'ERR_INVALID_CHAR',
|
|
1903
|
+
error: 'Internal Server Error',
|
|
1904
|
+
message: 'Invalid character in header content ["smile-encoded"]'
|
|
1905
|
+
})
|
|
1906
|
+
|
|
1907
|
+
await fastify.close()
|
|
1908
|
+
})
|
|
1909
|
+
|
|
1910
|
+
test('invalid response headers when sending back an error', async t => {
|
|
1911
|
+
const fastify = require('../..')()
|
|
1912
|
+
fastify.route({
|
|
1913
|
+
method: 'GET',
|
|
1914
|
+
url: '/bad-headers',
|
|
1915
|
+
handler: (req, reply) => {
|
|
1916
|
+
reply.log.warn = function mockWarn (obj, message) {
|
|
1917
|
+
t.equal(message, 'Invalid character in header content ["smile"]', 'only the first invalid header is logged')
|
|
1918
|
+
}
|
|
1919
|
+
|
|
1920
|
+
reply.header('smile', '😄')
|
|
1921
|
+
reply.send(new Error('user land error'))
|
|
1922
|
+
}
|
|
1923
|
+
})
|
|
1924
|
+
|
|
1925
|
+
await fastify.listen(0)
|
|
1926
|
+
|
|
1927
|
+
const { response, body } = await doGet(`http://localhost:${fastify.server.address().port}/bad-headers`)
|
|
1928
|
+
t.equal(response.statusCode, 500)
|
|
1929
|
+
t.same(JSON.parse(body), {
|
|
1930
|
+
statusCode: 500,
|
|
1931
|
+
code: 'ERR_INVALID_CHAR',
|
|
1932
|
+
error: 'Internal Server Error',
|
|
1933
|
+
message: 'Invalid character in header content ["smile"]'
|
|
1934
|
+
})
|
|
1935
|
+
|
|
1936
|
+
await fastify.close()
|
|
1937
|
+
})
|
|
1938
|
+
|
|
1939
|
+
test('invalid response headers and custom error handler', async t => {
|
|
1940
|
+
const fastify = require('../..')()
|
|
1941
|
+
fastify.route({
|
|
1942
|
+
method: 'GET',
|
|
1943
|
+
url: '/bad-headers',
|
|
1944
|
+
handler: (req, reply) => {
|
|
1945
|
+
reply.log.warn = function mockWarn (obj, message) {
|
|
1946
|
+
t.equal(message, 'Invalid character in header content ["smile"]', 'only the first invalid header is logged')
|
|
1947
|
+
}
|
|
1948
|
+
|
|
1949
|
+
reply.header('smile', '😄')
|
|
1950
|
+
reply.send(new Error('user land error'))
|
|
1951
|
+
}
|
|
1952
|
+
})
|
|
1953
|
+
|
|
1954
|
+
fastify.setErrorHandler(function (error, request, reply) {
|
|
1955
|
+
t.equal(error.message, 'user land error', 'custom error handler receives the error')
|
|
1956
|
+
reply.status(500).send({ ops: true })
|
|
1957
|
+
})
|
|
1958
|
+
|
|
1959
|
+
await fastify.listen(0)
|
|
1960
|
+
|
|
1961
|
+
const { response, body } = await doGet(`http://localhost:${fastify.server.address().port}/bad-headers`)
|
|
1962
|
+
t.equal(response.statusCode, 500)
|
|
1963
|
+
t.same(JSON.parse(body), {
|
|
1964
|
+
statusCode: 500,
|
|
1965
|
+
code: 'ERR_INVALID_CHAR',
|
|
1966
|
+
error: 'Internal Server Error',
|
|
1967
|
+
message: 'Invalid character in header content ["smile"]'
|
|
1968
|
+
})
|
|
1969
|
+
|
|
1970
|
+
await fastify.close()
|
|
1971
|
+
})
|
|
@@ -4,8 +4,9 @@ const { test } = require('tap')
|
|
|
4
4
|
|
|
5
5
|
const Request = require('../../lib/request')
|
|
6
6
|
|
|
7
|
+
process.removeAllListeners('warning')
|
|
8
|
+
|
|
7
9
|
test('Regular request', t => {
|
|
8
|
-
t.plan(15)
|
|
9
10
|
const headers = {
|
|
10
11
|
host: 'hostname'
|
|
11
12
|
}
|
|
@@ -15,22 +16,27 @@ test('Regular request', t => {
|
|
|
15
16
|
socket: { remoteAddress: 'ip' },
|
|
16
17
|
headers
|
|
17
18
|
}
|
|
19
|
+
req.connection = req.socket
|
|
18
20
|
const request = new Request('id', 'params', req, 'query', 'log')
|
|
19
21
|
t.type(request, Request)
|
|
20
22
|
t.equal(request.id, 'id')
|
|
21
23
|
t.equal(request.params, 'params')
|
|
22
|
-
t.
|
|
24
|
+
t.equal(request.raw, req)
|
|
23
25
|
t.equal(request.query, 'query')
|
|
24
26
|
t.equal(request.headers, headers)
|
|
25
27
|
t.equal(request.log, 'log')
|
|
26
28
|
t.equal(request.ip, 'ip')
|
|
27
29
|
t.equal(request.ips, undefined)
|
|
28
30
|
t.equal(request.hostname, 'hostname')
|
|
29
|
-
t.equal(request.body,
|
|
31
|
+
t.equal(request.body, undefined)
|
|
30
32
|
t.equal(request.method, 'GET')
|
|
31
33
|
t.equal(request.url, '/')
|
|
34
|
+
t.equal(request.socket, req.socket)
|
|
32
35
|
t.equal(request.protocol, 'http')
|
|
33
|
-
|
|
36
|
+
|
|
37
|
+
// This will be removed, it's deprecated
|
|
38
|
+
t.equal(request.connection, req.connection)
|
|
39
|
+
t.end()
|
|
34
40
|
})
|
|
35
41
|
|
|
36
42
|
test('Regular request - hostname from authority', t => {
|
|
@@ -92,11 +98,11 @@ test('Request with trust proxy', t => {
|
|
|
92
98
|
t.equal(request.ip, '2.2.2.2')
|
|
93
99
|
t.same(request.ips, ['ip', '1.1.1.1', '2.2.2.2'])
|
|
94
100
|
t.equal(request.hostname, 'example.com')
|
|
95
|
-
t.equal(request.body,
|
|
101
|
+
t.equal(request.body, undefined)
|
|
96
102
|
t.equal(request.method, 'GET')
|
|
97
103
|
t.equal(request.url, '/')
|
|
104
|
+
t.equal(request.socket, req.socket)
|
|
98
105
|
t.equal(request.protocol, 'http')
|
|
99
|
-
t.same(request.socket, req.socket)
|
|
100
106
|
})
|
|
101
107
|
|
|
102
108
|
test('Request with trust proxy, encrypted', t => {
|
|
@@ -236,7 +242,7 @@ test('Request with undefined socket', t => {
|
|
|
236
242
|
t.equal(request.ip, undefined)
|
|
237
243
|
t.equal(request.ips, undefined)
|
|
238
244
|
t.equal(request.hostname, 'hostname')
|
|
239
|
-
t.
|
|
245
|
+
t.same(request.body, null)
|
|
240
246
|
t.equal(request.method, 'GET')
|
|
241
247
|
t.equal(request.url, '/')
|
|
242
248
|
t.equal(request.protocol, undefined)
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { test } = require('tap')
|
|
4
|
+
const proxyquire = require('proxyquire')
|
|
5
|
+
|
|
6
|
+
const Fastify = require('../../fastify')
|
|
7
|
+
const createServer = require('../../lib/server')
|
|
8
|
+
|
|
9
|
+
const handler = (req, res) => {
|
|
10
|
+
res.writeHead(200, { 'Content-Type': 'application/json' })
|
|
11
|
+
res.end(JSON.stringify({ data: 'Hello World!' }))
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
test('start listening', async t => {
|
|
15
|
+
const { server, listen } = createServer({}, handler)
|
|
16
|
+
await listen.call(Fastify(), 0, 'localhost')
|
|
17
|
+
server.close()
|
|
18
|
+
t.pass('server started')
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
test('DNS errors does not stop the main server on localhost - promise interface', async t => {
|
|
22
|
+
const createServer = proxyquire('../../lib/server', {
|
|
23
|
+
dns: {
|
|
24
|
+
lookup: (hostname, options, cb) => {
|
|
25
|
+
cb(new Error('DNS error'))
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
})
|
|
29
|
+
const { server, listen } = createServer({}, handler)
|
|
30
|
+
await listen.call(Fastify(), 0, 'localhost')
|
|
31
|
+
server.close()
|
|
32
|
+
t.pass('server started')
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
test('DNS errors does not stop the main server on localhost - callback interface', t => {
|
|
36
|
+
t.plan(2)
|
|
37
|
+
const createServer = proxyquire('../../lib/server', {
|
|
38
|
+
dns: {
|
|
39
|
+
lookup: (hostname, options, cb) => {
|
|
40
|
+
cb(new Error('DNS error'))
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
})
|
|
44
|
+
const { server, listen } = createServer({}, handler)
|
|
45
|
+
listen.call(Fastify(), 0, 'localhost', (err) => {
|
|
46
|
+
t.error(err)
|
|
47
|
+
server.close()
|
|
48
|
+
t.pass('server started')
|
|
49
|
+
})
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
test('DNS returns empty binding', t => {
|
|
53
|
+
t.plan(2)
|
|
54
|
+
const createServer = proxyquire('../../lib/server', {
|
|
55
|
+
dns: {
|
|
56
|
+
lookup: (hostname, options, cb) => {
|
|
57
|
+
cb(null, [])
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
})
|
|
61
|
+
const { server, listen } = createServer({}, handler)
|
|
62
|
+
listen.call(Fastify(), 0, 'localhost', (err) => {
|
|
63
|
+
t.error(err)
|
|
64
|
+
server.close()
|
|
65
|
+
t.pass('server started')
|
|
66
|
+
})
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
test('DNS returns more than two binding', t => {
|
|
70
|
+
t.plan(2)
|
|
71
|
+
const createServer = proxyquire('../../lib/server', {
|
|
72
|
+
dns: {
|
|
73
|
+
lookup: (hostname, options, cb) => {
|
|
74
|
+
cb(null, [
|
|
75
|
+
{ address: '::1', family: 6 },
|
|
76
|
+
{ address: '127.0.0.1', family: 4 },
|
|
77
|
+
{ address: '0.0.0.0', family: 4 }
|
|
78
|
+
])
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
})
|
|
82
|
+
const { server, listen } = createServer({}, handler)
|
|
83
|
+
listen.call(Fastify(), 0, 'localhost', (err) => {
|
|
84
|
+
t.error(err)
|
|
85
|
+
server.close()
|
|
86
|
+
t.pass('server started')
|
|
87
|
+
})
|
|
88
|
+
})
|
package/test/listen.test.js
CHANGED
|
@@ -4,8 +4,10 @@ const os = require('os')
|
|
|
4
4
|
const path = require('path')
|
|
5
5
|
const fs = require('fs')
|
|
6
6
|
const { test, before } = require('tap')
|
|
7
|
-
const Fastify = require('..')
|
|
8
7
|
const dns = require('dns').promises
|
|
8
|
+
const dnsCb = require('dns')
|
|
9
|
+
const sget = require('simple-get').concat
|
|
10
|
+
const Fastify = require('..')
|
|
9
11
|
|
|
10
12
|
let localhost
|
|
11
13
|
let localhostForURL
|
|
@@ -393,3 +395,84 @@ test('listen when firstArg is string(pipe) and with backlog', async t => {
|
|
|
393
395
|
const address = await fastify.listen('\\\\.\\pipe\\testPipe', 511)
|
|
394
396
|
t.equal(address, '\\\\.\\pipe\\testPipe')
|
|
395
397
|
})
|
|
398
|
+
|
|
399
|
+
test('listen on localhost binds IPv4 and IPv6 - promise interface', async t => {
|
|
400
|
+
const lookups = await dns.lookup('localhost', { all: true })
|
|
401
|
+
t.plan(2 * lookups.length)
|
|
402
|
+
|
|
403
|
+
const app = Fastify()
|
|
404
|
+
app.get('/', async () => 'hello localhost')
|
|
405
|
+
t.teardown(app.close.bind(app))
|
|
406
|
+
await app.listen(0, 'localhost')
|
|
407
|
+
|
|
408
|
+
for (const lookup of lookups) {
|
|
409
|
+
await new Promise((resolve, reject) => {
|
|
410
|
+
sget({
|
|
411
|
+
method: 'GET',
|
|
412
|
+
url: getUrl(app, lookup)
|
|
413
|
+
}, (err, response, body) => {
|
|
414
|
+
if (err) { return reject(err) }
|
|
415
|
+
t.equal(response.statusCode, 200)
|
|
416
|
+
t.same(body.toString(), 'hello localhost')
|
|
417
|
+
resolve()
|
|
418
|
+
})
|
|
419
|
+
})
|
|
420
|
+
}
|
|
421
|
+
})
|
|
422
|
+
|
|
423
|
+
test('listen on localhost binds to all interfaces (both IPv4 and IPv6 if present) - callback interface', t => {
|
|
424
|
+
dnsCb.lookup('localhost', { all: true }, (err, lookups) => {
|
|
425
|
+
t.plan(2 + (3 * lookups.length))
|
|
426
|
+
t.error(err)
|
|
427
|
+
|
|
428
|
+
const app = Fastify()
|
|
429
|
+
app.get('/', async () => 'hello localhost')
|
|
430
|
+
app.listen(0, 'localhost', (err) => {
|
|
431
|
+
t.error(err)
|
|
432
|
+
t.teardown(app.close.bind(app))
|
|
433
|
+
|
|
434
|
+
for (const lookup of lookups) {
|
|
435
|
+
sget({
|
|
436
|
+
method: 'GET',
|
|
437
|
+
url: getUrl(app, lookup)
|
|
438
|
+
}, (err, response, body) => {
|
|
439
|
+
t.error(err)
|
|
440
|
+
t.equal(response.statusCode, 200)
|
|
441
|
+
t.same(body.toString(), 'hello localhost')
|
|
442
|
+
})
|
|
443
|
+
}
|
|
444
|
+
})
|
|
445
|
+
})
|
|
446
|
+
})
|
|
447
|
+
|
|
448
|
+
test('addresses getter', async t => {
|
|
449
|
+
t.plan(4)
|
|
450
|
+
const app = Fastify()
|
|
451
|
+
app.get('/', async () => 'hello localhost')
|
|
452
|
+
|
|
453
|
+
t.same(app.addresses(), [], 'before ready')
|
|
454
|
+
await app.ready()
|
|
455
|
+
|
|
456
|
+
t.same(app.addresses(), [], 'after ready')
|
|
457
|
+
await app.listen(0, 'localhost')
|
|
458
|
+
const { port } = app.server.address()
|
|
459
|
+
const localAddresses = await dns.lookup('localhost', { all: true })
|
|
460
|
+
for (const address of localAddresses) {
|
|
461
|
+
address.port = port
|
|
462
|
+
address.family = 'IPv' + address.family
|
|
463
|
+
}
|
|
464
|
+
localAddresses.sort((a, b) => a.address.localeCompare(b.address))
|
|
465
|
+
t.same(app.addresses().sort((a, b) => a.address.localeCompare(b.address)), localAddresses, 'after listen')
|
|
466
|
+
|
|
467
|
+
await app.close()
|
|
468
|
+
t.same(app.addresses(), [], 'after close')
|
|
469
|
+
})
|
|
470
|
+
|
|
471
|
+
function getUrl (fastify, lookup) {
|
|
472
|
+
const { port } = fastify.server.address()
|
|
473
|
+
if (lookup.family === 6) {
|
|
474
|
+
return `http://[${lookup.address}]:${port}/`
|
|
475
|
+
} else {
|
|
476
|
+
return `http://${lookup.address}:${port}/`
|
|
477
|
+
}
|
|
478
|
+
}
|