import { CSSSelector } from '../shared';
import Swiper from '../swiper-class';

export interface ScrollbarMethods {
  /**
   * HTMLElement of Scrollbar container element
   */
  el: HTMLElement;

  /**
   * HTMLElement of Scrollbar draggable handler element
   */
  dragEl: HTMLElement;

  /**
   * Updates scrollbar track and handler sizes
   */
  updateSize(): void;

  /**
   * Updates scrollbar translate
   */
  setTranslate(): void;

  /**
   * Initialize scrollbar
   */
  init(): void;

  /**
   * Destroy scrollbar
   */
  destroy(): void;
}

export interface ScrollbarEvents {
  /**
   * Event will be fired on draggable scrollbar drag start
   */
  scrollbarDragStart: (swiper: Swiper, event: MouseEvent | TouchEvent | PointerEvent) => void;

  /**
   * Event will be fired on draggable scrollbar drag move
   */
  scrollbarDragMove: (swiper: Swiper, event: MouseEvent | TouchEvent | PointerEvent) => void;

  /**
   * Event will be fired on draggable scrollbar drag end
   */
  scrollbarDragEnd: (swiper: Swiper, event: MouseEvent | TouchEvent | PointerEvent) => void;
}

/**
 * Object with scrollbar parameters.
 *
 * @example
 * ```js
 * const swiper = new Swiper('.swiper', {
 *   scrollbar: {
 *     el: '.swiper-scrollbar',
 *     draggable: true,
 *   },
 * });
 * ```
 */
export interface ScrollbarOptions {
  /**
   * Boolean property to use with breakpoints to enable/disable scrollbar on certain breakpoints
   */
  enabled?: boolean;
  /**
   * String with CSS selector or HTML element of the container with scrollbar.
   *
   * @default null
   */
  el?: CSSSelector | HTMLElement | null;

  /**
   * Hide scrollbar automatically after user interaction
   *
   * @default true
   */
  hide?: boolean;

  /**
   * Set to `true` to enable make scrollbar draggable that allows you to control slider position
   *
   * @default false
   */
  draggable?: boolean;

  /**
   * Set to `true` to snap slider position to slides when you release scrollbar
   *
   * @default false
   */
  snapOnRelease?: boolean;

  /**
   * Size of scrollbar draggable element in px
   *
   * @default 'auto'
   */
  dragSize?: 'auto' | number;

  /**
   * Scrollbar element additional CSS class when it is disabled
   *
   * @default 'swiper-scrollbar-lock'
   */
  lockClass?: string;

  /**
   * Scrollbar draggable element CSS class
   *
   * @default 'swiper-scrollbar-drag'
   */
  dragClass?: string;

  /**
   * CSS class name added on swiper container and scrollbar element when scrollbar is disabled by breakpoint
   *
   * @default 'swiper-scrollbar-disabled'
   */
  scrollbarDisabledClass?: string;

  /**
   * CSS class name set to scrollbar in horizontal Swiper
   *
   * @default 'swiper-scrollbar-horizontal'
   */
  horizontalClass?: string;

  /**
   * CSS class name set to scrollbar in vertical Swiper
   *
   * @default 'swiper-scrollbar-vertical'
   */
  verticalClass?: string;
}


keep keep keep keep keep replace keep keep keep keep keep

setImmediate(wrapReplyEnd, context, req, res, 415, params, null, urlUtil.parse(req.url, true).query, null)

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

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

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

  if (method === 'GET' || method === 'DELETE' || method === 'HEAD') {
    return handler(context, 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, context)
    }

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

    setImmediate(wrapReplyEnd, context, 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, context)
      // custom parser for a given content type
      } else if (context.contentTypeParser.fastHasHeader(req.headers['content-type'])) {
        return context.contentTypeParser.run(req.headers['content-type'], handler, context, params, req, res, urlUtil.parse(req.url, true).query)
      }

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

  setImmediate(wrapReplyEnd, context, req, res, 405, params, null, urlUtil.parse(req.url, true).query)
  return
}

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

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

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

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

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

function hookIterator (fn, cb) {
  var ret = fn(this.request, this.reply, cb)
  if (ret && typeof ret.then === 'function') {
    ret.then(cb).catch(cb)
  }
}

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

  var result = this.context.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 (context, req, res, statusCode, params, body, query, payload) {
  const request = new context.Request(params, req, body, query, req.headers, req.log)
  const reply = new context.Reply(req, res, context, request)
  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 }


'use strict'

const t = require('tap')
const test = t.test
const net = require('net')
const Fastify = require('..')
const statusCodes = require('http').STATUS_CODES
const boom = require('boom')

const codes = Object.keys(statusCodes)
codes.forEach(code => {
  if (Number(code) >= 400) helper(code)
})

function helper (code) {
  test('Reply error handling - code: ' + code, t => {
    t.plan(3)
    const fastify = Fastify()
    const err = new Error('winter is coming')

    fastify.get('/', (req, reply) => {
      reply
        .code(Number(code))
        .send(err)
    })

    fastify.inject({
      method: 'GET',
      url: '/'
    }, res => {
      t.strictEqual(res.statusCode, Number(code))
      t.equal(res.headers['content-type'], 'application/json')
      t.deepEqual(
        {
          error: statusCodes[code],
          message: err.message,
          statusCode: Number(code)
        },
        JSON.parse(res.payload)
      )
    })
  })
}

test('preHandler hook error handling with external code', t => {
  t.plan(2)
  const fastify = Fastify()
  const err = new Error('winter is coming')

  fastify.addHook('preHandler', (req, reply, done) => {
    reply.code(400)
    done(err)
  })

  fastify.get('/', () => {})

  fastify.inject({
    method: 'GET',
    url: '/'
  }, res => {
    t.strictEqual(res.statusCode, 400)
    t.deepEqual(
      {
        error: statusCodes['400'],
        message: err.message,
        statusCode: 400
      },
      JSON.parse(res.payload)
    )
  })
})

test('onRequest hook error handling with external done', t => {
  t.plan(2)
  const fastify = Fastify()
  const err = new Error('winter is coming')

  fastify.addHook('onRequest', (req, res, done) => {
    res.statusCode = 400
    done(err)
  })

  fastify.get('/', () => {})

  fastify.inject({
    method: 'GET',
    url: '/'
  }, res => {
    t.strictEqual(res.statusCode, 400)
    t.deepEqual(
      {
        error: statusCodes['400'],
        message: err.message,
        statusCode: 400
      },
      JSON.parse(res.payload)
    )
  })
})

test('support for boom', t => {
  t.plan(3)
  const fastify = Fastify()

  fastify.get('/500', (req, reply) => {
    reply.send(boom.create(500, 'winter is coming', { hello: 'world' }))
  })

  fastify.get('/400', (req, reply) => {
    reply.send(boom.create(400, 'winter is coming', { hello: 'world' }))
  })

  fastify.get('/401', (req, reply) => {
    reply.send(boom.unauthorized('invalid password', 'sample'))
  })

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

    fastify.inject({
      method: 'GET',
      url: '/500'
    }, res => {
      t.strictEqual(res.statusCode, 500)
      t.strictEqual(res.headers['content-type'], 'application/json')
      t.deepEqual(
        // we are doing this because Boom creates also a stack that is stripped during the stringify phase
        JSON.parse(res.payload),
        {
          statusCode: 500,
          error: 'Internal Server Error',
          message: 'An internal server error occurred'
        }
      )
    })
  })

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

    fastify.inject({
      method: 'GET',
      url: '/400'
    }, res => {
      t.strictEqual(res.statusCode, 400)
      t.strictEqual(res.headers['content-type'], 'application/json')
      t.deepEqual(
        // we are doing this because Boom creates also a stack that is stripped during the stringify phase
        JSON.parse(res.payload),
        {
          statusCode: 400,
          error: 'Bad Request',
          message: 'winter is coming'
        }
      )
    })
  })

  t.test('401', t => {
    t.plan(4)

    fastify.inject({
      method: 'GET',
      url: '/401'
    }, res => {
      t.strictEqual(res.statusCode, 401)
      t.strictEqual(res.headers['content-type'], 'application/json')
      t.deepEqual(
        // we are doing this because Boom creates also a stack that is stripped during the stringify phase
        JSON.parse(res.payload),
        {
          statusCode: 401,
          error: 'Unauthorized',
          message: 'invalid password',
          attributes: {
            error: 'invalid password'
          }
        }
      )
      t.deepEqual(res.headers['www-authenticate'], 'sample error="invalid password"')
    })
  })
})

test('extendServerError should exist', t => {
  t.plan(2)
  const fastify = Fastify()
  t.ok(fastify.extendServerError)
  t.is(typeof fastify.extendServerError, 'function')
})

