'use strict';

const chai = require('chai');

const expect = chai.expect;
const Support = require('../../support');

const dialect = Support.getTestDialect();
const { DataTypes } = require('@sequelize/core');

if (dialect === 'mariadb') {
  describe('[MariaDB Specific] DAOFactory', () => {
    describe('constructor', () => {
      it('handles extended attributes (unique)', function () {
        const User = this.sequelize.define(`User${Support.rand()}`, {
          username: { type: DataTypes.STRING, unique: true },
        }, { timestamps: false });

        expect(this.sequelize.queryGenerator.attributesToSQL(User.getAttributes())).to.deep.equal({
          // note: UNIQUE is not specified here because it is only specified if the option passed to attributesToSQL is
          //  'unique: true'.
          // Model.init normalizes the 'unique' to ensure a consistent index, and createTableQuery handles adding
          //  a named UNIQUE constraint
          username: 'VARCHAR(255)',
          id: 'INTEGER NOT NULL auto_increment PRIMARY KEY',
        });
      });

      it('handles extended attributes (default)', function () {
        const User = this.sequelize.define(`User${Support.rand()}`, {
          username: { type: DataTypes.STRING, defaultValue: 'foo' },
        }, { timestamps: false });
        expect(
          this.sequelize.queryGenerator.attributesToSQL(
            User.getAttributes(),
          ),
        ).to.deep.equal({
          username: 'VARCHAR(255) DEFAULT \'foo\'',
          id: 'INTEGER NOT NULL auto_increment PRIMARY KEY',
        });
      });

      it('handles extended attributes (null)', function () {
        const User = this.sequelize.define(`User${Support.rand()}`, {
          username: { type: DataTypes.STRING, allowNull: false },
        }, { timestamps: false });
        expect(
          this.sequelize.queryGenerator.attributesToSQL(
            User.getAttributes(),
          ),
        ).to.deep.equal({
          username: 'VARCHAR(255) NOT NULL',
          id: 'INTEGER NOT NULL auto_increment PRIMARY KEY',
        });
      });

      it('handles extended attributes (primaryKey)', function () {
        const User = this.sequelize.define(`User${Support.rand()}`, {
          username: { type: DataTypes.STRING, primaryKey: true },
        }, { timestamps: false });
        expect(
          this.sequelize.queryGenerator.attributesToSQL(
            User.getAttributes(),
          ),
        ).to.deep.equal(
          { username: 'VARCHAR(255) PRIMARY KEY' },
        );
      });

      it('adds timestamps', function () {
        const User1 = this.sequelize.define(`User${Support.rand()}`, {});
        const User2 = this.sequelize.define(`User${Support.rand()}`, {},
          { timestamps: true });

        expect(
          this.sequelize.queryGenerator.attributesToSQL(
            User1.getAttributes(),
          ),
        ).to.deep.equal({
          id: 'INTEGER NOT NULL auto_increment PRIMARY KEY',
          updatedAt: 'DATETIME(6) NOT NULL',
          createdAt: 'DATETIME(6) NOT NULL',
        });
        expect(
          this.sequelize.queryGenerator.attributesToSQL(
            User2.getAttributes(),
          ),
        ).to.deep.equal({
          id: 'INTEGER NOT NULL auto_increment PRIMARY KEY',
          updatedAt: 'DATETIME(6) NOT NULL',
          createdAt: 'DATETIME(6) NOT NULL',
        });
      });

      it('adds deletedAt if paranoid', function () {
        const User = this.sequelize.define(`User${Support.rand()}`, {},
          { paranoid: true });
        expect(
          this.sequelize.queryGenerator.attributesToSQL(
            User.getAttributes(),
          ),
        ).to.deep.equal({
          id: 'INTEGER NOT NULL auto_increment PRIMARY KEY',
          deletedAt: 'DATETIME(6)',
          updatedAt: 'DATETIME(6) NOT NULL',
          createdAt: 'DATETIME(6) NOT NULL',
        });
      });

      it('underscores timestamps if underscored', function () {
        const User = this.sequelize.define(`User${Support.rand()}`, {},
          { paranoid: true, underscored: true });
        expect(
          this.sequelize.queryGenerator.attributesToSQL(
            User.getAttributes(),
          ),
        ).to.deep.equal({
          id: 'INTEGER NOT NULL auto_increment PRIMARY KEY',
          deleted_at: 'DATETIME(6)',
          updated_at: 'DATETIME(6) NOT NULL',
          created_at: 'DATETIME(6) NOT NULL',
        });
      });

      it('omits text fields with defaultValues', function () {
        const User = this.sequelize.define(`User${Support.rand()}`,
          { name: { type: DataTypes.TEXT, defaultValue: 'helloworld' } });
        expect(User.getAttributes().name.type.toString()).to.equal('TEXT');
      });

      it('omits blobs fields with defaultValues', function () {
        const User = this.sequelize.define(`User${Support.rand()}`,
          { name: { type: DataTypes.STRING.BINARY, defaultValue: 'helloworld' } });
        expect(User.getAttributes().name.type.toString()).to.equal(
          'VARCHAR(255) BINARY',
        );
      });
    });

    describe('primaryKeys', () => {
      it('determines the correct primaryKeys', function () {
        const User = this.sequelize.define(`User${Support.rand()}`, {
          foo: { type: DataTypes.STRING, primaryKey: true },
          bar: DataTypes.STRING,
        });
        expect(
          this.sequelize.queryGenerator.attributesToSQL(
            User.primaryKeys,
          ),
        ).to.deep.equal(
          { foo: 'VARCHAR(255) PRIMARY KEY' },
        );
      });
    });
  });
}


req.log = res.log = logger.child({ req: req })

keep keep keep replace keep keep keep keep

req.log = res.log = logger.child({ reqId: req.id })

req.log.info({ req }, 'incoming request')

'use strict'

const t = require('tap')
const test = t.test
const http = require('http')
const split = require('split2')
const Fastify = require('..')
const pino = require('pino')

test('test log stream', t => {
  t.plan(7)
  var fastify = null
  var stream = split(JSON.parse)
  try {
    fastify = Fastify({
      logger: {
        stream: stream,
        level: 'info'
      }
    })
  } catch (e) {
    t.fail()
  }

  fastify.get('/', function (req, reply) {
    t.ok(req.log)
    reply.send({ hello: 'world' })
  })

  fastify.listen(0, err => {
    t.error(err)
    fastify.server.unref()

    http.get('http://localhost:' + fastify.server.address().port)
    stream.on('data', line => {
      t.ok(line.req, 'req is defined')
      t.ok(line.res, 'res is defined')
      t.equal(line.msg, 'request completed', 'message is set')
      t.equal(line.req.method, 'GET', 'method is get')
      t.equal(line.res.statusCode, 200, 'statusCode is 200')
    })
  })
})

test('test error log stream', t => {
  t.plan(7)
  var fastify = null
  var stream = split(JSON.parse)
  try {
    fastify = Fastify({
      logger: {
        stream: stream,
        level: 'info'
      }
    })
  } catch (e) {
    t.fail()
  }

  fastify.get('/error', function (req, reply) {
    t.ok(req.log)
    reply.send(new Error('kaboom'))
  })

  fastify.listen(0, err => {
    t.error(err)
    fastify.server.unref()

    http.get('http://localhost:' + fastify.server.address().port + '/error')
    stream.once('data', line => {
      t.ok(line.req, 'req is defined')
      t.ok(line.res, 'res is defined')
      t.equal(line.msg, 'kaboom', 'message is set')
      t.equal(line.req.method, 'GET', 'method is get')
      t.equal(line.res.statusCode, 500, 'statusCode is 500')
    })
  })
})

test('can use external logger instance', t => {
  const lines = ['log success', 'request completed']
  t.plan(lines.length + 2)

  const splitStream = split(JSON.parse)
  splitStream.on('data', (line) => {
    t.is(line.msg, lines.shift())
  })

  const logger = require('pino')(splitStream)

  const localFastify = Fastify({logger: logger})

  localFastify.get('/foo', function (req, reply) {
    t.ok(req.log)
    req.log.info('log success')
    reply.send({ hello: 'world' })
  })

  localFastify.listen(0, err => {
    t.error(err)
    http.get('http://localhost:' + localFastify.server.address().port + '/foo', (res) => {
      res.resume()
      res.on('end', () => {
        localFastify.server.close()
      })
    })
  })
})

test('expose the logger', t => {
  t.plan(2)
  var fastify = null
  var stream = split(JSON.parse)
  try {
    fastify = Fastify({
      logger: {
        stream: stream,
        level: 'info'
      }
    })
  } catch (e) {
    t.fail()
  }

  t.ok(fastify.logger)
  t.is(typeof fastify.logger, 'object')
})

test('The logger should accept a custom genReqId function', t => {
  t.plan(3)

  const fastify = Fastify({
    logger: {
      genReqId: function () {
        return 'a'
      }
    }
  })

  fastify.get('/', (req, reply) => {
    t.ok(req.req.id)
    reply.send({ id: req.req.id })
  })

  fastify.listen(0, err => {
    t.error(err)
    fastify.inject({
      method: 'GET',
      url: 'http://localhost:' + fastify.server.address().port
    }, res => {
      const payload = JSON.parse(res.payload)
      t.equal(payload.id, 'a')
      fastify.close()
    })
  })
})

test('reply.send logs an error if called twice in a row', t => {
  const lines = ['Reply already sent', 'Reply already sent', 'request completed']
  t.plan(lines.length + 1)

  const splitStream = split(JSON.parse)
  splitStream.on('data', (line) => {
    t.is(line.msg, lines.shift())
  })

  const logger = pino(splitStream)

  const fastify = Fastify({
    logger
  })

  fastify.get('/', (req, reply) => {
    reply.send({ hello: 'world' })
    reply.send({ hello: 'world2' })
    reply.send({ hello: 'world3' })
  })

  fastify.inject({
    method: 'GET',
    url: '/'
  }, res => {
    const payload = JSON.parse(res.payload)
    t.deepEqual(payload, { hello: 'world' })
  })
})


'use strict'

const t = require('tap')
const test = t.test
const request = require('request')
const Fastify = require('..')
const fastify = require('..')()

test('hooks - add preHandler', t => {
  t.plan(1)
  try {
    fastify.addHook('preHandler', function (request, reply, next) {
      request.test = 'the request is coming'
      reply.test = 'the reply has come'
      if (request.req.method === 'HEAD') {
        next(new Error('some error'))
      } else {
        next()
      }
    })
    t.pass()
  } catch (e) {
    t.fail()
  }
})

test('hooks - add onRequest', t => {
  t.plan(1)
  try {
    fastify.addHook('onRequest', function (req, res, next) {
      req.raw = 'the request is coming'
      res.raw = 'the reply has come'
      if (req.method === 'DELETE') {
        next(new Error('some error'))
      } else {
        next()
      }
    })
    t.pass()
  } catch (e) {
    t.fail()
  }
})

fastify.addHook('onResponse', function (res, next) {
  t.ok('onResponse called')
  next()
})

fastify.get('/', function (req, reply) {
  t.is(req.req.raw, 'the request is coming')
  t.is(reply.res.raw, 'the reply has come')
  t.is(req.test, 'the request is coming')
  t.is(reply.test, 'the reply has come')
  reply.code(200).send({ hello: 'world' })
})

fastify.head('/', function (req, reply) {
  reply.code(200).send({ hello: 'world' })
})

fastify.delete('/', function (req, reply) {
  reply.code(200).send({ hello: 'world' })
})

fastify.listen(0, err => {
  t.error(err)
  fastify.server.unref()

  test('hooks - success', t => {
    t.plan(4)
    request({
      method: 'GET',
      uri: 'http://localhost:' + fastify.server.address().port
    }, (err, response, body) => {
      t.error(err)
      t.strictEqual(response.statusCode, 200)
      t.strictEqual(response.headers['content-length'], '' + body.length)
      t.deepEqual(JSON.parse(body), { hello: 'world' })
    })
  })

  test('hooks - throw preHandler', t => {
    t.plan(2)
    request({
      method: 'HEAD',
      uri: 'http://localhost:' + fastify.server.address().port
    }, (err, response, body) => {
      t.error(err)
      t.strictEqual(response.statusCode, 500)
    })
  })

  test('hooks - throw onRequest', t => {
    t.plan(2)
    request({
      method: 'DELETE',
      uri: 'http://localhost:' + fastify.server.address().port
    }, (err, response, body) => {
      t.error(err)
      t.strictEqual(response.statusCode, 500)
    })
  })
})

