fastify 4.23.2 → 4.24.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 +1 -1
- package/docs/Guides/Ecosystem.md +6 -0
- package/docs/Reference/Hooks.md +1 -0
- package/docs/Reference/Plugins.md +1 -1
- package/docs/Reference/Reply.md +4 -3
- package/docs/Reference/Request.md +3 -2
- package/docs/Reference/Server.md +31 -3
- package/docs/Reference/Type-Providers.md +2 -2
- package/docs/Reference/TypeScript.md +21 -7
- package/fastify.d.ts +2 -2
- package/fastify.js +8 -1
- package/lib/contentTypeParser.js +1 -1
- package/lib/reply.js +20 -3
- package/lib/reqIdGenFactory.js +15 -9
- package/lib/request.js +1 -1
- package/lib/route.js +28 -7
- package/lib/schemas.js +3 -3
- package/lib/warnings.js +3 -1
- package/package.json +33 -33
- package/test/404s.test.js +31 -39
- package/test/async-await.test.js +1 -1
- package/test/async-dispose.test.js +21 -0
- package/test/build-certificate.js +90 -1
- package/test/close-pipelining.test.js +5 -5
- package/test/close.test.js +1 -5
- package/test/constrained-routes.test.js +127 -3
- package/test/custom-http-server.test.js +94 -91
- package/test/custom-parser.0.test.js +21 -47
- package/test/custom-parser.1.test.js +10 -732
- package/test/custom-parser.2.test.js +102 -0
- package/test/custom-parser.3.test.js +245 -0
- package/test/custom-parser.4.test.js +239 -0
- package/test/custom-parser.5.test.js +149 -0
- package/test/head.test.js +204 -0
- package/test/helper.js +30 -8
- package/test/hooks-async.test.js +163 -13
- package/test/hooks.on-listen.test.js +7 -6
- package/test/hooks.test.js +4 -15
- package/test/http2/closing.test.js +7 -15
- package/test/https/custom-https-server.test.js +43 -40
- package/test/input-validation.js +3 -3
- package/test/internals/reply.test.js +33 -4
- package/test/listen.1.test.js +101 -0
- package/test/listen.2.test.js +103 -0
- package/test/listen.3.test.js +87 -0
- package/test/listen.4.test.js +164 -0
- package/test/listen.deprecated.test.js +3 -9
- package/test/logger/instantiation.test.js +347 -0
- package/test/logger/logger-test-utils.js +47 -0
- package/test/logger/logging.test.js +406 -0
- package/test/logger/options.test.js +500 -0
- package/test/logger/request.test.js +292 -0
- package/test/logger/response.test.js +184 -0
- package/test/plugin.1.test.js +249 -0
- package/test/plugin.2.test.js +328 -0
- package/test/plugin.3.test.js +311 -0
- package/test/plugin.4.test.js +416 -0
- package/test/reply-code.test.js +64 -0
- package/test/reply-trailers.test.js +1 -2
- package/test/route.1.test.js +309 -0
- package/test/route.2.test.js +99 -0
- package/test/route.3.test.js +205 -0
- package/test/route.4.test.js +131 -0
- package/test/route.5.test.js +230 -0
- package/test/route.6.test.js +306 -0
- package/test/route.7.test.js +370 -0
- package/test/route.8.test.js +142 -0
- package/test/stream.1.test.js +108 -0
- package/test/stream.2.test.js +119 -0
- package/test/stream.3.test.js +192 -0
- package/test/stream.4.test.js +223 -0
- package/test/stream.5.test.js +194 -0
- package/test/trust-proxy.test.js +2 -4
- package/test/types/reply.test-d.ts +3 -3
- package/test/types/request.test-d.ts +9 -9
- package/test/types/type-provider.test-d.ts +89 -0
- package/test/types/using.test-d.ts +14 -0
- package/test/upgrade.test.js +3 -3
- package/types/context.d.ts +9 -2
- package/types/instance.d.ts +4 -1
- package/types/plugin.d.ts +2 -1
- package/types/reply.d.ts +2 -2
- package/types/request.d.ts +3 -3
- package/types/route.d.ts +5 -5
- package/test/listen.test.js +0 -427
- package/test/plugin.test.js +0 -1275
- package/test/route.test.js +0 -1762
- package/test/serial/logger.0.test.js +0 -866
- package/test/serial/logger.1.test.js +0 -862
- package/test/stream.test.js +0 -816
- /package/test/{serial → logger}/tap-parallel-not-ok +0 -0
package/test/stream.test.js
DELETED
|
@@ -1,816 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
const t = require('tap')
|
|
4
|
-
const test = t.test
|
|
5
|
-
const proxyquire = require('proxyquire')
|
|
6
|
-
const sget = require('simple-get').concat
|
|
7
|
-
const fs = require('node:fs')
|
|
8
|
-
const resolve = require('node:path').resolve
|
|
9
|
-
const zlib = require('node:zlib')
|
|
10
|
-
const pipeline = require('node:stream').pipeline
|
|
11
|
-
const Fastify = require('..')
|
|
12
|
-
const errors = require('http-errors')
|
|
13
|
-
const JSONStream = require('JSONStream')
|
|
14
|
-
const send = require('send')
|
|
15
|
-
const Readable = require('node:stream').Readable
|
|
16
|
-
const split = require('split2')
|
|
17
|
-
const semver = require('semver')
|
|
18
|
-
const { kDisableRequestLogging } = require('../lib/symbols.js')
|
|
19
|
-
|
|
20
|
-
function getUrl (app) {
|
|
21
|
-
const { address, port } = app.server.address()
|
|
22
|
-
if (address === '::1') {
|
|
23
|
-
return `http://[${address}]:${port}`
|
|
24
|
-
} else {
|
|
25
|
-
return `http://${address}:${port}`
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
test('should respond with a stream', t => {
|
|
30
|
-
t.plan(6)
|
|
31
|
-
const fastify = Fastify()
|
|
32
|
-
|
|
33
|
-
fastify.get('/', function (req, reply) {
|
|
34
|
-
const stream = fs.createReadStream(__filename, 'utf8')
|
|
35
|
-
reply.code(200).send(stream)
|
|
36
|
-
})
|
|
37
|
-
|
|
38
|
-
fastify.listen({ port: 0 }, err => {
|
|
39
|
-
t.error(err)
|
|
40
|
-
t.teardown(() => { fastify.close() })
|
|
41
|
-
|
|
42
|
-
sget(`http://localhost:${fastify.server.address().port}`, function (err, response, data) {
|
|
43
|
-
t.error(err)
|
|
44
|
-
t.equal(response.headers['content-type'], undefined)
|
|
45
|
-
t.equal(response.statusCode, 200)
|
|
46
|
-
|
|
47
|
-
fs.readFile(__filename, (err, expected) => {
|
|
48
|
-
t.error(err)
|
|
49
|
-
t.equal(expected.toString(), data.toString())
|
|
50
|
-
})
|
|
51
|
-
})
|
|
52
|
-
})
|
|
53
|
-
})
|
|
54
|
-
|
|
55
|
-
test('should respond with a stream (error)', t => {
|
|
56
|
-
t.plan(3)
|
|
57
|
-
const fastify = Fastify()
|
|
58
|
-
|
|
59
|
-
fastify.get('/error', function (req, reply) {
|
|
60
|
-
const stream = fs.createReadStream('not-existing-file', 'utf8')
|
|
61
|
-
reply.code(200).send(stream)
|
|
62
|
-
})
|
|
63
|
-
|
|
64
|
-
fastify.listen({ port: 0 }, err => {
|
|
65
|
-
t.error(err)
|
|
66
|
-
t.teardown(() => { fastify.close() })
|
|
67
|
-
|
|
68
|
-
sget(`http://localhost:${fastify.server.address().port}/error`, function (err, response) {
|
|
69
|
-
t.error(err)
|
|
70
|
-
t.equal(response.statusCode, 500)
|
|
71
|
-
})
|
|
72
|
-
})
|
|
73
|
-
})
|
|
74
|
-
|
|
75
|
-
test('should trigger the onSend hook', t => {
|
|
76
|
-
t.plan(4)
|
|
77
|
-
const fastify = Fastify()
|
|
78
|
-
|
|
79
|
-
fastify.get('/', (req, reply) => {
|
|
80
|
-
reply.send(fs.createReadStream(__filename, 'utf8'))
|
|
81
|
-
})
|
|
82
|
-
|
|
83
|
-
fastify.addHook('onSend', (req, reply, payload, done) => {
|
|
84
|
-
t.ok(payload._readableState)
|
|
85
|
-
reply.header('Content-Type', 'application/javascript')
|
|
86
|
-
done()
|
|
87
|
-
})
|
|
88
|
-
|
|
89
|
-
fastify.inject({
|
|
90
|
-
url: '/'
|
|
91
|
-
}, (err, res) => {
|
|
92
|
-
t.error(err)
|
|
93
|
-
t.equal(res.headers['content-type'], 'application/javascript')
|
|
94
|
-
t.equal(res.payload, fs.readFileSync(__filename, 'utf8'))
|
|
95
|
-
fastify.close()
|
|
96
|
-
})
|
|
97
|
-
})
|
|
98
|
-
|
|
99
|
-
test('should trigger the onSend hook only twice if pumping the stream fails, first with the stream, second with the serialized error', t => {
|
|
100
|
-
t.plan(5)
|
|
101
|
-
const fastify = Fastify()
|
|
102
|
-
|
|
103
|
-
fastify.get('/', (req, reply) => {
|
|
104
|
-
reply.send(fs.createReadStream('not-existing-file', 'utf8'))
|
|
105
|
-
})
|
|
106
|
-
|
|
107
|
-
let counter = 0
|
|
108
|
-
fastify.addHook('onSend', (req, reply, payload, done) => {
|
|
109
|
-
if (counter === 0) {
|
|
110
|
-
t.ok(payload._readableState)
|
|
111
|
-
} else if (counter === 1) {
|
|
112
|
-
const error = JSON.parse(payload)
|
|
113
|
-
t.equal(error.statusCode, 500)
|
|
114
|
-
}
|
|
115
|
-
counter++
|
|
116
|
-
done()
|
|
117
|
-
})
|
|
118
|
-
|
|
119
|
-
fastify.listen({ port: 0 }, err => {
|
|
120
|
-
t.error(err)
|
|
121
|
-
t.teardown(() => { fastify.close() })
|
|
122
|
-
|
|
123
|
-
sget(`http://localhost:${fastify.server.address().port}`, function (err, response) {
|
|
124
|
-
t.error(err)
|
|
125
|
-
t.equal(response.statusCode, 500)
|
|
126
|
-
})
|
|
127
|
-
})
|
|
128
|
-
})
|
|
129
|
-
|
|
130
|
-
test('onSend hook stream', t => {
|
|
131
|
-
t.plan(4)
|
|
132
|
-
const fastify = Fastify()
|
|
133
|
-
|
|
134
|
-
fastify.get('/', function (req, reply) {
|
|
135
|
-
reply.send({ hello: 'world' })
|
|
136
|
-
})
|
|
137
|
-
|
|
138
|
-
fastify.addHook('onSend', (req, reply, payload, done) => {
|
|
139
|
-
const gzStream = zlib.createGzip()
|
|
140
|
-
|
|
141
|
-
reply.header('Content-Encoding', 'gzip')
|
|
142
|
-
pipeline(
|
|
143
|
-
fs.createReadStream(resolve(process.cwd() + '/test/stream.test.js'), 'utf8'),
|
|
144
|
-
gzStream,
|
|
145
|
-
t.error
|
|
146
|
-
)
|
|
147
|
-
done(null, gzStream)
|
|
148
|
-
})
|
|
149
|
-
|
|
150
|
-
fastify.inject({
|
|
151
|
-
url: '/',
|
|
152
|
-
method: 'GET'
|
|
153
|
-
}, (err, res) => {
|
|
154
|
-
t.error(err)
|
|
155
|
-
t.equal(res.headers['content-encoding'], 'gzip')
|
|
156
|
-
const file = fs.readFileSync(resolve(process.cwd() + '/test/stream.test.js'), 'utf8')
|
|
157
|
-
const payload = zlib.gunzipSync(res.rawPayload)
|
|
158
|
-
t.equal(payload.toString('utf-8'), file)
|
|
159
|
-
fastify.close()
|
|
160
|
-
})
|
|
161
|
-
})
|
|
162
|
-
|
|
163
|
-
test('onSend hook stream should work even if payload is not a proper stream', t => {
|
|
164
|
-
t.plan(1)
|
|
165
|
-
|
|
166
|
-
const reply = proxyquire('../lib/reply', {
|
|
167
|
-
'node:stream': {
|
|
168
|
-
finished: (...args) => {
|
|
169
|
-
if (args.length === 2) { args[1](new Error('test-error')) }
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
})
|
|
173
|
-
const Fastify = proxyquire('..', {
|
|
174
|
-
'./lib/reply.js': reply
|
|
175
|
-
})
|
|
176
|
-
const spyLogger = {
|
|
177
|
-
fatal: () => { },
|
|
178
|
-
error: () => { },
|
|
179
|
-
warn: (message) => {
|
|
180
|
-
t.equal(message, 'stream payload does not end properly')
|
|
181
|
-
fastify.close()
|
|
182
|
-
},
|
|
183
|
-
info: () => { },
|
|
184
|
-
debug: () => { },
|
|
185
|
-
trace: () => { },
|
|
186
|
-
child: () => { return spyLogger }
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
const fastify = Fastify({ logger: spyLogger })
|
|
190
|
-
fastify.get('/', function (req, reply) {
|
|
191
|
-
reply.send({ hello: 'world' })
|
|
192
|
-
})
|
|
193
|
-
fastify.addHook('onSend', (req, reply, payload, done) => {
|
|
194
|
-
const fakeStream = { pipe: () => { } }
|
|
195
|
-
done(null, fakeStream)
|
|
196
|
-
})
|
|
197
|
-
|
|
198
|
-
fastify.inject({
|
|
199
|
-
url: '/',
|
|
200
|
-
method: 'GET'
|
|
201
|
-
})
|
|
202
|
-
})
|
|
203
|
-
|
|
204
|
-
test('onSend hook stream should work on payload with "close" ending function', t => {
|
|
205
|
-
t.plan(1)
|
|
206
|
-
|
|
207
|
-
const reply = proxyquire('../lib/reply', {
|
|
208
|
-
'node:stream': {
|
|
209
|
-
finished: (...args) => {
|
|
210
|
-
if (args.length === 2) { args[1](new Error('test-error')) }
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
})
|
|
214
|
-
const Fastify = proxyquire('..', {
|
|
215
|
-
'./lib/reply.js': reply
|
|
216
|
-
})
|
|
217
|
-
|
|
218
|
-
const fastify = Fastify({ logger: false })
|
|
219
|
-
fastify.get('/', function (req, reply) {
|
|
220
|
-
reply.send({ hello: 'world' })
|
|
221
|
-
})
|
|
222
|
-
fastify.addHook('onSend', (req, reply, payload, done) => {
|
|
223
|
-
const fakeStream = {
|
|
224
|
-
pipe: () => { },
|
|
225
|
-
close: (cb) => {
|
|
226
|
-
cb()
|
|
227
|
-
t.pass()
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
done(null, fakeStream)
|
|
231
|
-
})
|
|
232
|
-
|
|
233
|
-
fastify.inject({
|
|
234
|
-
url: '/',
|
|
235
|
-
method: 'GET'
|
|
236
|
-
})
|
|
237
|
-
})
|
|
238
|
-
|
|
239
|
-
test('Destroying streams prematurely', t => {
|
|
240
|
-
t.plan(6)
|
|
241
|
-
|
|
242
|
-
let fastify = null
|
|
243
|
-
const logStream = split(JSON.parse)
|
|
244
|
-
try {
|
|
245
|
-
fastify = Fastify({
|
|
246
|
-
logger: {
|
|
247
|
-
stream: logStream,
|
|
248
|
-
level: 'info'
|
|
249
|
-
}
|
|
250
|
-
})
|
|
251
|
-
} catch (e) {
|
|
252
|
-
t.fail()
|
|
253
|
-
}
|
|
254
|
-
const stream = require('node:stream')
|
|
255
|
-
const http = require('node:http')
|
|
256
|
-
|
|
257
|
-
// Test that "premature close" errors are logged with level warn
|
|
258
|
-
logStream.on('data', line => {
|
|
259
|
-
if (line.res) {
|
|
260
|
-
t.equal(line.msg, 'stream closed prematurely')
|
|
261
|
-
t.equal(line.level, 30)
|
|
262
|
-
}
|
|
263
|
-
})
|
|
264
|
-
|
|
265
|
-
fastify.get('/', function (request, reply) {
|
|
266
|
-
t.pass('Received request')
|
|
267
|
-
|
|
268
|
-
let sent = false
|
|
269
|
-
const reallyLongStream = new stream.Readable({
|
|
270
|
-
read: function () {
|
|
271
|
-
if (!sent) {
|
|
272
|
-
this.push(Buffer.from('hello\n'))
|
|
273
|
-
}
|
|
274
|
-
sent = true
|
|
275
|
-
}
|
|
276
|
-
})
|
|
277
|
-
|
|
278
|
-
reply.send(reallyLongStream)
|
|
279
|
-
})
|
|
280
|
-
|
|
281
|
-
fastify.listen({ port: 0 }, err => {
|
|
282
|
-
t.error(err)
|
|
283
|
-
t.teardown(() => { fastify.close() })
|
|
284
|
-
|
|
285
|
-
const port = fastify.server.address().port
|
|
286
|
-
|
|
287
|
-
http.get(`http://localhost:${port}`, function (response) {
|
|
288
|
-
t.equal(response.statusCode, 200)
|
|
289
|
-
response.on('readable', function () {
|
|
290
|
-
response.destroy()
|
|
291
|
-
})
|
|
292
|
-
|
|
293
|
-
// Node bug? Node never emits 'close' here.
|
|
294
|
-
response.on('aborted', function () {
|
|
295
|
-
t.pass('Response closed')
|
|
296
|
-
})
|
|
297
|
-
})
|
|
298
|
-
})
|
|
299
|
-
})
|
|
300
|
-
|
|
301
|
-
test('Destroying streams prematurely should call close method', t => {
|
|
302
|
-
t.plan(7)
|
|
303
|
-
|
|
304
|
-
let fastify = null
|
|
305
|
-
const logStream = split(JSON.parse)
|
|
306
|
-
try {
|
|
307
|
-
fastify = Fastify({
|
|
308
|
-
logger: {
|
|
309
|
-
stream: logStream,
|
|
310
|
-
level: 'info'
|
|
311
|
-
}
|
|
312
|
-
})
|
|
313
|
-
} catch (e) {
|
|
314
|
-
t.fail()
|
|
315
|
-
}
|
|
316
|
-
const stream = require('node:stream')
|
|
317
|
-
const http = require('node:http')
|
|
318
|
-
|
|
319
|
-
// Test that "premature close" errors are logged with level warn
|
|
320
|
-
logStream.on('data', line => {
|
|
321
|
-
if (line.res) {
|
|
322
|
-
t.equal(line.msg, 'stream closed prematurely')
|
|
323
|
-
t.equal(line.level, 30)
|
|
324
|
-
}
|
|
325
|
-
})
|
|
326
|
-
|
|
327
|
-
fastify.get('/', function (request, reply) {
|
|
328
|
-
t.pass('Received request')
|
|
329
|
-
|
|
330
|
-
let sent = false
|
|
331
|
-
const reallyLongStream = new stream.Readable({
|
|
332
|
-
read: function () {
|
|
333
|
-
if (!sent) {
|
|
334
|
-
this.push(Buffer.from('hello\n'))
|
|
335
|
-
}
|
|
336
|
-
sent = true
|
|
337
|
-
}
|
|
338
|
-
})
|
|
339
|
-
reallyLongStream.destroy = undefined
|
|
340
|
-
reallyLongStream.close = () => t.ok('called')
|
|
341
|
-
reply.send(reallyLongStream)
|
|
342
|
-
})
|
|
343
|
-
|
|
344
|
-
fastify.listen({ port: 0 }, err => {
|
|
345
|
-
t.error(err)
|
|
346
|
-
t.teardown(() => { fastify.close() })
|
|
347
|
-
|
|
348
|
-
const port = fastify.server.address().port
|
|
349
|
-
|
|
350
|
-
http.get(`http://localhost:${port}`, function (response) {
|
|
351
|
-
t.equal(response.statusCode, 200)
|
|
352
|
-
response.on('readable', function () {
|
|
353
|
-
response.destroy()
|
|
354
|
-
})
|
|
355
|
-
// Node bug? Node never emits 'close' here.
|
|
356
|
-
response.on('aborted', function () {
|
|
357
|
-
t.pass('Response closed')
|
|
358
|
-
})
|
|
359
|
-
})
|
|
360
|
-
})
|
|
361
|
-
})
|
|
362
|
-
|
|
363
|
-
test('Destroying streams prematurely should call close method when destroy is not a function', t => {
|
|
364
|
-
t.plan(7)
|
|
365
|
-
|
|
366
|
-
let fastify = null
|
|
367
|
-
const logStream = split(JSON.parse)
|
|
368
|
-
try {
|
|
369
|
-
fastify = Fastify({
|
|
370
|
-
logger: {
|
|
371
|
-
stream: logStream,
|
|
372
|
-
level: 'info'
|
|
373
|
-
}
|
|
374
|
-
})
|
|
375
|
-
} catch (e) {
|
|
376
|
-
t.fail()
|
|
377
|
-
}
|
|
378
|
-
const stream = require('node:stream')
|
|
379
|
-
const http = require('node:http')
|
|
380
|
-
|
|
381
|
-
// Test that "premature close" errors are logged with level warn
|
|
382
|
-
logStream.on('data', line => {
|
|
383
|
-
if (line.res) {
|
|
384
|
-
t.equal(line.msg, 'stream closed prematurely')
|
|
385
|
-
t.equal(line.level, 30)
|
|
386
|
-
}
|
|
387
|
-
})
|
|
388
|
-
|
|
389
|
-
fastify.get('/', function (request, reply) {
|
|
390
|
-
t.pass('Received request')
|
|
391
|
-
|
|
392
|
-
let sent = false
|
|
393
|
-
const reallyLongStream = new stream.Readable({
|
|
394
|
-
read: function () {
|
|
395
|
-
if (!sent) {
|
|
396
|
-
this.push(Buffer.from('hello\n'))
|
|
397
|
-
}
|
|
398
|
-
sent = true
|
|
399
|
-
}
|
|
400
|
-
})
|
|
401
|
-
reallyLongStream.destroy = true
|
|
402
|
-
reallyLongStream.close = () => t.ok('called')
|
|
403
|
-
reply.send(reallyLongStream)
|
|
404
|
-
})
|
|
405
|
-
|
|
406
|
-
fastify.listen({ port: 0 }, err => {
|
|
407
|
-
t.error(err)
|
|
408
|
-
t.teardown(() => { fastify.close() })
|
|
409
|
-
|
|
410
|
-
const port = fastify.server.address().port
|
|
411
|
-
|
|
412
|
-
http.get(`http://localhost:${port}`, function (response) {
|
|
413
|
-
t.equal(response.statusCode, 200)
|
|
414
|
-
response.on('readable', function () {
|
|
415
|
-
response.destroy()
|
|
416
|
-
})
|
|
417
|
-
// Node bug? Node never emits 'close' here.
|
|
418
|
-
response.on('aborted', function () {
|
|
419
|
-
t.pass('Response closed')
|
|
420
|
-
})
|
|
421
|
-
})
|
|
422
|
-
})
|
|
423
|
-
})
|
|
424
|
-
|
|
425
|
-
test('Destroying streams prematurely should call abort method', t => {
|
|
426
|
-
t.plan(7)
|
|
427
|
-
|
|
428
|
-
let fastify = null
|
|
429
|
-
const logStream = split(JSON.parse)
|
|
430
|
-
try {
|
|
431
|
-
fastify = Fastify({
|
|
432
|
-
logger: {
|
|
433
|
-
stream: logStream,
|
|
434
|
-
level: 'info'
|
|
435
|
-
}
|
|
436
|
-
})
|
|
437
|
-
} catch (e) {
|
|
438
|
-
t.fail()
|
|
439
|
-
}
|
|
440
|
-
const stream = require('node:stream')
|
|
441
|
-
const http = require('node:http')
|
|
442
|
-
|
|
443
|
-
// Test that "premature close" errors are logged with level warn
|
|
444
|
-
logStream.on('data', line => {
|
|
445
|
-
if (line.res) {
|
|
446
|
-
t.equal(line.msg, 'stream closed prematurely')
|
|
447
|
-
t.equal(line.level, 30)
|
|
448
|
-
}
|
|
449
|
-
})
|
|
450
|
-
|
|
451
|
-
fastify.get('/', function (request, reply) {
|
|
452
|
-
t.pass('Received request')
|
|
453
|
-
|
|
454
|
-
let sent = false
|
|
455
|
-
const reallyLongStream = new stream.Readable({
|
|
456
|
-
read: function () {
|
|
457
|
-
if (!sent) {
|
|
458
|
-
this.push(Buffer.from('hello\n'))
|
|
459
|
-
}
|
|
460
|
-
sent = true
|
|
461
|
-
}
|
|
462
|
-
})
|
|
463
|
-
reallyLongStream.destroy = undefined
|
|
464
|
-
reallyLongStream.close = undefined
|
|
465
|
-
reallyLongStream.abort = () => t.ok('called')
|
|
466
|
-
reply.send(reallyLongStream)
|
|
467
|
-
})
|
|
468
|
-
|
|
469
|
-
fastify.listen({ port: 0 }, err => {
|
|
470
|
-
t.error(err)
|
|
471
|
-
t.teardown(() => { fastify.close() })
|
|
472
|
-
|
|
473
|
-
const port = fastify.server.address().port
|
|
474
|
-
|
|
475
|
-
http.get(`http://localhost:${port}`, function (response) {
|
|
476
|
-
t.equal(response.statusCode, 200)
|
|
477
|
-
response.on('readable', function () {
|
|
478
|
-
response.destroy()
|
|
479
|
-
})
|
|
480
|
-
// Node bug? Node never emits 'close' here.
|
|
481
|
-
response.on('aborted', function () {
|
|
482
|
-
t.pass('Response closed')
|
|
483
|
-
})
|
|
484
|
-
})
|
|
485
|
-
})
|
|
486
|
-
})
|
|
487
|
-
|
|
488
|
-
test('Destroying streams prematurely, log is disabled', t => {
|
|
489
|
-
t.plan(4)
|
|
490
|
-
|
|
491
|
-
let fastify = null
|
|
492
|
-
try {
|
|
493
|
-
fastify = Fastify({
|
|
494
|
-
logger: false
|
|
495
|
-
})
|
|
496
|
-
} catch (e) {
|
|
497
|
-
t.fail()
|
|
498
|
-
}
|
|
499
|
-
const stream = require('node:stream')
|
|
500
|
-
const http = require('node:http')
|
|
501
|
-
|
|
502
|
-
fastify.get('/', function (request, reply) {
|
|
503
|
-
reply.log[kDisableRequestLogging] = true
|
|
504
|
-
|
|
505
|
-
let sent = false
|
|
506
|
-
const reallyLongStream = new stream.Readable({
|
|
507
|
-
read: function () {
|
|
508
|
-
if (!sent) {
|
|
509
|
-
this.push(Buffer.from('hello\n'))
|
|
510
|
-
}
|
|
511
|
-
sent = true
|
|
512
|
-
}
|
|
513
|
-
})
|
|
514
|
-
reallyLongStream.destroy = true
|
|
515
|
-
reallyLongStream.close = () => t.ok('called')
|
|
516
|
-
reply.send(reallyLongStream)
|
|
517
|
-
})
|
|
518
|
-
|
|
519
|
-
fastify.listen({ port: 0 }, err => {
|
|
520
|
-
t.error(err)
|
|
521
|
-
t.teardown(() => { fastify.close() })
|
|
522
|
-
|
|
523
|
-
const port = fastify.server.address().port
|
|
524
|
-
|
|
525
|
-
http.get(`http://localhost:${port}`, function (response) {
|
|
526
|
-
t.equal(response.statusCode, 200)
|
|
527
|
-
response.on('readable', function () {
|
|
528
|
-
response.destroy()
|
|
529
|
-
})
|
|
530
|
-
// Node bug? Node never emits 'close' here.
|
|
531
|
-
response.on('aborted', function () {
|
|
532
|
-
t.pass('Response closed')
|
|
533
|
-
})
|
|
534
|
-
})
|
|
535
|
-
})
|
|
536
|
-
})
|
|
537
|
-
|
|
538
|
-
test('should respond with a stream1', t => {
|
|
539
|
-
t.plan(5)
|
|
540
|
-
const fastify = Fastify()
|
|
541
|
-
|
|
542
|
-
fastify.get('/', function (req, reply) {
|
|
543
|
-
const stream = JSONStream.stringify()
|
|
544
|
-
reply.code(200).type('application/json').send(stream)
|
|
545
|
-
stream.write({ hello: 'world' })
|
|
546
|
-
stream.end({ a: 42 })
|
|
547
|
-
})
|
|
548
|
-
|
|
549
|
-
fastify.listen({ port: 0 }, err => {
|
|
550
|
-
t.error(err)
|
|
551
|
-
t.teardown(() => { fastify.close() })
|
|
552
|
-
|
|
553
|
-
sget(`http://localhost:${fastify.server.address().port}`, function (err, response, body) {
|
|
554
|
-
t.error(err)
|
|
555
|
-
t.equal(response.headers['content-type'], 'application/json')
|
|
556
|
-
t.equal(response.statusCode, 200)
|
|
557
|
-
t.same(JSON.parse(body), [{ hello: 'world' }, { a: 42 }])
|
|
558
|
-
})
|
|
559
|
-
})
|
|
560
|
-
})
|
|
561
|
-
|
|
562
|
-
test('return a 404 if the stream emits a 404 error', t => {
|
|
563
|
-
t.plan(5)
|
|
564
|
-
|
|
565
|
-
const fastify = Fastify()
|
|
566
|
-
|
|
567
|
-
fastify.get('/', function (request, reply) {
|
|
568
|
-
t.pass('Received request')
|
|
569
|
-
|
|
570
|
-
const reallyLongStream = new Readable({
|
|
571
|
-
read: function () {
|
|
572
|
-
setImmediate(() => {
|
|
573
|
-
this.emit('error', new errors.NotFound())
|
|
574
|
-
})
|
|
575
|
-
}
|
|
576
|
-
})
|
|
577
|
-
|
|
578
|
-
reply.send(reallyLongStream)
|
|
579
|
-
})
|
|
580
|
-
|
|
581
|
-
fastify.listen({ port: 0 }, err => {
|
|
582
|
-
t.error(err)
|
|
583
|
-
t.teardown(() => { fastify.close() })
|
|
584
|
-
|
|
585
|
-
const port = fastify.server.address().port
|
|
586
|
-
|
|
587
|
-
sget(`http://localhost:${port}`, function (err, response) {
|
|
588
|
-
t.error(err)
|
|
589
|
-
t.equal(response.headers['content-type'], 'application/json; charset=utf-8')
|
|
590
|
-
t.equal(response.statusCode, 404)
|
|
591
|
-
})
|
|
592
|
-
})
|
|
593
|
-
})
|
|
594
|
-
|
|
595
|
-
test('should support send module 200 and 404', { skip: semver.gte(process.versions.node, '17.0.0') }, t => {
|
|
596
|
-
t.plan(8)
|
|
597
|
-
const fastify = Fastify()
|
|
598
|
-
|
|
599
|
-
fastify.get('/', function (req, reply) {
|
|
600
|
-
const stream = send(req.raw, __filename)
|
|
601
|
-
reply.code(200).send(stream)
|
|
602
|
-
})
|
|
603
|
-
|
|
604
|
-
fastify.get('/error', function (req, reply) {
|
|
605
|
-
const stream = send(req.raw, 'non-existing-file')
|
|
606
|
-
reply.code(200).send(stream)
|
|
607
|
-
})
|
|
608
|
-
|
|
609
|
-
fastify.listen({ port: 0 }, err => {
|
|
610
|
-
t.error(err)
|
|
611
|
-
t.teardown(() => { fastify.close() })
|
|
612
|
-
|
|
613
|
-
const url = getUrl(fastify)
|
|
614
|
-
|
|
615
|
-
sget(url, function (err, response, data) {
|
|
616
|
-
t.error(err)
|
|
617
|
-
t.equal(response.headers['content-type'], 'application/javascript; charset=UTF-8')
|
|
618
|
-
t.equal(response.statusCode, 200)
|
|
619
|
-
|
|
620
|
-
fs.readFile(__filename, (err, expected) => {
|
|
621
|
-
t.error(err)
|
|
622
|
-
t.equal(expected.toString(), data.toString())
|
|
623
|
-
})
|
|
624
|
-
})
|
|
625
|
-
|
|
626
|
-
sget(url + '/error', function (err, response) {
|
|
627
|
-
t.error(err)
|
|
628
|
-
t.equal(response.statusCode, 404)
|
|
629
|
-
})
|
|
630
|
-
})
|
|
631
|
-
})
|
|
632
|
-
|
|
633
|
-
test('should destroy stream when response is ended', t => {
|
|
634
|
-
t.plan(4)
|
|
635
|
-
const stream = require('node:stream')
|
|
636
|
-
const fastify = Fastify()
|
|
637
|
-
|
|
638
|
-
fastify.get('/error', function (req, reply) {
|
|
639
|
-
const reallyLongStream = new stream.Readable({
|
|
640
|
-
read: function () { },
|
|
641
|
-
destroy: function (err, callback) {
|
|
642
|
-
t.ok('called')
|
|
643
|
-
callback(err)
|
|
644
|
-
}
|
|
645
|
-
})
|
|
646
|
-
reply.code(200).send(reallyLongStream)
|
|
647
|
-
reply.raw.end(Buffer.from('hello\n'))
|
|
648
|
-
})
|
|
649
|
-
|
|
650
|
-
fastify.listen({ port: 0 }, err => {
|
|
651
|
-
t.error(err)
|
|
652
|
-
t.teardown(() => { fastify.close() })
|
|
653
|
-
|
|
654
|
-
sget(`http://localhost:${fastify.server.address().port}/error`, function (err, response) {
|
|
655
|
-
t.error(err)
|
|
656
|
-
t.equal(response.statusCode, 200)
|
|
657
|
-
})
|
|
658
|
-
})
|
|
659
|
-
})
|
|
660
|
-
|
|
661
|
-
test('should mark reply as sent before pumping the payload stream into response for async route handler', t => {
|
|
662
|
-
t.plan(3)
|
|
663
|
-
|
|
664
|
-
const handleRequest = proxyquire('../lib/handleRequest', {
|
|
665
|
-
'./wrapThenable': (thenable, reply) => {
|
|
666
|
-
thenable.then(function (payload) {
|
|
667
|
-
t.equal(reply.sent, true)
|
|
668
|
-
})
|
|
669
|
-
}
|
|
670
|
-
})
|
|
671
|
-
|
|
672
|
-
const route = proxyquire('../lib/route', {
|
|
673
|
-
'./handleRequest': handleRequest
|
|
674
|
-
})
|
|
675
|
-
|
|
676
|
-
const Fastify = proxyquire('..', {
|
|
677
|
-
'./lib/route': route
|
|
678
|
-
})
|
|
679
|
-
|
|
680
|
-
const fastify = Fastify()
|
|
681
|
-
|
|
682
|
-
fastify.get('/', async function (req, reply) {
|
|
683
|
-
const stream = fs.createReadStream(__filename, 'utf8')
|
|
684
|
-
return reply.code(200).send(stream)
|
|
685
|
-
})
|
|
686
|
-
|
|
687
|
-
fastify.inject({
|
|
688
|
-
url: '/',
|
|
689
|
-
method: 'GET'
|
|
690
|
-
}, (err, res) => {
|
|
691
|
-
t.error(err)
|
|
692
|
-
t.equal(res.payload, fs.readFileSync(__filename, 'utf8'))
|
|
693
|
-
fastify.close()
|
|
694
|
-
})
|
|
695
|
-
})
|
|
696
|
-
|
|
697
|
-
test('reply.send handles aborted requests', t => {
|
|
698
|
-
t.plan(2)
|
|
699
|
-
|
|
700
|
-
const spyLogger = {
|
|
701
|
-
level: 'error',
|
|
702
|
-
fatal: () => { },
|
|
703
|
-
error: () => {
|
|
704
|
-
t.fail('should not log an error')
|
|
705
|
-
},
|
|
706
|
-
warn: () => { },
|
|
707
|
-
info: () => { },
|
|
708
|
-
debug: () => { },
|
|
709
|
-
trace: () => { },
|
|
710
|
-
child: () => { return spyLogger }
|
|
711
|
-
}
|
|
712
|
-
const fastify = Fastify({
|
|
713
|
-
logger: spyLogger
|
|
714
|
-
})
|
|
715
|
-
|
|
716
|
-
fastify.get('/', (req, reply) => {
|
|
717
|
-
setTimeout(() => {
|
|
718
|
-
const stream = new Readable({
|
|
719
|
-
read: function () {
|
|
720
|
-
this.push(null)
|
|
721
|
-
}
|
|
722
|
-
})
|
|
723
|
-
reply.send(stream)
|
|
724
|
-
}, 6)
|
|
725
|
-
})
|
|
726
|
-
|
|
727
|
-
fastify.listen({ port: 0 }, err => {
|
|
728
|
-
t.error(err)
|
|
729
|
-
t.teardown(() => { fastify.close() })
|
|
730
|
-
|
|
731
|
-
const port = fastify.server.address().port
|
|
732
|
-
const http = require('node:http')
|
|
733
|
-
const req = http.get(`http://localhost:${port}`)
|
|
734
|
-
.on('error', (err) => {
|
|
735
|
-
t.equal(err.code, 'ECONNRESET')
|
|
736
|
-
fastify.close()
|
|
737
|
-
})
|
|
738
|
-
|
|
739
|
-
setTimeout(() => {
|
|
740
|
-
req.abort()
|
|
741
|
-
}, 1)
|
|
742
|
-
})
|
|
743
|
-
})
|
|
744
|
-
|
|
745
|
-
test('request terminated should not crash fastify', t => {
|
|
746
|
-
t.plan(10)
|
|
747
|
-
|
|
748
|
-
const spyLogger = {
|
|
749
|
-
level: 'error',
|
|
750
|
-
fatal: () => { },
|
|
751
|
-
error: () => {
|
|
752
|
-
t.fail('should not log an error')
|
|
753
|
-
},
|
|
754
|
-
warn: () => { },
|
|
755
|
-
info: () => { },
|
|
756
|
-
debug: () => { },
|
|
757
|
-
trace: () => { },
|
|
758
|
-
child: () => { return spyLogger }
|
|
759
|
-
}
|
|
760
|
-
const fastify = Fastify({
|
|
761
|
-
logger: spyLogger
|
|
762
|
-
})
|
|
763
|
-
|
|
764
|
-
fastify.get('/', async (req, reply) => {
|
|
765
|
-
const stream = new Readable()
|
|
766
|
-
stream._read = () => { }
|
|
767
|
-
reply.header('content-type', 'text/html; charset=utf-8')
|
|
768
|
-
reply.header('transfer-encoding', 'chunked')
|
|
769
|
-
stream.push('<h1>HTML</h1>')
|
|
770
|
-
|
|
771
|
-
reply.send(stream)
|
|
772
|
-
|
|
773
|
-
await new Promise((resolve) => { setTimeout(resolve, 100).unref() })
|
|
774
|
-
|
|
775
|
-
stream.push('<h1>should disply on second stream</h1>')
|
|
776
|
-
stream.push(null)
|
|
777
|
-
return reply
|
|
778
|
-
})
|
|
779
|
-
|
|
780
|
-
fastify.listen({ port: 0 }, err => {
|
|
781
|
-
t.error(err)
|
|
782
|
-
t.teardown(() => { fastify.close() })
|
|
783
|
-
|
|
784
|
-
const port = fastify.server.address().port
|
|
785
|
-
const http = require('node:http')
|
|
786
|
-
const req = http.get(`http://localhost:${port}`, function (res) {
|
|
787
|
-
const { statusCode, headers } = res
|
|
788
|
-
t.equal(statusCode, 200)
|
|
789
|
-
t.equal(headers['content-type'], 'text/html; charset=utf-8')
|
|
790
|
-
t.equal(headers['transfer-encoding'], 'chunked')
|
|
791
|
-
res.on('data', function (chunk) {
|
|
792
|
-
t.equal(chunk.toString(), '<h1>HTML</h1>')
|
|
793
|
-
})
|
|
794
|
-
|
|
795
|
-
setTimeout(() => {
|
|
796
|
-
req.destroy()
|
|
797
|
-
|
|
798
|
-
// the server is not crash, we can connect it
|
|
799
|
-
http.get(`http://localhost:${port}`, function (res) {
|
|
800
|
-
const { statusCode, headers } = res
|
|
801
|
-
t.equal(statusCode, 200)
|
|
802
|
-
t.equal(headers['content-type'], 'text/html; charset=utf-8')
|
|
803
|
-
t.equal(headers['transfer-encoding'], 'chunked')
|
|
804
|
-
let payload = ''
|
|
805
|
-
res.on('data', function (chunk) {
|
|
806
|
-
payload += chunk.toString()
|
|
807
|
-
})
|
|
808
|
-
res.on('end', function () {
|
|
809
|
-
t.equal(payload, '<h1>HTML</h1><h1>should disply on second stream</h1>')
|
|
810
|
-
t.pass('should end properly')
|
|
811
|
-
})
|
|
812
|
-
})
|
|
813
|
-
}, 1)
|
|
814
|
-
})
|
|
815
|
-
})
|
|
816
|
-
})
|