test('extend server error - encapsulation', t => {
  t.plan(9)
  const fastify = Fastify()
  const err = new Error('error')
  const date = new Date()

  fastify.get('/', (req, reply) => {
    t.notOk(reply._extendServerError)
    reply.send(err)
  })

  fastify.register((instance, opts, next) => {
    instance.extendServerError((payloadError) => {
      t.ok(payloadError instanceof Error)
      t.strictEqual(payloadError.name, err.name)
      t.strictEqual(payloadError.message, err.message)

      return {
        timestamp: date
      }
    })

    instance.get('/encapsulated', (req, reply) => {
      t.ok(reply._extendServerError)
      reply.send(err)
    })

    next()
  })

  fastify.inject({
    method: 'GET',
    url: '/'
  }, res => {
    t.strictEqual(res.statusCode, 500)
    t.deepEqual(
      {
        error: statusCodes['500'],
        message: err.message,
        statusCode: 500
      },
      JSON.parse(res.payload)
    )
  })

  fastify.inject({
    method: 'GET',
    url: '/encapsulated'
  }, res => {
    t.strictEqual(res.statusCode, 500)
    t.deepEqual(
      {
        error: statusCodes['500'],
        message: err.message,
        statusCode: 500,
        timestamp: date.toISOString()
      },
      JSON.parse(res.payload)
    )
  })
})

test('extend server error - should throw if the argument is not a function', t => {
  t.plan(1)
  const fastify = Fastify()

  try {
    fastify.extendServerError(null)
    t.fail()
  } catch (e) {
    t.is(e.message, 'The server error object must be a function')
  }
})

if (Number(process.versions.node[0]) >= 6) {
  test('Should reply 400 on client error', t => {
    t.plan(2)

    const fastify = Fastify()
    fastify.listen(0, err => {
      t.error(err)

      const client = net.connect(fastify.server.address().port)
      client.end('oooops!')

      var chunks = ''
      client.on('data', chunk => {
        chunks += chunk
      })

      client.once('end', () => {
        const body = JSON.stringify({
          error: 'Bad Request',
          message: 'Client Error',
          statusCode: 400
        })
        t.equal(`HTTP/1.1 400 Bad Request\r\nContent-Length: ${body.length}\r\nContent-Type: 'application/json'\r\n\r\n${body}`, chunks)
        fastify.close()
      })
    })
  })
}

test('Error instance sets HTTP status code', t => {
  t.plan(2)
  const fastify = Fastify()
  const err = new Error('winter is coming')
  err.statusCode = 418

  fastify.get('/', () => {
    return Promise.reject(err)
  })

  fastify.inject({
    method: 'GET',
    url: '/'
  }, res => {
    t.strictEqual(res.statusCode, 418)
    t.deepEqual(
      {
        error: statusCodes['418'],
        message: err.message,
        statusCode: 418
      },
      JSON.parse(res.payload)
    )
  })
})

test('Error status code below 400 defaults to 500', t => {
  t.plan(2)
  const fastify = Fastify()
  const err = new Error('winter is coming')
  err.statusCode = 399

  fastify.get('/', () => {
    return Promise.reject(err)
  })

  fastify.inject({
    method: 'GET',
    url: '/'
  }, res => {
    t.strictEqual(res.statusCode, 500)
    t.deepEqual(
      {
        error: statusCodes['500'],
        message: err.message,
        statusCode: 500
      },
      JSON.parse(res.payload)
    )
  })
})

test('Error.status property support', t => {
  t.plan(2)
  const fastify = Fastify()
  const err = new Error('winter is coming')
  err.status = 418

  fastify.get('/', () => {
    return Promise.reject(err)
  })

  fastify.inject({
    method: 'GET',
    url: '/'
  }, res => {
    t.strictEqual(res.statusCode, 418)
    t.deepEqual(
      {
        error: statusCodes['418'],
        message: err.message,
        statusCode: 418
      },
      JSON.parse(res.payload)
    )
  })
})


#! /bin/bash

NODE_VER=`node -v`

if [[ $NODE_VER =~ ^v6 && $TRAVIS_PULL_REQUEST == "false" && $TRAVIS_BRANCH == "master" ]]; then
  curl -X POST --header 'Content-Type: application/json' "https://circleci.com/api/v1.1/project/github/fastify/website?circle-token=$CIRCLECI_TOKEN"
fi


Fastify

## Routes

### Full declaration
```js
fastify.route(options)
```
* `method`: currently it supports `'DELETE'`, `'GET'`, `'HEAD'`, `'PATCH'`, `'POST'`, `'PUT'` and `'OPTIONS'`. It could also be an array of methods.

