You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							322 lines
						
					
					
						
							6.9 KiB
						
					
					
				
			
		
		
	
	
							322 lines
						
					
					
						
							6.9 KiB
						
					
					
				| 'use strict'
 | |
| 
 | |
| const { connect } = require('net')
 | |
| const t = require('tap')
 | |
| const semver = require('semver')
 | |
| const test = t.test
 | |
| const Fastify = require('..')
 | |
| const { kRequest } = require('../lib/symbols.js')
 | |
| 
 | |
| test('default 400 on request error', t => {
 | |
|   t.plan(4)
 | |
| 
 | |
|   const fastify = Fastify()
 | |
| 
 | |
|   fastify.post('/', function (req, reply) {
 | |
|     reply.send({ hello: 'world' })
 | |
|   })
 | |
| 
 | |
|   fastify.inject({
 | |
|     method: 'POST',
 | |
|     url: '/',
 | |
|     simulate: {
 | |
|       error: true
 | |
|     },
 | |
|     body: {
 | |
|       text: '12345678901234567890123456789012345678901234567890'
 | |
|     }
 | |
|   }, (err, res) => {
 | |
|     t.error(err)
 | |
|     t.equal(res.statusCode, 400)
 | |
|     t.equal(res.headers['content-type'], 'application/json; charset=utf-8')
 | |
|     t.same(JSON.parse(res.payload), {
 | |
|       error: 'Bad Request',
 | |
|       message: 'Simulated',
 | |
|       statusCode: 400
 | |
|     })
 | |
|   })
 | |
| })
 | |
| 
 | |
| test('default 400 on request error with custom error handler', t => {
 | |
|   t.plan(6)
 | |
| 
 | |
|   const fastify = Fastify()
 | |
| 
 | |
|   fastify.setErrorHandler(function (err, request, reply) {
 | |
|     t.type(request, 'object')
 | |
|     t.type(request, fastify[kRequest])
 | |
|     reply
 | |
|       .code(err.statusCode)
 | |
|       .type('application/json; charset=utf-8')
 | |
|       .send(err)
 | |
|   })
 | |
| 
 | |
|   fastify.post('/', function (req, reply) {
 | |
|     reply.send({ hello: 'world' })
 | |
|   })
 | |
| 
 | |
|   fastify.inject({
 | |
|     method: 'POST',
 | |
|     url: '/',
 | |
|     simulate: {
 | |
|       error: true
 | |
|     },
 | |
|     body: {
 | |
|       text: '12345678901234567890123456789012345678901234567890'
 | |
|     }
 | |
|   }, (err, res) => {
 | |
|     t.error(err)
 | |
|     t.equal(res.statusCode, 400)
 | |
|     t.equal(res.headers['content-type'], 'application/json; charset=utf-8')
 | |
|     t.same(JSON.parse(res.payload), {
 | |
|       error: 'Bad Request',
 | |
|       message: 'Simulated',
 | |
|       statusCode: 400
 | |
|     })
 | |
|   })
 | |
| })
 | |
| 
 | |
