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.
		
		
		
		
		
			
		
			
				
					332 lines
				
				7.1 KiB
			
		
		
			
		
	
	
					332 lines
				
				7.1 KiB
			| 
											3 years ago
										 | 'use strict' | ||
|  | 
 | ||
|  | const net = require('net') | ||
|  | const t = require('tap') | ||
|  | const test = t.test | ||
|  | const Fastify = require('..') | ||
|  | const { Client } = require('undici') | ||
|  | 
 | ||
|  | test('close callback', t => { | ||
|  |   t.plan(4) | ||
|  |   const fastify = Fastify() | ||
|  |   fastify.addHook('onClose', onClose) | ||
|  |   function onClose (instance, done) { | ||
|  |     t.type(fastify, instance) | ||
|  |     done() | ||
|  |   } | ||
|  | 
 | ||
|  |   fastify.listen(0, err => { | ||
|  |     t.error(err) | ||
|  | 
 | ||
|  |     fastify.close((err) => { | ||
|  |       t.error(err) | ||
|  |       t.ok('close callback') | ||
|  |     }) | ||
|  |   }) | ||
|  | }) | ||
|  | 
 | ||
|  | test('inside register', t => { | ||
|  |   t.plan(5) | ||
|  |   const fastify = Fastify() | ||
|  |   fastify.register(function (f, opts, done) { | ||
|  |     f.addHook('onClose', onClose) | ||
|  |     function onClose (instance, done) { | ||
|  |       t.ok(instance.prototype === fastify.prototype) | ||
|  |       t.equal(instance, f) | ||
|  |       done() | ||
|  |     } | ||
|  | 
 | ||
|  |     done() | ||
|  |   }) | ||
|  | 
 | ||
|  |   fastify.listen(0, err => { | ||
|  |     t.error(err) | ||
|  | 
 | ||
|  |     fastify.close((err) => { | ||
|  |       t.error(err) | ||
|  |       t.ok('close callback') | ||
|  |     }) | ||
|  |   }) | ||
|  | }) | ||
|  | 
 | ||
|  | test('close order', t => { | ||
|  |   t.plan(5) | ||
|  |   const fastify = Fastify() | ||
|  |   const order = [1, 2, 3] | ||
|  | 
 | ||
|  |   fastify.register(function (f, opts, done) { | ||
|  |     f.addHook('onClose', (instance, done) => { | ||
|  |       t.equal(order.shift(), 1) | ||
|  |       done() | ||
|  |     }) | ||
|  | 
 | ||
|  |     done() | ||
|  |   }) | ||
|  | 
 | ||
|  |   fastify.addHook('onClose', (instance, done) => { | ||
|  |     t.equal(order.shift(), 2) | ||
|  |     done() | ||
|  |   }) | ||
|  | 
 | ||
|  |   fastify.listen(0, err => { | ||
|  |     t.error(err) | ||
|  | 
 | ||
|  |     fastify.close((err) => { | ||
|  |       t.error(err) | ||
|  |       t.equal(order.shift(), 3) | ||
|  |     }) | ||
|  |   }) | ||
|  | }) | ||
|  | 
 | ||
|  | test('close order - async', async t => { | ||
|  |   t.plan(3) | ||
|  |   const fastify = Fastify() | ||
|  |   const order = [1, 2, 3] | ||
|  | 
 | ||
|  |   fastify.register(function (f, opts, done) { | ||
|  |     f.addHook('onClose', async instance => { | ||
|  |       t.equal(order.shift(), 1) | ||
|  |     }) | ||
|  | 
 | ||
|  |     done() | ||
|  |   }) | ||
|  | 
 | ||
|  |   fastify.addHook('onClose', () => { | ||
|  |     t.equal(order.shift(), 2) | ||
|  |   }) | ||
|  | 
 | ||
|  |   await fastify.listen(0) | ||
|  |   await fastify.close() | ||
|  | 
 | ||
|  |   t.equal(order.shift(), 3) | ||
|  | }) | ||
|  | 
 | ||
|  | test('should not throw an error if the server is not listening', t => { | ||
|  |   t.plan(2) | ||
|  |   const fastify = Fastify() | ||
|  |   fastify.addHook('onClose', onClose) | ||
|  |   function onClose (instance, done) { | ||
|  |     t.type(fastify, instance) | ||
|  |     done() | ||
|  |   } | ||
|  | 
 | ||
|  |   fastify.close((err) => { | ||
|  |     t.error(err) | ||
|  |   }) | ||
|  | }) | ||
|  | 
 | ||
