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)
							 | 
						||
| 
								 | 
							
								      })
							 | 
						||
| 
								 | 
							
								    })
							 | 
						||
| 
								 | 
							
								  })
							 | 
						||
| 
								 | 
							
								})
							 |