Skip to content

Commit ad89250

Browse files
gurgundayEomm
andauthored
perf: update method matching (fastify#5419)
* update method matching implementation * push back string comparison * extract encoding * add missing method test * check for transferEncoding as well * skip instead of removing test * Update test/delete.test.js Co-authored-by: Manuel Spigolon <[email protected]> Signed-off-by: Gürgün Dayıoğlu <[email protected]> * Update test/method-missing.test.js Co-authored-by: Manuel Spigolon <[email protected]> Signed-off-by: Gürgün Dayıoğlu <[email protected]> --------- Signed-off-by: Gürgün Dayıoğlu <[email protected]> Co-authored-by: Manuel Spigolon <[email protected]>
1 parent ccd16ac commit ad89250

File tree

5 files changed

+94
-43
lines changed

5 files changed

+94
-43
lines changed

lib/handleRequest.js

Lines changed: 16 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
'use strict'
22

3+
const { bodylessMethods, bodyMethods } = require('./httpMethods')
34
const { validate: validateSchema } = require('./validation')
45
const { preValidationHookRunner, preHandlerHookRunner } = require('./hooks')
56
const wrapThenable = require('./wrapThenable')
@@ -20,42 +21,36 @@ function handleRequest (err, request, reply) {
2021
const headers = request.headers
2122
const context = request[kRouteContext]
2223

23-
if (method === 'GET' || method === 'HEAD') {
24+
if (bodylessMethods.has(method)) {
2425
handler(request, reply)
2526
return
2627
}
2728

28-
const contentType = headers['content-type']
29+
if (bodyMethods.has(method)) {
30+
const contentType = headers['content-type']
31+
const contentLength = headers['content-length']
32+
const transferEncoding = headers['transfer-encoding']
2933

30-
if (method === 'POST' || method === 'PUT' || method === 'PATCH' || method === 'TRACE' || method === 'SEARCH' ||
31-
method === 'PROPFIND' || method === 'PROPPATCH' || method === 'LOCK') {
3234
if (contentType === undefined) {
3335
if (
34-
headers['transfer-encoding'] === undefined &&
35-
(headers['content-length'] === '0' || headers['content-length'] === undefined)
36-
) { // Request has no body to parse
36+
(contentLength === undefined || contentLength === '0') &&
37+
transferEncoding === undefined
38+
) {
39+
// Request has no body to parse
3740
handler(request, reply)
3841
} else {
3942
context.contentTypeParser.run('', handler, request, reply)
4043
}
4144
} else {
42-
context.contentTypeParser.run(contentType, handler, request, reply)
43-
}
44-
return
45-
}
45+
if (contentLength === undefined && transferEncoding === undefined && method === 'OPTIONS') {
46+
// OPTIONS can have a Content-Type header without a body
47+
handler(request, reply)
48+
return
49+
}
4650

47-
if (method === 'OPTIONS' || method === 'DELETE') {
48-
if (
49-
contentType !== undefined &&
50-
(
51-
headers['transfer-encoding'] !== undefined ||
52-
headers['content-length'] !== undefined
53-
)
54-
) {
5551
context.contentTypeParser.run(contentType, handler, request, reply)
56-
} else {
57-
handler(request, reply)
5852
}
53+
5954
return
6055
}
6156

lib/httpMethods.js

Lines changed: 32 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,38 @@
11
'use strict'
22

3+
const bodylessMethods = new Set([
4+
// Standard
5+
'GET',
6+
'HEAD',
7+
'TRACE',
8+
9+
// WebDAV
10+
'UNLOCK'
11+
])
12+
13+
const bodyMethods = new Set([
14+
// Standard
15+
'DELETE',
16+
'OPTIONS',
17+
'PATCH',
18+
'PUT',
19+
'POST',
20+
21+
// WebDAV
22+
'COPY',
23+
'LOCK',
24+
'MOVE',
25+
'MKCOL',
26+
'PROPFIND',
27+
'PROPPATCH',
28+
'SEARCH'
29+
])
30+
331
module.exports = {
32+
bodylessMethods,
33+
bodyMethods,
434
supportedMethods: [
5-
'DELETE',
6-
'GET',
7-
'HEAD',
8-
'PATCH',
9-
'POST',
10-
'PUT',
11-
'OPTIONS',
12-
'PROPFIND',
13-
'PROPPATCH',
14-
'MKCOL',
15-
'COPY',
16-
'MOVE',
17-
'LOCK',
18-
'UNLOCK',
19-
'TRACE',
20-
'SEARCH'
35+
...bodylessMethods,
36+
...bodyMethods
2137
]
2238
}

test/delete.test.js

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -300,8 +300,28 @@ fastify.listen({ port: 0 }, err => {
300300
})
301301
})
302302

303+
test('shorthand - delete with application/json Content-Type header and null body', t => {
304+
t.plan(4)
305+
const fastify = require('..')()
306+
fastify.delete('/', {}, (req, reply) => {
307+
t.equal(req.body, null)
308+
reply.send(req.body)
309+
})
310+
fastify.inject({
311+
method: 'DELETE',
312+
url: '/',
313+
headers: { 'Content-Type': 'application/json' },
314+
body: 'null'
315+
}, (err, response) => {
316+
t.error(err)
317+
t.equal(response.statusCode, 200)
318+
t.same(response.payload.toString(), 'null')
319+
})
320+
})
321+
303322
// https://github.com/fastify/fastify/issues/936
304-
test('shorthand - delete with application/json Content-Type header and without body', t => {
323+
// Skip this test because this is an invalid request
324+
test('shorthand - delete with application/json Content-Type header and without body', { skip: 'https://github.com/fastify/fastify/pull/5419' }, t => {
305325
t.plan(4)
306326
const fastify = require('..')()
307327
fastify.delete('/', {}, (req, reply) => {

test/helper.js

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -216,11 +216,7 @@ module.exports.payloadMethod = function (method, t, isSetErrorHandler = false) {
216216

217217
}, (err, response, body) => {
218218
t.error(err)
219-
if (upMethod === 'OPTIONS') {
220-
t.equal(response.statusCode, 200)
221-
} else {
222-
t.equal(response.statusCode, 415)
223-
}
219+
t.equal(response.statusCode, 415)
224220
})
225221
})
226222

test/method-missing.test.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
const http = require('http')
2+
const { test } = require('tap')
3+
const Fastify = require('../fastify')
4+
5+
test('missing method from http client', t => {
6+
t.plan(2)
7+
const fastify = Fastify()
8+
9+
fastify.listen({ port: 3000 }, (err) => {
10+
t.error(err)
11+
12+
const port = fastify.server.address().port
13+
const req = http.request({
14+
port,
15+
method: 'REBIND',
16+
path: '/'
17+
}, (res) => {
18+
t.equal(res.statusCode, 404)
19+
fastify.close()
20+
})
21+
22+
req.end()
23+
})
24+
})

0 commit comments

Comments
 (0)