* `url`: the path of the url to match this route (alias: `path`).
* `schema`: an object containing the schemas for the request and response.
They need to be in
  [JSON Schema](http://json-schema.org/) format, check [here](https://github.com/fastify/fastify/blob/master/docs/Validation-and-Serialization.md) for more info.

  * `body`: validates the body of the request if it is a POST or a
    PUT.
  * `querystring`: validates the querystring. This can be a complete JSON
  Schema object, with the property `type` of `object` and `properties` object of parameters, or
  simply the values of what would be contained in the `properties` object as shown below.
  * `params`: validates the params.
  * `response`: filter and generate a schema for the response, setting a
    schema allows us to have 10-20% more throughput.
* `beforeHandler(request, reply, done)`: a [function](https://github.com/fastify/fastify/blob/master/docs/Hooks.md#before-handler) called just before the request handler, useful if you need to perform authentication at route level for example, it could also be and array of functions.
* `handler(request, reply)`: the function that will handle this request.
* `schemaCompiler(schema)`: the function that build the schema for the validations. See [here](https://github.com/fastify/fastify/blob/master/docs/Validation-and-Serialization.md#schema-compiler)

  `request` is defined in [Request](https://github.com/fastify/fastify/blob/master/docs/Request.md).

  `reply` is defined in [Reply](https://github.com/fastify/fastify/blob/master/docs/Reply.md).

The routing is handled by [find-my-way](https://github.com/delvedor/find-my-way), so you can refer its documentation for the url building.

Example:
```js
fastify.route({
  method: 'GET',
  url: '/',
  schema: {
    querystring: {
      name: { type: 'string' },
      excitement: { type: 'integer' }
    },
    response: {
      200: {
        type: 'object',
        properties: {
          hello: { type: 'string' }
        }
      }
    }
  },
  handler: function (request, reply) {
    reply.send({ hello: 'world' })
  }
})
```

```js
fastify.route({
  method: 'GET',
  url: '/',
  schema: { ... },
  beforeHandler: function (request, reply, done) {
    // your authentication logic
    done()
  },
  handler: function (request, reply) {
    reply.send({ hello: 'world' })
  }
})
```

### Shorthand declaration
The above route declaration is more *Hapi*-like, but if you prefer an *Express/Restify* approach, we support it as well:
`fastify.get(path, [options], handler)`
`fastify.head(path, [options], handler)`
`fastify.post(path, [options], handler)`
`fastify.put(path, [options], handler)`
`fastify.delete(path, [options], handler)`
`fastify.options(path, [options], handler)`
`fastify.patch(path, [options], handler)`

Example:
```js
const opts = {
  schema: {
    response: {
      200: {
        type: 'object',
        properties: {
          hello: { type: 'string' }
        }
      }
    }
  }
}
fastify.get('/', opts, (req, reply) => {
  reply.send({ hello: 'world' })
})
```

`fastify.all(path, [options], handler)` will add the same handler to all the supported methods.

### Async Await
Are you an `async/await` user? We have you covered!
```js
fastify.get('/', options, async function (request, reply) {
  var data = await getData()
  var processed = await processData(data)
  return processed
})
```
As you can see we are not calling `reply.send` to send back the data to the user. You just need to return the body and you are done!
If you need it you can also send back the data to the user with `reply.send`.
```js
fastify.get('/', options, async function (request, reply) {
  var data = await getData()
  var processed = await processData(data)
  reply.send(processed)
})
```
**Warning:** If you use `return` and `reply.send` at the same time, the first one that happens takes precedence, the second value will be discarded, a *warn* log will also be emitted if the value is not `undefined`.

### Route Prefixing
Sometimes you need to maintain two or more different versions of the same api, a classic approach is to prefix all the routes with the api version number, `/v1/user` for example.
Fastify offers you a fast and smart way to create different version of the same api without changing all the route names by hand, *route prefixing*. Let's see how it works:

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

fastify.register(require('./routes/v1/users'), { prefix: '/v1' })
fastify.register(require('./routes/v2/users'), { prefix: '/v2' })

fastify.listen(3000)
```
```js
// routes/v1/users.js
module.exports = function (fastify, opts, next) {
  fastify.get('/user', handler_v1)
  next()
}
```
```js
// routes/v2/users.js
module.exports = function (fastify, opts, next) {
  fastify.get('/user', handler_v2)
  next()
}
```
Fastify will not complain because you are using the same name for two different routes, because at compilation time it will handle the prefix automatically *(this also means that the performance will not be affected at all!)*.

Now your clients will have access to the following routes:
- `/v1/user`
- `/v2/user`

You can do this as many times as you want, it works also for nested `register` and routes parameter are supported as well.
Be aware that if you use [`fastify-plugin`](https://github.com/fastify/fastify-plugin) this option won't work.


} else if (context.contentTypeParser.fastHasHeader(req.headers['content-type'])) {

setImmediate(wrapReplyEnd, context, req, res, 415, params, null, urlUtil.parse(req.url, true).query)

return handler(context, params, req, res, null, urlUtil.parse(req.url, true).query)

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

const t = require('tap')
const test = t.test
const internals = require('../../lib/handleRequest')[Symbol.for('internals')]
const Request = require('../../lib/request')
const Reply = require('../../lib/reply')
const buildSchema = require('../../lib/validation').build
const Hooks = require('../../lib/hooks')
const sget = require('simple-get').concat

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

function schemaCompiler (schema) {
  const validateFuncion = ajv.compile(schema)
  return function (body) {
    const isOk = validateFuncion(body)
    if (isOk) return
    return { error: new Error('Invalid body') }
  }
}

test('Request object', t => {
  t.plan(7)
  const req = new Request('params', 'req', 'body', 'query', 'headers', 'log')
  t.type(req, Request)
  t.equal(req.params, 'params')
  t.deepEqual(req.req, 'req')
  t.equal(req.body, 'body')
  t.equal(req.query, 'query')
  t.equal(req.headers, 'headers')
  t.equal(req.log, 'log')
})

test('handler function - invalid schema', t => {
  t.plan(2)
  const res = {}
  res.end = () => {
    t.equal(res.statusCode, 400)
    t.pass()
  }
  res.setHeader = (key, value) => {
    return
  }
  res.getHeader = (key) => {
    return
  }
  const handle = {
    schema: {
      body: {
        type: 'object',
        properties: {
          hello: { type: 'number' }
        }
      }
    },
    handler: () => {},
    Reply: Reply,
    Request: Request,
    preHandler: new Hooks().preHandler,
    onSend: new Hooks().onSend
  }
  buildSchema(handle, schemaCompiler)
  internals.handler(handle, null, { log: { error: () => {} } }, res, { hello: 'world' }, null)
})

test('handler function - reply', t => {
  t.plan(3)
  const res = {}
  res.end = () => {
    t.equal(res.statusCode, 204)
    t.pass()
  }
  res.getHeader = (key) => {
    return false
  }
  res.setHeader = (key, value) => {
    return
  }
  const handle = {
    handler: (req, reply) => {
      t.is(typeof reply, 'object')
      reply.code(204)
      reply.send(undefined)
    },
    Reply: Reply,
    Request: Request,
    preHandler: new Hooks().preHandler,
    onSend: new Hooks().onSend
  }
  buildSchema(handle, schemaCompiler)
  internals.handler(handle, null, { log: null }, res, null, null)
})

test('jsonBody and jsonBodyParsed should be functions', t => {
  t.plan(4)

  t.is(typeof internals.jsonBody, 'function')
  t.is(internals.jsonBody.length, 4)

  t.is(typeof internals.jsonBodyParsed, 'function')
  t.is(internals.jsonBodyParsed.length, 6)
})

test('jsonBody error handler', t => {
  t.plan(1)

  try {
    internals.jsonBody({ on: 'error' }, {})
    t.fail('jsonBody error')
  } catch (e) {
    t.pass('jsonBody error')
  }
})

test('request should be defined in onSend Hook on post request with content type application/json', t => {
  t.plan(3)
  const fastify = require('../..')()

  fastify.addHook('onSend', (request, reply, payload, done) => {
    if (!request) {
      reply.code(500)
    }
    done()
  })
  fastify.post('/', (request, reply) => {
    reply.send(200)
  })
  fastify.listen(0, err => {
    fastify.server.unref()
    t.error(err)
    sget({
      method: 'POST',
      url: 'http://localhost:' + fastify.server.address().port,
      headers: {
        'content-type': 'application/json'
      }
    }, (err, response, body) => {
      t.error(err)
      t.strictEqual(response.statusCode, 422)
    })
  })
})

test('request should be defined in onSend Hook on post request with content type application/x-www-form-urlencoded', t => {
  t.plan(3)
  const fastify = require('../..')()

  fastify.addHook('onSend', (request, reply, payload, done) => {
    if (!request) {
      reply.code(500)
    }
    done()
  })
  fastify.post('/', (request, reply) => {
    reply.send(200)
  })
  fastify.listen(0, err => {
    fastify.server.unref()
    t.error(err)
    sget({
      method: 'POST',
      url: 'http://localhost:' + fastify.server.address().port,
      headers: {
        'content-type': 'application/x-www-form-urlencoded'
      }
    }, (err, response, body) => {
      t.error(err)
      t.strictEqual(response.statusCode, 415)
    })
  })
})

test('request should be defined in onSend Hook on options request with content type application/x-www-form-urlencoded', t => {
  t.plan(3)
  const fastify = require('../..')()

  fastify.addHook('onSend', (request, reply, payload, done) => {
    if (!request) {
      reply.code(500)
    }
    done()
  })
  fastify.options('/', (request, reply) => {
    reply.send(200)
  })
  fastify.listen(0, err => {
    fastify.server.unref()
    t.error(err)
    sget({
      method: 'OPTIONS',
      url: 'http://localhost:' + fastify.server.address().port,
      headers: {
        'content-type': 'application/x-www-form-urlencoded'
      }
    }, (err, response, body) => {
      t.error(err)
      t.strictEqual(response.statusCode, 415)
    })
  })
})


'use strict'

const t = require('tap')
const test = t.test
const sget = require('simple-get').concat
const fastify = require('..')()

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

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

test('shorthand - output number', t => {
  t.plan(1)
  try {
    fastify.get('/number', opts, function (req, reply) {
      reply.code(201).send({ hello: 55 })
    })
    t.pass()
  } catch (e) {
    t.fail()
  }
})

test('wrong object for schema - output', t => {
  t.plan(1)
  try {
    fastify.get('/wrong-object-for-schema', opts, function (req, reply) {
      // will send { hello: null }
      reply.code(201).send({ hello: 'world' })
    })
    t.pass()
  } catch (e) {
    t.fail()
  }
})

test('empty response', t => {
  t.plan(1)
  try {
    // no checks
    fastify.get('/empty', opts, function (req, reply) {
      reply.code(204).send()
    })
    t.pass()
  } catch (e) {
    t.fail()
  }
})

test('unlisted response code', t => {
  t.plan(1)
  try {
    fastify.get('/400', opts, function (req, reply) {
      reply.code(400).send({ hello: 'DOOM' })
    })
    t.pass()
  } catch (e) {
    t.fail()
  }
})

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

  test('shorthand - string get ok', t => {
    t.plan(4)
    sget({
      method: 'GET',
      url: 'http://localhost:' + fastify.server.address().port + '/string'
    }, (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('shorthand - number get ok', t => {
    t.plan(4)
    sget({
      method: 'GET',
      url: 'http://localhost:' + fastify.server.address().port + '/number'
    }, (err, response, body) => {
      t.error(err)
      t.strictEqual(response.statusCode, 201)
      t.strictEqual(response.headers['content-length'], '' + body.length)
      t.deepEqual(JSON.parse(body), { hello: 55 })
    })
  })

  test('shorthand - wrong-object-for-schema', t => {
    t.plan(4)
    sget({
      method: 'GET',
      url: 'http://localhost:' + fastify.server.address().port + '/wrong-object-for-schema'
    }, (err, response, body) => {
      t.error(err)
      t.strictEqual(response.statusCode, 201)
      t.strictEqual(response.headers['content-length'], '' + body.length)
      t.deepEqual(JSON.parse(body), { hello: null })
    })
  })

  test('shorthand - empty', t => {
    t.plan(2)
    sget({
      method: 'GET',
      url: 'http://localhost:' + fastify.server.address().port + '/empty'
    }, (err, response, body) => {
      t.error(err)
      t.strictEqual(response.statusCode, 204)
    })
  })

  test('shorthand - 400', t => {
    t.plan(4)
    sget({
      method: 'GET',
      url: 'http://localhost:' + fastify.server.address().port + '/400'
    }, (err, response, body) => {
      t.error(err)
      t.strictEqual(response.statusCode, 400)
      t.strictEqual(response.headers['content-length'], '' + body.length)
      t.deepEqual(JSON.parse(body), { hello: 'DOOM' })
    })
  })
})


'use strict'

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

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

fastify.register(function (instance, options, next) {
  // the route will be '/english/hello'
  instance.get('/hello', opts, (req, reply) => {
    reply.send({ greet: 'hello' })
  })
  next()
}, { prefix: '/english' })

fastify.register(function (instance, options, next) {
  // the route will be '/italian/hello'
  instance.get('/hello', opts, (req, reply) => {
    reply.send({ greet: 'ciao' })
  })
  next()
}, { prefix: '/italian' })

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


'use strict'

const t = require('tap')
const test = t.test
const Fastify = require('../..')
const h2url = require('h2url')
const msg = { hello: 'world' }

var fastify
try {
  fastify = Fastify({
    http2: true
  })
  t.pass('http2 successfully loaded')
} catch (e) {
  t.fail('http2 loading failed', e)
}

fastify.get('/', function (req, reply) {
  reply.code(200).send(msg)
})

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

  test('http get request', async (t) => {
    t.plan(3)

    const url = `http://localhost:${fastify.server.address().port}`
    const res = await h2url.concat({ url })

    t.strictEqual(res.headers[':status'], 200)
    t.strictEqual(res.headers['content-length'], '' + JSON.stringify(msg).length)

    t.deepEqual(JSON.parse(res.body), msg)
  })
})


setImmediate(wrapReplyEnd, context, req, res, 405, params, null, urlUtil.parse(req.url, true).query)

keep keep keep keep keep replace keep keep keep

setImmediate(wrapReplyEnd, context, req, res, 405, params, null, urlUtil.parse(req.url, true).query, null)


'use strict'

const t = require('tap')
const test = t.test
const fs = require('fs')
const path = require('path')
const Fastify = require('../..')
const h2url = require('h2url')
const msg = { hello: 'world' }

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

fastify.get('/', function (req, reply) {
  reply.code(200).send(msg)
})

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

  test('https get request', async (t) => {
    t.plan(3)

    const url = `https://localhost:${fastify.server.address().port}`
    const res = await h2url.concat({ url })

    t.strictEqual(res.headers[':status'], 200)
    t.strictEqual(res.headers['content-length'], '' + JSON.stringify(msg).length)
    t.deepEqual(JSON.parse(res.body), msg)
  })
})