|  | test('onClose should keep the context', t => { | ||
|  |   t.plan(4) | ||
|  |   const fastify = Fastify() | ||
|  |   fastify.register(plugin) | ||
|  | 
 | ||
|  |   function plugin (instance, opts, done) { | ||
|  |     instance.decorate('test', true) | ||
|  |     instance.addHook('onClose', onClose) | ||
|  |     t.ok(instance.prototype === fastify.prototype) | ||
|  | 
 | ||
|  |     function onClose (i, done) { | ||
|  |       t.ok(i.test) | ||
|  |       t.equal(i, instance) | ||
|  |       done() | ||
|  |     } | ||
|  | 
 | ||
|  |     done() | ||
|  |   } | ||
|  | 
 | ||
|  |   fastify.close((err) => { | ||
|  |     t.error(err) | ||
|  |   }) | ||
|  | }) | ||
|  | 
 | ||
|  | test('Should return error while closing (promise) - injection', t => { | ||
|  |   t.plan(4) | ||
|  |   const fastify = Fastify() | ||
|  | 
 | ||
|  |   fastify.addHook('onClose', (instance, done) => { done() }) | ||
|  | 
 | ||
|  |   fastify.get('/', (req, reply) => { | ||
|  |     reply.send({ hello: 'world' }) | ||
|  |   }) | ||
|  | 
 | ||
|  |   fastify.inject({ | ||
|  |     method: 'GET', | ||
|  |     url: '/' | ||
|  |   }, (err, res) => { | ||
|  |     t.error(err) | ||
|  |     t.equal(res.statusCode, 200) | ||
|  |     fastify.close() | ||
|  | 
 | ||
|  |     process.nextTick(() => { | ||
|  |       fastify.inject({ | ||
|  |         method: 'GET', | ||
|  |         url: '/' | ||
|  |       }).catch(err => { | ||
|  |         t.ok(err) | ||
|  |         t.equal(err.message, 'Server is closed') | ||
|  |       }) | ||
|  |     }, 100) | ||
|  |   }) | ||
|  | }) | ||
|  | 
 | ||
|  | test('Should return error while closing (callback) - injection', t => { | ||
|  |   t.plan(4) | ||
|  |   const fastify = Fastify() | ||
|  | 
 | ||
|  |   fastify.addHook('onClose', (instance, done) => { | ||
|  |     setTimeout(done, 150) | ||
|  |   }) | ||
|  | 
 | ||
|  |   fastify.get('/', (req, reply) => { | ||
|  |     reply.send({ hello: 'world' }) | ||
|  |   }) | ||
|  | 
 | ||
|  |   fastify.inject({ | ||
|  |     method: 'GET', | ||
|  |     url: '/' | ||
|  |   }, (err, res) => { | ||
|  |     t.error(err) | ||
|  |     t.equal(res.statusCode, 200) | ||
|  |     fastify.close() | ||
|  | 
 | ||
|  |     setTimeout(() => { | ||
|  |       fastify.inject({ | ||
|  |         method: 'GET', | ||
|  |         url: '/' | ||
|  |       }, (err, res) => { | ||
|  |         t.ok(err) | ||
|  |         t.equal(err.message, 'Server is closed') | ||
|  |       }) | ||
|  |     }, 100) | ||
|  |   }) | ||
|  | }) | ||
|  | 
 | ||
|  | t.test('Current opened connection should continue to work after closing and return "connection: close" header - return503OnClosing: false', t => { | ||
|  |   const fastify = Fastify({ | ||
|  |     return503OnClosing: false | ||
|  |   }) | ||
|  | 
 | ||
|  |   fastify.get('/', (req, reply) => { | ||
|  |     fastify.close() | ||
|  |     reply.send({ hello: 'world' }) | ||
|  |   }) | ||
|  | 
 | ||
|  |   fastify.listen(0, err => { | ||
|  |     t.error(err) | ||
|  | 
 | ||
|  |     const port = fastify.server.address().port | ||
|  |     const client = net.createConnection({ port }, () => { | ||
|  |       client.write('GET / HTTP/1.1\r\n\r\n') | ||
|  | 
 | ||
|  |       client.once('data', data => { | ||
|  |         t.match(data.toString(), /Connection:\s*keep-alive/i) | ||
|  |         t.match(data.toString(), /200 OK/i) | ||
|  | 
 | ||
|  |         client.write('GET / HTTP/1.1\r\n\r\n') | ||
|  | 
 | ||
|  |         client.once('data', data => { | ||
|  |           t.match(data.toString(), /Connection:\s*close/i) | ||
|  |           t.match(data.toString(), /200 OK/i) | ||
|  | 
 | ||
|  |           // Test that fastify closes the TCP connection
 | ||
|  |           client.once('close', () => { | ||
|  |             t.end() | ||
|  |           }) | ||
|  |         }) | ||
|  |       }) | ||
|  |     }) | ||
|  |   }) | ||
|  | }) | ||
|  | 
 | ||
