fastify 4.2.0 → 4.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -14
- package/docs/Guides/Database.md +2 -2
- package/docs/Guides/Ecosystem.md +42 -18
- package/docs/Guides/Migration-Guide-V4.md +29 -0
- package/docs/Guides/Plugins-Guide.md +5 -0
- package/docs/Reference/HTTP2.md +1 -3
- package/docs/Reference/Hooks.md +4 -1
- package/docs/Reference/Logging.md +1 -1
- package/docs/Reference/Reply.md +177 -0
- package/docs/Reference/Request.md +171 -0
- package/docs/Reference/Routes.md +4 -2
- package/docs/Reference/Server.md +4 -1
- package/docs/Reference/Type-Providers.md +1 -15
- package/docs/Reference/TypeScript.md +27 -3
- package/fastify.d.ts +1 -1
- package/fastify.js +3 -3
- package/lib/contentTypeParser.js +10 -2
- package/lib/context.js +10 -1
- package/lib/error-serializer.js +12 -12
- package/lib/errors.js +8 -0
- package/lib/handleRequest.js +2 -2
- package/lib/httpMethods.js +22 -0
- package/lib/reply.js +101 -24
- package/lib/request.js +97 -1
- package/lib/route.js +3 -1
- package/lib/symbols.js +15 -9
- package/package.json +7 -7
- package/test/build/error-serializer.test.js +3 -1
- package/test/content-parser.test.js +15 -0
- package/test/copy.test.js +41 -0
- package/test/internals/all.test.js +8 -2
- package/test/internals/reply-serialize.test.js +583 -0
- package/test/internals/reply.test.js +4 -1
- package/test/internals/request-validate.test.js +1269 -0
- package/test/internals/request.test.js +11 -2
- package/test/lock.test.js +73 -0
- package/test/mkcol.test.js +38 -0
- package/test/move.test.js +45 -0
- package/test/propfind.test.js +108 -0
- package/test/proppatch.test.js +78 -0
- package/test/request-error.test.js +44 -1
- package/test/schema-validation.test.js +71 -0
- package/test/search.test.js +100 -0
- package/test/trace.test.js +21 -0
- package/test/types/fastify.test-d.ts +12 -1
- package/test/types/hooks.test-d.ts +1 -2
- package/test/types/import.ts +1 -1
- package/test/types/instance.test-d.ts +4 -1
- package/test/types/logger.test-d.ts +4 -5
- package/test/types/reply.test-d.ts +44 -3
- package/test/types/request.test-d.ts +10 -29
- package/test/types/type-provider.test-d.ts +79 -7
- package/test/unlock.test.js +41 -0
- package/test/validation-error-handling.test.js +6 -1
- package/types/hooks.d.ts +19 -39
- package/types/instance.d.ts +78 -130
- package/types/logger.d.ts +7 -4
- package/types/reply.d.ts +7 -1
- package/types/request.d.ts +16 -3
- package/types/route.d.ts +23 -29
- package/types/schema.d.ts +4 -1
- package/types/type-provider.d.ts +8 -20
- package/types/utils.d.ts +2 -1
|
@@ -19,6 +19,9 @@ test('Regular request', t => {
|
|
|
19
19
|
req.connection = req.socket
|
|
20
20
|
const request = new Request('id', 'params', req, 'query', 'log')
|
|
21
21
|
t.type(request, Request)
|
|
22
|
+
t.type(request.validateInput, Function)
|
|
23
|
+
t.type(request.getValidationFunction, Function)
|
|
24
|
+
t.type(request.compileValidationSchema, Function)
|
|
22
25
|
t.equal(request.id, 'id')
|
|
23
26
|
t.equal(request.params, 'params')
|
|
24
27
|
t.equal(request.raw, req)
|
|
@@ -74,7 +77,7 @@ test('Regular request - host header has precedence over authority', t => {
|
|
|
74
77
|
})
|
|
75
78
|
|
|
76
79
|
test('Request with trust proxy', t => {
|
|
77
|
-
t.plan(
|
|
80
|
+
t.plan(18)
|
|
78
81
|
const headers = {
|
|
79
82
|
'x-forwarded-for': '2.2.2.2, 1.1.1.1',
|
|
80
83
|
'x-forwarded-host': 'example.com'
|
|
@@ -103,6 +106,9 @@ test('Request with trust proxy', t => {
|
|
|
103
106
|
t.equal(request.url, '/')
|
|
104
107
|
t.equal(request.socket, req.socket)
|
|
105
108
|
t.equal(request.protocol, 'http')
|
|
109
|
+
t.type(request.validateInput, Function)
|
|
110
|
+
t.type(request.getValidationFunction, Function)
|
|
111
|
+
t.type(request.compileValidationSchema, Function)
|
|
106
112
|
})
|
|
107
113
|
|
|
108
114
|
test('Request with trust proxy, encrypted', t => {
|
|
@@ -221,7 +227,7 @@ test('Request with trust proxy - plain', t => {
|
|
|
221
227
|
})
|
|
222
228
|
|
|
223
229
|
test('Request with undefined socket', t => {
|
|
224
|
-
t.plan(
|
|
230
|
+
t.plan(18)
|
|
225
231
|
const headers = {
|
|
226
232
|
host: 'hostname'
|
|
227
233
|
}
|
|
@@ -247,6 +253,9 @@ test('Request with undefined socket', t => {
|
|
|
247
253
|
t.equal(request.url, '/')
|
|
248
254
|
t.equal(request.protocol, undefined)
|
|
249
255
|
t.same(request.socket, req.socket)
|
|
256
|
+
t.type(request.validateInput, Function)
|
|
257
|
+
t.type(request.getValidationFunction, Function)
|
|
258
|
+
t.type(request.compileValidationSchema, Function)
|
|
250
259
|
})
|
|
251
260
|
|
|
252
261
|
test('Request with trust proxy and undefined socket', t => {
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const t = require('tap')
|
|
4
|
+
const test = t.test
|
|
5
|
+
const sget = require('simple-get').concat
|
|
6
|
+
const fastify = require('..')()
|
|
7
|
+
|
|
8
|
+
test('can be created - lock', t => {
|
|
9
|
+
t.plan(1)
|
|
10
|
+
try {
|
|
11
|
+
fastify.route({
|
|
12
|
+
method: 'LOCK',
|
|
13
|
+
url: '*',
|
|
14
|
+
handler: function (req, reply) {
|
|
15
|
+
reply
|
|
16
|
+
.code(200)
|
|
17
|
+
.send(`<?xml version="1.0" encoding="utf-8" ?>
|
|
18
|
+
<D:prop xmlns:D="DAV:">
|
|
19
|
+
<D:lockdiscovery>
|
|
20
|
+
<D:activelock>
|
|
21
|
+
<D:locktype>
|
|
22
|
+
<D:write/>
|
|
23
|
+
</D:locktype>
|
|
24
|
+
<D:lockscope>
|
|
25
|
+
<D:exclusive/>
|
|
26
|
+
</D:lockscope>
|
|
27
|
+
<D:depth>infinity</D:depth>
|
|
28
|
+
<D:owner>
|
|
29
|
+
<D:href>http://example.org/~ejw/contact.html</D:href>
|
|
30
|
+
</D:owner>
|
|
31
|
+
<D:timeout>Second-604800</D:timeout>
|
|
32
|
+
<D:locktoken>
|
|
33
|
+
<D:href>urn:uuid:e71d4fae-5dec-22d6-fea5-00a0c91e6be4</:href>
|
|
34
|
+
</D:locktoken>
|
|
35
|
+
<D:lockroot>
|
|
36
|
+
<D:href>http://example.com/workspace/webdav/proposal.oc</D:href>
|
|
37
|
+
</D:lockroot>
|
|
38
|
+
</D:activelock>
|
|
39
|
+
</D:lockdiscovery>
|
|
40
|
+
</D:prop>`
|
|
41
|
+
)
|
|
42
|
+
}
|
|
43
|
+
})
|
|
44
|
+
t.pass()
|
|
45
|
+
} catch (e) {
|
|
46
|
+
t.fail()
|
|
47
|
+
}
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
fastify.listen({ port: 0 }, err => {
|
|
51
|
+
t.error(err)
|
|
52
|
+
t.teardown(() => { fastify.close() })
|
|
53
|
+
|
|
54
|
+
test('request - lock', t => {
|
|
55
|
+
t.plan(3)
|
|
56
|
+
sget({
|
|
57
|
+
url: `http://localhost:${fastify.server.address().port}/test/a.txt`,
|
|
58
|
+
body: `<?xml version="1.0" encoding="utf-8" ?>
|
|
59
|
+
<D:lockinfo xmlns:D='DAV:'>
|
|
60
|
+
<D:lockscope> <D:exclusive/> </D:lockscope>
|
|
61
|
+
<D:locktype> <D:write/> </D:locktype>
|
|
62
|
+
<D:owner>
|
|
63
|
+
<D:href>http://example.org/~ejw/contact.html</D:href>
|
|
64
|
+
</D:owner>
|
|
65
|
+
</D:lockinfo> `,
|
|
66
|
+
method: 'LOCK'
|
|
67
|
+
}, (err, response, body) => {
|
|
68
|
+
t.error(err)
|
|
69
|
+
t.equal(response.statusCode, 200)
|
|
70
|
+
t.equal(response.headers['content-length'], '' + body.length)
|
|
71
|
+
})
|
|
72
|
+
})
|
|
73
|
+
})
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const t = require('tap')
|
|
4
|
+
const test = t.test
|
|
5
|
+
const sget = require('simple-get').concat
|
|
6
|
+
const fastify = require('..')()
|
|
7
|
+
|
|
8
|
+
test('can be created - mkcol', t => {
|
|
9
|
+
t.plan(1)
|
|
10
|
+
try {
|
|
11
|
+
fastify.route({
|
|
12
|
+
method: 'MKCOL',
|
|
13
|
+
url: '*',
|
|
14
|
+
handler: function (req, reply) {
|
|
15
|
+
reply.code(201).send()
|
|
16
|
+
}
|
|
17
|
+
})
|
|
18
|
+
t.pass()
|
|
19
|
+
} catch (e) {
|
|
20
|
+
t.fail()
|
|
21
|
+
}
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
fastify.listen({ port: 0 }, err => {
|
|
25
|
+
t.error(err)
|
|
26
|
+
t.teardown(() => { fastify.close() })
|
|
27
|
+
|
|
28
|
+
test('request - mkcol', t => {
|
|
29
|
+
t.plan(2)
|
|
30
|
+
sget({
|
|
31
|
+
url: `http://localhost:${fastify.server.address().port}/test/`,
|
|
32
|
+
method: 'MKCOL'
|
|
33
|
+
}, (err, response, body) => {
|
|
34
|
+
t.error(err)
|
|
35
|
+
t.equal(response.statusCode, 201)
|
|
36
|
+
})
|
|
37
|
+
})
|
|
38
|
+
})
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const t = require('tap')
|
|
4
|
+
const test = t.test
|
|
5
|
+
const sget = require('simple-get').concat
|
|
6
|
+
const fastify = require('..')()
|
|
7
|
+
|
|
8
|
+
test('shorthand - move', t => {
|
|
9
|
+
t.plan(1)
|
|
10
|
+
try {
|
|
11
|
+
fastify.route({
|
|
12
|
+
method: 'MOVE',
|
|
13
|
+
url: '*',
|
|
14
|
+
handler: function (req, reply) {
|
|
15
|
+
const destination = req.headers.destination
|
|
16
|
+
reply.code(201)
|
|
17
|
+
.header('location', destination)
|
|
18
|
+
.send()
|
|
19
|
+
}
|
|
20
|
+
})
|
|
21
|
+
t.pass()
|
|
22
|
+
} catch (e) {
|
|
23
|
+
t.fail()
|
|
24
|
+
}
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
fastify.listen({ port: 0 }, err => {
|
|
28
|
+
t.error(err)
|
|
29
|
+
t.teardown(() => { fastify.close() })
|
|
30
|
+
|
|
31
|
+
test('request - move', t => {
|
|
32
|
+
t.plan(3)
|
|
33
|
+
sget({
|
|
34
|
+
url: `http://localhost:${fastify.server.address().port}/test.txt`,
|
|
35
|
+
method: 'MOVE',
|
|
36
|
+
headers: {
|
|
37
|
+
Destination: '/test2.txt'
|
|
38
|
+
}
|
|
39
|
+
}, (err, response, body) => {
|
|
40
|
+
t.error(err)
|
|
41
|
+
t.equal(response.statusCode, 201)
|
|
42
|
+
t.equal(response.headers.location, '/test2.txt')
|
|
43
|
+
})
|
|
44
|
+
})
|
|
45
|
+
})
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const t = require('tap')
|
|
4
|
+
const test = t.test
|
|
5
|
+
const sget = require('simple-get').concat
|
|
6
|
+
const fastify = require('..')()
|
|
7
|
+
|
|
8
|
+
test('can be created - propfind', t => {
|
|
9
|
+
t.plan(1)
|
|
10
|
+
try {
|
|
11
|
+
fastify.route({
|
|
12
|
+
method: 'PROPFIND',
|
|
13
|
+
url: '*',
|
|
14
|
+
handler: function (req, reply) {
|
|
15
|
+
return reply.code(207)
|
|
16
|
+
.send(`<?xml version="1.0" encoding="utf-8"?>
|
|
17
|
+
<D:multistatus xmlns:D="DAV:">
|
|
18
|
+
<D:response xmlns:lp1="DAV:">
|
|
19
|
+
<D:href>/</D:href>
|
|
20
|
+
<D:propstat>
|
|
21
|
+
<D:prop>
|
|
22
|
+
<lp1:resourcetype>
|
|
23
|
+
<D:collection/>
|
|
24
|
+
</lp1:resourcetype>
|
|
25
|
+
<lp1:creationdate>2022-04-13T12:35:30Z</lp1:creationdate>
|
|
26
|
+
<lp1:getlastmodified>Wed, 13 Apr 2022 12:35:30 GMT</lp1:getlastmodified>
|
|
27
|
+
<lp1:getetag>"e0-5dc8869b53ef1"</lp1:getetag>
|
|
28
|
+
<D:supportedlock>
|
|
29
|
+
<D:lockentry>
|
|
30
|
+
<D:lockscope>
|
|
31
|
+
<D:exclusive/>
|
|
32
|
+
</D:lockscope>
|
|
33
|
+
<D:locktype>
|
|
34
|
+
<D:write/>
|
|
35
|
+
</D:locktype>
|
|
36
|
+
</D:lockentry>
|
|
37
|
+
<D:lockentry>
|
|
38
|
+
<D:lockscope>
|
|
39
|
+
<D:shared/>
|
|
40
|
+
</D:lockscope>
|
|
41
|
+
<D:locktype>
|
|
42
|
+
<D:write/>
|
|
43
|
+
</D:locktype>
|
|
44
|
+
</D:lockentry>
|
|
45
|
+
</D:supportedlock>
|
|
46
|
+
<D:lockdiscovery/>
|
|
47
|
+
<D:getcontenttype>httpd/unix-directory</D:getcontenttype>
|
|
48
|
+
</D:prop>
|
|
49
|
+
<D:status>HTTP/1.1 200 OK</D:status>
|
|
50
|
+
</D:propstat>
|
|
51
|
+
</D:response>
|
|
52
|
+
</D:multistatus>`
|
|
53
|
+
)
|
|
54
|
+
}
|
|
55
|
+
})
|
|
56
|
+
t.pass()
|
|
57
|
+
} catch (e) {
|
|
58
|
+
t.fail()
|
|
59
|
+
}
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
fastify.listen({ port: 0 }, err => {
|
|
63
|
+
t.error(err)
|
|
64
|
+
t.teardown(() => { fastify.close() })
|
|
65
|
+
|
|
66
|
+
test('request - propfind', t => {
|
|
67
|
+
t.plan(3)
|
|
68
|
+
sget({
|
|
69
|
+
url: `http://localhost:${fastify.server.address().port}/`,
|
|
70
|
+
method: 'PROPFIND'
|
|
71
|
+
}, (err, response, body) => {
|
|
72
|
+
t.error(err)
|
|
73
|
+
t.equal(response.statusCode, 207)
|
|
74
|
+
t.equal(response.headers['content-length'], '' + body.length)
|
|
75
|
+
})
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
test('request with other path - propfind', t => {
|
|
79
|
+
t.plan(3)
|
|
80
|
+
sget({
|
|
81
|
+
url: `http://localhost:${fastify.server.address().port}/test`,
|
|
82
|
+
method: 'PROPFIND'
|
|
83
|
+
}, (err, response, body) => {
|
|
84
|
+
t.error(err)
|
|
85
|
+
t.equal(response.statusCode, 207)
|
|
86
|
+
t.equal(response.headers['content-length'], '' + body.length)
|
|
87
|
+
})
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
test('request with body - propfind', t => {
|
|
91
|
+
t.plan(3)
|
|
92
|
+
sget({
|
|
93
|
+
url: `http://localhost:${fastify.server.address().port}/test`,
|
|
94
|
+
body: `<?xml version="1.0" encoding="utf-8" ?>
|
|
95
|
+
<D:propfind xmlns:D="DAV:">
|
|
96
|
+
<D:prop xmlns:R="http://ns.example.com/boxschema/">
|
|
97
|
+
<R:bigbox/> <R:author/> <R:DingALing/> <R:Random/>
|
|
98
|
+
</D:prop>
|
|
99
|
+
</D:propfind>
|
|
100
|
+
`,
|
|
101
|
+
method: 'PROPFIND'
|
|
102
|
+
}, (err, response, body) => {
|
|
103
|
+
t.error(err)
|
|
104
|
+
t.equal(response.statusCode, 207)
|
|
105
|
+
t.equal(response.headers['content-length'], '' + body.length)
|
|
106
|
+
})
|
|
107
|
+
})
|
|
108
|
+
})
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const t = require('tap')
|
|
4
|
+
const test = t.test
|
|
5
|
+
const sget = require('simple-get').concat
|
|
6
|
+
const fastify = require('..')()
|
|
7
|
+
|
|
8
|
+
test('shorthand - proppatch', t => {
|
|
9
|
+
t.plan(1)
|
|
10
|
+
try {
|
|
11
|
+
fastify.route({
|
|
12
|
+
method: 'PROPPATCH',
|
|
13
|
+
url: '*',
|
|
14
|
+
handler: function (req, reply) {
|
|
15
|
+
reply
|
|
16
|
+
.code(207)
|
|
17
|
+
.send(`<?xml version="1.0" encoding="utf-8" ?>
|
|
18
|
+
<D:multistatus xmlns:D="DAV:"
|
|
19
|
+
xmlns:Z="http://ns.example.com/standards/z39.50/">
|
|
20
|
+
<D:response>
|
|
21
|
+
<D:href>http://www.example.com/bar.html</D:href>
|
|
22
|
+
<D:propstat>
|
|
23
|
+
<D:prop>
|
|
24
|
+
<Z:Authors/>
|
|
25
|
+
</D:prop>
|
|
26
|
+
<D:status>HTTP/1.1 424 Failed Dependency</D:status>
|
|
27
|
+
</D:propstat>
|
|
28
|
+
<D:propstat>
|
|
29
|
+
<D:prop>
|
|
30
|
+
<Z:Copyright-Owner/>
|
|
31
|
+
</D:prop>
|
|
32
|
+
<D:status>HTTP/1.1 409 Conflict</D:status>
|
|
33
|
+
</D:propstat>
|
|
34
|
+
<D:responsedescription> Copyright Owner cannot be deleted or altered.</D:responsedescription>
|
|
35
|
+
</D:response>
|
|
36
|
+
</D:multistatus>`
|
|
37
|
+
)
|
|
38
|
+
}
|
|
39
|
+
})
|
|
40
|
+
t.pass()
|
|
41
|
+
} catch (e) {
|
|
42
|
+
t.fail()
|
|
43
|
+
}
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
fastify.listen({ port: 0 }, err => {
|
|
47
|
+
t.error(err)
|
|
48
|
+
t.teardown(() => { fastify.close() })
|
|
49
|
+
|
|
50
|
+
test('request - proppatch', t => {
|
|
51
|
+
t.plan(3)
|
|
52
|
+
sget({
|
|
53
|
+
url: `http://localhost:${fastify.server.address().port}/test/a.txt`,
|
|
54
|
+
body: `<?xml version="1.0" encoding="utf-8" ?>
|
|
55
|
+
<D:propertyupdate xmlns:D="DAV:"
|
|
56
|
+
xmlns:Z="http://ns.example.com/standards/z39.50/">
|
|
57
|
+
<D:set>
|
|
58
|
+
<D:prop>
|
|
59
|
+
<Z:Authors>
|
|
60
|
+
<Z:Author>Jim Whitehead</Z:Author>
|
|
61
|
+
<Z:Author>Roy Fielding</Z:Author>
|
|
62
|
+
</Z:Authors>
|
|
63
|
+
</D:prop>
|
|
64
|
+
</D:set>
|
|
65
|
+
<D:remove>
|
|
66
|
+
<D:prop>
|
|
67
|
+
<Z:Copyright-Owner/>
|
|
68
|
+
</D:prop>
|
|
69
|
+
</D:remove>
|
|
70
|
+
</D:propertyupdate>`,
|
|
71
|
+
method: 'PROPPATCH'
|
|
72
|
+
}, (err, response, body) => {
|
|
73
|
+
t.error(err)
|
|
74
|
+
t.equal(response.statusCode, 207)
|
|
75
|
+
t.equal(response.headers['content-length'], '' + body.length)
|
|
76
|
+
})
|
|
77
|
+
})
|
|
78
|
+
})
|
|
@@ -150,7 +150,7 @@ test('default clientError handler ignores sockets in destroyed state', t => {
|
|
|
150
150
|
})
|
|
151
151
|
|
|
152
152
|
test('default clientError handler destroys sockets in writable state', t => {
|
|
153
|
-
t.plan(
|
|
153
|
+
t.plan(2)
|
|
154
154
|
|
|
155
155
|
const fastify = Fastify({
|
|
156
156
|
bodyLimit: 1,
|
|
@@ -166,6 +166,9 @@ test('default clientError handler destroys sockets in writable state', t => {
|
|
|
166
166
|
},
|
|
167
167
|
destroy () {
|
|
168
168
|
t.pass('destroy should be called')
|
|
169
|
+
},
|
|
170
|
+
write (response) {
|
|
171
|
+
t.match(response, /^HTTP\/1.1 400 Bad Request/)
|
|
169
172
|
}
|
|
170
173
|
})
|
|
171
174
|
})
|
|
@@ -186,6 +189,9 @@ test('default clientError handler destroys http sockets in non-writable state',
|
|
|
186
189
|
},
|
|
187
190
|
destroy () {
|
|
188
191
|
t.pass('destroy should be called')
|
|
192
|
+
},
|
|
193
|
+
write (response) {
|
|
194
|
+
t.fail('write should not be called')
|
|
189
195
|
}
|
|
190
196
|
})
|
|
191
197
|
})
|
|
@@ -270,3 +276,40 @@ test('encapsulated error handler binding', t => {
|
|
|
270
276
|
t.equal(fastify.hello, undefined)
|
|
271
277
|
})
|
|
272
278
|
})
|
|
279
|
+
|
|
280
|
+
test('default clientError replies with bad request on reused keep-alive connection', t => {
|
|
281
|
+
t.plan(2)
|
|
282
|
+
|
|
283
|
+
let response = ''
|
|
284
|
+
|
|
285
|
+
const fastify = Fastify({
|
|
286
|
+
bodyLimit: 1,
|
|
287
|
+
keepAliveTimeout: 100
|
|
288
|
+
})
|
|
289
|
+
|
|
290
|
+
fastify.get('/', (request, reply) => {
|
|
291
|
+
reply.send('OK\n')
|
|
292
|
+
})
|
|
293
|
+
|
|
294
|
+
fastify.listen({ port: 0 }, function (err) {
|
|
295
|
+
t.error(err)
|
|
296
|
+
fastify.server.unref()
|
|
297
|
+
|
|
298
|
+
const client = connect(fastify.server.address().port)
|
|
299
|
+
|
|
300
|
+
client.on('data', chunk => {
|
|
301
|
+
response += chunk.toString('utf-8')
|
|
302
|
+
})
|
|
303
|
+
|
|
304
|
+
client.on('end', () => {
|
|
305
|
+
t.match(response, /^HTTP\/1.1 200 OK.*HTTP\/1.1 400 Bad Request/s)
|
|
306
|
+
})
|
|
307
|
+
|
|
308
|
+
client.resume()
|
|
309
|
+
client.write('GET / HTTP/1.1\r\n')
|
|
310
|
+
client.write('\r\n\r\n')
|
|
311
|
+
client.write('GET /?a b HTTP/1.1\r\n')
|
|
312
|
+
client.write('Connection: close\r\n')
|
|
313
|
+
client.write('\r\n\r\n')
|
|
314
|
+
})
|
|
315
|
+
})
|
|
@@ -4,6 +4,7 @@ const { test } = require('tap')
|
|
|
4
4
|
const Fastify = require('..')
|
|
5
5
|
|
|
6
6
|
const AJV = require('ajv')
|
|
7
|
+
const Schema = require('fluent-json-schema')
|
|
7
8
|
|
|
8
9
|
const customSchemaCompilers = {
|
|
9
10
|
body: new AJV({
|
|
@@ -946,3 +947,73 @@ test('Custom AJV settings on different parameters - pt2', t => {
|
|
|
946
947
|
}
|
|
947
948
|
})
|
|
948
949
|
})
|
|
950
|
+
|
|
951
|
+
test("The same $id in route's schema must not overwrite others", t => {
|
|
952
|
+
t.plan(4)
|
|
953
|
+
const fastify = Fastify()
|
|
954
|
+
|
|
955
|
+
const UserSchema = Schema.object()
|
|
956
|
+
.id('http://mydomain.com/user')
|
|
957
|
+
.title('User schema')
|
|
958
|
+
.description('Contains all user fields')
|
|
959
|
+
.prop('id', Schema.integer())
|
|
960
|
+
.prop('username', Schema.string().minLength(4))
|
|
961
|
+
.prop('firstName', Schema.string().minLength(1))
|
|
962
|
+
.prop('lastName', Schema.string().minLength(1))
|
|
963
|
+
.prop('fullName', Schema.string().minLength(1))
|
|
964
|
+
.prop('email', Schema.string())
|
|
965
|
+
.prop('password', Schema.string().minLength(6))
|
|
966
|
+
.prop('bio', Schema.string())
|
|
967
|
+
|
|
968
|
+
const userCreateSchema = UserSchema.only([
|
|
969
|
+
'username',
|
|
970
|
+
'firstName',
|
|
971
|
+
'lastName',
|
|
972
|
+
'email',
|
|
973
|
+
'bio',
|
|
974
|
+
'password',
|
|
975
|
+
'password_confirm'
|
|
976
|
+
])
|
|
977
|
+
.required([
|
|
978
|
+
'username',
|
|
979
|
+
'firstName',
|
|
980
|
+
'lastName',
|
|
981
|
+
'email',
|
|
982
|
+
'bio',
|
|
983
|
+
'password'
|
|
984
|
+
])
|
|
985
|
+
|
|
986
|
+
const userPatchSchema = UserSchema.only([
|
|
987
|
+
'firstName',
|
|
988
|
+
'lastName',
|
|
989
|
+
'bio'
|
|
990
|
+
])
|
|
991
|
+
|
|
992
|
+
fastify
|
|
993
|
+
.patch('/user/:id', {
|
|
994
|
+
schema: { body: userPatchSchema },
|
|
995
|
+
handler: () => { return 'ok' }
|
|
996
|
+
})
|
|
997
|
+
.post('/user', {
|
|
998
|
+
schema: { body: userCreateSchema },
|
|
999
|
+
handler: () => { return 'ok' }
|
|
1000
|
+
})
|
|
1001
|
+
|
|
1002
|
+
fastify.inject({
|
|
1003
|
+
method: 'POST',
|
|
1004
|
+
url: '/user',
|
|
1005
|
+
body: {}
|
|
1006
|
+
}, (err, res) => {
|
|
1007
|
+
t.error(err)
|
|
1008
|
+
t.same(res.json().message, "body must have required property 'username'")
|
|
1009
|
+
})
|
|
1010
|
+
|
|
1011
|
+
fastify.inject({
|
|
1012
|
+
url: '/user/1',
|
|
1013
|
+
method: 'PATCH',
|
|
1014
|
+
body: {}
|
|
1015
|
+
}, (err, res) => {
|
|
1016
|
+
t.error(err)
|
|
1017
|
+
t.same(res.payload, 'ok')
|
|
1018
|
+
})
|
|
1019
|
+
})
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const t = require('tap')
|
|
4
|
+
const test = t.test
|
|
5
|
+
const fastify = require('..')()
|
|
6
|
+
|
|
7
|
+
const schema = {
|
|
8
|
+
schema: {
|
|
9
|
+
response: {
|
|
10
|
+
'2xx': {
|
|
11
|
+
type: 'object',
|
|
12
|
+
properties: {
|
|
13
|
+
hello: {
|
|
14
|
+
type: 'string'
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const querySchema = {
|
|
23
|
+
schema: {
|
|
24
|
+
querystring: {
|
|
25
|
+
type: 'object',
|
|
26
|
+
properties: {
|
|
27
|
+
hello: {
|
|
28
|
+
type: 'integer'
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const paramsSchema = {
|
|
36
|
+
schema: {
|
|
37
|
+
params: {
|
|
38
|
+
type: 'object',
|
|
39
|
+
properties: {
|
|
40
|
+
foo: {
|
|
41
|
+
type: 'string'
|
|
42
|
+
},
|
|
43
|
+
test: {
|
|
44
|
+
type: 'integer'
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
test('shorthand - search', t => {
|
|
52
|
+
t.plan(1)
|
|
53
|
+
try {
|
|
54
|
+
fastify.route({
|
|
55
|
+
method: 'SEARCH',
|
|
56
|
+
url: '/',
|
|
57
|
+
schema,
|
|
58
|
+
handler: function (request, reply) {
|
|
59
|
+
reply.code(200).send({ hello: 'world' })
|
|
60
|
+
}
|
|
61
|
+
})
|
|
62
|
+
t.pass()
|
|
63
|
+
} catch (e) {
|
|
64
|
+
t.fail()
|
|
65
|
+
}
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
test('shorthand - search params', t => {
|
|
69
|
+
t.plan(1)
|
|
70
|
+
try {
|
|
71
|
+
fastify.route({
|
|
72
|
+
method: 'SEARCH',
|
|
73
|
+
url: '/params/:foo/:test',
|
|
74
|
+
paramsSchema,
|
|
75
|
+
handler: function (request, reply) {
|
|
76
|
+
reply.code(200).send(request.params)
|
|
77
|
+
}
|
|
78
|
+
})
|
|
79
|
+
t.pass()
|
|
80
|
+
} catch (e) {
|
|
81
|
+
t.fail()
|
|
82
|
+
}
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
test('shorthand - get, querystring schema', t => {
|
|
86
|
+
t.plan(1)
|
|
87
|
+
try {
|
|
88
|
+
fastify.route({
|
|
89
|
+
method: 'SEARCH',
|
|
90
|
+
url: '/query',
|
|
91
|
+
querySchema,
|
|
92
|
+
handler: function (request, reply) {
|
|
93
|
+
reply.code(200).send(request.query)
|
|
94
|
+
}
|
|
95
|
+
})
|
|
96
|
+
t.pass()
|
|
97
|
+
} catch (e) {
|
|
98
|
+
t.fail()
|
|
99
|
+
}
|
|
100
|
+
})
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const t = require('tap')
|
|
4
|
+
const test = t.test
|
|
5
|
+
const fastify = require('..')()
|
|
6
|
+
|
|
7
|
+
test('shorthand - trace', t => {
|
|
8
|
+
t.plan(1)
|
|
9
|
+
try {
|
|
10
|
+
fastify.route({
|
|
11
|
+
method: 'TRACE',
|
|
12
|
+
url: '/',
|
|
13
|
+
handler: function (request, reply) {
|
|
14
|
+
reply.code(200).send('TRACE OK')
|
|
15
|
+
}
|
|
16
|
+
})
|
|
17
|
+
t.pass()
|
|
18
|
+
} catch (e) {
|
|
19
|
+
t.fail()
|
|
20
|
+
}
|
|
21
|
+
})
|
|
@@ -194,7 +194,18 @@ expectAssignable<FastifyInstance>(fastify({ frameworkErrors: () => { } }))
|
|
|
194
194
|
expectAssignable<FastifyInstance>(fastify({
|
|
195
195
|
rewriteUrl: (req) => req.url === '/hi' ? '/hello' : req.url!
|
|
196
196
|
}))
|
|
197
|
-
expectAssignable<FastifyInstance>(fastify({
|
|
197
|
+
expectAssignable<FastifyInstance>(fastify({
|
|
198
|
+
schemaErrorFormatter: (errors, dataVar) => {
|
|
199
|
+
console.log(
|
|
200
|
+
errors[0].keyword.toLowerCase(),
|
|
201
|
+
errors[0].message?.toLowerCase(),
|
|
202
|
+
errors[0].params,
|
|
203
|
+
errors[0].instancePath.toLowerCase(),
|
|
204
|
+
errors[0].schemaPath.toLowerCase()
|
|
205
|
+
)
|
|
206
|
+
return new Error()
|
|
207
|
+
}
|
|
208
|
+
}))
|
|
198
209
|
expectAssignable<FastifyInstance>(fastify({
|
|
199
210
|
clientErrorHandler: (err, socket) => {
|
|
200
211
|
expectType<ConnectionError>(err)
|
|
@@ -223,8 +223,7 @@ RawReplyDefaultExpression,
|
|
|
223
223
|
RouteGenericInterface,
|
|
224
224
|
ContextConfigDefault,
|
|
225
225
|
FastifySchema,
|
|
226
|
-
FastifyTypeProviderDefault
|
|
227
|
-
ResolveFastifyRequestType<FastifyTypeProviderDefault, FastifySchema, RouteGenericInterface>
|
|
226
|
+
FastifyTypeProviderDefault
|
|
228
227
|
> = async function (request, reply): Promise<void> {
|
|
229
228
|
expectType<FastifyInstance>(this)
|
|
230
229
|
expectAssignable<FastifyRequest>(request)
|
package/test/types/import.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import { FastifyLogFn } from '../../fastify'
|
|
1
|
+
import { FastifyListenOptions, FastifyLogFn } from '../../fastify'
|