test('onRequest hook should support encapsulation / 1', t => {
  t.plan(3)
  const fastify = Fastify()

  fastify.register((instance, opts, next) => {
    instance.addHook('onRequest', () => {})
    t.is(instance._hooks.onRequest.length, 1)
    next()
  })

  fastify.ready(err => {
    t.error(err)
    t.is(fastify._hooks.onRequest.length, 0)
  })
})

test('onRequest hook should support encapsulation / 2', t => {
  t.plan(3)
  const fastify = Fastify()

  fastify.addHook('onRequest', () => {})

  fastify.register((instance, opts, next) => {
    instance.addHook('onRequest', () => {})
    t.is(instance._hooks.onRequest.length, 2)
    next()
  })

  fastify.ready(err => {
    t.error(err)
    t.is(fastify._hooks.onRequest.length, 1)
  })
})

test('onRequest hook should support encapsulation / 3', t => {
  t.plan(13)
  const fastify = Fastify()

  fastify.addHook('onRequest', (req, res, next) => {
    req.first = true
    next()
  })

  fastify.get('/first', (req, reply) => {
    t.ok(req.req.first)
    t.notOk(req.req.second)
    reply.send({ hello: 'world' })
  })

  fastify.register((instance, opts, next) => {
    instance.addHook('onRequest', (req, res, next) => {
      req.second = true
      next()
    })

    instance.get('/second', (req, reply) => {
      t.ok(req.req.first)
      t.ok(req.req.second)
      reply.send({ hello: 'world' })
    })

    next()
  })

  fastify.listen(0, err => {
    t.error(err)
    fastify.server.unref()

    request({
      method: 'GET',
      uri: 'http://localhost:' + fastify.server.address().port + '/first'
    }, (err, response, body) => {
      t.error(err)
      t.strictEqual(response.statusCode, 200)
      t.strictEqual(response.headers['content-length'], '' + body.length)
      t.deepEqual(JSON.parse(body), { hello: 'world' })
    })

    request({
      method: 'GET',
      uri: 'http://localhost:' + fastify.server.address().port + '/second'
    }, (err, response, body) => {
      t.error(err)
      t.strictEqual(response.statusCode, 200)
      t.strictEqual(response.headers['content-length'], '' + body.length)
      t.deepEqual(JSON.parse(body), { hello: 'world' })
    })
  })
})

test('preHandler hook should support encapsulation / 5', t => {
  t.plan(13)
  const fastify = Fastify()

  fastify.addHook('preHandler', (req, res, next) => {
    req.first = true
    next()
  })

  fastify.get('/first', (req, reply) => {
    t.ok(req.first)
    t.notOk(req.second)
    reply.send({ hello: 'world' })
  })

  fastify.register((instance, opts, next) => {
    instance.addHook('preHandler', (req, res, next) => {
      req.second = true
      next()
    })

    instance.get('/second', (req, reply) => {
      t.ok(req.first)
      t.ok(req.second)
      reply.send({ hello: 'world' })
    })

    next()
  })

  fastify.listen(0, err => {
    t.error(err)
    fastify.server.unref()

    request({
      method: 'GET',
      uri: 'http://localhost:' + fastify.server.address().port + '/first'
    }, (err, response, body) => {
      t.error(err)
      t.strictEqual(response.statusCode, 200)
      t.strictEqual(response.headers['content-length'], '' + body.length)
      t.deepEqual(JSON.parse(body), { hello: 'world' })
    })

    request({
      method: 'GET',
      uri: 'http://localhost:' + fastify.server.address().port + '/second'
    }, (err, response, body) => {
      t.error(err)
      t.strictEqual(response.statusCode, 200)
      t.strictEqual(response.headers['content-length'], '' + body.length)
      t.deepEqual(JSON.parse(body), { hello: 'world' })
    })
  })
})

test('onResponse hook should support encapsulation / 1', t => {
  t.plan(3)
  const fastify = Fastify()

  fastify.register((instance, opts, next) => {
    instance.addHook('onResponse', () => {})
    t.is(instance._hooks.onResponse.length, 1)
    next()
  })

  fastify.ready(err => {
    t.error(err)
    t.is(fastify._hooks.onResponse.length, 0)
  })
})

test('onResponse hook should support encapsulation / 2', t => {
  t.plan(3)
  const fastify = Fastify()

  fastify.addHook('onResponse', () => {})

  fastify.register((instance, opts, next) => {
    instance.addHook('onResponse', () => {})
    t.is(instance._hooks.onResponse.length, 2)
    next()
  })

  fastify.ready(err => {
    t.error(err)
    t.is(fastify._hooks.onResponse.length, 1)
  })
})

test('onResponse hook should support encapsulation / 3', t => {
  t.plan(12)
  const fastify = Fastify()

  fastify.addHook('onResponse', (res, next) => {
    t.ok('onResponse called')
    next()
  })

  fastify.get('/first', (req, reply) => {
    reply.send({ hello: 'world' })
  })

  fastify.register((instance, opts, next) => {
    instance.addHook('onResponse', (res, next) => {
      t.ok('onResponse called')
      next()
    })

    instance.get('/second', (req, reply) => {
      reply.send({ hello: 'world' })
    })

    next()
  })

  fastify.listen(0, err => {
    t.error(err)
    fastify.server.unref()

    request({
      method: 'GET',
      uri: 'http://localhost:' + fastify.server.address().port + '/first'
    }, (err, response, body) => {
      t.error(err)
      t.strictEqual(response.statusCode, 200)
      t.strictEqual(response.headers['content-length'], '' + body.length)
      t.deepEqual(JSON.parse(body), { hello: 'world' })
    })

    request({
      method: 'GET',
      uri: 'http://localhost:' + fastify.server.address().port + '/second'
    }, (err, response, body) => {
      t.error(err)
      t.strictEqual(response.statusCode, 200)
      t.strictEqual(response.headers['content-length'], '' + body.length)
      t.deepEqual(JSON.parse(body), { hello: 'world' })
    })
  })
})


'use strict'

function ContentTypeParser () {
  this.customParsers = {}
  this.parserList = []
}

ContentTypeParser.prototype.add = function (contentType, fn) {
  if (this.hasParser(contentType)) {
    throw new Error(`Content type parser '${contentType}' already present.`)
  }
  this.customParsers[contentType] = fn
  this.parserList.push(contentType)
}

ContentTypeParser.prototype.hasParser = function (contentType) {
  return contentType in this.customParsers
}

ContentTypeParser.prototype.fastHasHeader = function (contentType) {
  if (!contentType) return false

  for (var i = 0; i < this.parserList.length; i++) {
    if (contentType.indexOf(this.parserList[i]) > -1) return true
  }
  return false
}

ContentTypeParser.prototype.getHandler = function (contentType) {
  for (var i = 0; i < this.parserList.length; i++) {
    if (contentType.indexOf(this.parserList[i]) > -1) {
      return this.customParsers[this.parserList[i]]
    }
  }
}

ContentTypeParser.prototype.run = function (contentType, handler, handle, params, req, res, query) {
  this.getHandler(contentType)(req, done)

  function done (body) {
    if (body instanceof Error) {
      const reply = new handle.Reply(req, res, handle)
      return reply.send(body)
    }
    handler(handle, params, req, res, body, query)
  }
}

function buildContentTypeParser (c) {
  function _ContentTypeParser () {}
  _ContentTypeParser.prototype = new ContentTypeParser()
  const C = new _ContentTypeParser()
  C.customParsers = Object.create(c.customParsers)
  C.parserList = Object.create(c.parserList)
  return C
}

module.exports = ContentTypeParser
module.exports.buildContentTypeParser = buildContentTypeParser


fastify

Fast and low overhead web framework, for Node.js

tsc test/types/index.ts --target es6 --module commonjs --noEmit

npm run lint && npm run unit && npm run typescript

npm run unit -- --cov --coverage-report=html

node ./examples/$npm_config_usingfile & pid=$! && autocannon -c 100 -d 5 -p 10 localhost:3000/ && kill -9 $pid


test('server logs an error if reply.send is called and a value is returned via async/await', t => {

const lines = ['Reply already sent', 'request completed']

keep keep keep keep replace keep keep keep

const lines = ['incoming request', 'Reply already sent', 'request completed']

'use strict'

const t = require('tap')
const test = t.test
const Fastify = require('../..')
const loggerUtils = require('../../lib/logger')

test('gen id factory', t => {
  t.plan(2)
  const genReqId = loggerUtils.reqIdGenFactory()
  t.is(typeof genReqId, 'function')
  t.is(typeof genReqId({}), 'number')
})

test('time resolution', t => {
  t.plan(2)
  t.is(typeof loggerUtils.now, 'function')
  t.is(typeof loggerUtils.now(), 'number')
})

test('The logger should add a unique id for every request', t => {
  const ids = []

  const fastify = Fastify()
  fastify.get('/', (req, reply) => {
    t.ok(req.req.id)
    reply.send({ id: req.req.id })
  })

  fastify.listen(0, err => {
    t.error(err)
    const queue = new Queue()
    for (var i = 0; i < 10; i++) {
      queue.add(checkId)
    }
    queue.add(() => {
      fastify.close()
      t.end()
    })
  })

  function checkId (done) {
    fastify.inject({
      method: 'GET',
      url: 'http://localhost:' + fastify.server.address().port
    }, res => {
      const payload = JSON.parse(res.payload)
      t.ok(ids.indexOf(payload.id) === -1, 'the id should not be duplicated')
      ids.push(payload.id)
      done()
    })
  }
})

function Queue () {
  this.q = []
  this.running = false
}

Queue.prototype.add = function add (job) {
  this.q.push(job)
  if (!this.running) this.run()
}

Queue.prototype.run = function run () {
  this.running = true
  const job = this.q.shift()
  job(() => {
    if (this.q.length) {
      this.run()
    } else {
      this.running = false
    }
  })
}


Fastify

## Testing
Testing is one of the most important part when you are developing an application.
Fastify does not offer a testing framework out of the box, but we can recommend you an handy and nice way to build your unit testing environment.

The modules you'll need:
- [Tap](https://www.npmjs.com/package/tap): an excellent testing framework, it will give you out of the box a very good assertion library and a lot utilities.
- [Request](https://www.npmjs.com/package/request): a complete library to perform request of any kind.
- [Minimist](https://www.npmjs.com/package/minimist): a CLI parser, that you will use to run server from the command line.

```js
// server.js
const minimist = require('minimist')
const fastify = require('fastify')()

const options = {
  schema: {
    response: {
      200: {
        type: 'object',
        properties: {
          hello: { type: 'string' }
        }
      }
    }
  }
}

function start (opts, callback) {
  fastify.get('/', options, function (request, reply) {
    reply.send({ hello: 'world' })
  })

  fastify.listen(opts.port, function (err) {
    callback(err, fastify)
  })
}

// In this way you can run the server both from the CLI and as a required module.
if (require.main === module) {
  // Run the server with:
  // $ node server.js -p 8080
  start(minimist(process.argv.slice(2), {
    integer: ['port'],
    alias: {
      port: 'p'
    },
    default: {
      port: 3000
    }
  }), (err, instance) => {
    if (err) throw err

    console.log(`server listening on ${instance.server.address().port}`)
  })
}

// Here we are exposing the function that starts the server
// in this way inside the test files we can require and run it.
module.exports = { start }
```

```js
// test.js
const t = require('tap')
const test = t.test
const request = require('request')
const server = require('./server')

// Run the server
server.start({ port: 0 }, (err, fastify) => {
  t.error(err)

  test('The server should start', t => {
    t.plan(4)
    // Perform the request
    request({
      method: 'GET',
      uri: `http://localhost:${fastify.server.address().port}`
    }, (err, response, body) => {
      // Unit test
      t.error(err)
      t.strictEqual(response.statusCode, 200)
      t.strictEqual(response.headers['content-length'], '' + body.length)
      t.deepEqual(JSON.parse(body), { hello: 'world' })
      fastify.close()
    })
  })
})
```

### Testing with http injection
Fastify supports fake http injection thanks to [shot](https://github.com/hapijs/shot).
To support this method, you should list `shot` library in your package as dev dependency using
```
npm install shot --save-dev
```
You just need to use the api `inject`:
```js
fastify.inject({
  method: String,
  url: String,
  payload: Object,
  headers: Object
}, response => {
  // your tests
})
```
Example:
```js
// server.js
const minimist = require('minimist')
const fastify = require('fastify')()

