UNPKG

10.4 kBJavaScriptView Raw
1'use strict'
2
3const t = require('tap')
4const test = t.test
5const sget = require('simple-get').concat
6const fs = require('fs')
7const resolve = require('path').resolve
8const zlib = require('zlib')
9const pump = require('pump')
10const Fastify = require('..')
11const errors = require('http-errors')
12const JSONStream = require('JSONStream')
13const send = require('send')
14const Readable = require('stream').Readable
15const split = require('split2')
16
17test('should respond with a stream', t => {
18 t.plan(8)
19 const fastify = Fastify()
20
21 fastify.get('/', function (req, reply) {
22 const stream = fs.createReadStream(__filename, 'utf8')
23 reply.code(200).send(stream)
24 })
25
26 fastify.get('/error', function (req, reply) {
27 const stream = fs.createReadStream('not-existing-file', 'utf8')
28 reply.code(200).send(stream)
29 })
30
31 fastify.listen(0, err => {
32 t.error(err)
33 fastify.server.unref()
34
35 sget(`http://localhost:${fastify.server.address().port}`, function (err, response, data) {
36 t.error(err)
37 t.strictEqual(response.headers['content-type'], 'application/octet-stream')
38 t.strictEqual(response.statusCode, 200)
39
40 fs.readFile(__filename, (err, expected) => {
41 t.error(err)
42 t.equal(expected.toString(), data.toString())
43 })
44 })
45
46 sget(`http://localhost:${fastify.server.address().port}/error`, function (err, response) {
47 t.error(err)
48 t.strictEqual(response.statusCode, 500)
49 })
50 })
51})
52
53test('should trigger the onSend hook', t => {
54 t.plan(4)
55 const fastify = Fastify()
56
57 fastify.get('/', (req, reply) => {
58 reply.send(fs.createReadStream(__filename, 'utf8'))
59 })
60
61 fastify.addHook('onSend', (req, reply, payload, next) => {
62 t.ok(payload._readableState)
63 reply.header('Content-Type', 'application/javascript')
64 next()
65 })
66
67 fastify.inject({
68 url: '/'
69 }, (err, res) => {
70 t.error(err)
71 t.strictEqual(res.headers['content-type'], 'application/javascript')
72 t.strictEqual(res.payload, fs.readFileSync(__filename, 'utf8'))
73 fastify.close()
74 })
75})
76
77test('should trigger the onSend hook only twice if pumping the stream fails, first with the stream, second with the serialized error', t => {
78 t.plan(5)
79 const fastify = Fastify()
80
81 fastify.get('/', (req, reply) => {
82 reply.send(fs.createReadStream('not-existing-file', 'utf8'))
83 })
84
85 let counter = 0
86 fastify.addHook('onSend', (req, reply, payload, next) => {
87 if (counter === 0) {
88 t.ok(payload._readableState)
89 } else if (counter === 1) {
90 const error = JSON.parse(payload)
91 t.strictEqual(error.statusCode, 500)
92 }
93 counter++
94 next()
95 })
96
97 fastify.listen(0, err => {
98 t.error(err)
99
100 fastify.server.unref()
101
102 sget(`http://localhost:${fastify.server.address().port}`, function (err, response) {
103 t.error(err)
104 t.strictEqual(response.statusCode, 500)
105 })
106 })
107})
108
109test('onSend hook stream', t => {
110 t.plan(4)
111 const fastify = Fastify()
112
113 fastify.get('/', function (req, reply) {
114 reply.send({ hello: 'world' })
115 })
116
117 fastify.addHook('onSend', (req, reply, payload, next) => {
118 const gzStream = zlib.createGzip()
119
120 reply.header('Content-Encoding', 'gzip')
121 pump(
122 fs.createReadStream(resolve(process.cwd() + '/test/stream.test.js'), 'utf8'),
123 gzStream,
124 t.error
125 )
126 next(null, gzStream)
127 })
128
129 fastify.inject({
130 url: '/',
131 method: 'GET'
132 }, (err, res) => {
133 t.error(err)
134 t.strictEqual(res.headers['content-encoding'], 'gzip')
135 const file = fs.readFileSync(resolve(process.cwd() + '/test/stream.test.js'), 'utf8')
136 const payload = zlib.gunzipSync(res.rawPayload)
137 t.strictEqual(payload.toString('utf-8'), file)
138 fastify.close()
139 })
140})
141
142test('Destroying streams prematurely', t => {
143 t.plan(6)
144
145 let fastify = null
146 const logStream = split(JSON.parse)
147 try {
148 fastify = Fastify({
149 logger: {
150 stream: logStream,
151 level: 'warn'
152 }
153 })
154 } catch (e) {
155 t.fail()
156 }
157 const stream = require('stream')
158 const http = require('http')
159
160 // Test that "premature close" errors are logged with level warn
161 logStream.once('data', line => {
162 t.equal(line.msg, 'response terminated with an error with headers already sent')
163 t.equal(line.level, 40)
164 })
165
166 fastify.get('/', function (request, reply) {
167 t.pass('Received request')
168
169 var sent = false
170 var reallyLongStream = new stream.Readable({
171 read: function () {
172 if (!sent) {
173 this.push(Buffer.from('hello\n'))
174 }
175 sent = true
176 }
177 })
178
179 reply.send(reallyLongStream)
180 })
181
182 fastify.listen(0, err => {
183 t.error(err)
184 fastify.server.unref()
185
186 var port = fastify.server.address().port
187
188 http.get(`http://localhost:${port}`, function (response) {
189 t.strictEqual(response.statusCode, 200)
190 response.on('readable', function () {
191 response.destroy()
192 })
193
194 // Node bug? Node never emits 'close' here.
195 response.on('aborted', function () {
196 t.pass('Response closed')
197 })
198 })
199 })
200})
201
202test('Destroying streams prematurely should call close method', t => {
203 t.plan(7)
204
205 let fastify = null
206 const logStream = split(JSON.parse)
207 try {
208 fastify = Fastify({
209 logger: {
210 stream: logStream,
211 level: 'warn'
212 }
213 })
214 } catch (e) {
215 t.fail()
216 }
217 const stream = require('stream')
218 const http = require('http')
219
220 // Test that "premature close" errors are logged with level warn
221 logStream.once('data', line => {
222 t.equal(line.msg, 'response terminated with an error with headers already sent')
223 t.equal(line.level, 40)
224 })
225
226 fastify.get('/', function (request, reply) {
227 t.pass('Received request')
228
229 var sent = false
230 var reallyLongStream = new stream.Readable({
231 read: function () {
232 if (!sent) {
233 this.push(Buffer.from('hello\n'))
234 }
235 sent = true
236 }
237 })
238 reallyLongStream.destroy = undefined
239 reallyLongStream.close = () => t.ok('called')
240 reply.send(reallyLongStream)
241 })
242
243 fastify.listen(0, err => {
244 t.error(err)
245 fastify.server.unref()
246
247 var port = fastify.server.address().port
248
249 http.get(`http://localhost:${port}`, function (response) {
250 t.strictEqual(response.statusCode, 200)
251 response.on('readable', function () {
252 response.destroy()
253 })
254 // Node bug? Node never emits 'close' here.
255 response.on('aborted', function () {
256 t.pass('Response closed')
257 })
258 })
259 })
260})
261
262test('Destroying streams prematurely should call abort method', t => {
263 t.plan(7)
264
265 let fastify = null
266 const logStream = split(JSON.parse)
267 try {
268 fastify = Fastify({
269 logger: {
270 stream: logStream,
271 level: 'warn'
272 }
273 })
274 } catch (e) {
275 t.fail()
276 }
277 const stream = require('stream')
278 const http = require('http')
279
280 // Test that "premature close" errors are logged with level warn
281 logStream.once('data', line => {
282 t.equal(line.msg, 'response terminated with an error with headers already sent')
283 t.equal(line.level, 40)
284 })
285
286 fastify.get('/', function (request, reply) {
287 t.pass('Received request')
288
289 var sent = false
290 var reallyLongStream = new stream.Readable({
291 read: function () {
292 if (!sent) {
293 this.push(Buffer.from('hello\n'))
294 }
295 sent = true
296 }
297 })
298 reallyLongStream.destroy = undefined
299 reallyLongStream.close = undefined
300 reallyLongStream.abort = () => t.ok('called')
301 reply.send(reallyLongStream)
302 })
303
304 fastify.listen(0, err => {
305 t.error(err)
306 fastify.server.unref()
307
308 var port = fastify.server.address().port
309
310 http.get(`http://localhost:${port}`, function (response) {
311 t.strictEqual(response.statusCode, 200)
312 response.on('readable', function () {
313 response.destroy()
314 })
315 // Node bug? Node never emits 'close' here.
316 response.on('aborted', function () {
317 t.pass('Response closed')
318 })
319 })
320 })
321})
322
323test('should respond with a stream1', t => {
324 t.plan(5)
325 const fastify = Fastify()
326
327 fastify.get('/', function (req, reply) {
328 const stream = JSONStream.stringify()
329 reply.code(200).type('application/json').send(stream)
330 stream.write({ hello: 'world' })
331 stream.end({ a: 42 })
332 })
333
334 fastify.listen(0, err => {
335 t.error(err)
336 fastify.server.unref()
337
338 sget(`http://localhost:${fastify.server.address().port}`, function (err, response, body) {
339 t.error(err)
340 t.strictEqual(response.headers['content-type'], 'application/json')
341 t.strictEqual(response.statusCode, 200)
342 t.deepEqual(JSON.parse(body), [{ hello: 'world' }, { a: 42 }])
343 })
344 })
345})
346
347test('return a 404 if the stream emits a 404 error', t => {
348 t.plan(5)
349
350 const fastify = Fastify()
351
352 fastify.get('/', function (request, reply) {
353 t.pass('Received request')
354
355 var reallyLongStream = new Readable({
356 read: function () {
357 setImmediate(() => {
358 this.emit('error', new errors.NotFound())
359 })
360 }
361 })
362
363 reply.send(reallyLongStream)
364 })
365
366 fastify.listen(0, err => {
367 t.error(err)
368 fastify.server.unref()
369
370 var port = fastify.server.address().port
371
372 sget(`http://localhost:${port}`, function (err, response) {
373 t.error(err)
374 t.strictEqual(response.headers['content-type'], 'application/json; charset=utf-8')
375 t.strictEqual(response.statusCode, 404)
376 })
377 })
378})
379
380test('should support send module 200 and 404', t => {
381 t.plan(8)
382 const fastify = Fastify()
383
384 fastify.get('/', function (req, reply) {
385 const stream = send(req.req, __filename)
386 reply.code(200).send(stream)
387 })
388
389 fastify.get('/error', function (req, reply) {
390 const stream = send(req.req, 'non-existing-file')
391 reply.code(200).send(stream)
392 })
393
394 fastify.listen(0, err => {
395 t.error(err)
396 fastify.server.unref()
397
398 sget(`http://localhost:${fastify.server.address().port}`, function (err, response, data) {
399 t.error(err)
400 t.strictEqual(response.headers['content-type'], 'application/octet-stream')
401 t.strictEqual(response.statusCode, 200)
402
403 fs.readFile(__filename, (err, expected) => {
404 t.error(err)
405 t.equal(expected.toString(), data.toString())
406 })
407 })
408
409 sget(`http://localhost:${fastify.server.address().port}/error`, function (err, response) {
410 t.error(err)
411 t.strictEqual(response.statusCode, 404)
412 })
413 })
414})