|  | t.test('Current opened connection should not accept new incoming connections', t => { | ||
|  |   t.plan(3) | ||
|  |   const fastify = Fastify() | ||
|  |   fastify.get('/', (req, reply) => { | ||
|  |     fastify.close() | ||
|  |     setTimeout(() => { | ||
|  |       reply.send({ hello: 'world' }) | ||
|  |     }, 250) | ||
|  |   }) | ||
|  | 
 | ||
|  |   fastify.listen(0, err => { | ||
|  |     t.error(err) | ||
|  |     const instance = new Client('http://localhost:' + fastify.server.address().port) | ||
|  |     instance.request({ path: '/', method: 'GET' }).then(data => { | ||
|  |       t.equal(data.statusCode, 200) | ||
|  |     }) | ||
|  |     instance.request({ path: '/', method: 'GET' }).then(data => { | ||
|  |       t.equal(data.statusCode, 503) | ||
|  |     }) | ||
|  |   }) | ||
|  | }) | ||
|  | 
 | ||
|  | test('Cannot be reopened the closed server without listen callback', async t => { | ||
|  |   t.plan(2) | ||
|  |   const fastify = Fastify() | ||
|  | 
 | ||
|  |   await fastify.listen(0) | ||
|  |   await fastify.close() | ||
|  | 
 | ||
|  |   try { | ||
|  |     await fastify.listen(0) | ||
|  |   } catch (err) { | ||
|  |     t.ok(err) | ||
|  |     t.equal(err.code, 'FST_ERR_REOPENED_CLOSE_SERVER') | ||
|  |   } | ||
|  | }) | ||
|  | 
 | ||
|  | test('Cannot be reopened the closed server has listen callback', async t => { | ||
|  |   t.plan(2) | ||
|  |   const fastify = Fastify() | ||
|  | 
 | ||
|  |   await fastify.listen(0) | ||
|  |   await fastify.close() | ||
|  | 
 | ||
|  |   await new Promise((resolve, reject) => { | ||
|  |     fastify.listen(0, err => { | ||
|  |       reject(err) | ||
|  |     }) | ||
|  |   }).catch(err => { | ||
|  |     t.equal(err.code, 'FST_ERR_REOPENED_CLOSE_SERVER') | ||
|  |     t.ok(err) | ||
|  |   }) | ||
|  | }) | ||
|  | 
 | ||
|  | test('shutsdown while keep-alive connections are active (non-async)', t => { | ||
|  |   t.plan(5) | ||
|  | 
 | ||
|  |   const timeoutTime = 2 * 60 * 1000 | ||
|  |   const fastify = Fastify({ forceCloseConnections: true }) | ||
|  | 
 | ||
|  |   fastify.server.setTimeout(timeoutTime) | ||
|  |   fastify.server.keepAliveTimeout = timeoutTime | ||
|  | 
 | ||
|  |   fastify.get('/', (req, reply) => { | ||
|  |     reply.send({ hello: 'world' }) | ||
|  |   }) | ||
|  | 
 | ||
|  |   fastify.listen(0, (err, address) => { | ||
|  |     t.error(err) | ||
|  | 
 | ||
|  |     const client = new Client( | ||
|  |       'http://localhost:' + fastify.server.address().port, | ||
|  |       { keepAliveTimeout: 1 * 60 * 1000 } | ||
|  |     ) | ||
|  |     client.request({ path: '/', method: 'GET' }, (err, response) => { | ||
|  |       t.error(err) | ||
|  |       t.equal(client.closed, false) | ||
|  | 
 | ||
|  |       fastify.close((err) => { | ||
|  |         t.error(err) | ||
|  | 
 | ||
|  |         // Due to the nature of the way we reap these keep-alive connections,
 | ||
|  |         // there hasn't been enough time before the server fully closed in order
 | ||
|  |         // for the client to have seen the socket get destroyed. The mere fact
 | ||
|  |         // that we have reached this callback is enough indication that the
 | ||
|  |         // feature being tested works as designed.
 | ||
|  |         t.equal(client.closed, false) | ||
|  |       }) | ||
|  |     }) | ||
|  |   }) | ||
|  | }) |