const options = {
  schema: {
    response: {
      200: {
        type: 'object',
        properties: {
          hello: { type: 'string' }
        }
      }
    }
  }
}

fastify.get('/', options, function (request, reply) {
  reply.send({ hello: 'world' })
})

function start (opts, callback) {
  fastify.listen(opts.port, function (err) {
    callback(err, fastify)
  })
}

// In this way you can run the server both from the CLI and as a required module.
if (require.main === module) {
  // Run the server with:
  // $ node server.js -p 8080
  start(minimist(process.argv.slice(2), {
    integer: ['port'],
    alias: {
      port: 'p'
    },
    default: {
      port: 3000
    }
  }), (err, instance) => {
    if (err) throw err

    console.log(`server listening on ${instance.server.address().port}`)
  })
}

// note that now we are also exposing the fastify instance
module.exports = { start, fastify }
```

```js
// test.js
const t = require('tap')
const test = t.test
const request = require('request')
const fastify = require('./server').fastify

test('GET `/` route', t => {
  t.plan(3)

  fastify.inject({
    method: 'GET',
    url: '/'
  }, res => {
    t.strictEqual(res.statusCode, 200)
    t.strictEqual(res.headers['content-length'], '' + body.length)
    t.deepEqual(JSON.parse(res.payload), { hello: 'world' })
    // even if the server is not running (inject does not run the server)
    // at the end of your tests is highly recommended call `.close()`,
    // in this way you will close all the connections to external services
    fastify.close()
  })
})
```


environment:
  matrix:
    - nodejs_version: 4
      platform: x64
      msvs_toolset: 12
    - nodejs_version: 6
      platform: x64
      msvs_toolset: 12
    - nodejs_version: 7
      platform: x64
      msvs_toolset: 12
    - nodejs_version: 8
      platform: x64
      msvs_toolset: 12

# Install scripts. (runs after repo cloning)
install:
  # Get the latest stable version of Node.js or io.js
  - ps: Install-Product node $env:nodejs_version
  # install modules
  - npm install

# Post-install test scripts.
test_script:
  # Output useful info for debugging.
  - node --version
  - npm --version
  # run tests
  - npm test

os: Visual Studio 2015

build: OFF


language: node_js

node_js:
  - "8"
  - "7"
  - "6"
  - "4"

script:
  - npm run coveralls

after_script:
  - ./tools/website.sh

notifications:
  email:
    on_success: never
    on_failure: always


const loggerUtils = require('../../lib/logger')

const genReqId = loggerUtils.reqIdGenFactory()

keep keep keep replace replace replace replace replace replace replace keep keep keep


'use strict'

const t = require('tap')
require('./helper').payloadMethod('post', t)
require('./input-validation').payloadMethod('post', t)


'use strict'

const fastify = require('../fastify')()

const opts = {
  schema: {
    response: {
      '2xx': {
        type: 'object',
        properties: {
          hello: {
            type: 'string'
          }
        }
      }
    }
  }
}

fastify
  .addHook('onRequest', function (req, res, next) {
    console.log('onRequest')
    next()
  })
  .addHook('preHandler', function (request, reply, next) {
    console.log('preHandler')
    next()
  })
  .addHook('onResponse', function (res, next) {
    console.log('onResponse')
    next()
  })

fastify.get('/', opts, function (req, reply) {
  reply.send({ hello: 'world' })
})

fastify.listen(3000, function (err) {
  if (err) {
    throw err
  }
  console.log(`server listening on ${fastify.server.address().port}`)
})


const Fastify = require('..')
const pino = require('pino')
test('test log stream', t => {
t.plan(7)
var fastify = null
var stream = split(JSON.parse)
try {
fastify = Fastify({
logger: {

keep keep keep keep replace keep keep keep keep keep

'use strict'

const FindMyWay = require('find-my-way')
const avvio = require('avvio')
const Ajv = require('ajv')
const http = require('http')
const https = require('https')
const Middie = require('middie')
const runHooks = require('fastseries')()

var shot = null
try { shot = require('shot') } catch (e) { }

const Reply = require('./lib/reply')
const Request = require('./lib/request')
const supportedMethods = ['DELETE', 'GET', 'HEAD', 'PATCH', 'POST', 'PUT', 'OPTIONS']
const buildSchema = require('./lib/validation').build
const handleRequest = require('./lib/handleRequest')
const isValidLogger = require('./lib/validation').isValidLogger
const schemaCompiler = require('./lib/validation').schemaCompiler
const decorator = require('./lib/decorate')
const ContentTypeParser = require('./lib/ContentTypeParser')
const Hooks = require('./lib/hooks')
const loggerUtils = require('./lib/logger')

function build (options) {
  options = options || {}
  if (typeof options !== 'object') {
    throw new TypeError('Options must be an object')
  }

  var logger
  if (isValidLogger(options.logger)) {
    logger = loggerUtils.createLogger({ logger: options.logger, serializers: loggerUtils.serializers })
  } else {
    options.logger = options.logger || {}
    options.logger.level = options.logger.level || 'fatal'
    options.logger.serializers = options.logger.serializers || loggerUtils.serializers
    logger = loggerUtils.createLogger(options.logger)
  }

  const ajv = new Ajv(Object.assign({ coerceTypes: true }, options.ajv))

  const router = FindMyWay({ defaultRoute: defaultRoute })
  const map = new Map()

  // logger utils
  const customGenReqId = options.logger ? options.logger.genReqId : null
  const genReqId = customGenReqId || loggerUtils.reqIdGenFactory()
  const now = loggerUtils.now
  const OnResponseState = loggerUtils.OnResponseState
  const onResponseIterator = loggerUtils.onResponseIterator
  const onResponseCallback = loggerUtils.onResponseCallback

  const app = avvio(fastify, {})
  // Override to allow the plugin incapsulation
  app.override = override

  var listening = false
  // true when Fastify is ready to go
  var started = false
  app.on('start', () => {
    started = true
  })

  var server
  if (options.https) {
    server = https.createServer(options.https, fastify)
  } else {
    server = http.createServer(fastify)
  }

  fastify.onClose((instance, done) => {
    if (listening) {
      instance.server.close(done)
    } else {
      done(null)
    }
  })

  if (Number(process.versions.node[0]) >= 6) {
    server.on('clientError', handleClientError)
  }

  // shorthand methods
  fastify.delete = _delete
  fastify.get = _get
  fastify.head = _head
  fastify.patch = _patch
  fastify.post = _post
  fastify.put = _put
  fastify.options = _options
  fastify.all = _all
  // extended route
  fastify.route = route
  fastify._RoutePrefix = new RoutePrefix()

  // expose logger instance
  fastify.logger = logger

  // hooks
  fastify.addHook = addHook
  fastify._hooks = new Hooks()

  // custom parsers
  fastify.addContentTypeParser = addContentTypeParser
  fastify.hasContentTypeParser = hasContentTypeParser
  fastify._contentTypeParser = new ContentTypeParser()

  fastify.setSchemaCompiler = setSchemaCompiler
  fastify._schemaCompiler = schemaCompiler.bind({ ajv: ajv })

  // plugin
  fastify.register = fastify.use
  fastify.listen = listen
  fastify.server = server

  // extend server methods
  fastify.decorate = decorator.add
  fastify.hasDecorator = decorator.exist
  fastify.decorateReply = decorator.decorateReply
  fastify.decorateRequest = decorator.decorateRequest
  fastify.extendServerError = decorator.extendServerError

  fastify._Reply = Reply
  fastify._Request = Request

  // middleware support
  fastify.use = use
  fastify._middie = Middie(onRunMiddlewares)
  fastify._middlewares = []

  // exposes the routes map
  fastify[Symbol.iterator] = iterator

  // fake http injection (for testing purposes)
  fastify.inject = inject

  return fastify

  function fastify (req, res) {
    req.id = genReqId()
    req.log = res.log = logger.child({ req: req })

    res._startTime = now()
    res._context = null
    res.on('finish', onResFinished)
    res.on('error', onResFinished)

    router.lookup(req, res)
  }

  function onResFinished (err) {
    this.removeListener('finish', onResFinished)
    this.removeListener('error', onResFinished)

    var ctx = this._context

    if (ctx !== null && ctx.onResponse.length > 0) {
      // deferring this with setImmediate will
      // slow us by 10%
      runHooks(new OnResponseState(err, this),
        onResponseIterator,
        ctx.onResponse,
        wrapOnResponseCallback)
    } else {
      onResponseCallback(err, this)
    }
  }

  function wrapOnResponseCallback (err) {
    onResponseCallback(this.err || err, this.res)
  }

  function listen (port, address, cb) {
    const hasAddress = arguments.length === 3
    const _cb = (hasAddress) ? cb : address
    fastify.ready(function (err) {
      if (err) return _cb(err)
      if (listening) {
        return _cb(new Error('Fastify is already listening'))
      }

      server.on('error', wrap)
      if (hasAddress) {
        server.listen(port, address, wrap)
      } else {
        server.listen(port, wrap)
      }
      listening = true
    })

    function wrap (err) {
      server.removeListener('error', wrap)
      if (_cb) {
        _cb(err)
      } else if (err) {
        // this will crash the process
        // it will go to 'uncaughtException'
        throw err
      }
    }
  }

  function startHooks (req, res, params, store) {
    res._context = store
    runHooks(
      new State(req, res, params, store),
      hookIterator,
      store.onRequest,
      middlewareCallback
    )
  }

  function middlewareCallback (err) {
    if (err) {
      const reply = new Reply(this.req, this.res, this.store)
      reply.send(err)
      return
    }
    this.store._middie.run(this.req, this.res, this)
  }

  function onRunMiddlewares (err, req, res, ctx) {
    if (err) {
      const reply = new Reply(req, res, ctx.store)
      reply.send(err)
      return
    }

    handleRequest(req, res, ctx.params, ctx.store)
  }

  function State (req, res, params, store) {
    this.req = req
    this.res = res
    this.params = params
    this.store = store
  }

  function hookIterator (fn, cb) {
    fn(this.req, this.res, cb)
  }

  function override (old, fn, opts) {
    if (fn[Symbol.for('skip-override')]) {
      return old
    }

    const middlewares = Object.assign([], old._middlewares)
    const instance = Object.create(old)
    instance._Reply = Reply.buildReply(instance._Reply)
    instance._Request = Request.buildRequest(instance._Request)
    instance._contentTypeParser = ContentTypeParser.buildContentTypeParser(instance._contentTypeParser)
    instance._hooks = Hooks.buildHooks(instance._hooks)
    instance._RoutePrefix = buildRoutePrefix(instance._RoutePrefix, opts)
    instance._middlewares = []
    instance._middie = Middie(onRunMiddlewares)

    for (var i = 0; i < middlewares.length; i++) {
      instance.use.apply(instance, middlewares[i])
    }

    return instance
  }

  function RoutePrefix () {
    this.prefix = ''
  }

  function buildRoutePrefix (r, opts) {
    const _RoutePrefix = Object.create(opts)
    const R = _RoutePrefix
    R.prefix = r.prefix
    if (typeof opts.prefix === 'string') {
      if (opts.prefix[0] !== '/') {
        opts.prefix = '/' + opts.prefix
      }
      R.prefix += opts.prefix
    }
    return R
  }

  // Shorthand methods
  function _delete (url, opts, handler) {
    return _route(this, 'DELETE', url, opts, handler)
  }

  function _get (url, opts, handler) {
    return _route(this, 'GET', url, opts, handler)
  }

  function _head (url, opts, handler) {
    return _route(this, 'HEAD', url, opts, handler)
  }

  function _patch (url, opts, handler) {
    return _route(this, 'PATCH', url, opts, handler)
  }

  function _post (url, opts, handler) {
    return _route(this, 'POST', url, opts, handler)
  }

  function _put (url, opts, handler) {
    return _route(this, 'PUT', url, opts, handler)
  }

  function _options (url, opts, handler) {
    return _route(this, 'OPTIONS', url, opts, handler)
  }

  function _all (url, opts, handler) {
    return _route(this, supportedMethods, url, opts, handler)
  }

  function _route (self, method, url, options, handler) {
    if (!handler && typeof options === 'function') {
      handler = options
      options = {}
    }
    return route.call(self, {
      method,
      url,
      handler,
      schema: options.schema || {},
      Reply: self._Reply,
      Request: self._Request,
      contentTypeParser: self._contentTypeParser,
      onRequest: self._hooks.onRequest,
      preHandler: self._hooks.preHandler,
      RoutePrefix: self._RoutePrefix,
      beforeHandler: options.beforeHandler,
      onResponse: options.onResponse,
      config: options.config,
      middie: self._middie,
      schemaCompiler: options.schemaCompiler
    })
  }

  // Route management
  function route (opts) {
    const _fastify = this

    if (Array.isArray(opts.method)) {
      for (var i = 0; i < opts.method.length; i++) {
        if (supportedMethods.indexOf(opts.method[i]) === -1) {
          throw new Error(`${opts.method[i]} method is not supported!`)
        }
      }
    } else {
      if (supportedMethods.indexOf(opts.method) === -1) {
        throw new Error(`${opts.method} method is not supported!`)
      }
    }

    if (!opts.handler) {
      throw new Error(`Missing handler function for ${opts.method}:${opts.url} route.`)
    }

    _fastify._RoutePrefix = opts.RoutePrefix || _fastify._RoutePrefix

    _fastify.after((notHandledErr, done) => {
      const path = opts.url || opts.path
      const prefix = _fastify._RoutePrefix.prefix
      const url = prefix + (path === '/' && prefix.length > 0 ? '' : path)

      const config = opts.config || {}
      config.url = url

      const store = new Store(
        opts.schema,
        opts.handler,
        opts.Reply || _fastify._Reply,
        opts.Request || _fastify._Request,
        opts.contentTypeParser || _fastify._contentTypeParser,
        opts.onRequest || _fastify._hooks.onRequest,
        [],
        opts.onResponse || _fastify._hooks.onResponse,
        config,
        opts.middie || _fastify._middie
      )

      buildSchema(store, opts.schemaCompiler || _fastify._schemaCompiler)

      store.preHandler.push.apply(store.preHandler, (opts.preHandler || _fastify._hooks.preHandler))
      if (opts.beforeHandler) {
        opts.beforeHandler = Array.isArray(opts.beforeHandler) ? opts.beforeHandler : [opts.beforeHandler]
        store.preHandler.push.apply(store.preHandler, opts.beforeHandler)
      }

      if (map.has(url)) {
        if (map.get(url)[opts.method]) {
          return done(new Error(`${opts.method} already set for ${url}`))
        }

        if (Array.isArray(opts.method)) {
          for (i = 0; i < opts.method.length; i++) {
            map.get(url)[opts.method[i]] = store
          }
        } else {
          map.get(url)[opts.method] = store
        }
        router.on(opts.method, url, startHooks, store)
      } else {
        const node = {}
        if (Array.isArray(opts.method)) {
          for (i = 0; i < opts.method.length; i++) {
            node[opts.method[i]] = store
          }
        } else {
          node[opts.method] = store
        }
        map.set(url, node)
        router.on(opts.method, url, startHooks, store)
      }
      done()
    })

    // chainable api
    return _fastify
  }

  function Store (schema, handler, Reply, Request, contentTypeParser, onRequest, preHandler, onResponse, config, middie) {
    this.schema = schema
    this.handler = handler
    this.Reply = Reply
    this.Request = Request
    this.contentTypeParser = contentTypeParser
    this.onRequest = onRequest
    this.preHandler = preHandler
    this.onResponse = onResponse
    this.config = config
    this._middie = middie
  }

  function iterator () {
    var entries = map.entries()
    var it = {}
    it.next = function () {
      var next = entries.next()

      if (next.done) {
        return {
          value: null,
          done: true
        }
      }

      var value = {}
      var methods = {}

      value[next.value[0]] = methods

      // out methods are saved Uppercase,
      // so we lowercase them for a better usability
      for (var method in next.value[1]) {
        methods[method.toLowerCase()] = next.value[1][method]
      }

      return {
        value: value,
        done: false
      }
    }
    return it
  }

  function inject (opts, cb) {
    if (!shot) throw new Error('"shot" library is not installed: "npm install shot --save-dev"')

    if (started) {
      shot.inject(this, opts, cb)
      return
    }

    this.ready(err => {
      if (err) throw err
      shot.inject(this, opts, cb)
    })
  }

  function use (url, fn) {
    if (typeof url === 'string') {
      const prefix = this._RoutePrefix.prefix
      url = prefix + (url === '/' && prefix.length > 0 ? '' : url)
    }
    this._middlewares.push([url, fn])
    this._middie.use(url, fn)
    return this
  }

  function addHook (name, fn) {
    if (name === 'onClose') {
      this.onClose(fn)
    } else {
      this._hooks.add(name, fn)
    }
    return this
  }

  function addContentTypeParser (contentType, fn) {
    this._contentTypeParser.add(contentType, fn)
    return this
  }

  function hasContentTypeParser (contentType, fn) {
    return this._contentTypeParser.hasParser(contentType)
  }

  function handleClientError (e, socket) {
    const body = JSON.stringify({
      error: http.STATUS_CODES['400'],
      message: 'Client Error',
      statusCode: 400
    })
    logger.error(e, 'client error')
    socket.end(`HTTP/1.1 400 Bad Request\r\nContent-Length: ${body.length}\r\nContent-Type: 'application/json'\r\n\r\n${body}`)
  }

  function defaultRoute (req, res, params) {
    const reply = new Reply(req, res, null)
    reply.code(404).send(new Error('Not found'))
  }

  function setSchemaCompiler (schemaCompiler) {
    this._schemaCompiler = schemaCompiler
    return this
  }
}

module.exports = build


'use strict'

const t = require('tap')
const test = t.test
const fastify = require('..')()

const noop = () => {}
const opts = {
  schema: {
    response: {
      '2xx': {
        type: 'object',
        properties: {
          hello: {
            type: 'string'
          }
        }
      }
    }
  }
}

test('chainable - get', t => {
  t.plan(1)
  t.type(fastify.get('/', opts, noop), fastify)
})

test('chainable - post', t => {
  t.plan(1)
  t.type(fastify.post('/', opts, noop), fastify)
})

test('chainable - route', t => {
  t.plan(1)
  t.type(fastify.route({
    method: 'GET',
    url: '/other',
    schema: opts.schema,
    handler: noop
  }), fastify)
})


Fastify

## Middlewares

Fastify provides out of the box an asynchronous [middleware engine](https://github.com/fastify/middie) compatible with [Express](https://expressjs.com/) and [Restify](http://restify.com/) middlewares.

*If you need a visual feedback to understand when the middlewares are executed take a look to the [lifecycle](https://github.com/fastify/fastify/blob/master/docs/Lifecycle.md) page.*

Fastify middlewares don't support the full syntax `middleware(err, req, res, next)`, because error handling is done inside Fastify.

Also, if you are using Middlewares that bundles different small middlewares, such as [*helmet*](https://helmetjs.github.io/), we recommend to use the single modules to get better performances.

```js
fastify.use(require('cors')())
fastify.use(require('dns-prefetch-control')())
fastify.use(require('frameguard')())
fastify.use(require('hide-powered-by')())
fastify.use(require('hsts')())
fastify.use(require('ienoopen')())
fastify.use(require('x-xss-protection')())
```

or, in the specific case of *helmet*, you can use the [*fastify-helmet*](https://github.com/fastify/fastify-helmet) [plugin](Plugins.md), which is an optimized helmet integration for fastify:

```js
const fastify = require('fastify')()
const helmet = require('fastify-helmet')

