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
			| 
											3 years ago
										 | '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') | ||
|  |   }) | ||
|  | }) |