Fastify

## Reply
The second parameter of the handler function is `Reply`.
Reply is a core Fastify object that exposes the following functions:

- `.code(statusCode)` - Sets the status code.
- `.header(name, value)` - Sets the headers.
- `.type(value)` - Sets the header `Content-Type`.
- `.redirect([code,] url)` - Redirect to the specified url, the status code is optional (default to `302`).
- `.serializer(function)` - Sets a custom serializer for the payload.
- `.send(payload)` - Sends the payload to the user, could be a plain text, JSON, stream, or an Error object.
- `.sent` - A boolean value that you can use if you need to know it `send` has already been called.

```js
fastify.get('/', options, function (request, reply) {
  // You code
  reply
    .code(200)
    .header('Content-Type', 'application/json')
    .send({ hello: 'world' })
})
```

### Code
If not set via `reply.code`, the resulting `statusCode` will be `200`.

### Header
Sets a custom header to the response.
If you not set a `'Content-Type'` header, Fastify assumes that you are using `'application/json'`, unless you are send a stream, in that cases Fastify recognize it and sets the `'Content-Type'` at `'application/octet-stream'`.

*Note that if you are using a custom serializer that does not serialize to JSON, you must set a custom `'Content-Type'` header.*

### Redirect
Redirects a request to the specified url, the status code is optional, default to `302`.
```js
reply.redirect('/home')
```

### Type
Sets the content type for the response.
This is a shortcut for `reply.header('Content-Type', 'the/type')`.

```js
reply.type('text/html')
```