fastify.register(helmet)
```

Also remember that middlewares can be encapsulated, this means that you can decide where your middlewares should run by using `register` as explained in the [plugins guide](https://github.com/fastify/fastify/blob/master/docs/Plugins-Guide.md).

#### Restrict middleware execution to a certain path(s)
If you need to run a middleware only under certain path(s), just pass the path as first parameter to `use` and you are done!

*Note that this does not support routes with parameters, (eg: `/user/:id/comments`) and wildcard is not supported in multiple paths.*

```js
const serveStatic = require('serve-static')

// Single path
fastify.use('/css', serveStatic('/assets'))

// Wildcard path
fastify.use('/css/*', serveStatic('/assets'))

// Multiple paths
fastify.use(['/css', '/js'], serveStatic('/assets'))
```


'use strict'

const request = require('request')

module.exports.payloadMethod = function (method, t) {
  const test = t.test
  const fastify = require('..')()
  const upMethod = method.toUpperCase()
  const loMethod = method.toLowerCase()

  const schema = {
    schema: {
      response: {
        '2xx': {
          type: 'object',
          properties: {
            hello: {
              type: 'string'
            }
          }
        }
      }
    }
  }

  test(`${upMethod} can be created`, t => {
    t.plan(1)
    try {
      fastify[loMethod]('/', schema, function (req, reply) {
        reply.code(200).send(req.body)
      })
      t.pass()
    } catch (e) {
      t.fail()
    }
  })

  test(`${upMethod} without schema can be created`, t => {
    t.plan(1)
    try {
      fastify[loMethod]('/missing', function (req, reply) {
        reply.code(200).send(req.body)
      })
      t.pass()
    } catch (e) {
      t.fail()
    }
  })

  test(`${upMethod} with body and querystring`, t => {
    t.plan(1)
    try {
      fastify[loMethod]('/with-query', function (req, reply) {
        req.body.hello = req.body.hello + req.query.foo
        reply.code(200).send(req.body)
      })
      t.pass()
    } catch (e) {
      t.fail()
    }
  })

  fastify.listen(0, function (err) {
    if (err) {
      t.error(err)
    }

    fastify.server.unref()

    test(`${upMethod} - correctly replies`, t => {
      t.plan(3)
      request({
        method: upMethod,
        uri: 'http://localhost:' + fastify.server.address().port,
        body: {
          hello: 'world'
        },
        json: true
      }, (err, response, body) => {
        t.error(err)
        t.strictEqual(response.statusCode, 200)
        t.deepEqual(body, { hello: 'world' })
      })
    })

    test(`${upMethod} - correctly replies if the content type has the charset`, t => {
      t.plan(3)
      request({
        method: upMethod,
        uri: 'http://localhost:' + fastify.server.address().port,
        body: JSON.stringify({ hello: 'world' }),
        headers: {
          'content-type': 'application/json;charset=utf-8'
        }
      }, (err, response, body) => {
        t.error(err)
        t.strictEqual(response.statusCode, 200)
        t.deepEqual(body, JSON.stringify({ hello: 'world' }))
      })
    })

    test(`${upMethod} without schema - correctly replies`, t => {
      t.plan(3)
      request({
        method: upMethod,
        uri: 'http://localhost:' + fastify.server.address().port + '/missing',
        body: {
          hello: 'world'
        },
        json: true
      }, (err, response, body) => {
        t.error(err)
        t.strictEqual(response.statusCode, 200)
        t.deepEqual(body, { hello: 'world' })
      })
    })

    test(`${upMethod} with body and querystring - correctly replies`, t => {
      t.plan(3)
      request({
        method: upMethod,
        uri: 'http://localhost:' + fastify.server.address().port + '/with-query?foo=hello',
        body: {
          hello: 'world'
        },
        json: true
      }, (err, response, body) => {
        t.error(err)
        t.strictEqual(response.statusCode, 200)
        t.deepEqual(body, { hello: 'worldhello' })
      })
    })

    test(`${upMethod} returns 415 - incorrect media type if body is not json`, t => {
      t.plan(2)
      request({
        method: upMethod,
        uri: 'http://localhost:' + fastify.server.address().port + '/missing',
        body: 'hello world',
        timeout: 500
      }, (err, response, body) => {
        t.error(err)
        if (upMethod === 'OPTIONS') {
          t.strictEqual(response.statusCode, 200)
        } else {
          t.strictEqual(response.statusCode, 415)
        }
      })
    })

    if (loMethod === 'options') {
      test(`OPTIONS returns 415 - should return 415 if Content-Type is not json`, t => {
        t.plan(2)
        request({
          method: upMethod,
          uri: 'http://localhost:' + fastify.server.address().port + '/missing',
          body: 'hello world',
          headers: {
            'Content-Type': 'text/plain'
          },
          timeout: 500
        }, (err, response, body) => {
          t.error(err)
          t.strictEqual(response.statusCode, 415)
        })
      })
    }

    test(`${upMethod} returns 422 - Unprocessable Entity`, t => {
      t.plan(2)
      request({
        method: upMethod,
        uri: 'http://localhost:' + fastify.server.address().port,
        body: 'hello world',
        headers: {
          'Content-Type': 'application/json'
        },
        timeout: 500
      }, (err, response, body) => {
        t.error(err)
        t.strictEqual(response.statusCode, 422)
      })
    })
  })
}


/* eslint-disable no-useless-return */
'use strict'

const urlUtil = require('url')
const runHooks = require('fastseries')()
const Reply = require('./reply')
const validation = require('./validation')
const validateSchema = validation.validate

function handleRequest (req, res, params, store) {
  var method = req.method

  if (method === 'GET' || method === 'DELETE' || method === 'HEAD') {
    return handler(store, params, req, res, null, urlUtil.parse(req.url, true).query)
  }

  if (method === 'POST' || method === 'PUT' || method === 'PATCH') {
    // application/json content type
    if (req.headers['content-type'] && req.headers['content-type'].indexOf('application/json') > -1) {
      return jsonBody(req, res, params, store)
    }

    // custom parser for a given content type
    if (store.contentTypeParser.fastHasHeader(req.headers['content-type'])) {
      return store.contentTypeParser.run(req.headers['content-type'], handler, store, params, req, res, urlUtil.parse(req.url, true).query)
    }

    setImmediate(wrapReplyEnd, req, res, 415)
    return
  }

  if (method === 'OPTIONS') {
    if (req.headers['content-type']) {
      // application/json content type
      if (req.headers['content-type'].indexOf('application/json') > -1) {
        return jsonBody(req, res, params, store)
      // custom parser for a given content type
      } else if (store.contentTypeParser.fastHasHeader(req.headers['content-type'])) {
        return store.contentTypeParser.run(req.headers['content-type'], handler, store, params, req, res, urlUtil.parse(req.url, true).query)
      }

      setImmediate(wrapReplyEnd, req, res, 415)
      return
    }
    return handler(store, params, req, res, null, urlUtil.parse(req.url, true).query)
  }

  setImmediate(wrapReplyEnd, req, res, 405)
  return
}

function jsonBody (req, res, params, handle) {
  var body = ''
  req.on('error', onError)
  req.on('data', onData)
  req.on('end', onEnd)
  function onError (err) {
    jsonBodyParsed(err, null, req, res, params, handle)
  }
  function onData (chunk) {
    body += chunk
  }
  function onEnd () {
    setImmediate(parse, body)
  }
  function parse (json) {
    try {
      jsonBodyParsed(null, JSON.parse(body), req, res, params, handle)
    } catch (err) {
      jsonBodyParsed(err, null, req, res, params, handle)
    }
  }
}

function jsonBodyParsed (err, body, req, res, params, handle) {
  if (err) {
    setImmediate(wrapReplyEnd, req, res, 422)
    return
  }
  handler(handle, params, req, res, body, urlUtil.parse(req.url, true).query)
}

function handler (store, params, req, res, body, query, headers) {
  var request = new store.Request(params, req, body, query, req.headers, req.log)
  var valid = validateSchema(store, request)
  if (valid !== true) {
    setImmediate(wrapReplyEnd, req, res, 400, valid)
    return
  }

  // preHandler hook
  setImmediate(
    runHooks,
    new State(request, new store.Reply(req, res, store), store),
    hookIterator,
    store.preHandler,
    preHandlerCallback
  )
}

function State (request, reply, store) {
  this.request = request
  this.reply = reply
  this.store = store
}

function hookIterator (fn, cb) {
  fn(this.request, this.reply, cb)
}

function preHandlerCallback (err, code) {
  if (err) {
    if (code[0]) this.reply.code(code[0])
    this.reply.send(err)
    return
  }

  var result = this.store.handler(this.request, this.reply)
  if (result && typeof result.then === 'function') {
    result.then((payload) => {
      // this is for async functions that
      // are using reply.send directly
      if (payload !== undefined || this.reply.res.statusCode === 204) {
        this.reply.send(payload)
      }
    }).catch((err) => {
      this.reply.send(err)
    })
  }
}

function wrapReplyEnd (req, res, statusCode, payload) {
  const reply = new Reply(req, res, null)
  if (payload instanceof Error) {
    reply.code(statusCode).send(payload)
  } else {
    reply.code(statusCode).send(new Error(payload || ''))
  }
  return
}

module.exports = handleRequest
module.exports[Symbol.for('internals')] = { jsonBody, jsonBodyParsed, handler }


t.equal(line.msg, 'request completed', 'message is set')

t.equal(line.req.method, 'GET', 'method is get')

t.equal(line.msg, 'incoming request', 'message is set')


root = true

[*]
end_of_line = lf
insert_final_newline = true
indent_style = space
indent_size = 2

[*.md]
trim_trailing_whitespace = false


Fastify

## Lifecycle
Following the schema of the internal lifecycle of Fastify.
On the right branch of every section there is the next phase of the lifecycle, on the left branch there is the corresponding error code that will be generated if the parent throws an error *(note that all the errors are automatically handled by Fastify)*.
```
Incoming Request
  │
  └─▶ Instance Logger
        │
        └─▶ Routing
             │
       404 ◀─┴─▶ onRequest Hook
                  │
        4**/5** ◀─┴─▶ run Middlewares
                        │
              4**/5** ◀─┴─▶ Parsing
                             │
                       415 ◀─┴─▶ Validation
                                   │
                             400 ◀─┴─▶ preHandler Hook
                                         │
                               4**/5** ◀─┴─▶ beforeHandler
                                               │
                                     4**/5** ◀─┴─▶ User Handler
                                                     │
                                                     └─▶ Reply
                                                          │ │
                                                          │ └─▶ Outgoing Response
                                                          │
                                                          └─▶ onResponse Hook