| test('default clientError handler ignores ECONNRESET', t => {
 | |
|   t.plan(3)
 | |
| 
 | |
|   let logs = ''
 | |
|   let response = ''
 | |
| 
 | |
|   const fastify = Fastify({
 | |
|     bodyLimit: 1,
 | |
|     keepAliveTimeout: 100,
 | |
|     logger: {
 | |
|       level: 'trace',
 | |
|       stream: {
 | |
|         write () {
 | |
|           logs += JSON.stringify(arguments)
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   })
 | |
| 
 | |
|   fastify.get('/', (request, reply) => {
 | |
|     reply.send('OK')
 | |
| 
 | |
|     process.nextTick(() => {
 | |
|       const error = new Error()
 | |
|       error.code = 'ECONNRESET'
 | |
| 
 | |
|       fastify.server.emit('clientError', error, request.raw.socket)
 | |
|     })
 | |
|   })
 | |
| 
 | |
|   fastify.listen(0, function (err) {
 | |
|     t.error(err)
 | |
|     fastify.server.unref()
 | |
| 
 | |
|     const client = connect(fastify.server.address().port)
 | |
| 
 | |
|     client.on('data', chunk => {
 | |
|       response += chunk.toString('utf-8')
 | |
|     })
 | |
| 
 | |
|     client.on('end', () => {
 | |
|       t.match(response, /^HTTP\/1.1 200 OK/)
 | |
|       t.notMatch(logs, /ECONNRESET/)
 | |
|     })
 | |
| 
 | |
|     client.resume()
 | |
|     client.write('GET / HTTP/1.1\r\n')
 | |
|     client.write('Connection: close\r\n')
 | |
|     client.write('\r\n\r\n')
 | |
|   })
 | |
| })
 | |
| 
 | |
| test('default clientError handler ignores sockets in destroyed state', t => {
 | |
|   t.plan(1)
 | |
| 
 | |
|   const fastify = Fastify({
 | |
|     bodyLimit: 1,
 | |
|     keepAliveTimeout: 100,
 | |
|     logger: {
 | |
|       level: 'trace'
 | |
|     }
 | |
|   })
 | |
|   fastify.server.on('clientError', () => {
 | |
|     // this handler is called after default handler, so we can make sure end was not called
 | |
|     t.pass()
 | |
|   })
 | |
|   fastify.server.emit('clientError', new Error(), {
 | |
|     destroyed: true,
 | |
|     end () {
 | |
|       t.fail('end should not be called')
 | |
|     },
 | |
|     destroy () {
 | |
|       t.fail('destroy should not be called')
 | |
|     }
 | |
|   })
 | |
| })
 | |
| 
 | |
| test('default clientError handler destroys sockets in writable state', t => {
 | |
|   t.plan(2)
 | |
| 
 | |
|   const fastify = Fastify({
 | |
|     bodyLimit: 1,
 | |
|     keepAliveTimeout: 100
 | |
|   })
 | |
| 
 | |
|   fastify.server.emit('clientError', new Error(), {
 | |
|     destroyed: false,
 | |
|     writable: true,
 | |
|     encrypted: true,
 | |
|     end () {
 | |
|       t.fail('end should not be called')
 | |
|     },
 | |
|     destroy () {
 | |
|       t.pass('destroy should be called')
 | |
|     },
 | |
|     write (response) {
 | |
|       t.match(response, /^HTTP\/1.1 400 Bad Request/)
 | |
|     }
 | |
|   })
 | |
| })
 | |
| 
 | |
| test('default clientError handler destroys http sockets in non-writable state', t => {
 | |
|   t.plan(1)
 | |
| 
 | |
|   const fastify = Fastify({
 | |
|     bodyLimit: 1,
 | |
|     keepAliveTimeout: 100
 | |
|   })
 | |
| 
 | |
|   fastify.server.emit('clientError', new Error(), {
 | |
|     destroyed: false,
 | |
|     writable: false,
 | |
|     end () {
 | |
|       t.fail('end should not be called')
 | |
|     },
 | |
|     destroy () {
 | |
|       t.pass('destroy should be called')
 | |
|     },
 | |
|     write (response) {
 | |
|       t.fail('write should not be called')
 | |
|     }
 | |
|   })
 | |
| })
 | |
| 
 | |
| test('error handler binding', t => {
 | |
|   t.plan(5)
 | |
| 
 | |
|   const fastify = Fastify()
 | |
| 
 | |
|   fastify.setErrorHandler(function (err, request, reply) {
 | |
|     t.equal(this, fastify)
 | |
|     reply
 | |
|       .code(err.statusCode)
 | |
|       .type('application/json; charset=utf-8')
 | |
|       .send(err)
 | |
|   })
 | |
| 
 | |
|   fastify.post('/', function (req, reply) {
 | |
|     reply.send({ hello: 'world' })
 | |
|   })
 | |
| 
 | |
|   fastify.inject({
 | |
|     method: 'POST',
 | |
|     url: '/',
 | |
|     simulate: {
 | |
|       error: true
 | |
|     },
 | |
|     body: {
 | |
|       text: '12345678901234567890123456789012345678901234567890'
 | |
|     }
 | |
|   }, (err, res) => {
 | |
|     t.error(err)
 | |
|     t.equal(res.statusCode, 400)
 | |
|     t.equal(res.headers['content-type'], 'application/json; charset=utf-8')
 | |
|     t.same(JSON.parse(res.payload), {
 | |
|       error: 'Bad Request',
 | |
|       message: 'Simulated',
 | |
|       statusCode: 400
 | |
|     })
 | |
|   })
 | |
| })
 | |
| 
 | |
| test('encapsulated error handler binding', t => {
 | |
|   t.plan(7)
 | |
| 
 | |
|   const fastify = Fastify()
 | |
| 
 | |
|   fastify.register(function (app, opts, done) {
 | |
|     app.decorate('hello', 'world')
 | |
|     t.equal(app.hello, 'world')
 | |
|     app.post('/', function (req, reply) {
 | |
|       reply.send({ hello: 'world' })
 | |
|     })
 | |
|     app.setErrorHandler(function (err, request, reply) {
 | |
|       t.equal(this.hello, 'world')
 | |
|       reply
 | |
|         .code(err.statusCode)
 | |
|         .type('application/json; charset=utf-8')
 | |
|         .send(err)
 | |
|     })
 | |
|     done()
 | |
|   })
 | |
| 
 | |
|   fastify.inject({
 | |
|     method: 'POST',
 | |
|     url: '/',
 | |
|     simulate: {
 | |
|       error: true
 | |
|     },
 | |
|     body: {
 | |
|       text: '12345678901234567890123456789012345678901234567890'
 | |
|     }
 | |
|   }, (err, res) => {
 | |
|     t.error(err)
 | |
|     t.equal(res.statusCode, 400)
 | |
|     t.equal(res.headers['content-type'], 'application/json; charset=utf-8')
 | |
|     t.same(res.json(), {
 | |
|       error: 'Bad Request',
 | |
|       message: 'Simulated',
 | |
|       statusCode: 400
 | |
|     })
 | |
|     t.equal(fastify.hello, undefined)
 | |
|   })
 | |
| })
 | |
| 
 | |
| const skip = semver.lt(process.versions.node, '11.0.0')
 | |
| 
 | |
| test('default clientError replies with bad request on reused keep-alive connection', { skip }, t => {
 | |
|   t.plan(2)
 | |
| 
 | |
|   let response = ''
 | |
| 
 | |
|   const fastify = Fastify({
 | |
|     bodyLimit: 1,
 | |
|     keepAliveTimeout: 100
 | |
|   })
 | |
| 
 | |
|   fastify.get('/', (request, reply) => {
 | |
|     reply.send('OK\n')
 | |
|   })
 | |
| 
 | |
|   fastify.listen({ port: 0 }, function (err) {
 | |
|     t.error(err)
 | |
|     fastify.server.unref()
 | |
| 
 | |
|     const client = connect(fastify.server.address().port)
 | |
| 
 | |
|     client.on('data', chunk => {
 | |
|       response += chunk.toString('utf-8')
 | |
|     })
 | |
| 
 | |
|     client.on('end', () => {
 | |
|       t.match(response, /^HTTP\/1.1 200 OK.*HTTP\/1.1 400 Bad Request/s)
 | |
|     })
 | |
| 
 | |
|     client.resume()
 | |
|     client.write('GET / HTTP/1.1\r\n')
 | |
|     client.write('\r\n\r\n')
 | |
|     client.write('GET /?a b HTTP/1.1\r\n')
 | |
|     client.write('Connection: close\r\n')
 | |
|     client.write('\r\n\r\n')
 | |
|   })
 | |
| })
 |