Aktueller Stand

This commit is contained in:
2026-01-22 19:05:45 +01:00
parent 85dee61a4d
commit e280e4eadb
1967 changed files with 397327 additions and 74093 deletions

View File

@@ -1,16 +1,17 @@
'use strict'
const sget = require('simple-get').concat
const dns = require('node:dns').promises
const stream = require('node:stream')
const { promisify } = require('node:util')
const symbols = require('../lib/symbols')
const { waitForCb } = require('./toolkit')
const assert = require('node:assert')
module.exports.sleep = promisify(setTimeout)
/**
* @param method HTTP request method
* @param t tap instance
* @param t node:test instance
* @param isSetErrorHandler true: using setErrorHandler
*/
module.exports.payloadMethod = function (method, t, isSetErrorHandler = false) {
@@ -19,8 +20,8 @@ module.exports.payloadMethod = function (method, t, isSetErrorHandler = false) {
if (isSetErrorHandler) {
fastify.setErrorHandler(function (err, request, reply) {
t.type(request, 'object')
t.type(request, fastify[symbols.kRequest].parent)
assert.ok(request instanceof fastify[symbols.kRequest].parent)
assert.strictEqual(typeof request, 'object')
reply
.code(err.statusCode)
.type('application/json; charset=utf-8')
@@ -52,9 +53,9 @@ module.exports.payloadMethod = function (method, t, isSetErrorHandler = false) {
fastify[loMethod]('/', schema, function (req, reply) {
reply.code(200).send(req.body)
})
t.pass()
t.assert.ok(true)
} catch (e) {
t.fail()
t.assert.fail()
}
})
@@ -64,9 +65,9 @@ module.exports.payloadMethod = function (method, t, isSetErrorHandler = false) {
fastify[loMethod]('/missing', function (req, reply) {
reply.code(200).send(req.body)
})
t.pass()
t.assert.ok(true)
} catch (e) {
t.fail()
t.assert.fail()
}
})
@@ -77,9 +78,9 @@ module.exports.payloadMethod = function (method, t, isSetErrorHandler = false) {
req.body.hello = req.body.hello + req.query.foo
reply.code(200).send(req.body)
})
t.pass()
t.assert.ok(true)
} catch (e) {
t.fail()
t.assert.fail()
}
})
@@ -89,111 +90,114 @@ module.exports.payloadMethod = function (method, t, isSetErrorHandler = false) {
fastify[loMethod]('/with-limit', { bodyLimit: 1 }, function (req, reply) {
reply.send(req.body)
})
t.pass()
t.assert.ok(true)
} catch (e) {
t.fail()
t.assert.fail()
}
})
fastify.listen({ port: 0 }, function (err) {
if (err) {
t.error(err)
t.assert.ifError(err)
return
}
t.teardown(() => { fastify.close() })
t.after(() => { fastify.close() })
test(`${upMethod} - correctly replies`, t => {
test(`${upMethod} - correctly replies`, async (t) => {
t.plan(3)
sget({
const result = await fetch('http://localhost:' + fastify.server.address().port, {
method: upMethod,
url: 'http://localhost:' + fastify.server.address().port,
body: {
hello: 'world'
},
json: true
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.same(body, { hello: 'world' })
body: JSON.stringify({ hello: 'world' }),
headers: {
'Content-Type': 'application/json'
}
})
t.assert.ok(result.ok)
t.assert.strictEqual(result.status, 200)
t.assert.deepStrictEqual(await result.json(), { hello: 'world' })
})
test(`${upMethod} - correctly replies with very large body`, t => {
test(`${upMethod} - correctly replies with very large body`, async (t) => {
t.plan(3)
const largeString = 'world'.repeat(13200)
sget({
const result = await fetch('http://localhost:' + fastify.server.address().port, {
method: upMethod,
url: 'http://localhost:' + fastify.server.address().port,
body: { hello: largeString },
json: true
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.same(body, { hello: largeString })
body: JSON.stringify({ hello: largeString }),
headers: {
'Content-Type': 'application/json'
}
})
t.assert.ok(result.ok)
t.assert.strictEqual(result.status, 200)
t.assert.deepStrictEqual(await result.json(), { hello: largeString })
})
test(`${upMethod} - correctly replies if the content type has the charset`, t => {
test(`${upMethod} - correctly replies if the content type has the charset`, async (t) => {
t.plan(3)
sget({
const result = await fetch('http://localhost:' + fastify.server.address().port, {
method: upMethod,
url: 'http://localhost:' + fastify.server.address().port,
body: JSON.stringify({ hello: 'world' }),
headers: {
'content-type': 'application/json; charset=utf-8'
}
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.same(body.toString(), JSON.stringify({ hello: 'world' }))
})
t.assert.ok(result.ok)
t.assert.strictEqual(result.status, 200)
t.assert.deepStrictEqual(await result.text(), JSON.stringify({ hello: 'world' }))
})
test(`${upMethod} without schema - correctly replies`, t => {
test(`${upMethod} without schema - correctly replies`, async (t) => {
t.plan(3)
sget({
const result = await fetch('http://localhost:' + fastify.server.address().port + '/missing', {
method: upMethod,
url: 'http://localhost:' + fastify.server.address().port + '/missing',
body: {
hello: 'world'
},
json: true
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.same(body, { hello: 'world' })
body: JSON.stringify({ hello: 'world' }),
headers: {
'Content-Type': 'application/json'
}
})
t.assert.ok(result.ok)
t.assert.strictEqual(result.status, 200)
t.assert.deepStrictEqual(await result.json(), { hello: 'world' })
})
test(`${upMethod} with body and querystring - correctly replies`, t => {
test(`${upMethod} with body and querystring - correctly replies`, async (t) => {
t.plan(3)
sget({
const result = await fetch('http://localhost:' + fastify.server.address().port + '/with-query?foo=hello', {
method: upMethod,
url: 'http://localhost:' + fastify.server.address().port + '/with-query?foo=hello',
body: {
hello: 'world'
},
json: true
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.same(body, { hello: 'worldhello' })
body: JSON.stringify({ hello: 'world' }),
headers: {
'Content-Type': 'application/json'
}
})
t.assert.ok(result.ok)
t.assert.strictEqual(result.status, 200)
t.assert.deepStrictEqual(await result.json(), { hello: 'worldhello' })
})
test(`${upMethod} with no body - correctly replies`, t => {
t.plan(6)
sget({
const { stepIn, patience } = waitForCb({ steps: 2 })
fetch('http://localhost:' + fastify.server.address().port + '/missing', {
method: upMethod,
url: 'http://localhost:' + fastify.server.address().port + '/missing',
headers: { 'Content-Length': '0' }
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.equal(body.toString(), '')
}).then(async (response) => {
t.assert.ok(response.ok)
t.assert.strictEqual(response.status, 200)
t.assert.strictEqual(await response.text(), '')
stepIn()
})
// Must use inject to make a request without a Content-Length header
@@ -201,91 +205,109 @@ module.exports.payloadMethod = function (method, t, isSetErrorHandler = false) {
method: upMethod,
url: '/missing'
}, (err, res) => {
t.error(err)
t.equal(res.statusCode, 200)
t.equal(res.payload.toString(), '')
t.assert.ifError(err)
t.assert.strictEqual(res.statusCode, 200)
t.assert.strictEqual(res.payload.toString(), '')
stepIn()
})
return patience
})
test(`${upMethod} returns 415 - incorrect media type if body is not json`, t => {
test(`${upMethod} returns 415 - incorrect media type if body is not json`, async (t) => {
t.plan(2)
sget({
method: upMethod,
url: 'http://localhost:' + fastify.server.address().port + '/missing',
body: 'hello world'
}, (err, response, body) => {
t.error(err)
if (upMethod === 'OPTIONS') {
t.equal(response.statusCode, 200)
} else {
t.equal(response.statusCode, 415)
const result = await fetch('http://localhost:' + fastify.server.address().port + '/missing', {
method: upMethod,
body: 'hello world',
headers: {
'Content-Type': undefined
}
})
t.assert.ok(!result.ok)
t.assert.strictEqual(result.status, 415)
})
if (loMethod === 'options') {
test('OPTIONS returns 415 - should return 415 if Content-Type is not json or plain text', t => {
test('OPTIONS returns 415 - should return 415 if Content-Type is not json or plain text', async (t) => {
t.plan(2)
sget({
const result = await fetch('http://localhost:' + fastify.server.address().port + '/missing', {
method: upMethod,
url: 'http://localhost:' + fastify.server.address().port + '/missing',
body: 'hello world',
headers: {
'Content-Type': 'text/xml'
}
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 415)
})
t.assert.ok(!result.ok)
t.assert.strictEqual(result.status, 415)
})
}
test(`${upMethod} returns 400 - Bad Request`, t => {
t.plan(4)
const isOptions = upMethod === 'OPTIONS'
t.plan(isOptions ? 2 : 4)
sget({
const { stepIn, patience } = waitForCb({ steps: isOptions ? 1 : 2 })
fetch('http://localhost:' + fastify.server.address().port, {
method: upMethod,
url: 'http://localhost:' + fastify.server.address().port,
body: 'hello world',
headers: {
'Content-Type': 'application/json'
}
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 400)
}).then((response) => {
t.assert.ok(!response.ok)
t.assert.strictEqual(response.status, 400)
stepIn()
})
sget({
method: upMethod,
url: 'http://localhost:' + fastify.server.address().port,
headers: {
'Content-Type': 'application/json',
'Content-Length': '0'
}
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 400)
})
if (!isOptions) {
fetch(`http://localhost:${fastify.server.address().port}`, {
method: upMethod,
headers: {
'Content-Type': 'application/json',
'Content-Length': '0'
}
}).then((response) => {
t.assert.ok(!response.ok)
t.assert.strictEqual(response.status, 400)
stepIn()
})
}
return patience
})
test(`${upMethod} returns 413 - Payload Too Large`, t => {
t.plan(upMethod === 'OPTIONS' ? 4 : 6)
const isOptions = upMethod === 'OPTIONS'
t.plan(isOptions ? 3 : 5)
sget({
const { stepIn, patience } = waitForCb({ steps: isOptions ? 2 : 3 })
fetch(`http://localhost:${fastify.server.address().port}`, {
method: upMethod,
url: 'http://localhost:' + fastify.server.address().port,
body: JSON.stringify({ w: 'w'.repeat(1024 * 1024 + 1) }),
headers: {
'Content-Type': 'application/json',
'Content-Length': 1024 * 1024 + 1
'Content-Type': 'application/json'
}
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 413)
}).then((response) => {
t.assert.strictEqual(response.status, 413)
stepIn()
}).catch((err) => {
// Handle EPIPE error - server closed connection after sending 413
if (err.cause?.code === 'EPIPE' || err.message.includes('fetch failed')) {
t.assert.ok(true, 'Expected EPIPE error due to server closing connection on 413')
} else {
throw err
}
stepIn()
})
// Node errors for OPTIONS requests with a stream body and no Content-Length header
if (upMethod !== 'OPTIONS') {
if (!isOptions) {
let chunk = Buffer.alloc(1024 * 1024 + 1, 0)
const largeStream = new stream.Readable({
read () {
@@ -293,34 +315,46 @@ module.exports.payloadMethod = function (method, t, isSetErrorHandler = false) {
chunk = null
}
})
sget({
fetch('http://localhost:' + fastify.server.address().port, {
method: upMethod,
url: 'http://localhost:' + fastify.server.address().port,
headers: { 'Content-Type': 'application/json' },
body: largeStream
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 413)
body: largeStream,
duplex: 'half'
}).then((response) => {
t.assert.ok(!response.ok)
t.assert.strictEqual(response.status, 413)
stepIn()
}).catch((err) => {
// Handle EPIPE error - server closed connection after sending 413
if (err.cause?.code === 'EPIPE' || err.message.includes('fetch failed')) {
t.assert.ok(true, 'Expected EPIPE error due to server closing connection on 413')
} else {
throw err
}
stepIn()
})
}
sget({
fetch(`http://localhost:${fastify.server.address().port}/with-limit`, {
method: upMethod,
url: `http://localhost:${fastify.server.address().port}/with-limit`,
headers: { 'Content-Type': 'application/json' },
body: {},
json: true
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 413)
body: JSON.stringify({})
}).then((response) => {
t.assert.ok(!response.ok)
t.assert.strictEqual(response.status, 413)
stepIn()
})
return patience
})
test(`${upMethod} should fail with empty body and application/json content-type`, t => {
if (upMethod === 'OPTIONS') return t.end()
if (upMethod === 'OPTIONS') return
t.plan(12)
const { stepIn, patience } = waitForCb({ steps: 5 })
fastify.inject({
method: `${upMethod}`,
url: '/',
@@ -328,8 +362,8 @@ module.exports.payloadMethod = function (method, t, isSetErrorHandler = false) {
'Content-Type': 'application/json'
}
}, (err, res) => {
t.error(err)
t.same(JSON.parse(res.payload), {
t.assert.ifError(err)
t.assert.deepStrictEqual(JSON.parse(res.payload), {
error: 'Bad Request',
code: 'FST_ERR_CTP_EMPTY_JSON_BODY',
message: 'Body cannot be empty when content-type is set to \'application/json\'',
@@ -337,20 +371,20 @@ module.exports.payloadMethod = function (method, t, isSetErrorHandler = false) {
})
})
sget({
fetch(`http://localhost:${fastify.server.address().port}`, {
method: upMethod,
url: `http://localhost:${fastify.server.address().port}`,
headers: {
'Content-Type': 'application/json'
}
}, (err, res, body) => {
t.error(err)
t.same(JSON.parse(body.toString()), {
}).then(async (res) => {
t.assert.ok(!res.ok)
t.assert.deepStrictEqual(await res.json(), {
error: 'Bad Request',
code: 'FST_ERR_CTP_EMPTY_JSON_BODY',
message: 'Body cannot be empty when content-type is set to \'application/json\'',
statusCode: 400
})
stepIn()
})
fastify.inject({
@@ -361,30 +395,31 @@ module.exports.payloadMethod = function (method, t, isSetErrorHandler = false) {
},
payload: null
}, (err, res) => {
t.error(err)
t.same(JSON.parse(res.payload), {
t.assert.ifError(err)
t.assert.deepStrictEqual(JSON.parse(res.payload), {
error: 'Bad Request',
code: 'FST_ERR_CTP_EMPTY_JSON_BODY',
message: 'Body cannot be empty when content-type is set to \'application/json\'',
statusCode: 400
})
stepIn()
})
sget({
fetch(`http://localhost:${fastify.server.address().port}`, {
method: upMethod,
url: `http://localhost:${fastify.server.address().port}`,
headers: {
'Content-Type': 'application/json'
},
payload: null
}, (err, res, body) => {
t.error(err)
t.same(JSON.parse(body.toString()), {
body: null
}).then(async (res) => {
t.assert.ok(!res.ok)
t.assert.deepStrictEqual(await res.json(), {
error: 'Bad Request',
code: 'FST_ERR_CTP_EMPTY_JSON_BODY',
message: 'Body cannot be empty when content-type is set to \'application/json\'',
statusCode: 400
})
stepIn()
})
fastify.inject({
@@ -395,31 +430,34 @@ module.exports.payloadMethod = function (method, t, isSetErrorHandler = false) {
},
payload: undefined
}, (err, res) => {
t.error(err)
t.same(JSON.parse(res.payload), {
t.assert.ifError(err)
t.assert.deepStrictEqual(JSON.parse(res.payload), {
error: 'Bad Request',
code: 'FST_ERR_CTP_EMPTY_JSON_BODY',
message: 'Body cannot be empty when content-type is set to \'application/json\'',
statusCode: 400
})
stepIn()
})
sget({
fetch(`http://localhost:${fastify.server.address().port}`, {
method: upMethod,
url: `http://localhost:${fastify.server.address().port}`,
headers: {
'Content-Type': 'application/json'
},
payload: undefined
}, (err, res, body) => {
t.error(err)
t.same(JSON.parse(body.toString()), {
body: undefined
}).then(async (res) => {
t.assert.ok(!res.ok)
t.assert.deepStrictEqual(await res.json(), {
error: 'Bad Request',
code: 'FST_ERR_CTP_EMPTY_JSON_BODY',
message: 'Body cannot be empty when content-type is set to \'application/json\'',
statusCode: 400
})
stepIn()
})
return patience
})
})
}