```


t.equal(line.res.statusCode, 200, 'statusCode is 200')

t.equal(line.msg, 'request completed', 'message is set')


'use strict'

const fastify = require('../fastify')()

const schema = {
  schema: {
    response: {
      200: {
        type: 'object',
        properties: {
          hello: {
            type: 'string'
          }
        }
      }
    }
  }
}

fastify
  .get('/', schema, function (req, reply) {
    reply
      .send({ hello: 'world' })
  })

fastify.listen(3000, err => {
  if (err) throw err
  console.log(`server listening on ${fastify.server.address().port}`)
})


Fastify

## Server Methods

#### server
`fastify.server`: The Node core [server](https://nodejs.org/api/http.html#http_class_http_server) object

#### ready
Function called when all the plugins has been loaded.
It takes an error parameter if something went wrong.
```js
fastify.ready(err => {
  if (err) throw err
})
```

#### listen
Starts the server on the given port after all the plugins are loaded, internally waits for the `.ready()` event. The callback is the same as the Node core.
```js
fastify.listen(3000, err => {
  if (err) throw err
})
```

Specifying an address is also supported:

```js
fastify.listen(3000, '127.0.0.1', err => {
  if (err) throw err
})
```

#### route
Method to add routes to the server, it also have shorthands functions, check [here](https://github.com/fastify/fastify/blob/master/docs/Routes.md).

#### routes iterator
The Fastify instance is an Iterable object with all the registered routes.
The route properties are the same the developer has declared [here](https://github.com/fastify/fastify/blob/master/docs/Routes.md).
```js
fastify.get('/route', opts, handler)