### Serializer
Fastify was born as a full JSON compatible server, so out of the box will serialize your payload that you put in the `.send()` function using the internal serializers, [fast-json-stringify](https://www.npmjs.com/package/fast-json-stringify) if you setted an output schema, otherwise [fast-safe-stringify](https://www.npmjs.com/package/fast-safe-stringify).

If you need to use a custom serializer, such as [msgpack5](https://github.com/mcollina/msgpack5) or [protobuf](https://github.com/dcodeIO/ProtoBuf.js/), you can use the `.serializer()` utility. As noted above, if you are using a custom serializer that does not serialize to JSON, you must set a custom `'Content-Type'` header.

```js
reply
  .header('Content-Type', 'application/x-protobuf')
  .serializer(protoBuf.serialize)
```
*Take a look [here](https://github.com/fastify/fastify/blob/master/docs/Validation-and-Serialization.md#serialization) to understand how serialization is done.*

### Send
 As the name suggests, `.send()` is the function that sends the payload to the end user.

#### Objects
As writed above, if you are sending JSON objects, *send* will serialize the object with [fast-json-stringify](https://www.npmjs.com/package/fast-json-stringify) if you setted an output schema, otherwise [fast-safe-stringify](https://www.npmjs.com/package/fast-safe-stringify).
```js
fastify.get('/json', options, function (request, reply) {
  reply.send({ hello: 'world' })
})
```

#### Promises
*send* handle natively the *promises* and supports out of the box *async-await*.
```js
fastify.get('/promises', options, function (request, reply) {
  const promise = new Promise(...)
  reply
    .code(200)
    .header('Content-Type', 'application/json')
    .send(promise)
})

fastify.get('/async-await', options, async function (request, reply) {
  var res = await new Promise(function (resolve) {
    setTimeout(resolve, 200, { hello: 'world' })
  })
  // note that we are returning the promise without using '.send()'
  return res
})
```

Rejected promises default to a `500` HTTP status code. Reject the promise, or `throw` in an `async function`, with an object that has `statusCode` (or `status`) and `message` properties to modify the reply.

```js
fastify.get('/teapot', async function (request, reply) => {
  const err = new Error()
  err.statusCode = 418
  err.message = 'short and stout'
  throw err
})
```

If you want to know more please review [Routes#async-await](https://github.com/fastify/fastify/blob/master/docs/Routes.md#async-await)!

#### Streams
*send* can also handle streams out of the box, internally uses [pump](https://www.npmjs.com/package/pump) to avoid leaks of file descriptors. If you are sending a stream and you have not setted a `'Content-Type'` header, *send* will set it at `'application/octet-stream'`.
```js
fastify.get('/streams', function (request, reply) {
  const fs = require('fs')
  const stream = fs.createReadStream('some-file', 'utf8')
  reply.send(stream)
})
```

#### Errors
If you pass to *send* an object that is an instance of *Error*, Fastify will automatically create an error structured as the following:
```js
{
  error: String        // the http error message
  message: String      // the user error message
  statusCode: Number   // the http status code
}
```
If you want it extend this error, check out [`extendServerError`](https://github.com/fastify/fastify/blob/master/docs/Decorators.md#extend-server-error).

*If you are passing an error to send and the statusCode is less than 400, Fastify will automatically set it at 500.*

#### Type of the final payload
It is crucial that the sent payload is a `string` or a `Buffer`, otherwise *send* will throw at runtime.


'use strict'

const fp = require('fastify-plugin')

module.exports = fp(function (fastify, opts, next) {
  fastify.decorate('test', () => {})
  next()
})


function jsonBodyParsed (err, body, req, res, params, context) {

const query = urlUtil.parse(req.url, true).query

var query = urlUtil.parse(req.url, true).query


'use strict'

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

test('default 500', t => {
  t.plan(3)

  const fastify = Fastify()

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

  fastify.inject({
    method: 'GET',
    url: '/'
  }, (res) => {
    t.strictEqual(res.statusCode, 500)
    t.strictEqual(res.headers['content-type'], 'application/json')
    t.deepEqual(JSON.parse(res.payload), {
      error: 'Internal Server Error',
      message: 'kaboom',
      statusCode: 500
    })
  })
})

test('custom 500', t => {
  t.plan(3)

  const fastify = Fastify()

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

  fastify.setErrorHandler(function (err, reply) {
    reply.type('text/plain').send('an error happened: ' + err.message)
  })

  fastify.inject({
    method: 'GET',
    url: '/'
  }, (res) => {
    t.strictEqual(res.statusCode, 500)
    t.strictEqual(res.headers['content-type'], 'text/plain')
    t.deepEqual(res.payload.toString(), 'an error happened: kaboom')
  })
})

test('encapsulated 500', t => {
  t.plan(6)

  const fastify = Fastify()

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

  fastify.register(function (f, opts, next) {
    f.get('/', function (req, reply) {
      reply.send(new Error('kaboom'))
    })

    f.setErrorHandler(function (err, reply) {
      reply.type('text/plain').send('an error happened: ' + err.message)
    })

    next()
  }, { prefix: 'test' })

  fastify.inject({
    method: 'GET',
    url: '/test'
  }, (res) => {
    t.strictEqual(res.statusCode, 500)
    t.strictEqual(res.headers['content-type'], 'text/plain')
    t.deepEqual(res.payload.toString(), 'an error happened: kaboom')
  })

  fastify.inject({
    method: 'GET',
    url: '/'
  }, (res) => {
    t.strictEqual(res.statusCode, 500)
    t.strictEqual(res.headers['content-type'], 'application/json')
    t.deepEqual(JSON.parse(res.payload), {
      error: 'Internal Server Error',
      message: 'kaboom',
      statusCode: 500
    })
  })
})

test('custom 500 with hooks', t => {
  t.plan(6)

  const fastify = Fastify()

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

  fastify.setErrorHandler(function (err, reply) {
    reply.type('text/plain').send('an error happened: ' + err.message)
  })

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

  fastify.inject({
    method: 'GET',
    url: '/'
  }, (res) => {
    t.strictEqual(res.statusCode, 500)
    t.strictEqual(res.headers['content-type'], 'text/plain')
    t.deepEqual(res.payload.toString(), 'an error happened: kaboom')
  })
})


'use strict'

const t = require('tap')
const test = t.test
const fs = require('fs')
const path = require('path')
const Fastify = require('../..')
const h2url = require('h2url')
const sget = require('simple-get').concat
const msg = { hello: 'world' }

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

fastify.get('/', function (req, reply) {
  reply.code(200).send(msg)
})

fastify.post('/', function (req, reply) {
  reply.code(200).send(req.body)
})

fastify.get('/error', async function (req, reply) {
  throw new Error('kaboom')
})

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

  test('https get error', async (t) => {
    t.plan(1)

    const url = `https://localhost:${fastify.server.address().port}/error`
    const res = await h2url.concat({ url })

    t.strictEqual(res.headers[':status'], 500)
  })

  test('https post', async (t) => {
    t.plan(2)

    const url = `https://localhost:${fastify.server.address().port}`
    const res = await h2url.concat({
      url,
      method: 'POST',
      body: JSON.stringify({ hello: 'http2' }),
      headers: {
        'content-type': 'application/json'
      }
    })

    t.strictEqual(res.headers[':status'], 200)
    t.deepEqual(JSON.parse(res.body), { hello: 'http2' })
  })

  test('https get request', async (t) => {
    t.plan(3)

    const url = `https://localhost:${fastify.server.address().port}`
    const res = await h2url.concat({ url })

    t.strictEqual(res.headers[':status'], 200)
    t.strictEqual(res.headers['content-length'], '' + JSON.stringify(msg).length)
    t.deepEqual(JSON.parse(res.body), msg)
  })

  test('http1 get request', t => {
    t.plan(4)
    sget({
      method: 'GET',
      url: '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' })
    })
  })

  test('http1 get error', (t) => {
    t.plan(2)
    sget({
      method: 'GET',
      url: 'https://localhost:' + fastify.server.address().port + '/error',
      rejectUnauthorized: false
    }, (err, response, body) => {
      t.error(err)
      t.strictEqual(response.statusCode, 500)
    })
  })
})


setImmediate(wrapReplyEnd, context, req, res, 422, params, body, query)

handler(context, params, req, res, body, query)

setImmediate(wrapReplyEnd, context, req, res, 422, params, body, query, null)


'use strict'

const t = require('tap')
const test = t.test
const sget = require('simple-get').concat
const Fastify = require('..')

const payload = { hello: 'world' }

test('hooks', t => {
  t.plan(21)
  const fastify = Fastify()

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

  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.addHook('onSend', function (req, reply, thePayload, next) {
    t.ok('onSend 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(payload)
  })

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

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

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

    sget({
      method: 'GET',
      url: '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' })
    })

    sget({
      method: 'HEAD',
      url: 'http://localhost:' + fastify.server.address().port
    }, (err, response, body) => {
      t.error(err)
      t.strictEqual(response.statusCode, 500)
    })

    sget({
      method: 'DELETE',
      url: '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(20)
  const fastify = Fastify()
  fastify.decorate('hello', 'world')

  fastify.addHook('onRequest', function (req, res, next) {
    t.ok(this.hello)
    t.ok(this.hello2)
    req.first = true
    next()
  })

  fastify.decorate('hello2', 'world')

  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.decorate('hello3', 'world')
    instance.addHook('onRequest', function (req, res, next) {
      t.ok(this.hello)
      t.ok(this.hello2)
      t.ok(this.hello3)
      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()

    sget({
      method: 'GET',
      url: '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' })
    })

    sget({
      method: 'GET',
      url: '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(17)
  const fastify = Fastify()
  fastify.decorate('hello', 'world')

  fastify.addHook('preHandler', function (req, res, next) {
    t.ok(this.hello)
    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.decorate('hello2', 'world')
    instance.addHook('preHandler', function (req, res, next) {
      t.ok(this.hello)
      t.ok(this.hello2)
      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()

    sget({
      method: 'GET',
      url: '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' })
    })

    sget({
      method: 'GET',
      url: '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(16)
  const fastify = Fastify()
  fastify.decorate('hello', 'world')

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

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

  fastify.register((instance, opts, next) => {
    instance.decorate('hello2', 'world')
    instance.addHook('onResponse', function (res, next) {
      t.ok(this.hello)
      t.ok(this.hello2)
      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()

    sget({
      method: 'GET',
      url: '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' })
    })

    sget({
      method: 'GET',
      url: '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('onSend hook should support encapsulation / 1', t => {
  t.plan(3)
  const fastify = Fastify()

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

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

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

test('onSend hook should support encapsulation / 2', t => {
  t.plan(16)
  const fastify = Fastify()
  fastify.decorate('hello', 'world')

  fastify.addHook('onSend', function (request, reply, thePayload, next) {
    t.ok(this.hello)
    t.ok('onSend called')
    next()
  })

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

  fastify.register((instance, opts, next) => {
    instance.decorate('hello2', 'world')
    instance.addHook('onSend', function (request, reply, thePayload, next) {
      t.ok(this.hello)
      t.ok(this.hello2)
      t.ok('onSend called')
      next()
    })

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

    next()
  })

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

    sget({
      method: 'GET',
      url: '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' })
    })

    sget({
      method: 'GET',
      url: '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('verify payload', t => {
  t.plan(7)
  const fastify = Fastify()
  const payload = { hello: 'world' }
  const modifiedPayload = { hello: 'modified' }

  fastify.addHook('onSend', function (request, reply, thePayload, next) {
    t.ok('onSend called')
    t.deepEqual(thePayload, payload)
    // onSend allows only to modify Object keys and not the full object's reference
    thePayload.hello = 'modified'
    next()
  })

  fastify.addHook('onSend', function (request, reply, thePayload, next) {
    t.ok('onSend called')
    t.deepEqual(thePayload, modifiedPayload)
    next()
  })

  fastify.get('/', (req, reply) => {
    reply.send(payload)
  })

  fastify.inject({
    method: 'GET',
    url: '/'
  }, res => {
    t.deepEqual(modifiedPayload, JSON.parse(res.payload))
    t.strictEqual(res.statusCode, 200)
    t.strictEqual(res.headers['content-length'], 20)
  })
})

test('onSend hook throws', t => {
  t.plan(7)
  const fastify = Fastify()
  fastify.addHook('onSend', function (request, reply, payload, next) {
    if (request.req.method === 'DELETE') {
      next(new Error('some error'))
      return
    }
    next()
  })

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

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

  fastify.listen(0, err => {
    t.error(err)
    fastify.server.unref()
    sget({
      method: 'GET',
      url: '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' })
    })
    sget({
      method: 'DELETE',
      url: 'http://localhost:' + fastify.server.address().port
    }, (err, response, body) => {
      t.error(err)
      t.strictEqual(response.statusCode, 500)
    })
  })
})

if (Number(process.versions.node[0]) >= 8) {
  require('./hooks-async')(t)
} else {
  t.pass('Skip because Node version < 8')
  t.end()
}


'use strict'

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

test('Prefix options should add a prefix for all the routes inside a register / 1', t => {
  t.plan(3)
  const fastify = Fastify()

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

  fastify.register(function (fastify, opts, next) {
    fastify.get('/first', (req, reply) => {
      reply.send({ route: '/v1/first' })
    })

    fastify.register(function (fastify, opts, next) {
      fastify.get('/first', (req, reply) => {
        reply.send({ route: '/v1/v2/first' })
      })
      next()
    }, { prefix: '/v2' })

    next()
  }, { prefix: '/v1' })

  fastify.inject({
    method: 'GET',
    url: '/first'
  }, res => {
    t.same(JSON.parse(res.payload), { route: '/first' })
  })

  fastify.inject({
    method: 'GET',
    url: '/v1/first'
  }, res => {
    t.same(JSON.parse(res.payload), { route: '/v1/first' })
  })

  fastify.inject({
    method: 'GET',
    url: '/v1/v2/first'
  }, res => {
    t.same(JSON.parse(res.payload), { route: '/v1/v2/first' })
  })
})

test('Prefix options should add a prefix for all the routes inside a register / 2', t => {
  t.plan(2)
  const fastify = Fastify()

  fastify.register(function (fastify, opts, next) {
    fastify.get('/first', (req, reply) => {
      reply.send({ route: '/v1/first' })
    })

    fastify.get('/second', (req, reply) => {
      reply.send({ route: '/v1/second' })
    })
    next()
  }, { prefix: '/v1' })

  fastify.inject({
    method: 'GET',
    url: '/v1/first'
  }, res => {
    t.same(JSON.parse(res.payload), { route: '/v1/first' })
  })

  fastify.inject({
    method: 'GET',
    url: '/v1/second'
  }, res => {
    t.same(JSON.parse(res.payload), { route: '/v1/second' })
  })
})

test('Prefix options should add a prefix for all the chained routes inside a register / 3', t => {
  t.plan(2)

  const fastify = Fastify()

  fastify.register(function (fastify, opts, next) {
    fastify
      .get('/first', (req, reply) => {
        reply.send({ route: '/v1/first' })
      })
      .get('/second', (req, reply) => {
        reply.send({ route: '/v1/second' })
      })
    next()
  }, { prefix: '/v1' })

  fastify.inject({
    method: 'GET',
    url: '/v1/first'
  }, res => {
    t.same(JSON.parse(res.payload), { route: '/v1/first' })
  })

  fastify.inject({
    method: 'GET',
    url: '/v1/second'
  }, res => {
    t.same(JSON.parse(res.payload), { route: '/v1/second' })
  })
})

test('Prefix should support parameters as well', t => {
  t.plan(1)
  const fastify = Fastify()

  fastify.register(function (fastify, opts, next) {
    fastify.get('/hello', (req, reply) => {
      reply.send({ id: req.params.id })
    })
    next()
  }, { prefix: '/v1/:id' })

  fastify.inject({
    method: 'GET',
    url: '/v1/param/hello'
  }, res => {
    t.same(JSON.parse(res.payload), { id: 'param' })
  })
})

test('Prefix should support /', t => {
  t.plan(1)
  const fastify = Fastify()

  fastify.register(function (fastify, opts, next) {
    fastify.get('/', (req, reply) => {
      reply.send({ hello: 'world' })
    })
    next()
  }, { prefix: '/v1' })

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

test('Prefix without /', t => {
  t.plan(1)
  const fastify = Fastify()

  fastify.register(function (fastify, opts, next) {
    fastify.get('/', (req, reply) => {
      reply.send({ hello: 'world' })
    })
    next()
  }, { prefix: 'v1' })

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


'use strict'

const t = require('tap')
const test = t.test
const sget = require('simple-get').concat
const fastify = require('..')()

const schema = {
  schema: {
    response: {
      '2xx': {
        type: 'null'
      }
    }
  }
}

const querySchema = {
  schema: {
    querystring: {
      type: 'object',
      properties: {
        hello: {
          type: 'integer'
        }
      }
    }
  }
}

const paramsSchema = {
  schema: {
    params: {
      type: 'object',
      properties: {
        foo: {
          type: 'string'
        },
        test: {
          type: 'integer'
        }
      }
    }
  }
}

test('shorthand - head', t => {
  t.plan(1)
  try {
    fastify.head('/', schema, function (req, reply) {
      reply.code(200).send(null)
    })
    t.pass()
  } catch (e) {
    t.fail()
  }
})

test('shorthand - head params', t => {
  t.plan(1)
  try {
    fastify.head('/params/:foo/:test', paramsSchema, function (req, reply) {
      reply.send(null)
    })
    t.pass()
  } catch (e) {
    t.fail()
  }
})

test('shorthand - head, querystring schema', t => {
  t.plan(1)
  try {
    fastify.head('/query', querySchema, function (req, reply) {
      reply.code(200).send(null)
    })
    t.pass()
  } catch (e) {
    t.fail()
  }
})

test('missing schema - head', t => {
  t.plan(1)
  try {
    fastify.head('/missing', function (req, reply) {
      reply.code(200).send(null)
    })
    t.pass()
  } catch (e) {
    t.fail()
  }
})

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

  test('shorthand - request head', t => {
    t.plan(2)
    sget({
      method: 'HEAD',
      url: 'http://localhost:' + fastify.server.address().port
    }, (err, response) => {
      t.error(err)
      t.strictEqual(response.statusCode, 200)
    })
  })

  test('shorthand - request head params schema', t => {
    t.plan(2)
    sget({
      method: 'HEAD',
      url: 'http://localhost:' + fastify.server.address().port + '/params/world/123'
    }, (err, response) => {
      t.error(err)
      t.strictEqual(response.statusCode, 200)
    })
  })

  test('shorthand - request head params schema error', t => {
    t.plan(2)
    sget({
      method: 'HEAD',
      url: 'http://localhost:' + fastify.server.address().port + '/params/world/string'
    }, (err, response) => {
      t.error(err)
      t.strictEqual(response.statusCode, 400)
    })
  })

  test('shorthand - request head querystring schema', t => {
    t.plan(2)
    sget({
      method: 'HEAD',
      url: 'http://localhost:' + fastify.server.address().port + '/query?hello=123'
    }, (err, response) => {
      t.error(err)
      t.strictEqual(response.statusCode, 200)
    })
  })

  test('shorthand - request head querystring schema error', t => {
    t.plan(2)
    sget({
      method: 'HEAD',
      url: 'http://localhost:' + fastify.server.address().port + '/query?hello=world'
    }, (err, response) => {
      t.error(err)
      t.strictEqual(response.statusCode, 400)
    })
  })

  test('shorthand - request head missing schema', t => {
    t.plan(2)
    sget({
      method: 'HEAD',
      url: 'http://localhost:' + fastify.server.address().port + '/missing'
    }, (err, response) => {
      t.error(err)
      t.strictEqual(response.statusCode, 200)
    })
  })
})


fastify.addHook('onSend', (request, reply, payload, done) => {

if (!request || !request.req || !request.params || !request.query) {


'use strict'

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


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

branchcmp -r 2 -g -s "npm run benchmark"

npx concurrently -k -s first "node ./examples/simple.js" "npx autocannon -c 100 -d 5 -p 10 localhost:3000/"


Fastify

# The hitchhiker's guide to plugins
First of all, `DON'T PANIC`!

Fastify has been built since the beginning to be an extremely modular system, we built a powerful api that allows you to add methods and utilities to Fastify by creating a namespace, we built a system that creates an encapsulation model that allows you to split you application in multiple microservices at any moment, without the need to refactor the entire application.

**Table of contents**
- [Register](#register)
- [Decorators](#decorators)
- [Hooks](#hooks)
- [Middlewares](#middlewares)
- [How to handle encapsulation and distribution](#distribution)
- [Let's start!](#start)

## Register
As in JavaScript everything is an object, in Fastify everything is a plugin.
Your routes, your utilities and so on are all plugins. And to add a new plugin, whatever its functionality is, in Fastify you have a nice and unique api to use: [`register`](https://github.com/fastify/fastify/blob/master/docs/Plugins.md).
```js
fastify.register(
  require('./my-plugin'),
  { options },
  callback
)
```
`register` creates for you a new Fastify context, this means that if you do any change to the Fastify instance, that change(s) will not be reflected into the context's ancestors. In other words, encapsulation!

*Why encapsulation is important?*
Well, let's say you are creating a new disruptive startup, what do you do? You create an api server with all your stuff, everything in the same place, a monolith!
Ok, you are growing very fast and you want to change your architecture and try microservices. Usually this implies an huge amount of work, because of cross dependencies and the lack of separation of concerns.
Fastify helps you a lot in this direction, because thanks to the encapsulation model it will completely avoid cross dependencies, and will help you structure your code in cohesive blocks.

*Let's return to how to use correctly `register`.*
As you probably know, the required plugins must expose a single function with the following signature
```js
module.exports = function (fastify, options, next) {}
```
Where `fastify` is (pretty obvious) the encapsulated Fastify instance, `options` is the options object and `next` is the function you **must** call when you plugin is ready.

The Fastify's plugin model is fully reentrant and graph-based, it handles without any kind of problem asynchronous code and it guarantees the load order of the plugins, even the close order! *How?* Glad you asked, checkout [`avvio`](https://github.com/mcollina/avvio)!

Inside a plugin you can do whatever you want, register routes, utilities (we'll see this in a moment) and do nested registers, just remember to call `next` when everything is set up!
```js
module.exports = function (fastify, options, next) {
  fastify.get('/plugin', (req, reply) => {
    reply.send({ hello: 'world' })
  })

  next()
}
```

Well, now you know how to use the `register` api and how it works, but how add new functionalities to fastify and even better, share them with other developers?

## Decorators
Okay, let's say that you wrote an utility that is so good that you decided to make it available along all your code. How would you do it? Probably something like the following:
```js
// your-awesome-utility.js
module.exports = function (a, b) {
  return a + b
}
```
```js
const util = require('./your-awesome-utility')
console.log(util('that is ', ' awesome'))
```
And now you will import your utility in every file you need it. (and don't forget that probably you will need it also in your test).

Fastify offers you a way nicer and elegant way to do this, *decorators*.
Create a decorator is extremely easy, just use the [`decorate`](https://github.com/fastify/fastify/blob/master/docs/Decorators.md) api:
```js
fastify.decorate('util', (a, b) => a + b)
```
Now you can access your utility just by doing `fastify.util` whenever you need it, even inside your test.
And here's starts the magic; do you remember that few lines above we talked about encapsulation? Well, use `register` and `decorate` in conjunction enable exactly that, let me show you an example to clarify this:
```js
fastify.register((instance, opts, next) => {
  instance.decorate('util', (a, b) => a + b)
  console.log(instance.util('that is ', ' awesome'))

  next()
})

fastify.register((instance, opts, next) => {
  console.log(instance.util('that is ', ' awesome')) // this will throw an error

  next()
})
```
Inside the second register call `instance.util` will throw an error, because `util` exist only inside the first register context.
Let's step back for a moment and get deepen on this: when using the `register` api you will create a new context every time and this avoid situations like the one mentioned few line above. But pay attention, the encapsulation works only for the ancestors and the brothers, but not for the sons.
```js
fastify.register((instance, opts, next) => {
  instance.decorate('util', (a, b) => a + b)
  console.log(instance.util('that is ', ' awesome'))

  fastify.register((instance, opts, next) => {
    console.log(instance.util('that is ', ' awesome')) // this will not throw an error
    next()
  })

  next()
})

fastify.register((instance, opts, next) => {
  console.log(instance.util('that is ', ' awesome')) // this will throw an error

  next()
})
```
*Take home message: if you need that an utility is available in every part of your application, pay attention that is declared at the root scope of your application. Otherwise you can use `fastify-plugin` utility as described [here](#distribution).*

`decorate` is not the unique api that you can use to extend the server functionalities, you can also use `decorateRequest` and `decorateReply`.

*`decorateRequest` and `decorateReply`? Why do we need them if we already have `decorate`?*
Good question, we added them to make Fastify more developer-friendly. Let's see an example:
```js
fastify.decorate('html', payload => {
  return generateHtml(payload)
})

fastify.get('/html', (req, reply) => {
  reply
    .type('text/html')
    .send(fastify.html({ hello: 'world' }))
})
```
It works, but it can be way better!
```js
fastify.decorateReply('html', function (payload) {
  this.type('text/html') // this is the 'Reply' object
  this.send(generateHtml(payload))
})

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

And in the same way you can do this for the `request` object:
```js
fastify.decorate('getHeader', (req, header) => {
  return req.headers[header]
})

fastify.addHook('preHandler', (req, reply, done) => {
  req.isHappy = fastify.getHeader(req.req, 'happy')
  done()
})

fastify.get('/happiness', (req, reply) => {
  reply.send({ happy: req.isHappy })
})
```
Again, it works, but it can be way better!
```js
fastify.decorateRequest('setHeader', function (header) {
  this.isHappy = this.req.headers[header]
})

fastify.decorateRequest('isHappy', false) // this will be added to the Request object prototype, yay speed!

fastify.addHook('preHandler', (req, reply, done) => {
  req.setHeader('happy')
  done()
})

fastify.get('/happiness', (req, reply) => {
  reply.send({ happy: req.isHappy })
})
```

We've seen how extend server functionalities and how handle the encapsulation system, but what if you need to add a function that must be executed every time that the server "[emits](https://github.com/fastify/fastify/blob/master/docs/Lifecycle.md)" an event?

## Hooks
You just built an amazing utility, but now you need to execute that for every request, this is what you will likely do:
```js
fastify.decorate('util', (req, key, value) => { req.key = value })

fastify.get('/plugin1', (req, reply) => {
  fastify.util(req, 'timestamp', new Date())
  reply.send(req)
})

fastify.get('/plugin2', (req, reply) => {
  fastify.util(req, 'timestamp', new Date())
  reply.send(req)
})
```
I think we all agree that this is terrible. Code repeat, awful readability and it cannot scale.

So what can you do to avoid this annoying issue? Yes, you are right, use an [hook](https://github.com/fastify/fastify/blob/master/docs/Hooks.md)!
```js
fastify.decorate('util', (req, key, value) => { req.key = value })

fastify.addHook('preHandler', (req, reply, done) => {
  fastify.util(req, 'timestamp', new Date())
  done()
})

fastify.get('/plugin1', (req, reply) => {
  reply.send(req)
})

fastify.get('/plugin2', (req, reply) => {
  reply.send(req)
})
```
Now for every request you will run your utility, it is obvious that you can register as many hooks as you need.
It can happen that you want a hook that must be executed just for a subset of routes, how can you do that?  Yep, encapsulation!

```js
fastify.register((instance, opts, next) => {
  instance.decorate('util', (req, key, value) => { req.key = value })

  instance.addHook('preHandler', (req, reply, done) => {
    instance.util(req, 'timestamp', new Date())
    done()
  })

  instance.get('/plugin1', (req, reply) => {
    reply.send(req)
  })

  next()
})

fastify.get('/plugin2', (req, reply) => {
  reply.send(req)
})
```
Now your hook will run just for the first route!

As you probably noticed at this time, `request` and `reply` are not the standard Nodejs *request* and *response* objects, but Fastify's objects.
Let's say that you are arriving from a framework like Express or Restify, and you already have some Middleware that do exactly what you need, and you don't want to redo all the work.

## Middlewares
Fastify [supports](https://github.com/fastify/fastify/blob/master/docs/Middlewares.md) out of the box Express/Restify/Connect middlewares, this means that you can just drop-in your old code and it will work! *(faster, by the way)*
How we can do that? Checkout our middlewares engine, [middie](https://github.com/fastify/middie).
```js
const yourMiddleware = require('your-middleware')
fastify.use(yourMiddleware)
```

## How to handle encapsulation and distribution
Perfect, now you know (almost) all the tools that you can use to extend Fastify. But probably there is something you noted when trying out your code.
How can you distribute your code?

The preferred way to distribute an utility is to wrap all your code inside a `register`, in this way your plugin can support an asynchronous bootstrap *(since `decorate` is a synchronous api)*, in the case of a database connection for example.

*Wait, what? Don't you told me that `register` creates and encapsulation and what I create inside there will not be available outside?*
Yes, I told that. But what I didn't told you, is that you can tell to Fastify to avoid this behavior, with the [`fastify-plugin`](https://github.com/fastify/fastify-plugin) module.
```js
const fp = require('fastify-plugin')
const dbClient = require('db-client')

function dbPlugin (fastify, opts, next) {
  dbClient.connect(opts.url, (err, conn) => {
    fastify.decorate('db', conn)
    next()
  })
}

module.exports = fp(dbPlugin)
```
You can also tell to `fastify-plugin` to check the installed version of Fastify, in case of you need a specific api.

## Let's start!
Awesome, now you know everything you need to know about Fastify and its plugin system to start building your first plugin, and please if you do, tell us! We will add it to the [*ecosystem*](https://github.com/fastify/fastify#ecosystem) section of our documentation!

If you want to see some real world example, checkout:
- [`point-of-view`](https://github.com/fastify/point-of-view)
Templates rendering (*ejs, pug, handlebars, marko*) plugin support for 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-helmet`](https://github.com/fastify/fastify-helmet) Important security headers for Fastify

*Do you feel it's missing something here? Let us know! :)*


keep keep keep keep replace keep keep keep keep keep


'use strict'

function decorate (name, fn, dependencies) {
  if (checkExistence(this, name)) {
    throw new Error(`The decorator '${name}' has been already added!`)
  }

  if (dependencies) {
    checkDependencies(this, dependencies)
  }

  this[name] = fn
  return this
}

function checkExistence (instance, name) {
  if (!name) {
    name = instance
    instance = this
  }
  return name in instance
}

function checkExistenceInPrototype (klass, name) {
  return name in klass.prototype
}

function checkDependencies (instance, deps) {
  for (var i = 0; i < deps.length; i++) {
    if (!checkExistence(instance, deps[i])) {
      throw new Error(`Fastify decorator: missing dependency: '${deps[i]}'.`)
    }
  }
}

function decorateReply (name, fn, dependencies) {
  if (checkExistenceInPrototype(this._Reply, name)) {
    throw new Error(`The decorator '${name}' has been already added to Reply!`)
  }

  if (dependencies) {
    checkDependencies(this._Reply, dependencies)
  }

  this._Reply.prototype[name] = fn
  return this
}

function extendServerError (fn) {
  if (typeof fn !== 'function') {
    throw new TypeError('The server error object must be a function')
  }

  this._Reply.prototype['_extendServerError'] = fn
  return this
}

function decorateRequest (name, fn, dependencies) {
  if (checkExistenceInPrototype(this._Request, name)) {
    throw new Error(`The decorator '${name}' has been already added to Request!`)
  }

  if (dependencies) {
    checkDependencies(this._Request, dependencies)
  }

  this._Request.prototype[name] = fn
  return this
}

module.exports = {
  add: decorate,
  exist: checkExistence,
  dependencies: checkDependencies,
  decorateReply: decorateReply,
  decorateRequest: decorateRequest,
  extendServerError: extendServerError
}


'use strict'

const t = require('tap')
const test = t.test
const sget = require('simple-get').concat
const Fastify = require('..')

test('register', t => {
  t.plan(33)

  const fastify = Fastify()

  fastify.register(function (instance, opts, done) {
    t.notEqual(instance, fastify)
    t.ok(fastify.isPrototypeOf(instance))

    t.is(typeof opts, 'object')
    t.is(typeof done, 'function')

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

  fastify.register(function (instance, opts, done) {
    t.notEqual(instance, fastify)
    t.ok(fastify.isPrototypeOf(instance))

    t.is(typeof opts, 'object')
    t.is(typeof done, 'function')

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

  const route1 = function (instance, opts, done) {
    t.notEqual(instance, fastify)
    t.ok(fastify.isPrototypeOf(instance))

    t.is(typeof opts, 'object')
    t.is(typeof done, 'function')

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

  const route2 = function (instance, opts, done) {
    t.notEqual(instance, fastify)
    t.ok(fastify.isPrototypeOf(instance))

    t.is(typeof opts, 'object')
    t.is(typeof done, 'function')

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

  fastify.register([route1, route2])

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

    makeRequest('first')
    makeRequest('second')
    makeRequest('third')
    makeRequest('fourth')
  })

  function makeRequest (path) {
    sget({
      method: 'GET',
      url: 'http://localhost:' + fastify.server.address().port + '/' + path
    }, (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('internal route declaration should pass the error generated by the register to the next handler / 1', t => {
  t.plan(1)
  const fastify = Fastify()

  fastify.register((instance, opts, next) => {
    next(new Error('kaboom'))
  })

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

  fastify.listen(0, err => {
    fastify.close()
    t.is(err.message, 'kaboom')
  })
})

test('internal route declaration should pass the error generated by the register to the next handler / 2', t => {
  t.plan(2)
  const fastify = Fastify()

  fastify.register((instance, opts, next) => {
    next(new Error('kaboom'))
  })

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

  fastify.after(err => {
    t.is(err.message, 'kaboom')
  })

  fastify.listen(0, err => {
    fastify.close()
    t.error(err)
  })
})


'use strict'

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

test('405', t => {
  t.plan(1)

  const fastify = Fastify()

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

  const injectOptions = {
    method: 'TRACE',
    url: '/',
    payload: '{}'
  }
  fastify.inject(injectOptions)
    .then(response => {
      t.strictEqual(response.statusCode, 405)
    })
})


keep keep keep replace keep keep keep keep

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

const t = require('tap')
const test = t.test
const internals = require('../../lib/handleRequest')[Symbol.for('internals')]
const Request = require('../../lib/request')
const Reply = require('../../lib/reply')
const buildSchema = require('../../lib/validation').build
const Hooks = require('../../lib/hooks')
const sget = require('simple-get').concat

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

function schemaCompiler (schema) {
  const validateFuncion = ajv.compile(schema)
  return function (body) {
    const isOk = validateFuncion(body)
    if (isOk) return
    return { error: new Error('Invalid body') }
  }
}

test('Request object', t => {
  t.plan(7)
  const req = new Request('params', 'req', 'body', 'query', 'headers', 'log')
  t.type(req, Request)
  t.equal(req.params, 'params')
  t.deepEqual(req.req, 'req')
  t.equal(req.body, 'body')
  t.equal(req.query, 'query')
  t.equal(req.headers, 'headers')
  t.equal(req.log, 'log')
})

test('handler function - invalid schema', t => {
  t.plan(2)
  const res = {}
  res.end = () => {
    t.equal(res.statusCode, 400)
    t.pass()
  }
  res.setHeader = (key, value) => {
    return
  }
  res.getHeader = (key) => {
    return
  }
  const handle = {
    schema: {
      body: {
        type: 'object',
        properties: {
          hello: { type: 'number' }
        }
      }
    },
    handler: () => {},
    Reply: Reply,
    Request: Request,
    preHandler: new Hooks().preHandler,
    onSend: new Hooks().onSend
  }
  buildSchema(handle, schemaCompiler)
  internals.handler(handle, null, { log: { error: () => {} } }, res, { hello: 'world' }, null)
})

test('handler function - reply', t => {
  t.plan(3)
  const res = {}
  res.end = () => {
    t.equal(res.statusCode, 204)
    t.pass()
  }
  res.getHeader = (key) => {
    return false
  }
  res.setHeader = (key, value) => {
    return
  }
  const handle = {
    handler: (req, reply) => {
      t.is(typeof reply, 'object')
      reply.code(204)
      reply.send(undefined)
    },
    Reply: Reply,
    Request: Request,
    preHandler: new Hooks().preHandler,
    onSend: new Hooks().onSend
  }
  buildSchema(handle, schemaCompiler)
  internals.handler(handle, null, { log: null }, res, null, null)
})

test('jsonBody and jsonBodyParsed should be functions', t => {
  t.plan(4)

  t.is(typeof internals.jsonBody, 'function')
  t.is(internals.jsonBody.length, 4)

  t.is(typeof internals.jsonBodyParsed, 'function')
  t.is(internals.jsonBodyParsed.length, 6)
})

test('jsonBody error handler', t => {
  t.plan(1)

  try {
    internals.jsonBody({ on: 'error' }, {})
    t.fail('jsonBody error')
  } catch (e) {
    t.pass('jsonBody error')
  }
})

test('request should be defined in onSend Hook on post request with content type application/json', t => {
  t.plan(3)
  const fastify = require('../..')()

  fastify.addHook('onSend', (request, reply, payload, done) => {
    if (!request) {
      reply.code(500)
    }
    done()
  })
  fastify.post('/', (request, reply) => {
    reply.send(200)
  })
  fastify.listen(0, err => {
    fastify.server.unref()
    t.error(err)
    sget({
      method: 'POST',
      url: 'http://localhost:' + fastify.server.address().port,
      headers: {
        'content-type': 'application/json'
      }
    }, (err, response, body) => {
      t.error(err)
      t.strictEqual(response.statusCode, 422)
    })
  })
})

test('request should be defined in onSend Hook on post request with content type application/x-www-form-urlencoded', t => {
  t.plan(3)
  const fastify = require('../..')()

  fastify.addHook('onSend', (request, reply, payload, done) => {
    if (!request) {
      reply.code(500)
    }
    done()
  })
  fastify.post('/', (request, reply) => {
    reply.send(200)
  })
  fastify.listen(0, err => {
    fastify.server.unref()
    t.error(err)
    sget({
      method: 'POST',
      url: 'http://localhost:' + fastify.server.address().port,
      headers: {
        'content-type': 'application/x-www-form-urlencoded'
      }
    }, (err, response, body) => {
      t.error(err)
      t.strictEqual(response.statusCode, 415)
    })
  })
})

test('request should be defined in onSend Hook on options request with content type application/x-www-form-urlencoded', t => {
  t.plan(3)
  const fastify = require('../..')()

  fastify.addHook('onSend', (request, reply, payload, done) => {
    if (!request) {
      reply.code(500)
    }
    done()
  })
  fastify.options('/', (request, reply) => {
    reply.send(200)
  })
  fastify.listen(0, err => {
    fastify.server.unref()
    t.error(err)
    sget({
      method: 'OPTIONS',
      url: 'http://localhost:' + fastify.server.address().port,
      headers: {
        'content-type': 'application/x-www-form-urlencoded'
      }
    }, (err, response, body) => {
      t.error(err)
      t.strictEqual(response.statusCode, 415)
    })
  })
})


'use strict'

const t = require('tap')
const test = t.test
const sget = require('simple-get').concat
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)
    sget({
      method: 'GET',
      url: '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' })
    })
  })
})


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

const opts = {
  schema: {
    response: {
      '2xx': {
        type: 'object',
        properties: {
          hello: {
            type: 'string'
          }
        }
      }
    }
  }
}
fastify.register(require('./plugin'), opts, function (err) {
  if (err) throw err
})

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


'use strict'

module.exports = function (fastify, opts, next) {
  fastify
    .get('/', opts, function (req, reply) {
      reply.send({ hello: 'world' })
    })
    .post('/', opts, function (req, reply) {
      reply.send({ hello: 'world' })
    })
  next()
}