fastify.ready(() => {
  for (var route of fastify) {
    console.log(route)
    /* will output:
    {
      '/route': {
        get: {
          method: String,
          ulr: String,
          schema: Object,
          handler: Function,
          Request: Function,
          Reply: Function
        }
      }
    }
    */
  }
})
```

#### close
`fastify.close(callback)`: call this function to close the server instance and run the [`'onClose'`](https://github.com/fastify/fastify/blob/master/docs/Hooks.md#on-close) hook.

#### decorate*
Function useful if you need to decorate the fastify instance, Reply or Request, check [here](https://github.com/fastify/fastify/blob/master/docs/Decorators.md).

#### register
Fastify allows the user to extend its functionalities with plugins.
A plugin can be a set of routes, a server decorator or whatever, check [here](https://github.com/fastify/fastify/blob/master/docs/Plugins.md).

#### use
Function to add middlewares to Fastify, check  [here](https://github.com/fastify/fastify/blob/master/docs/Middlewares.md).

#### addHook
Function to add a specific hook in the lifecycle of Fastify, check  [here](https://github.com/fastify/fastify/blob/master/docs/Hooks.md).

#### logger
The logger instance, check  [here](https://github.com/fastify/fastify/blob/master/docs/Logging.md).

#### inject
Fake http injection (for testing purposes)  [here](https://github.com/fastify/fastify/blob/master/docs/Testing.md#inject).

#### setSchemaCompiler
Set the schema compiler for all routes  [here](https://github.com/fastify/fastify/blob/master/docs/Validation-And-Serialize.md#schema-compiler).


/* eslint-disable no-useless-return */

'use strict'

const pump = require('pump')
const xtend = require('xtend')
const validation = require('./validation')
const serialize = validation.serialize
const statusCodes = require('http').STATUS_CODES
const stringify = JSON.stringify
const flatstr = require('flatstr')

function Reply (req, res, store) {
  this.res = res
  this.store = store
  this._req = req
  this.sent = false
  this._serializer = null
}

/**
 * Instead of using directly res.end(), we are using setImmediate(…)
 * This because we have observed that with this technique we are faster at responding to the various requests,
 * since the setImmediate forwards the res.end at the end of the poll phase of libuv in the event loop.
 * So we can gather multiple requests and then handle all the replies in a different moment,
 * causing a general improvement of performances, ~+10%.
 */
Reply.prototype.send = function (payload) {
  if (this.sent) {
    this._req.log.warn(new Error('Reply already sent'))
    return
  }

  this.sent = true

  if (payload === undefined) {
    this.res.setHeader('Content-Length', '0')
    setImmediate(wrapReplyEnd, this, '')
    return
  }

  if (payload && payload.isBoom) {
    this._req.log.error(payload)
    this.res.statusCode = payload.output.statusCode

    this.res.setHeader('Content-Type', 'application/json')
    this.headers(payload.output.headers)

    setImmediate(
      wrapReplyEnd,
      this,
      stringify(payload.output.payload)
    )
    return
  }

  if (payload instanceof Error) {
    if (!this.res.statusCode || this.res.statusCode < 400) {
      this.res.statusCode = 500
    }

    this._req.log.error({ res: this.res, err: payload }, payload.message)
    this.res.setHeader('Content-Type', 'application/json')
    setImmediate(
      wrapReplyEnd,
      this,
      stringify(xtend({
        error: statusCodes[this.res.statusCode + ''],
        message: payload.message,
        statusCode: this.res.statusCode
      }, this._extendServerError && this._extendServerError(payload)))
    )
    return
  }

  if (payload && typeof payload.then === 'function') {
    return payload.then(wrapReplySend(this)).catch(wrapReplySend(this))
  }

  if (payload && payload._readableState) {
    if (!this.res.getHeader('Content-Type')) {
      this.res.setHeader('Content-Type', 'application/octet-stream')
    }
    return pump(payload, this.res, wrapPumpCallback(this))
  }

  if (!this.res.getHeader('Content-Type') || this.res.getHeader('Content-Type') === 'application/json') {
    this.res.setHeader('Content-Type', 'application/json')

    // Here we are assuming that the custom serializer is a json serializer
    var str = this._serializer ? this._serializer(payload) : serialize(this.store, payload, this.res.statusCode)
    if (typeof str === 'string') {
      flatstr(str)
    }
    setImmediate(wrapReplyEnd, this, str)
    return
  }

  // All the code below must have a 'content-type' setted
  if (this._serializer) {
    setImmediate(wrapReplyEnd, this, this._serializer(payload))
    return
  }

  setImmediate(wrapReplyEnd, this, payload)
  return
}

Reply.prototype.header = function (key, value) {
  this.res.setHeader(key, value)
  return this
}

Reply.prototype.headers = function (headers) {
  var keys = Object.keys(headers)
  for (var i = 0; i < keys.length; i++) {
    this.header(keys[i], headers[keys[i]])
  }
  return this
}

Reply.prototype.code = function (code) {
  this.res.statusCode = code
  return this
}

Reply.prototype.serializer = function (fn) {
  this._serializer = fn
  return this
}

Reply.prototype.type = function (type) {
  this.res.setHeader('Content-Type', type)
  return this
}

Reply.prototype.redirect = function (code, url) {
  if (typeof code === 'string') {
    url = code
    code = 302
  }

  this.res.writeHead(code, { Location: url })
  this.res.end()
}

function wrapPumpCallback (reply) {
  return function pumpCallback (err) {
    if (err) {
      reply._req.log.error(err)
      setImmediate(wrapReplyEnd, reply, '')
    }
  }
}

function wrapReplyEnd (reply, payload) {
  reply.sent = true
  if (!reply.res.getHeader('Content-Length')) {
    reply.res.setHeader('Content-Length', Buffer.byteLength(payload))
  }
  reply.res.end(payload)
}

function wrapReplySend (reply, payload) {
  return function send (payload) {
    // reset sent so that we can call it again
    reply.sent = false
    return reply.send(payload)
  }
}

function buildReply (R) {
  function _Reply (req, res, store) {
    this.res = res
    this.store = store
    this._req = req
    this.sent = false
    this._serializer = null
  }
  _Reply.prototype = new R()
  return _Reply
}

module.exports = Reply
module.exports.buildReply = buildReply


keep keep keep keep keep replace keep keep keep keep

'use strict'

const t = require('tap')
const test = t.test
const http = require('http')
const split = require('split2')
const Fastify = require('..')
const pino = require('pino')

test('test log stream', t => {
  t.plan(7)
  var fastify = null
  var stream = split(JSON.parse)
  try {
    fastify = Fastify({
      logger: {
        stream: stream,
        level: 'info'
      }
    })
  } catch (e) {
    t.fail()
  }

  fastify.get('/', function (req, reply) {
    t.ok(req.log)
    reply.send({ hello: 'world' })
  })

  fastify.listen(0, err => {
    t.error(err)
    fastify.server.unref()

    http.get('http://localhost:' + fastify.server.address().port)
    stream.on('data', line => {
      t.ok(line.req, 'req is defined')
      t.ok(line.res, 'res is defined')
      t.equal(line.msg, 'request completed', 'message is set')
      t.equal(line.req.method, 'GET', 'method is get')
      t.equal(line.res.statusCode, 200, 'statusCode is 200')
    })
  })
})

test('test error log stream', t => {
  t.plan(7)
  var fastify = null
  var stream = split(JSON.parse)
  try {
    fastify = Fastify({
      logger: {
        stream: stream,
        level: 'info'
      }
    })
  } catch (e) {
    t.fail()
  }

  fastify.get('/error', function (req, reply) {
    t.ok(req.log)
    reply.send(new Error('kaboom'))
  })

  fastify.listen(0, err => {
    t.error(err)
    fastify.server.unref()

    http.get('http://localhost:' + fastify.server.address().port + '/error')
    stream.once('data', line => {
      t.ok(line.req, 'req is defined')
      t.ok(line.res, 'res is defined')
      t.equal(line.msg, 'kaboom', 'message is set')
      t.equal(line.req.method, 'GET', 'method is get')
      t.equal(line.res.statusCode, 500, 'statusCode is 500')
    })
  })
})

test('can use external logger instance', t => {
  const lines = ['log success', 'request completed']
  t.plan(lines.length + 2)

  const splitStream = split(JSON.parse)
  splitStream.on('data', (line) => {
    t.is(line.msg, lines.shift())
  })

  const logger = require('pino')(splitStream)

  const localFastify = Fastify({logger: logger})

  localFastify.get('/foo', function (req, reply) {
    t.ok(req.log)
    req.log.info('log success')
    reply.send({ hello: 'world' })
  })

  localFastify.listen(0, err => {
    t.error(err)
    http.get('http://localhost:' + localFastify.server.address().port + '/foo', (res) => {
      res.resume()
      res.on('end', () => {
        localFastify.server.close()
      })
    })
  })
})

test('expose the logger', t => {
  t.plan(2)
  var fastify = null
  var stream = split(JSON.parse)
  try {
    fastify = Fastify({
      logger: {
        stream: stream,
        level: 'info'
      }
    })
  } catch (e) {
    t.fail()
  }

  t.ok(fastify.logger)
  t.is(typeof fastify.logger, 'object')
})

test('The logger should accept a custom genReqId function', t => {
  t.plan(3)

  const fastify = Fastify({
    logger: {
      genReqId: function () {
        return 'a'
      }
    }
  })

  fastify.get('/', (req, reply) => {
    t.ok(req.req.id)
    reply.send({ id: req.req.id })
  })

  fastify.listen(0, err => {
    t.error(err)
    fastify.inject({
      method: 'GET',
      url: 'http://localhost:' + fastify.server.address().port
    }, res => {
      const payload = JSON.parse(res.payload)
      t.equal(payload.id, 'a')
      fastify.close()
    })
  })
})

test('reply.send logs an error if called twice in a row', t => {
  const lines = ['Reply already sent', 'Reply already sent', 'request completed']
  t.plan(lines.length + 1)

  const splitStream = split(JSON.parse)
  splitStream.on('data', (line) => {
    t.is(line.msg, lines.shift())
  })

  const logger = pino(splitStream)

  const fastify = Fastify({
    logger
  })

  fastify.get('/', (req, reply) => {
    reply.send({ hello: 'world' })
    reply.send({ hello: 'world2' })
    reply.send({ hello: 'world3' })
  })

  fastify.inject({
    method: 'GET',
    url: '/'
  }, res => {
    const payload = JSON.parse(res.payload)
    t.deepEqual(payload, { hello: 'world' })
  })
})


'use strict'

function ContentTypeParser () {
  this.customParsers = {}
  this.parserList = []
}

ContentTypeParser.prototype.add = function (contentType, fn) {
  if (this.hasParser(contentType)) {
    throw new Error(`Content type parser '${contentType}' already present.`)
  }
  this.customParsers[contentType] = fn
  this.parserList.push(contentType)
}

ContentTypeParser.prototype.hasParser = function (contentType) {
  return contentType in this.customParsers
}

ContentTypeParser.prototype.fastHasHeader = function (contentType) {
  if (!contentType) return false

  for (var i = 0; i < this.parserList.length; i++) {
    if (contentType.indexOf(this.parserList[i]) > -1) return true
  }
  return false
}

ContentTypeParser.prototype.getHandler = function (contentType) {
  for (var i = 0; i < this.parserList.length; i++) {
    if (contentType.indexOf(this.parserList[i]) > -1) {
      return this.customParsers[this.parserList[i]]
    }
  }
}

ContentTypeParser.prototype.run = function (contentType, handler, handle, params, req, res, query) {
  this.getHandler(contentType)(req, done)

  function done (body) {
    if (body instanceof Error) {
      const reply = new handle.Reply(req, res, handle)
      return reply.send(body)
    }
    handler(handle, params, req, res, body, query)
  }
}

function buildContentTypeParser (c) {
  function _ContentTypeParser () {}
  _ContentTypeParser.prototype = new ContentTypeParser()
  const C = new _ContentTypeParser()
  C.customParsers = Object.create(c.customParsers)
  C.parserList = Object.create(c.parserList)
  return C
}

module.exports = ContentTypeParser
module.exports.buildContentTypeParser = buildContentTypeParser


[![Build Status](https://travis-ci.org/fastify/fastify.svg?branch=master)](https://travis-ci.org/fastify/fastify)
[![Coverage Status](https://coveralls.io/repos/github/fastify/fastify/badge.svg?branch=master)](https://coveralls.io/github/fastify/fastify?branch=master)
[![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat)](http://standardjs.com/)
[![NPM version](https://img.shields.io/npm/v/fastify.svg?style=flat)](https://www.npmjs.com/package/fastify)
[![NPM downloads](https://img.shields.io/npm/dm/fastify.svg?style=flat)](https://www.npmjs.com/package/fastify) [![Gitter](https://badges.gitter.im/gitterHQ/gitter.svg)](https://gitter.im/fastify)
[![Build status](https://ci.appveyor.com/api/projects/status/xduljw5nsu1ya72x/branch/master?svg=true)](https://ci.appveyor.com/project/mcollina/fastify/branch/master)

An efficient server implies a lower cost of the infrastructure, a better responsiveness under load and happy users.
How can you efficiently handle the resources of your server, knowing that you are serving the highest number of requests as possible, without sacrificing security validations and handy development?

Enter Fastify. Fastify is a web framework highly focused on speed and low overhead. It is inspired from Hapi and Express and as far as we know, it is one of the fastest web frameworks in town.
Use Fastify can increase your throughput up to 100%.

### Install

```
npm i fastify --save
```

### Example

```js
// Require the framework and instantiate it
const fastify = require('fastify')()

// Declare a route
fastify.get('/', function (request, reply) {
  reply.send({ hello: 'world' })
})

// Run the server!
fastify.listen(3000, function (err) {
  if (err) throw err
  console.log(`server listening on ${fastify.server.address().port}`)
})
```

Do you want to know more? Head to the Getting Started.

### Core features

- **100% asynchronous:** all the core is implemented with asynchronous code, in this way not even a millisecond is wasted.
- **Highly performant:** as far as we know, Fastify is one of the fastest web frameworks in town, depending on the code complexity we can serve up to 20000 request per second.
- **Extendible:** Fastify is fully extensible via its hooks, plugins and decorators.
- **Schema based:** even if it is not mandatory we recommend to use [JSON Schema](http://json-schema.org/) to validate your routes and serialize your outputs, internally Fastify compiles the schema in a highly performant function.
- **Logging:** logs are extremely important but are costly; we chose the best logger to almost remove this cost, [Pino](https://github.com/pinojs/pino)!
- **Developer friendly:** the framework is built to be very expressive and help the developer in his daily use, without sacrificing performance and security.

### Benchmarks

__Machine:__ Intel Xeon E5-2686 v4 @ 2.30GHz (4 cores, 8 threads), 16GiB RAM (Amazon EC2 m4.xlarge)

__Method:__: `autocannon -c 100 -d 5 -p 10 localhost:3000` * 2, taking the second average

| Framework          | Version                    | Router?      |  Requests/sec |
| :----------------- | :------------------------- | :----------: | ------------: |
| hapi               | 16.5.2                     | ✓     | 4,226         |
| Restify            | 5.2.0                      | ✓     | 16,395        |
| Express            | 4.15.4                     | ✓     | 18,740        |
| Koa (`koa-router`) | 2.3.0 (`koa-router@7.2.1`) | ✓     | 21,361        |
| take-five          | 1.3.4                      | ✓     | 25,838        |
| Koa                | 2.3.0                      | ✗     | 26,228        |
| **Fastify**        | **0.27.0**                 | **✓** | **29,340**    |
| -                  |                            |              |               |
| `http.Server`      | 8.4.0                      | ✗     | 37,846        |

Benchmarks taken using https://github.com/fastify/benchmarks.

## Documentation
* Getting Started
* Server Methods
* Routes
* Logging
* Middlewares
* Hooks
* Decorators
* Validation and Serialize
* Lifecycle
* Reply
* Request
* Content Type Parser
* Plugins
* Testing
* Plugins Guide

## Ecosystem
- [`fastify-accepts`](https://github.com/fastify/fastify-accepts) to have [accepts](https://www.npmjs.com/package/accepts) in your request object.
- [`fastify-accepts-serializer`](https://github.com/fastify/fastify-accepts-serializer) to serialize to output according to `Accept` header
- [`fastify-apollo`](https://github.com/coopnd/fastify-apollo) Run an [Apollo Server](https://github.com/apollographql/apollo-server) with Fastify.
- [`fastify-auth`](https://github.com/fastify/fastify-auth) Run multiple auth functions in Fastify
- [`fastify-bearer-auth`](https://github.com/fastify/fastify-bearer-auth)
Bearer auth plugin for Fastify
- [`fastify-cookie`](https://github.com/fastify/fastify-cookie) Parse and set cookie headers
- [`fastify-env`](https://github.com/fastify/fastify-env) Load and check configuration
- [`fastify-formbody`](https://github.com/fastify/fastify-formbody)
Plugin to parse x-www-form-urlencoded bodies
- [`fastify-helmet`](https://github.com/fastify/fastify-helmet) Important security headers for Fastify
- [`fastify-jwt`](https://github.com/fastify/fastify-jwt) JWT utils for Fastify, internally uses [jsonwebtoken](https://github.com/auth0/node-jsonwebtoken)
- [`fastify-leveldb`](https://github.com/fastify/fastify-leveldb) Plugin to share a common LevelDB connection across Fastify.
- [`fastify-mongodb`](https://github.com/fastify/fastify-mongodb)
Fastify MongoDB connection plugin, with this you can share the same MongoDb connection pool in every part of your server.
- [`fastify-multipart`](https://github.com/fastify/fastify-multipart)
Multipart support for Fastify
- [`fastify-pigeon`](https://github.com/fastify/fastify-pigeon) [Bankai](https://github.com/yoshuawuyts/bankai) assets compiler for Fastify
- [`fastify-react`](https://github.com/fastify/fastify-react) React server side rendering support for Fastify with [Next](https://github.com/zeit/next.js/)
- [`fastify-redis`](https://github.com/fastify/fastify-redis)
Fastify Redis connection plugin, with this you can share the same Redis connection in every part of your server.
- [`fastify-register-timeout`](https://github.com/fastify/fastify-register-timeout) Register plugin with a timeout
- [`fastify-sse`](https://github.com/lolo32/fastify-sse) to provide Server-Sent Events with `reply.sse( … )` to Fastify
- [`fastify-swagger`](https://github.com/fastify/fastify-swagger)
Swagger documentation generator for Fastify
- [`fastify-websocket`](https://github.com/fastify/fastify-websocket) WebSocket support for Fastify. Built upon [websocket-stream](https://github.com/maxogden/websocket-stream)
- [`point-of-view`](https://github.com/fastify/point-of-view)
Templates rendering (*ejs, pug, handlebars, marko*) plugin support for Fastify.
- [`fastify-orientdb`](https://github.com/mahmed8003/fastify-orientdb)
Fastify OrientDB connection plugin, with this you can share the orientdb connection in every part of your server.
- *More coming soon*

## Team

### Matteo Collina

<https://github.com/mcollina>

<https://www.npmjs.com/~matteo.collina>

<https://twitter.com/matteocollina>

### Tomas Della Vedova

<https://github.com/delvedor>

<https://www.npmjs.com/~delvedor>

<https://twitter.com/delvedor>

## Acknowledgements

This project is kindly sponsored by:
- [nearForm](http://nearform.com)
- [LetzDoIt](http://www.letzdoitapp.com/)

## License

Licensed under [MIT](./LICENSE).


'use strict'

const t = require('tap')
const test = t.test
const request = require('request')
const fastify = require('..')
const cors = require('cors')
const helmet = require('helmet')

test('use a middleware', t => {
  t.plan(7)

  const instance = fastify()

  const useRes = instance.use(function (req, res, next) {
    t.pass('middleware called')
    next()
  })

  t.equal(useRes, instance)

  instance.get('/', function (request, reply) {
    reply.send({ hello: 'world' })
  })

  instance.listen(0, err => {
    t.error(err)

    t.tearDown(instance.server.close.bind(instance.server))

    request({
      method: 'GET',
      uri: 'http://localhost:' + instance.server.address().port
    }, (err, response, body) => {
      t.error(err)
      t.strictEqual(response.statusCode, 200)
      t.strictEqual(response.headers['content-length'], '' + body.length)
      t.deepEqual(JSON.parse(body), { hello: 'world' })
    })
  })
})

test('use cors', t => {
  t.plan(3)

  const instance = fastify()

  instance.use(cors())

  instance.get('/', function (request, reply) {
    reply.send({ hello: 'world' })
  })

  instance.listen(0, err => {
    t.error(err)

    t.tearDown(instance.server.close.bind(instance.server))

    request({
      method: 'GET',
      uri: 'http://localhost:' + instance.server.address().port
    }, (err, response, body) => {
      t.error(err)
      t.equal(response.headers['access-control-allow-origin'], '*')
    })
  })
})

test('use helmet', t => {
  t.plan(3)

  const instance = fastify()

  instance.use(helmet())

  instance.get('/', function (request, reply) {
    reply.send({ hello: 'world' })
  })

  instance.listen(0, err => {
    t.error(err)

    t.tearDown(instance.server.close.bind(instance.server))

    request({
      method: 'GET',
      uri: 'http://localhost:' + instance.server.address().port
    }, (err, response, body) => {
      t.error(err)
      t.ok(response.headers['x-xss-protection'])
    })
  })
})

test('use helmet and cors', t => {
  t.plan(4)

  const instance = fastify()

  instance.use(cors())
  instance.use(helmet())

  instance.get('/', function (request, reply) {
    reply.send({ hello: 'world' })
  })

  instance.listen(0, err => {
    t.error(err)

    t.tearDown(instance.server.close.bind(instance.server))

    request({
      method: 'GET',
      uri: 'http://localhost:' + instance.server.address().port
    }, (err, response, body) => {
      t.error(err)
      t.ok(response.headers['x-xss-protection'])
      t.equal(response.headers['access-control-allow-origin'], '*')
    })
  })
})

test('middlewares should support encapsulation / 1', t => {
  t.plan(9)

  const instance = fastify()

  instance.register((i, opts, done) => {
    t.ok(i._middlewares.length === 0)
    i.use(function (req, res, next) {
      t.fail('this should not be called')
      next()
    })
    t.ok(i._middlewares.length > 0)
    done()
  })

  instance.get('/', function (request, reply) {
    t.ok(instance._middlewares.length === 0)
    reply.send({ hello: 'world' })
  })

  instance.listen(0, err => {
    t.error(err)
    t.ok(instance._middlewares.length === 0)
    t.tearDown(instance.server.close.bind(instance.server))

    request({
      method: 'GET',
      uri: 'http://localhost:' + instance.server.address().port
    }, (err, response, body) => {
      t.error(err)
      t.strictEqual(response.statusCode, 200)
      t.strictEqual(response.headers['content-length'], '' + body.length)
      t.deepEqual(JSON.parse(body), { hello: 'world' })
    })
  })
})

test('middlewares should support encapsulation / 2', t => {
  t.plan(13)

  const instance = fastify()

  instance.use(function (req, res, next) {
    req.global = true
    next()
  })

  instance.register((i, opts, done) => {
    i.use(function (req, res, next) {
      req.local = true
      next()
    })

    i.get('/local', function (request, reply) {
      t.ok(request.req.global)
      t.ok(request.req.local)
      reply.send({ hello: 'world' })
    })

    done()
  })

  instance.get('/global', function (request, reply) {
    t.ok(request.req.global)
    t.notOk(request.req.local)
    reply.send({ hello: 'world' })
  })

  instance.listen(0, err => {
    t.error(err)
    t.tearDown(instance.server.close.bind(instance.server))

    request({
      method: 'GET',
      uri: 'http://localhost:' + instance.server.address().port + '/global'
    }, (err, response, body) => {
      t.error(err)
      t.strictEqual(response.statusCode, 200)
      t.strictEqual(response.headers['content-length'], '' + body.length)
      t.deepEqual(JSON.parse(body), { hello: 'world' })

      request({
        method: 'GET',
        uri: 'http://localhost:' + instance.server.address().port + '/local'
      }, (err, response, body) => {
        t.error(err)
        t.strictEqual(response.statusCode, 200)
        t.strictEqual(response.headers['content-length'], '' + body.length)
        t.deepEqual(JSON.parse(body), { hello: 'world' })
      })
    })
  })
})

test('middlewares should support encapsulation / 3', t => {
  t.plan(15)

  const instance = fastify()

  instance.use(function (req, res, next) {
    req.global = true
    next()
  })

  instance.register((i, opts, done) => {
    i.use(function (req, res, next) {
      req.firstLocal = true
      next()
    })

    i.use(function (req, res, next) {
      req.secondLocal = true
      next()
    })

    i.get('/local', function (request, reply) {
      t.ok(request.req.global)
      t.ok(request.req.firstLocal)
      t.ok(request.req.secondLocal)
      reply.send({ hello: 'world' })
    })

    done()
  })

  instance.get('/global', function (request, reply) {
    t.ok(request.req.global)
    t.notOk(request.req.firstLocal)
    t.notOk(request.req.secondLocal)
    reply.send({ hello: 'world' })
  })

  instance.listen(0, err => {
    t.error(err)
    t.tearDown(instance.server.close.bind(instance.server))

    request({
      method: 'GET',
      uri: 'http://localhost:' + instance.server.address().port + '/global'
    }, (err, response, body) => {
      t.error(err)
      t.strictEqual(response.statusCode, 200)
      t.strictEqual(response.headers['content-length'], '' + body.length)
      t.deepEqual(JSON.parse(body), { hello: 'world' })

      request({
        method: 'GET',
        uri: 'http://localhost:' + instance.server.address().port + '/local'
      }, (err, response, body) => {
        t.error(err)
        t.strictEqual(response.statusCode, 200)
        t.strictEqual(response.headers['content-length'], '' + body.length)
        t.deepEqual(JSON.parse(body), { hello: 'world' })
      })
    })
  })
})

test('middlewares should support encapsulation / 4', t => {
  t.plan(25)

  const instance = fastify()

  instance.use(function (req, res, next) {
    req.global = true
    next()
  })

  instance.register((i, opts, done) => {
    i.use(function (req, res, next) {
      req.firstLocal = true
      next()
    })

    i.register((f, opts, d) => {
      f.use(function (req, res, next) {
        req.secondLocal = true
        next()
      })

      f.get('/secondLocal', function (request, reply) {
        t.ok(request.req.global)
        t.ok(request.req.firstLocal)
        t.ok(request.req.secondLocal)
        t.ok(request.req.thirdLocal)
        reply.send({ hello: 'world' })
      })

      f.use(function (req, res, next) {
        req.thirdLocal = true
        next()
      })

      d()
    }, done)

    i.get('/firstLocal', function (request, reply) {
      t.ok(request.req.global)
      t.ok(request.req.firstLocal)
      t.notOk(request.req.secondLocal)
      t.notOk(request.req.thirdLocal)
      reply.send({ hello: 'world' })
    })
  })

  instance.get('/global', function (request, reply) {
    t.ok(request.req.global)
    t.notOk(request.req.firstLocal)
    t.notOk(request.req.secondLocal)
    t.notOk(request.req.thirdLocal)
    reply.send({ hello: 'world' })
  })

  instance.listen(0, err => {
    t.error(err)
    t.tearDown(instance.server.close.bind(instance.server))

    request({
      method: 'GET',
      uri: 'http://localhost:' + instance.server.address().port + '/global'
    }, (err, response, body) => {
      t.error(err)
      t.strictEqual(response.statusCode, 200)
      t.strictEqual(response.headers['content-length'], '' + body.length)
      t.deepEqual(JSON.parse(body), { hello: 'world' })

      request({
        method: 'GET',
        uri: 'http://localhost:' + instance.server.address().port + '/firstLocal'
      }, (err, response, body) => {
        t.error(err)
        t.strictEqual(response.statusCode, 200)
        t.strictEqual(response.headers['content-length'], '' + body.length)
        t.deepEqual(JSON.parse(body), { hello: 'world' })

        request({
          method: 'GET',
          uri: 'http://localhost:' + instance.server.address().port + '/secondLocal'
        }, (err, response, body) => {
          t.error(err)
          t.strictEqual(response.statusCode, 200)
          t.strictEqual(response.headers['content-length'], '' + body.length)
          t.deepEqual(JSON.parse(body), { hello: 'world' })
        })
      })
    })
  })
})

test('middlewares should support encapsulation / 5', t => {
  t.plan(9)

  const instance = fastify()

  instance.use(function (req, res, next) {
    req.global = true
    next()
  })

  instance.register((i, opts, done) => {
    i.use(function (req, res, next) {
      next(new Error('kaboom!'))
    })

    i.get('/local', function (request, reply) {
      t.fail('this should not be called')
    })

    done()
  })

  instance.get('/global', function (request, reply) {
    t.ok(request.req.global)
    reply.send({ hello: 'world' })
  })

  instance.listen(0, err => {
    t.error(err)
    t.tearDown(instance.server.close.bind(instance.server))

    request({
      method: 'GET',
      uri: 'http://localhost:' + instance.server.address().port + '/global'
    }, (err, response, body) => {
      t.error(err)
      t.strictEqual(response.statusCode, 200)
      t.strictEqual(response.headers['content-length'], '' + body.length)
      t.deepEqual(JSON.parse(body), { hello: 'world' })

      request({
        method: 'GET',
        uri: 'http://localhost:' + instance.server.address().port + '/local'
      }, (err, response, body) => {
        t.error(err)
        t.strictEqual(response.statusCode, 500)
        t.deepEqual(JSON.parse(body), {
          error: 'Internal Server Error',
          message: 'kaboom!',
          statusCode: 500
        })
      })
    })
  })
})

test('middlewares should support encapsulation with prefix', t => {
  t.plan(9)

  const instance = fastify()

  instance.use(function (req, res, next) {
    req.global = true
    next()
  })

  instance.register((i, opts, done) => {
    i.use(function (req, res, next) {
      next(new Error('kaboom!'))
    })

    i.get('/', function (request, reply) {
      t.fail('this should not be called')
    })

    done()
  }, { prefix: '/local' })

  instance.get('/global', function (request, reply) {
    t.ok(request.req.global)
    reply.send({ hello: 'world' })
  })

  instance.listen(0, err => {
    t.error(err)
    t.tearDown(instance.server.close.bind(instance.server))

    request({
      method: 'GET',
      uri: 'http://localhost:' + instance.server.address().port + '/global'
    }, (err, response, body) => {
      t.error(err)
      t.strictEqual(response.statusCode, 200)
      t.strictEqual(response.headers['content-length'], '' + body.length)
      t.deepEqual(JSON.parse(body), { hello: 'world' })

      request({
        method: 'GET',
        uri: 'http://localhost:' + instance.server.address().port + '/local'
      }, (err, response, body) => {
        t.error(err)
        t.strictEqual(response.statusCode, 500)
        t.deepEqual(JSON.parse(body), {
          error: 'Internal Server Error',
          message: 'kaboom!',
          statusCode: 500
        })
      })
    })
  })
})


http.get('http://localhost:' + fastify.server.address().port + '/error')


'use strict'

const t = require('tap')
const test = t.test

const Ajv = require('ajv')
const ajv = new Ajv({ coerceTypes: true })

const validation = require('../../lib/validation')
const symbols = require('../../lib/validation').symbols

test('Symbols', t => {
  t.plan(5)
  t.is(typeof symbols.responseSchema, 'symbol')
  t.is(typeof symbols.bodySchema, 'symbol')
  t.is(typeof symbols.querystringSchema, 'symbol')
  t.is(typeof symbols.paramsSchema, 'symbol')
  t.is(typeof symbols.headersSchema, 'symbol')
})

test('build schema - missing schema', t => {
  t.plan(1)
  const opts = {}
  validation.build(opts)
  t.is(typeof opts[symbols.responseSchema], 'undefined')
})

test('build schema - missing output schema', t => {
  t.plan(1)
  const opts = { schema: {} }
  validation.build(opts)
  t.is(typeof opts[symbols.responseSchema], 'undefined')
})

test('build schema - output schema', t => {
  t.plan(2)
  const opts = {
    schema: {
      response: {
        '2xx': {
          type: 'object',
          properties: {
            hello: { type: 'string' }
          }
        },
        201: {
          type: 'object',
          properties: {
            hello: { type: 'number' }
          }
        }
      }
    }
  }
  validation.build(opts, schema => ajv.compile(schema))
  t.is(typeof opts[symbols.responseSchema]['2xx'], 'function')
  t.is(typeof opts[symbols.responseSchema]['201'], 'function')
})

test('build schema - payload schema', t => {
  t.plan(1)
  const opts = {
    schema: {
      body: {
        type: 'object',
        properties: {
          hello: { type: 'string' }
        }
      }
    }
  }
  validation.build(opts, schema => ajv.compile(schema))
  t.is(typeof opts[symbols.bodySchema], 'function')
})

test('build schema - querystring schema', t => {
  t.plan(2)
  const opts = {
    schema: {
      querystring: {
        type: 'object',
        properties: {
          hello: { type: 'string' }
        }
      }
    }
  }
  validation.build(opts, schema => ajv.compile(schema))
  t.type(opts[symbols.querystringSchema].schema.type, 'string')
  t.is(typeof opts[symbols.querystringSchema], 'function')
})

test('build schema - querystring schema abbreviated', t => {
  t.plan(2)
  const opts = {
    schema: {
      querystring: {
        hello: { type: 'string' }
      }
    }
  }
  validation.build(opts, schema => ajv.compile(schema))
  t.type(opts[symbols.querystringSchema].schema.type, 'string')
  t.is(typeof opts[symbols.querystringSchema], 'function')
})

test('build schema - params schema', t => {
  t.plan(1)
  const opts = {
    schema: {
      params: {
        type: 'object',
        properties: {
          hello: { type: 'string' }
        }
      }
    }
  }
  validation.build(opts, schema => ajv.compile(schema))
  t.is(typeof opts[symbols.paramsSchema], 'function')
})

test('build schema - headers schema', t => {
  t.plan(1)
  const opts = {
    schema: {
      headers: {
        type: 'object',
        properties: {
          'content-type': { type: 'string' }
        }
      }
    }
  }
  validation.build(opts, schema => ajv.compile(schema))
  t.is(typeof opts[symbols.headersSchema], 'function')
})


Fastify

## Testing
Testing is one of the most important part when you are developing an application.
Fastify does not offer a testing framework out of the box, but we can recommend you an handy and nice way to build your unit testing environment.

The modules you'll need:
- [Tap](https://www.npmjs.com/package/tap): an excellent testing framework, it will give you out of the box a very good assertion library and a lot utilities.
- [Request](https://www.npmjs.com/package/request): a complete library to perform request of any kind.
- [Minimist](https://www.npmjs.com/package/minimist): a CLI parser, that you will use to run server from the command line.

```js
// server.js
const minimist = require('minimist')
const fastify = require('fastify')()

const options = {
  schema: {
    response: {
      200: {
        type: 'object',
        properties: {
          hello: { type: 'string' }
        }
      }
    }
  }
}

function start (opts, callback) {
  fastify.get('/', options, function (request, reply) {
    reply.send({ hello: 'world' })
  })

  fastify.listen(opts.port, function (err) {
    callback(err, fastify)
  })
}

// In this way you can run the server both from the CLI and as a required module.
if (require.main === module) {
  // Run the server with:
  // $ node server.js -p 8080
  start(minimist(process.argv.slice(2), {
    integer: ['port'],
    alias: {
      port: 'p'
    },
    default: {
      port: 3000
    }
  }), (err, instance) => {
    if (err) throw err

    console.log(`server listening on ${instance.server.address().port}`)
  })
}

// Here we are exposing the function that starts the server
// in this way inside the test files we can require and run it.
module.exports = { start }
```

```js
// test.js
const t = require('tap')
const test = t.test
const request = require('request')
const server = require('./server')

// Run the server
server.start({ port: 0 }, (err, fastify) => {
  t.error(err)

  test('The server should start', t => {
    t.plan(4)
    // Perform the request
    request({
      method: 'GET',
      uri: `http://localhost:${fastify.server.address().port}`
    }, (err, response, body) => {
      // Unit test
      t.error(err)
      t.strictEqual(response.statusCode, 200)
      t.strictEqual(response.headers['content-length'], '' + body.length)
      t.deepEqual(JSON.parse(body), { hello: 'world' })
      fastify.close()
    })
  })
})
```

### Testing with http injection
Fastify supports fake http injection thanks to [shot](https://github.com/hapijs/shot).
To support this method, you should list `shot` library in your package as dev dependency using
```
npm install shot --save-dev
```
You just need to use the api `inject`:
```js
fastify.inject({
  method: String,
  url: String,
  payload: Object,
  headers: Object
}, response => {
  // your tests
})
```
Example:
```js
// server.js
const minimist = require('minimist')
const fastify = require('fastify')()

const options = {
  schema: {
    response: {
      200: {
        type: 'object',
        properties: {
          hello: { type: 'string' }
        }
      }
    }
  }
}

fastify.get('/', options, function (request, reply) {
  reply.send({ hello: 'world' })
})

function start (opts, callback) {
  fastify.listen(opts.port, function (err) {
    callback(err, fastify)
  })
}

// In this way you can run the server both from the CLI and as a required module.
if (require.main === module) {
  // Run the server with:
  // $ node server.js -p 8080
  start(minimist(process.argv.slice(2), {
    integer: ['port'],
    alias: {
      port: 'p'
    },
    default: {
      port: 3000
    }
  }), (err, instance) => {
    if (err) throw err

    console.log(`server listening on ${instance.server.address().port}`)
  })
}

// note that now we are also exposing the fastify instance
module.exports = { start, fastify }
```

```js
// test.js
const t = require('tap')
const test = t.test
const request = require('request')
const fastify = require('./server').fastify

test('GET `/` route', t => {
  t.plan(3)

  fastify.inject({
    method: 'GET',
    url: '/'
  }, res => {
    t.strictEqual(res.statusCode, 200)
    t.strictEqual(res.headers['content-length'], '' + body.length)
    t.deepEqual(JSON.parse(res.payload), { hello: 'world' })
    // even if the server is not running (inject does not run the server)
    // at the end of your tests is highly recommended call `.close()`,
    // in this way you will close all the connections to external services
    fastify.close()
  })
})
```


'use strict'

const t = require('tap')
const test = t.test
const request = require('request')
const fs = require('fs')
const path = require('path')
const Fastify = require('../..')

try {
  var fastify = Fastify({
    https: {
      key: fs.readFileSync(path.join(__dirname, 'fastify.key')),
      cert: fs.readFileSync(path.join(__dirname, 'fastify.cert'))
    }
  })
  t.pass('Key/cert successfully loaded')
} catch (e) {
  t.fail('Key/cert loading failed', e)
}

test('https get', t => {
  t.plan(1)
  try {
    fastify.get('/', function (req, reply) {
      reply.code(200).send({ hello: 'world' })
    })
    t.pass()
  } catch (e) {
    t.fail()
  }
})

fastify.listen(0, err => {
  t.error(err)
  fastify.server.unref()

  test('https get request', t => {
    t.plan(4)
    request({
      method: 'GET',
      uri: 'https://localhost:' + fastify.server.address().port,
      rejectUnauthorized: false
    }, (err, response, body) => {
      t.error(err)
      t.strictEqual(response.statusCode, 200)
      t.strictEqual(response.headers['content-length'], '' + body.length)
      t.deepEqual(JSON.parse(body), { hello: 'world' })
    })
  })
})


t.equal(line.msg, 'kaboom', 'message is set')