import { FastifyInstance } from './instance'
import { RawServerBase, RawRequestDefaultExpression, RawReplyDefaultExpression, RawServerDefault } from './utils'

/**
 * FastifyPluginCallback
 *
 * Fastify allows the user to extend its functionalities with plugins. A plugin can be a set of routes, a server decorator or whatever. To activate plugins, use the `fastify.register()` method.
 */
export type FastifyPluginCallback<Options extends FastifyPluginOptions = {}, Server extends RawServerBase = RawServerDefault> = (
  instance: FastifyInstance<Server, RawRequestDefaultExpression<Server>, RawReplyDefaultExpression<Server>>,
  opts: Options,
  next: (err?: Error) => void
) => void

/**
 * FastifyPluginAsync
 *
 * Fastify allows the user to extend its functionalities with plugins. A plugin can be a set of routes, a server decorator or whatever. To activate plugins, use the `fastify.register()` method.
 */
export type FastifyPluginAsync<Options extends FastifyPluginOptions = {}, Server extends RawServerBase = RawServerDefault> = (
  instance: FastifyInstance<Server, RawRequestDefaultExpression<Server>, RawReplyDefaultExpression<Server>>,
  opts: Options
) => Promise<void>;

/**
 * Generic plugin type.
 * @deprecated union type doesn't work well with type inference in TS and is therefore deprecated in favor of explicit types. See FastifyRegister.
 */
export type FastifyPlugin<Options extends FastifyPluginOptions = {}> = FastifyPluginCallback<Options> | FastifyPluginAsync<Options>

export interface FastifyPluginOptions {
  [key: string]: any;
}


Request extends RawRequestDefaultExpression<Server> = RawRequestDefaultExpression<Server>,

Reply extends RawReplyDefaultExpression<Server> = RawReplyDefaultExpression<Server>,

Logger extends FastifyLoggerInstance = FastifyLoggerInstance

keep keep keep keep keep replace keep keep keep

>(opts?: FastifyServerOptions<Server, Logger>): FastifyInstance<Server, Request, Reply, Logger> & PromiseLike<FastifyInstance<Server, Request, Reply, Logger>>

'use strict'

const createError = require('fastify-error')
const codes = {
  /**
   * Basic
   */
  FST_ERR_NOT_FOUND: createError(
    'FST_ERR_NOT_FOUND',
    'Not Found',
    404
  ),

  /**
   * ContentTypeParser
  */
  FST_ERR_CTP_ALREADY_PRESENT: createError(
    'FST_ERR_CTP_ALREADY_PRESENT',
    "Content type parser '%s' already present."
  ),
  FST_ERR_CTP_INVALID_TYPE: createError(
    'FST_ERR_CTP_INVALID_TYPE',
    'The content type should be a string',
    500,
    TypeError
  ),
  FST_ERR_CTP_EMPTY_TYPE: createError(
    'FST_ERR_CTP_EMPTY_TYPE',
    'The content type cannot be an empty string',
    500,
    TypeError
  ),
  FST_ERR_CTP_INVALID_HANDLER: createError(
    'FST_ERR_CTP_INVALID_HANDLER',
    'The content type handler should be a function',
    500,
    TypeError
  ),
  FST_ERR_CTP_INVALID_PARSE_TYPE: createError(
    'FST_ERR_CTP_INVALID_PARSE_TYPE',
    "The body parser can only parse your data as 'string' or 'buffer', you asked '%s' which is not supported.",
    500,
    TypeError
  ),
  FST_ERR_CTP_BODY_TOO_LARGE: createError(
    'FST_ERR_CTP_BODY_TOO_LARGE',
    'Request body is too large',
    413,
    RangeError
  ),
  FST_ERR_CTP_INVALID_MEDIA_TYPE: createError(
    'FST_ERR_CTP_INVALID_MEDIA_TYPE',
    'Unsupported Media Type: %s',
    415
  ),
  FST_ERR_CTP_INVALID_CONTENT_LENGTH: createError(
    'FST_ERR_CTP_INVALID_CONTENT_LENGTH',
    'Request body size did not match Content-Length',
    400,
    RangeError
  ),
  FST_ERR_CTP_EMPTY_JSON_BODY: createError(
    'FST_ERR_CTP_EMPTY_JSON_BODY',
    "Body cannot be empty when content-type is set to 'application/json'",
    400
  ),

  /**
   * decorate
  */
  FST_ERR_DEC_ALREADY_PRESENT: createError(
    'FST_ERR_DEC_ALREADY_PRESENT',
    "The decorator '%s' has already been added!"
  ),
  FST_ERR_DEC_MISSING_DEPENDENCY: createError(
    'FST_ERR_DEC_MISSING_DEPENDENCY',
    "The decorator is missing dependency '%s'."
  ),
  FST_ERR_DEC_AFTER_START: createError(
    'FST_ERR_DEC_AFTER_START',
    "The decorator '%s' has been added after start!"
  ),

  /**
   * hooks
  */
  FST_ERR_HOOK_INVALID_TYPE: createError(
    'FST_ERR_HOOK_INVALID_TYPE',
    'The hook name must be a string',
    500,
    TypeError
  ),
  FST_ERR_HOOK_INVALID_HANDLER: createError(
    'FST_ERR_HOOK_INVALID_HANDLER',
    'The hook callback must be a function',
    500,
    TypeError
  ),

  /**
   * Middlewares
   */
  FST_ERR_MISSING_MIDDLEWARE: createError(
    'FST_ERR_MISSING_MIDDLEWARE',
    'You must register a plugin for handling middlewares, visit fastify.io/docs/latest/Middleware/ for more info.',
    500
  ),

  /**
   * logger
  */
  FST_ERR_LOG_INVALID_DESTINATION: createError(
    'FST_ERR_LOG_INVALID_DESTINATION',
    'Cannot specify both logger.stream and logger.file options'
  ),

  /**
   * reply
  */
  FST_ERR_REP_INVALID_PAYLOAD_TYPE: createError(
    'FST_ERR_REP_INVALID_PAYLOAD_TYPE',
    "Attempted to send payload of invalid type '%s'. Expected a string or Buffer.",
    500,
    TypeError
  ),
  FST_ERR_REP_ALREADY_SENT: createError(
    'FST_ERR_REP_ALREADY_SENT',
    'Reply was already sent.'
  ),
  FST_ERR_REP_SENT_VALUE: createError(
    'FST_ERR_REP_SENT_VALUE',
    'The only possible value for reply.sent is true.'
  ),
  FST_ERR_SEND_INSIDE_ONERR: createError(
    'FST_ERR_SEND_INSIDE_ONERR',
    'You cannot use `send` inside the `onError` hook'
  ),
  FST_ERR_SEND_UNDEFINED_ERR: createError(
    'FST_ERR_SEND_UNDEFINED_ERR',
    'Undefined error has occured'
  ),
  FST_ERR_BAD_STATUS_CODE: createError(
    'FST_ERR_BAD_STATUS_CODE',
    'Called reply with an invalid status code: %s'
  ),

  /**
   * schemas
  */
  FST_ERR_SCH_MISSING_ID: createError(
    'FST_ERR_SCH_MISSING_ID',
    'Missing schema $id property'
  ),
  FST_ERR_SCH_ALREADY_PRESENT: createError(
    'FST_ERR_SCH_ALREADY_PRESENT',
    "Schema with id '%s' already declared!"
  ),
  FST_ERR_SCH_DUPLICATE: createError(
    'FST_ERR_SCH_DUPLICATE',
    "Schema with '%s' already present!"
  ),
  FST_ERR_SCH_VALIDATION_BUILD: createError(
    'FST_ERR_SCH_VALIDATION_BUILD',
    'Failed building the validation schema for %s: %s, due to error %s'
  ),
  FST_ERR_SCH_SERIALIZATION_BUILD: createError(
    'FST_ERR_SCH_SERIALIZATION_BUILD',
    'Failed building the serialization schema for %s: %s, due to error %s'
  ),

  /**
   * wrapThenable
   */
  FST_ERR_PROMISE_NOT_FULLFILLED: createError(
    'FST_ERR_PROMISE_NOT_FULLFILLED',
    "Promise may not be fulfilled with 'undefined' when statusCode is not 204"
  ),

  /**
   * http2
   */
  FST_ERR_HTTP2_INVALID_VERSION: createError(
    'FST_ERR_HTTP2_INVALID_VERSION',
    'HTTP2 is available only from node >= 8.8.1'
  ),

  /**
   * initialConfig
   */
  FST_ERR_INIT_OPTS_INVALID: createError(
    'FST_ERR_INIT_OPTS_INVALID',
    "Invalid initialization options: '%s'"
  ),

  /**
   * router
   */
  FST_ERR_BAD_URL: createError(
    'FST_ERR_BAD_URL',
    "'%s' is not a valid url component",
    400
  ),

  /**
   *  again listen when close server
   */
  FST_ERR_REOPENED_CLOSE_SERVER: createError(
    'FST_ERR_REOPENED_CLOSE_SERVER',
    'Fastify has already been closed and cannot be reopened'
  ),
  FST_ERR_REOPENED_SERVER: createError(
    'FST_ERR_REOPENED_SERVER',
    'Fastify is already listening'
  )
}

module.exports = codes


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


keep keep keep keep keep replace replace keep

expectType<FastifyInstance<http.Server, http.IncomingMessage, http.ServerResponse> & PromiseLike<FastifyInstance<http.Server, http.IncomingMessage, http.ServerResponse>>>(fastify())

expectType<FastifyInstance<http.Server, http.IncomingMessage, http.ServerResponse> & PromiseLike<FastifyInstance<http.Server, http.IncomingMessage, http.ServerResponse>>>(fastify({}))

import fastify, { FastifyInstance, FastifyServerOptions } from '../../fastify'
import * as http from 'http'
import * as https from 'https'
import * as http2 from 'http2'
import { Chain as LightMyRequestChain } from 'light-my-request';
import { expectType, expectError, expectAssignable } from 'tsd'
import { FastifyLoggerInstance, FastifyLoggerOptions } from '../../types/logger';

// FastifyInstance
// http server
expectType<FastifyInstance<http.Server, http.IncomingMessage, http.ServerResponse>>(fastify())
expectType<FastifyInstance<http.Server, http.IncomingMessage, http.ServerResponse>>(fastify({}))
// https server
expectType<FastifyInstance<https.Server, http.IncomingMessage, http.ServerResponse>>(fastify({ https: {} }))
// http2 server
expectType<FastifyInstance<http2.Http2Server, http2.Http2ServerRequest, http2.Http2ServerResponse>>(fastify({ http2: true, http2SessionTimeout: 1000 }))
expectType<FastifyInstance<http2.Http2SecureServer, http2.Http2ServerRequest, http2.Http2ServerResponse>>(fastify({ http2: true, https: {} }))
expectType<LightMyRequestChain>(fastify({ http2: true, https: {} }).inject())

expectError(fastify<http2.Http2Server>({ http2: false })) // http2 option must be true
expectError(fastify<http2.Http2SecureServer>({ http2: false })) // http2 option must be true

// server options
expectAssignable<FastifyInstance>(fastify({ http2: true }))
expectAssignable<FastifyInstance>(fastify({ ignoreTrailingSlash: true }))
expectAssignable<FastifyInstance>(fastify({ connectionTimeout: 1000 }))
expectAssignable<FastifyInstance>(fastify({ keepAliveTimeout: 1000 }))
expectAssignable<FastifyInstance>(fastify({ pluginTimeout: 1000 }))
expectAssignable<FastifyInstance>(fastify({ bodyLimit: 100 }))
expectAssignable<FastifyInstance>(fastify({ maxParamLength: 100 }))
expectAssignable<FastifyInstance>(fastify({ disableRequestLogging: true }))
expectAssignable<FastifyInstance>(fastify({ requestIdLogLabel: 'request-id' }))
expectAssignable<FastifyInstance>(fastify({ onProtoPoisoning: 'error' }))
expectAssignable<FastifyInstance>(fastify({ onConstructorPoisoning: 'error' }))
expectAssignable<FastifyInstance<http.Server, http.IncomingMessage, http.ServerResponse>>(fastify({ logger: true }))
expectAssignable<FastifyInstance<http.Server, http.IncomingMessage, http.ServerResponse, FastifyLoggerInstance>>(fastify({ logger: true }))
expectAssignable<FastifyInstance<http.Server, http.IncomingMessage, http.ServerResponse, FastifyLoggerInstance>>(fastify({
  logger: {
    level: 'info',
    genReqId: () => 'request-id',
    serializers: {
      req: () => {
        return {
          method: 'GET',
          url: '/',
          version: '1.0.0',
          hostname: 'localhost',
          remoteAddress: '127.0.0.1',
          remotePort: 3000
        }
      },
      res: () => {
        return {
          statusCode: 200
        }
      },
      err: () => {
        return {
          type: 'Error',
          message: 'foo',
          stack: ''
        }
      },
    }
  }
}))
const customLogger = {
  info: () => { },
  warn: () => { },
  error: () => { },
  fatal: () => { },
  trace: () => { },
  debug: () => { },
  child: () => customLogger
}
expectAssignable<FastifyInstance<http.Server, http.IncomingMessage, http.ServerResponse, FastifyLoggerInstance>>(fastify({ logger: customLogger }))
expectAssignable<FastifyInstance>(fastify({ serverFactory: () => http.createServer() }))
expectAssignable<FastifyInstance>(fastify({ caseSensitive: true }))
expectAssignable<FastifyInstance>(fastify({ requestIdHeader: 'request-id' }))
expectAssignable<FastifyInstance>(fastify({ genReqId: () => 'request-id' }))
expectAssignable<FastifyInstance>(fastify({ trustProxy: true }))
expectAssignable<FastifyInstance>(fastify({ querystringParser: () => ({ foo: 'bar' }) }))
expectAssignable<FastifyInstance>(fastify({
  versioning: {
    storage: () => ({
      get: () => 'foo',
      set: () => { },
      del: () => { },
      empty: () => { }
    }),
    deriveVersion: () => 'foo'
  }
}))
expectAssignable<FastifyInstance>(fastify({ return503OnClosing: true }))
expectAssignable<FastifyInstance>(fastify({
  ajv: {
    customOptions: {
      nullable: false
    },
    plugins: [() => { }]
  }
}))
expectAssignable<FastifyInstance>(fastify({ frameworkErrors: () => { } }))
expectAssignable<FastifyInstance>(fastify({
  rewriteUrl: (req) => req.url === '/hi' ? '/hello' : req.url!
}))


'use strict'

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

test('bodyLimit', t => {
  t.plan(5)

  try {
    Fastify({ bodyLimit: 1.3 })
    t.fail('option must be an integer')
  } catch (err) {
    t.ok(err)
  }

  try {
    Fastify({ bodyLimit: [] })
    t.fail('option must be an integer')
  } catch (err) {
    t.ok(err)
  }

  const fastify = Fastify({ bodyLimit: 1 })

  fastify.post('/', (request, reply) => {
    reply.send({ error: 'handler should not be called' })
  })

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

    sget({
      method: 'POST',
      url: 'http://localhost:' + fastify.server.address().port,
      headers: { 'Content-Type': 'application/json' },
      body: [],
      json: true
    }, (err, response, body) => {
      t.error(err)
      t.strictEqual(response.statusCode, 413)
    })
  })
})


'use strict'

const { test } = require('tap')
const Fastify = require('..')

test('Should accept a custom genReqId function', t => {
  t.plan(4)

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

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

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


'use strict'

const proxyAddr = require('proxy-addr')
const { emitWarning } = require('./warnings')

function Request (id, params, req, query, log) {
  this.id = id
  this.params = params
  this.raw = req
  this.query = query
  this.log = log
  this.body = null
}

function getTrustProxyFn (tp) {
  if (typeof tp === 'function') {
    return tp
  }
  if (tp === true) {
    // Support plain true/false
    return function () { return true }
  }
  if (typeof tp === 'number') {
    // Support trusting hop count
    return function (a, i) { return i < tp }
  }
  if (typeof tp === 'string') {
    // Support comma-separated tps
    const vals = tp.split(',').map(it => it.trim())
    return proxyAddr.compile(vals)
  }
  return proxyAddr.compile(tp || [])
}

function buildRequest (R, trustProxy) {
  if (trustProxy) {
    return buildRequestWithTrustProxy(R, trustProxy)
  }

  return buildRegularRequest(R)
}

function buildRegularRequest (R) {
  function _Request (id, params, req, query, log) {
    this.id = id
    this.params = params
    this.raw = req
    this.query = query
    this.log = log
    this.body = null
  }
  _Request.prototype = new R()

  return _Request
}

function buildRequestWithTrustProxy (R, trustProxy) {
  const _Request = buildRegularRequest(R)
  const proxyFn = getTrustProxyFn(trustProxy)

  Object.defineProperties(_Request.prototype, {
    ip: {
      get () {
        return proxyAddr(this.raw, proxyFn)
      }
    },
    ips: {
      get () {
        return proxyAddr.all(this.raw, proxyFn)
      }
    },
    hostname: {
      get () {
        if (this.ip !== undefined && this.headers['x-forwarded-host']) {
          return this.headers['x-forwarded-host']
        }
        return this.headers.host || this.headers[':authority']
      }
    }
  })

  return _Request
}

Object.defineProperties(Request.prototype, {
  req: {
    get () {
      emitWarning('FSTDEP001')
      return this.raw
    }
  },
  url: {
    get () {
      return this.raw.url
    }
  },
  method: {
    get () {
      return this.raw.method
    }
  },
  connection: {
    get () {
      return this.raw.connection
    }
  },
  ip: {
    get () {
      return this.connection.remoteAddress
    }
  },
  hostname: {
    get () {
      return this.raw.headers.host || this.raw.headers[':authority']
    }
  },
  headers: {
    get () {
      return this.raw.headers
    }
  }
})

module.exports = Request
module.exports.buildRequest = buildRequest


expectType<FastifyInstance<https.Server, http.IncomingMessage, http.ServerResponse>>(fastify({ https: {} }))

expectType<FastifyInstance<https.Server, http.IncomingMessage, http.ServerResponse> & PromiseLike<FastifyInstance<https.Server, http.IncomingMessage, http.ServerResponse>>>(fastify({ https: {} }))


comment: |
  Hello! Thank you for contributing!
  It appears that you have changed the framework code, but the tests that verify your change are missing. Could you please add them?

fileExtensions:
  - '.ts'
  - '.js'

testDir: 'test'


Fastify

## Reply
- [Reply](#reply)
  - [Introduction](#introduction)
  - [.code(statusCode)](#codestatuscode)
  - [.statusCode](#statusCode)
  - [.header(key, value)](#headerkey-value)
  - [.headers(object)](#headersobject)
  - [.getHeader(key)](#getheaderkey)
  - [.getHeaders()](#getheaders)
  - [.removeHeader(key)](#removeheaderkey)
  - [.hasHeader(key)](#hasheaderkey)
  - [.redirect(dest)](#redirectdest)
  - [.callNotFound()](#callnotfound)
  - [.getResponseTime()](#getresponsetime)
  - [.type(contentType)](#typecontenttype)
  - [.serializer(func)](#serializerfunc)
  - [.sent](#sent)
  - [.send(data)](#senddata)
    - [Objects](#objects)
    - [Strings](#strings)
    - [Streams](#streams)
    - [Buffers](#buffers)
    - [Errors](#errors)
    - [Type of the final payload](#type-of-the-final-payload)
    - [Async-Await and Promises](#async-await-and-promises)
  - [.then](#then)

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

- `.code(statusCode)` - Sets the status code.
- `.status(statusCode)` - An alias for `.code(statusCode)`.
- `.statusCode` - Read and set the HTTP status code.
- `.header(name, value)` - Sets a response header.
- `.headers(object)` - Sets all the keys of the object as a response headers.
- `.getHeader(name)` - Retrieve value of already set header.
- `.getHeaders()` - Gets a shallow copy of all current response headers.
- `.removeHeader(key)` - Remove the value of a previously set header.
- `.hasHeader(name)` - Determine if a header has been set.
- `.type(value)` - Sets the header `Content-Type`.
- `.redirect([code,] url)` - Redirect to the specified url, the status code is optional (default to `302`).
- `.callNotFound()` - Invokes the custom not found handler.
- `.serialize(payload)` - Serializes the specified payload using the default json serializer or using the custom serializer (if one is set) and returns the serialized payload.
- `.serializer(function)` - Sets a custom serializer for the payload.
- `.send(payload)` - Sends the payload to the user, could be a plain text, a buffer, JSON, stream, or an Error object.
- `.sent` - A boolean value that you can use if you need to know if `send` has already been called.
- `.raw` - The [`http.ServerResponse`](https://nodejs.org/dist/latest/docs/api/http.html#http_class_http_serverresponse) from Node core.
- `.res` *(deprecated, use `.raw` instead)* - The [`http.ServerResponse`](https://nodejs.org/dist/latest/docs/api/http.html#http_class_http_serverresponse) from Node core.
- `.log` - The logger instance of the incoming request.
- `.request` - The incoming request.

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

Additionally, `Reply` provides access to the context of the request:

```js
fastify.get('/', {config: {foo: 'bar'}}, function (request, reply) {
  reply.send('handler config.foo = ' + reply.context.config.foo)
})
```

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

### .statusCode
This property reads and sets the HTTP status code. It is an alias for `reply.code()` when used as a setter.
```js
if (reply.statusCode >= 299) {
  reply.statusCode = 500
}
```

### .header(key, value)
Sets a response header. If the value is omitted or undefined it is coerced
to `''`.

For more information, see [`http.ServerResponse#setHeader`](https://nodejs.org/dist/latest/docs/api/http.html#http_response_setheader_name_value).

### .headers(object)
Sets all the keys of the object as response headers. [`.header`](#headerkey-value) will be called under the hood.
```js
reply.headers({
  'x-foo': 'foo',
  'x-bar': 'bar'
})
```

### .getHeader(key)
Retrieves the value of a previously set header.
```js
reply.header('x-foo', 'foo') // setHeader: key, value
reply.getHeader('x-foo') // 'foo'
```

### .getHeaders()

Gets a shallow copy of all current response headers, including those set via the raw `http.ServerResponse`. Note that headers set via Fastify take precedence over those set via `http.ServerResponse`.

```js
reply.header('x-foo', 'foo')
reply.header('x-bar', 'bar')
reply.raw.setHeader('x-foo', 'foo2')
reply.getHeaders() // { 'x-foo': 'foo', 'x-bar': 'bar' }
```

### .removeHeader(key)

Remove the value of a previously set header.
```js
reply.header('x-foo', 'foo')
reply.removeHeader('x-foo')
reply.getHeader('x-foo') // undefined
```

### .hasHeader(key)
Returns a boolean indicating if the specified header has been set.

### .redirect([code ,] dest)
Redirects a request to the specified url, the status code is optional, default to `302` (if status code is not already set by calling `code`).

Example (no `reply.code()` call) sets status code to `302` and redirects to `/home`
```js
reply.redirect('/home')
```

Example (no `reply.code()` call) sets status code to `303` and redirects to `/home`
```js
reply.redirect(303, '/home')
```

Example (`reply.code()` call) sets status code to `303` and redirects to `/home`
```js
reply.code(303).redirect('/home')
```

Example (`reply.code()` call) sets status code to `302` and redirects to `/home`
```js
reply.code(303).redirect(302, '/home')
```

### .callNotFound()
Invokes the custom not found handler. Note that it will only call `preHandler` hook specified in [`setNotFoundHandler`](./Server.md#set-not-found-handler).

```js
reply.callNotFound()
```

### .getResponseTime()
Invokes the custom response time getter to calculate the amount of time passed since the request was started.

```js
const milliseconds = reply.getResponseTime()
```

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

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

### .serializer(func)
`.send()` will by default JSON-serialize any value that is not one of: `Buffer`, `stream`, `string`, `undefined`, `Error`. If you need to replace the default serializer with a custom serializer for a particular request, you can do so with the `.serializer()` utility. Be aware that if you are using a custom serializer, you must set a custom `'Content-Type'` header.

```js
reply
  .header('Content-Type', 'application/x-protobuf')
  .serializer(protoBuf.serialize)
```

Note that you don't need to use this utility inside a `handler` because Buffers, streams, and strings (unless a serializer is set) are considered to already be serialized.

```js
reply
  .header('Content-Type', 'application/x-protobuf')
  .send(protoBuf.serialize(data))
```

See [`.send()`](#send) for more information on sending different types of values.

### .sent

As the name suggests, `.sent` is a property to indicate if
a response has been sent via `reply.send()`.

In case a route handler is defined as an async function or it
returns a promise, it is possible to set `reply.sent = true`
to indicate that the automatic invocation of `reply.send()` once the
handler promise resolve should be skipped. By setting `reply.sent =
true`, an application claims full responsibility of the low-level
request and response. Moreover, hooks will not be invoked.

As an example:

```js
app.get('/', (req, reply) => {
  reply.sent = true
  reply.raw.end('hello world')

  return Promise.resolve('this will be skipped')
})
```

If the handler rejects, the error will be logged.

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

#### Objects
As noted 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 set an output schema, otherwise `JSON.stringify()` will be used.
```js
fastify.get('/json', options, function (request, reply) {
  reply.send({ hello: 'world' })
})
```

#### Strings
If you pass a string to `send` without a `Content-Type`, it will be sent as `text/plain; charset=utf-8`. If you set the `Content-Type` header and pass a string to `send`, it will be serialized with the custom serializer if one is set, otherwise it will be sent unmodified (unless the `Content-Type` header is set to `application/json; charset=utf-8`, in which case it will be JSON-serialized like an object — see the section above).
```js
fastify.get('/json', options, function (request, reply) {
  reply.send('plain string')
})
```

#### 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 set 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)
})
```

#### Buffers
If you are sending a buffer and you have not set a `'Content-Type'` header, *send* will set it to `'application/octet-stream'`.
```js
const fs = require('fs')
fastify.get('/streams', function (request, reply) {
  fs.readFile('some-file', (err, fileBuffer) => {
    reply.send(err || fileBuffer)
  })
})
```

#### 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
  code: String         // the Fastify error code
  message: String      // the user error message
  statusCode: Number   // the http status code
}
```
You can add some custom property to the Error object, such as `headers`, that will be used to enhance the http response.

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

Tip: you can simplify errors by using the [`http-errors`](https://npm.im/http-errors) module or [`fastify-sensible`](https://github.com/fastify/fastify-sensible) plugin to generate errors:

```js
fastify.get('/', function (request, reply) {
  reply.send(httpErrors.Gone())
})
```

If you want to completely customize the error handling, checkout [`setErrorHandler`](./Server.md#seterrorhandler) API.

*Note: you are responsibile for logging when customizing the error handler*

API:

```js
fastify.setErrorHandler(function (error, request, reply) {
  request.log.warn(error)
  var statusCode = error.statusCode >= 400 ? error.statusCode : 500
  reply
    .code(statusCode)
    .type('text/plain')
    .send(statusCode >= 500 ? 'Internal server error' : error.message)
})
```

The not found errors generated by the router will use the  [`setNotFoundHandler`](./Server.md#setnotfoundhandler)

API:

```js
fastify.setNotFoundHandler(function (request, reply) {
  reply
    .code(404)
    .type('text/plain')
    .send('a custom not found')
})
```

#### Type of the final payload
The type of the sent payload (after serialization and going through any [`onSend` hooks](./Hooks.md#the-onsend-hook)) must be one of the following types, otherwise an error will be thrown:

- `string`
- `Buffer`
- `stream`
- `undefined`
- `null`

#### Async-Await and Promises
Fastify natively handles promises and supports async-await.

*Note that in the following examples we are not using reply.send.*
```js
const delay = promisify(setTimeout)

fastify.get('/promises', options, function (request, reply) {
 return delay(200).then(() => { return { hello: 'world' }})
})

fastify.get('/async-await', options, async function (request, reply) {
  await delay(200)
  return { hello: 'world' }
})
```

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

fastify.get('/botnet', async function (request, reply) => {
  throw { statusCode: 418, message: 'short and stout' }
  // will return to the client the same json
})
```

If you want to know more please review [Routes#async-await](./Routes.md#async-await).

### .then(fullfilled, rejected)

As the name suggests, a `Reply` object can be awaited upon, i.e. `await reply` will wait until the reply is sent.
The `await` syntax calls the `reply.then()`.

`reply.then(fullfilled, rejected)` accepts two parameters:

- `fullfilled` will be called when a response has been fully sent,
- `rejected` will be called if the underlying stream had an error, e.g.
the socket has been destroyed.

For more details, see:

- https://github.com/fastify/fastify/issues/1864 for the discussion about this feature
- https://promisesaplus.com/ for the definition of thenables
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then for the signature


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
  │
  └─▶ Routing
        │
        └─▶ Instance Logger
             │
       404 ◀─┴─▶ onRequest Hook
                  │
        4**/5** ◀─┴─▶ preParsing Hook
                        │
              4**/5** ◀─┴─▶ Parsing
                             │
                   4**/5** ◀─┴─▶ preValidation Hook
                                  │
                            415 ◀─┴─▶ Validation
                                        │
                                  400 ◀─┴─▶ preHandler Hook
                                              │
                                    4**/5** ◀─┴─▶ User Handler
                                                    │
                                                    └─▶ Reply
                                                          │
                                                4**/5** ◀─┴─▶ preSerialization Hook
                                                                │
                                                                └─▶ onSend Hook
                                                                      │
                                                            4**/5** ◀─┴─▶ Outgoing Response
                                                                            │
                                                                            └─▶ onResponse Hook
```


expectType<FastifyInstance<http2.Http2Server, http2.Http2ServerRequest, http2.Http2ServerResponse>>(fastify({ http2: true, http2SessionTimeout: 1000 }))

expectType<FastifyInstance<http2.Http2SecureServer, http2.Http2ServerRequest, http2.Http2ServerResponse>>(fastify({ http2: true, https: {} }))

expectType<LightMyRequestChain>(fastify({ http2: true, https: {} }).inject())

expectError(fastify<http2.Http2Server>({ http2: false })) // http2 option must be true

expectError(fastify<http2.Http2SecureServer>({ http2: false })) // http2 option must be true

keep replace replace keep keep keep keep

expectType<FastifyInstance<http2.Http2Server, http2.Http2ServerRequest, http2.Http2ServerResponse> & PromiseLike<FastifyInstance<http2.Http2Server, http2.Http2ServerRequest, http2.Http2ServerResponse>>>(fastify({ http2: true, http2SessionTimeout: 1000 }))

expectType<FastifyInstance<http2.Http2SecureServer, http2.Http2ServerRequest, http2.Http2ServerResponse> & PromiseLike<FastifyInstance<http2.Http2SecureServer, http2.Http2ServerRequest, http2.Http2ServerResponse>>>(fastify({ http2: true, https: {} }))

import * as http from 'http'
import * as http2 from 'http2'
import * as https from 'https'
import * as LightMyRequest from 'light-my-request'

import { FastifyRequest, RequestGenericInterface } from './types/request'
import { RawServerBase, RawServerDefault, RawRequestDefaultExpression, RawReplyDefaultExpression } from './types/utils'
import { FastifyLoggerInstance, FastifyLoggerOptions } from './types/logger'
import { FastifyInstance } from './types/instance'
import { FastifyServerFactory } from './types/serverFactory'
import * as ajv from 'ajv'
import { FastifyError } from 'fastify-error'
import { FastifyReply } from './types/reply'

/**
 * Fastify factor function for the standard fastify http, https, or http2 server instance.
 *
 * The default function utilizes http
 *
 * @param opts Fastify server options
 * @returns Fastify server instance
 */
declare function fastify<
  Server extends http2.Http2SecureServer,
  Request extends RawRequestDefaultExpression<Server> = RawRequestDefaultExpression<Server>,
  Reply extends RawReplyDefaultExpression<Server> = RawReplyDefaultExpression<Server>,
  Logger extends FastifyLoggerInstance = FastifyLoggerInstance
>(opts: FastifyHttp2SecureOptions<Server, Logger>): FastifyInstance<Server, Request, Reply, Logger>
declare function fastify<
  Server extends http2.Http2Server,
  Request extends RawRequestDefaultExpression<Server> = RawRequestDefaultExpression<Server>,
  Reply extends RawReplyDefaultExpression<Server> = RawReplyDefaultExpression<Server>,
  Logger extends FastifyLoggerInstance = FastifyLoggerInstance
>(opts: FastifyHttp2Options<Server, Logger>): FastifyInstance<Server, Request, Reply, Logger>
declare function fastify<
  Server extends https.Server,
  Request extends RawRequestDefaultExpression<Server> = RawRequestDefaultExpression<Server>,
  Reply extends RawReplyDefaultExpression<Server> = RawReplyDefaultExpression<Server>,
  Logger extends FastifyLoggerInstance = FastifyLoggerInstance
>(opts: FastifyHttpsOptions<Server, Logger>): FastifyInstance<Server, Request, Reply, Logger>
declare function fastify<
  Server extends http.Server,
  Request extends RawRequestDefaultExpression<Server> = RawRequestDefaultExpression<Server>,
  Reply extends RawReplyDefaultExpression<Server> = RawReplyDefaultExpression<Server>,
  Logger extends FastifyLoggerInstance = FastifyLoggerInstance
>(opts?: FastifyServerOptions<Server, Logger>): FastifyInstance<Server, Request, Reply, Logger>
export default fastify

type FastifyHttp2SecureOptions<
  Server extends http2.Http2SecureServer,
  Logger extends FastifyLoggerInstance = FastifyLoggerInstance
> = FastifyServerOptions<Server, Logger> & {
  http2: true,
  https: http2.SecureServerOptions
}

type FastifyHttp2Options<
  Server extends http2.Http2Server,
  Logger extends FastifyLoggerInstance = FastifyLoggerInstance
> = FastifyServerOptions<Server, Logger> & {
  http2: true,
  http2SessionTimeout?: number,
}

type FastifyHttpsOptions<
  Server extends https.Server,
  Logger extends FastifyLoggerInstance = FastifyLoggerInstance
> = FastifyServerOptions<Server, Logger> & {
  https: https.ServerOptions
}
/**
 * Options for a fastify server instance. Utilizes conditional logic on the generic server parameter to enforce certain https and http2
 */
export type FastifyServerOptions<
  RawServer extends RawServerBase = RawServerDefault,
  Logger extends FastifyLoggerInstance = FastifyLoggerInstance
> = {
  ignoreTrailingSlash?: boolean,
  connectionTimeout?: number,
  keepAliveTimeout?: number,
  pluginTimeout?: number,
  bodyLimit?: number,
  maxParamLength?: number,
  disableRequestLogging?: boolean,
  onProtoPoisoning?: 'error' | 'remove' | 'ignore',
  onConstructorPoisoning?: 'error' | 'remove' | 'ignore',
  logger?: boolean | FastifyLoggerOptions<RawServer> | Logger,
  serverFactory?: FastifyServerFactory<RawServer>,
  caseSensitive?: boolean,
  requestIdHeader?: string,
  requestIdLogLabel?: string;
  genReqId?: <RequestGeneric extends RequestGenericInterface = RequestGenericInterface>(req: FastifyRequest<RequestGeneric, RawServer, RawRequestDefaultExpression<RawServer>>) => string,
  trustProxy?: boolean | string | string[] | number | TrustProxyFunction,
  querystringParser?: (str: string) => { [key: string]: string | string[] },
  versioning?: {
    storage(): {
      get(version: string): string | null,
      set(version: string, store: Function): void
      del(version: string): void,
      empty(): void
    },
    deriveVersion<Context>(req: Object, ctx?: Context): string // not a fan of using Object here. Also what is Context? Can either of these be better defined?
  },
  return503OnClosing?: boolean,
  ajv?: {
    customOptions?: ajv.Options,
    plugins?: Function[]
  },
  frameworkErrors?: <RequestGeneric extends RequestGenericInterface = RequestGenericInterface>(
    error: FastifyError,
    req: FastifyRequest<RequestGeneric, RawServer, RawRequestDefaultExpression<RawServer>>,
    res: FastifyReply<RawServer, RawRequestDefaultExpression<RawServer>, RawReplyDefaultExpression<RawServer>>
  ) => void,
  rewriteUrl?: (req: RawRequestDefaultExpression<RawServer>) => string
}

type TrustProxyFunction = (address: string, hop: number) => boolean

/* Export all additional types */
export { FastifyRequest, RequestGenericInterface } from './types/request'
export { FastifyReply } from './types/reply'
export { FastifyPluginCallback, FastifyPluginAsync, FastifyPluginOptions, FastifyPlugin } from './types/plugin'
export { FastifyInstance } from './types/instance'
export { FastifyLoggerOptions, FastifyLoggerInstance, FastifyLogFn, LogLevel } from './types/logger'
export { FastifyContext } from './types/context'
export { RouteHandler, RouteHandlerMethod, RouteOptions, RouteShorthandMethod, RouteShorthandOptions, RouteShorthandOptionsWithHandler } from './types/route'
export * from './types/register'
export { FastifyBodyParser, FastifyContentTypeParser, AddContentTypeParser, hasContentTypeParser } from './types/content-type-parser'
export { FastifyError, ValidationResult } from 'fastify-error'
export { FastifySchema, FastifySchemaCompiler } from './types/schema'
export { HTTPMethods, RawServerBase, RawRequestDefaultExpression, RawReplyDefaultExpression, RawServerDefault, ContextConfigDefault, RequestBodyDefault, RequestQuerystringDefault, RequestParamsDefault, RequestHeadersDefault } from './types/utils'
export * from './types/hooks'
export { FastifyServerFactory, FastifyServerFactoryHandler } from './types/serverFactory'
export { fastify }


import * as http from 'http'
import * as http2 from 'http2'
import * as https from 'https'

/**
 * Standard HTTP method strings
 */
export type HTTPMethods = 'DELETE' | 'GET' | 'HEAD' | 'PATCH' | 'POST' | 'PUT' | 'OPTIONS'

/**
 * A union type of the Node.js server types from the http, https, and http2 modules.
 */
export type RawServerBase = http.Server | https.Server | http2.Http2Server | http2.Http2SecureServer

/**
 * The default server type
 */
export type RawServerDefault = http.Server

/**
 * The default request type based on the server type. Utilizes generic constraining.
 */
export type RawRequestDefaultExpression<
  RawServer extends RawServerBase = RawServerDefault
> = RawServer extends http.Server | https.Server ? http.IncomingMessage
  : RawServer extends http2.Http2Server | http2.Http2SecureServer ? http2.Http2ServerRequest
    : never

/**
 * The default reply type based on the server type. Utilizes generic constraining.
 */
export type RawReplyDefaultExpression<
  RawServer extends RawServerBase = RawServerDefault
> = RawServer extends http.Server | https.Server ? http.ServerResponse
  : RawServer extends http2.Http2Server | http2.Http2SecureServer ? http2.Http2ServerResponse
    : never

export type RequestBodyDefault = unknown
export type RequestQuerystringDefault = unknown
export type RequestParamsDefault = unknown
export type RequestHeadersDefault = unknown

export type ContextConfigDefault = unknown
export type ReplyDefault = unknown


import fastify, { FastifyServerFactory } from '../../fastify'
import * as http from 'http'
import { expectType } from 'tsd'

// Custom Server
type CustomType = void;
interface CustomIncomingMessage extends http.IncomingMessage {
  fakeMethod?: () => CustomType;
}

interface CustomServerResponse extends http.ServerResponse {
  fakeMethod?: () => CustomType;
}

const serverFactory: FastifyServerFactory<http.Server> = (handler, opts) => {
  const server = http.createServer((req: CustomIncomingMessage, res: CustomServerResponse) => {
    req.fakeMethod = () => {}
    res.fakeMethod = () => {}

    handler(req, res)
  })

  return server
}

// The request and reply objects should have the fakeMethods available (even though they may be undefined)
const customServer = fastify<http.Server, CustomIncomingMessage, CustomServerResponse>({ serverFactory })

customServer.get('/', function (request, reply) {
  if (request.raw.fakeMethod) {
    expectType<CustomType>(request.raw.fakeMethod())
  }

  if (reply.raw.fakeMethod) {
    expectType<CustomType>(reply.raw.fakeMethod())
  }
})


'use strict'

/* eslint no-prototype-builtins: 0 */

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

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

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

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

    makeRequest('first')
    makeRequest('second')
  })

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

test('awaitable register and after', async t => {
  const fastify = Fastify()
  let first = false
  let second = false
  let third = false

  await fastify.register(async (instance, opts, next) => {
    first = true
  })

  t.is(first, true)

  fastify.register(async (instance, opts, next) => {
    second = true
  })

  await fastify.after()
  t.is(second, true)

  fastify.register(async (instance, opts, next) => {
    third = true
  })

  await fastify.ready()
  t.is(third, true)
})

function thenableRejects (t, promise, error) {
  return t.rejects(async () => { await promise }, error)
}

test('awaitable register error handling', async t => {
  const fastify = Fastify()

  const e = new Error('kaboom')

  await thenableRejects(t, fastify.register(async (instance, opts) => {
    throw e
  }), e)

  fastify.register(async (instance, opts) => {
    t.fail('should not be executed')
  })

  await t.rejects(fastify.after(), e)

  fastify.register(async (instance, opts, next) => {
    t.fail('should not be executed')
  })

  await thenableRejects(t, fastify.ready(), e)
})

test('awaitable after error handling', async t => {
  const fastify = Fastify()

  const e = new Error('kaboom')

  fastify.register(async (instance, opts) => {
    throw e
  })

  fastify.register(async (instance, opts) => {
    t.fail('should not be executed')
  })

  await t.rejects(fastify.after(), e)

  fastify.register(async (instance, opts, next) => {
    t.fail('should not be executed')
  })

  await t.rejects(fastify.ready())
})

test('chainable register', async t => {
  t.plan(3)

  const fastify = Fastify()

  fastify.register(async () => {
    t.pass('first loaded')
  }).register(async () => {
    t.pass('second loaded')
  }).register(async () => {
    t.pass('third loaded')
  })

  await fastify.ready()
})


expectAssignable<FastifyInstance>(fastify({ frameworkErrors: () => { } }))

rewriteUrl: (req) => req.url === '/hi' ? '/hello' : req.url!

expectAssignable<PromiseLike<FastifyInstance>>(fastify({ return503OnClosing: true }))


'use strict'

const t = require('tap')
const test = t.test
const Stream = require('stream')
const util = require('util')
const Fastify = require('..')
const FormData = require('form-data')

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

test('should wait for the ready event', t => {
  t.plan(4)
  const fastify = Fastify()
  const payload = { hello: 'world' }

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

    setTimeout(next, 500)
  })

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

test('inject get request', t => {
  t.plan(4)
  const fastify = Fastify()
  const payload = { hello: 'world' }

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

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

test('inject get request - code check', t => {
  t.plan(4)
  const fastify = Fastify()
  const payload = { hello: 'world' }

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

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

test('inject get request - headers check', t => {
  t.plan(4)
  const fastify = Fastify()

  fastify.get('/', (req, reply) => {
    reply.header('content-type', 'text/plain').send('')
  })

  fastify.inject({
    method: 'GET',
    url: '/'
  }, (err, res) => {
    t.error(err)
    t.strictEqual('', res.payload)
    t.strictEqual(res.headers['content-type'], 'text/plain')
    t.strictEqual(res.headers['content-length'], '0')
  })
})

test('inject get request - querystring', t => {
  t.plan(4)
  const fastify = Fastify()

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

  fastify.inject({
    method: 'GET',
    url: '/?hello=world'
  }, (err, res) => {
    t.error(err)
    t.deepEqual({ hello: 'world' }, JSON.parse(res.payload))
    t.strictEqual(res.statusCode, 200)
    t.strictEqual(res.headers['content-length'], '17')
  })
})

test('inject get request - params', t => {
  t.plan(4)
  const fastify = Fastify()

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

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

test('inject get request - wildcard', t => {
  t.plan(4)
  const fastify = Fastify()

  fastify.get('/test/*', (req, reply) => {
    reply.send(req.params)
  })

  fastify.inject({
    method: 'GET',
    url: '/test/wildcard'
  }, (err, res) => {
    t.error(err)
    t.deepEqual({ '*': 'wildcard' }, JSON.parse(res.payload))
    t.strictEqual(res.statusCode, 200)
    t.strictEqual(res.headers['content-length'], '16')
  })
})

test('inject get request - headers', t => {
  t.plan(4)
  const fastify = Fastify()

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

  fastify.inject({
    method: 'GET',
    url: '/',
    headers: { hello: 'world' }
  }, (err, res) => {
    t.error(err)
    t.strictEqual('world', JSON.parse(res.payload).hello)
    t.strictEqual(res.statusCode, 200)
    t.strictEqual(res.headers['content-length'], '69')
  })
})

test('inject post request', t => {
  t.plan(4)
  const fastify = Fastify()
  const payload = { hello: 'world' }

  fastify.post('/', (req, reply) => {
    reply.send(req.body)
  })

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

test('inject post request - send stream', t => {
  t.plan(4)
  const fastify = Fastify()

  fastify.post('/', (req, reply) => {
    reply.send(req.body)
  })

  fastify.inject({
    method: 'POST',
    url: '/',
    headers: { 'content-type': 'application/json' },
    payload: getStream()
  }, (err, res) => {
    t.error(err)
    t.deepEqual('{"hello":"world"}', res.payload)
    t.strictEqual(res.statusCode, 200)
    t.strictEqual(res.headers['content-length'], '17')
  })
})

test('inject get request - reply stream', t => {
  t.plan(3)
  const fastify = Fastify()

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

  fastify.inject({
    method: 'GET',
    url: '/'
  }, (err, res) => {
    t.error(err)
    t.deepEqual('{"hello":"world"}', res.payload)
    t.strictEqual(res.statusCode, 200)
  })
})

test('inject promisify - waiting for ready event', t => {
  t.plan(1)
  const fastify = Fastify()
  const payload = { hello: 'world' }

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

  const injectParams = {
    method: 'GET',
    url: '/'
  }
  fastify.inject(injectParams)
    .then(res => {
      t.strictEqual(res.statusCode, 200)
    })
    .catch(t.fail)
})

test('inject promisify - after the ready event', t => {
  t.plan(2)
  const fastify = Fastify()
  const payload = { hello: 'world' }

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

  fastify.ready(err => {
    t.error(err)

    const injectParams = {
      method: 'GET',
      url: '/'
    }
    fastify.inject(injectParams)
      .then(res => {
        t.strictEqual(res.statusCode, 200)
      })
      .catch(t.fail)
  })
})

test('inject promisify - when the server is up', t => {
  t.plan(2)
  const fastify = Fastify()
  const payload = { hello: 'world' }

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

  fastify.ready(err => {
    t.error(err)

    // setTimeout because the ready event don't set "started" flag
    // in this iteration of the 'event loop'
    setTimeout(() => {
      const injectParams = {
        method: 'GET',
        url: '/'
      }
      fastify.inject(injectParams)
        .then(res => {
          t.strictEqual(res.statusCode, 200)
        })
        .catch(t.fail)
    }, 10)
  })
})

test('should reject in error case', t => {
  t.plan(1)
  const fastify = Fastify()

  const error = new Error('DOOM!')
  fastify.register((instance, opts, next) => {
    setTimeout(next, 500, error)
  })

  fastify.inject({
    method: 'GET',
    url: '/'
  })
    .catch(e => {
      t.strictEqual(e, error)
    })
})

test('inject a multipart request using form-body', t => {
  t.plan(2)
  const fastify = Fastify()

  fastify.addContentTypeParser('*', function (req, payload, done) {
    var body = ''
    payload.on('data', d => {
      body += d
    })
    payload.on('end', () => {
      done(null, body)
    })
  })
  fastify.post('/', (req, reply) => {
    reply.send(req.body)
  })

  const form = new FormData()
  form.append('my_field', 'my value')

  fastify.inject({
    method: 'POST',
    url: '/',
    payload: form
  })
    .then(response => {
      t.equal(response.statusCode, 200)
      t.ok(/Content-Disposition: form-data; name="my_field"/.test(response.payload))
    })
})

// https://github.com/hapijs/shot/blob/master/test/index.js#L836
function getStream () {
  const Read = function () {
    Stream.Readable.call(this)
  }
  util.inherits(Read, Stream.Readable)
  const word = '{"hello":"world"}'
  var i = 0

  Read.prototype._read = function (size) {
    this.push(word[i] ? word[i++] : null)
  }

  return new Read()
}

test('should error the promise if ready errors', t => {
  t.plan(3)
  const fastify = Fastify()

  fastify.register((instance, opts) => {
    return Promise.reject(new Error('kaboom'))
  }).after(function () {
    t.pass('after is called')
  })

  fastify.inject({
    method: 'GET',
    url: '/'
  }).then(() => {
    t.fail('this should not be called')
  }).catch(err => {
    t.ok(err)
    t.strictequal(err.message, 'kaboom')
  })
})

test('should throw error if callback specified and if ready errors', t => {
  t.plan(2)
  const fastify = Fastify()
  const error = new Error('kaboom')

  fastify.register((instance, opts) => {
    return Promise.reject(error)
  })

  fastify.inject({
    method: 'GET',
    url: '/'
  }, err => {
    t.ok(err)
    t.strictEqual(err, error)
  })
})

test('should support builder-style injection with ready app', async (t) => {
  t.plan(3)
  const fastify = Fastify()
  const payload = { hello: 'world' }

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

  await fastify.ready()
  const res = await fastify.inject().get('/').end()
  t.deepEqual(payload, JSON.parse(res.payload))
  t.strictEqual(res.statusCode, 200)
  t.strictEqual(res.headers['content-length'], '17')
})

test('should support builder-style injection with non-ready app', async (t) => {
  t.plan(3)
  const fastify = Fastify()
  const payload = { hello: 'world' }

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

  const res = await fastify.inject().get('/').end()
  t.deepEqual(payload, JSON.parse(res.payload))
  t.strictEqual(res.statusCode, 200)
  t.strictEqual(res.headers['content-length'], '17')
})

test('should handle errors in builder-style injection correctly', async (t) => {
  t.plan(2)
  const fastify = Fastify()
  fastify.register((instance, opts, next) => {
    next(new Error('Kaboom'))
  })

  try {
    await fastify.inject().get('/')
  } catch (err) {
    t.ok(err)
    t.strictEqual(err.message, 'Kaboom')
  }
})


'use strict'

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

test('proto-poisoning error', t => {
  t.plan(3)

  const fastify = Fastify()
  t.tearDown(fastify.close.bind(fastify))

  fastify.post('/', (request, reply) => {
    t.fail('handler should not be called')
  })

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

    sget({
      method: 'POST',
      url: 'http://localhost:' + fastify.server.address().port,
      headers: { 'Content-Type': 'application/json' },
      body: '{ "__proto__": { "a": 42 } }'
    }, (err, response, body) => {
      t.error(err)
      t.strictEqual(response.statusCode, 400)
    })
  })
})

test('proto-poisoning remove', t => {
  t.plan(4)

  const fastify = Fastify({ onProtoPoisoning: 'remove' })
  t.tearDown(fastify.close.bind(fastify))

  fastify.post('/', (request, reply) => {
    t.equal(undefined, Object.assign({}, request.body).a)
    reply.send({ ok: true })
  })

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

    sget({
      method: 'POST',
      url: 'http://localhost:' + fastify.server.address().port,
      headers: { 'Content-Type': 'application/json' },
      body: '{ "__proto__": { "a": 42 }, "b": 42 }'
    }, (err, response, body) => {
      t.error(err)
      t.strictEqual(response.statusCode, 200)
    })
  })
})

test('proto-poisoning ignore', t => {
  t.plan(4)

  const fastify = Fastify({ onProtoPoisoning: 'ignore' })
  t.tearDown(fastify.close.bind(fastify))

  fastify.post('/', (request, reply) => {
    t.equal(42, Object.assign({}, request.body).a)
    reply.send({ ok: true })
  })

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

    sget({
      method: 'POST',
      url: 'http://localhost:' + fastify.server.address().port,
      headers: { 'Content-Type': 'application/json' },
      body: '{ "__proto__": { "a": 42 }, "b": 42 }'
    }, (err, response, body) => {
      t.error(err)
      t.strictEqual(response.statusCode, 200)
    })
  })
})

test('constructor-poisoning error (default in v3)', t => {
  t.plan(3)

  const fastify = Fastify()
  t.tearDown(fastify.close.bind(fastify))

  fastify.post('/', (request, reply) => {
    reply.send('ok')
  })

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

    sget({
      method: 'POST',
      url: 'http://localhost:' + fastify.server.address().port,
      headers: { 'Content-Type': 'application/json' },
      body: '{ "constructor": { "prototype": { "foo": "bar" } } }'
    }, (err, response, body) => {
      t.error(err)
      t.strictEqual(response.statusCode, 400)
    })
  })
})

test('constructor-poisoning error', t => {
  t.plan(3)

  const fastify = Fastify({ onConstructorPoisoning: 'error' })
  t.tearDown(fastify.close.bind(fastify))

  fastify.post('/', (request, reply) => {
    t.fail('handler should not be called')
  })

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

    sget({
      method: 'POST',
      url: 'http://localhost:' + fastify.server.address().port,
      headers: { 'Content-Type': 'application/json' },
      body: '{ "constructor": { "prototype": { "foo": "bar" } } }'
    }, (err, response, body) => {
      t.error(err)
      t.strictEqual(response.statusCode, 400)
    })
  })
})

test('constructor-poisoning remove', t => {
  t.plan(4)

  const fastify = Fastify({ onConstructorPoisoning: 'remove' })
  t.tearDown(fastify.close.bind(fastify))

  fastify.post('/', (request, reply) => {
    t.equal(undefined, Object.assign({}, request.body).foo)
    reply.send({ ok: true })
  })

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

    sget({
      method: 'POST',
      url: 'http://localhost:' + fastify.server.address().port,
      headers: { 'Content-Type': 'application/json' },
      body: '{ "constructor": { "prototype": { "foo": "bar" } } }'
    }, (err, response, body) => {
      t.error(err)
      t.strictEqual(response.statusCode, 200)
    })
  })
})


Fastify

## Plugins
Fastify allows the user to extend its functionalities with plugins.
A plugin can be a set of routes, a server [decorator](./Decorators.md) or whatever. The API that you will need to use one or more plugins, is `register`.

By default, `register` creates a *new scope*, this means that if you do some changes to the Fastify instance (via `decorate`), this change will not be reflected to the current context ancestors, but only to its sons. This feature allows us to achieve plugin *encapsulation* and *inheritance*, in this way we create a *direct acyclic graph* (DAG) and we will not have issues caused by cross dependencies.

You already see in the [getting started](./Getting-Started.md#register) section how using this API is pretty straightforward.
```
fastify.register(plugin, [options])
```

### Plugin Options
The optional `options` parameter for `fastify.register` supports a predefined set of options that Fastify itself will use, except when the plugin has been wrapped with [fastify-plugin](https://github.com/fastify/fastify-plugin). This options object will also be passed to the plugin upon invocation, regardless of whether or not the plugin has been wrapped. The currently supported list of Fastify specific options is:

+ [`logLevel`](./Routes.md#custom-log-level)
+ [`logSerializers`](./Routes.md#custom-log-serializer)
+ [`prefix`](./Plugins.md#route-prefixing-options)

**Note: Those options will be ignored when used with fastify-plugin**

It is possible that Fastify will directly support other options in the future. Thus, to avoid collisions, a plugin should consider namespacing its options. For example, a plugin `foo` might be registered like so:

```js
fastify.register(require('fastify-foo'), {
  prefix: '/foo',
  foo: {
    fooOption1: 'value',
    fooOption2: 'value'
  }
})
```

If collisions are not a concern, the plugin may simply accept the options object as-is:

```js
fastify.register(require('fastify-foo'), {
  prefix: '/foo',
  fooOption1: 'value',
  fooOption2: 'value'
})
```

The `options` parameter can also be a `Function` which will be evaluated at the time the plugin is registered while giving access to the fastify instance via the first positional argument:

```js
const fp = require('fastify-plugin')

fastify.register(fp((fastify, opts, done) => {
  fastify.decorate('foo_bar', { hello: 'world' })

  done()
}))

// The opts argument of fastify-foo will be { hello: 'world' }
fastify.register(require('fastify-foo'), parent => parent.foo_bar)
```

The fastify instance passed on to the function is the latest state of the **external fastify instance** the plugin was declared on, allowing access to variables injected via [`decorate`](./Decorators.md) by preceding plugins according to the **order of registration**. This is useful in case a plugin depends on changes made to the Fastify instance by a preceding plugin f.e. utilizing an existing database connection to wrap around it.

Keep in mind that the fastify instance passed on to the function is the same as the one that will be passed in to the plugin, a copy of the external fastify instance rather than a reference. Any usage of the instance will behave the same as it would if called within the plugin's function i.e. if `decorate` is called, the decorated variables will be available within the plugin's function unless it was wrapped with [`fastify-plugin`](https://github.com/fastify/fastify-plugin).

#### Route Prefixing option
If you pass an option with the key `prefix` with a `string` value, Fastify will use it to prefix all the routes inside the register, for more info check [here](./Routes.md#route-prefixing).

Be aware that if you use [`fastify-plugin`](https://github.com/fastify/fastify-plugin) this option won't work.

#### Error handling
The error handling is done by [avvio](https://github.com/mcollina/avvio#error-handling).

As general rule, it is highly recommended that you handle your errors in the next `after` or `ready` block, otherwise you will get them inside the `listen` callback.

```js
fastify.register(require('my-plugin'))

// `after` will be executed once
// the previous declared `register` has finished
fastify.after(err => console.log(err))

// `ready` will be executed once all the registers declared
// have finished their execution
fastify.ready(err => console.log(err))

// `listen` is a special ready,
// so it behaves in the same way
fastify.listen(3000, (err, address) => {
  if (err) console.log(err)
})
```

### async/await

*async/await* is supported by `after`, `ready` and `listen`, as well as
`fastify` being a [Thenable](https://promisesaplus.com/).

```js
await fastify.register(require('my-plugin'))

await fastify.after()

await fastify.ready()

await fastify.listen(3000)
```

#### ESM support

ESM is supported as well from [Node.js `v13.3.0`](https://nodejs.org/api/esm.html) and above!

```js
// main.mjs
import Fastify from 'fastify'
const fastify = Fastify()

fastify.register(import('./plugin.mjs'))

fastify.listen(3000, console.log)

// plugin.mjs
async function plugin (fastify, opts) {
  fastify.get('/', async (req, reply) => {
    return { hello: 'world' }
  })
}

export default plugin
```

### Create a plugin
Creating a plugin is very easy, you just need to create a function that takes three parameters, the `fastify` instance, an `options` object and the `done` callback.

Example:
```js
module.exports = function (fastify, opts, done) {
  fastify.decorate('utility', () => {})

  fastify.get('/', handler)

  done()
}
```
You can also use `register` inside another `register`:
```js
module.exports = function (fastify, opts, done) {
  fastify.decorate('utility', () => {})

  fastify.get('/', handler)

  fastify.register(require('./other-plugin'))

  done()
}
```
Sometimes, you will need to know when the server is about to close, for example because you must close a connection to a database. To know when this is going to happen, you can use the [`'onClose'`](./Hooks.md#on-close) hook.

Do not forget that `register` will always create a new Fastify scope, if you don't need that, read the following section.

### Handle the scope
If you are using `register` only for extending the functionality of the server with  [`decorate`](./Decorators.md), it is your responsibility to tell Fastify to not create a new scope, otherwise your changes will not be accessible by the user in the upper scope.

You have two ways to tell Fastify to avoid the creation of a new context:
- Use the [`fastify-plugin`](https://github.com/fastify/fastify-plugin) module
- Use the `'skip-override'` hidden property

We recommend to using the `fastify-plugin` module, because it solves this problem for you, and you can pass a version range of Fastify as a parameter that your plugin will support.
```js
const fp = require('fastify-plugin')

module.exports = fp(function (fastify, opts, done) {
  fastify.decorate('utility', () => {})
  done()
}, '0.x')
```
Check the [`fastify-plugin`](https://github.com/fastify/fastify-plugin) documentation to know more about how use this module.

If you don't use the `fastify-plugin` module, you can use the `'skip-override'` hidden property, but we do not recommend it. If in the future the Fastify API changes it will be a your responsibility update the module, while if you use `fastify-plugin`, you can be sure about backwards compatibility.
```js
function yourPlugin (fastify, opts, done) {
  fastify.decorate('utility', () => {})
  done()
}
yourPlugin[Symbol.for('skip-override')] = true
module.exports = yourPlugin
```


keep keep keep keep keep replace keep keep keep keep keep

expectType<FastifyInstance<https.Server, http.IncomingMessage, http.ServerResponse> & PromiseLike<FastifyInstance<https.Server, http.IncomingMessage, http.ServerResponse>>>(httpsServer)

import fastify, { FastifyInstance, FastifyPluginOptions } from '../../fastify'
import * as http from 'http'
import * as https from 'https'
import { expectType, expectError, expectAssignable } from 'tsd'
import { FastifyPluginCallback, FastifyPluginAsync } from '../../types/plugin'
import { FastifyError } from 'fastify-error'

// FastifyPlugin & FastifyRegister
interface TestOptions extends FastifyPluginOptions {
  option1: string;
  option2: boolean;
}
const testPluginOpts: FastifyPluginCallback<TestOptions> = function (instance, opts, next) { }
const testPluginOptsAsync: FastifyPluginAsync<TestOptions> = async function (instance, opts) { }

const testPluginOptsWithType = (instance: FastifyInstance, opts: FastifyPluginOptions, next: (error?: FastifyError) => void) => { };
const testPluginOptsWithTypeAsync = async (instance: FastifyInstance, opts: FastifyPluginOptions) => { };

expectError(fastify().register(testPluginOpts, {})) // error because missing required options from generic declaration
expectError(fastify().register(testPluginOptsAsync, {})) // error because missing required options from generic declaration

expectAssignable<FastifyInstance>(fastify().register(testPluginOpts, { option1: '', option2: true }))
expectAssignable<FastifyInstance>(fastify().register(testPluginOptsAsync, { option1: '', option2: true }))

expectAssignable<FastifyInstance>(fastify().register(function (instance, opts, next) { }))
expectAssignable<FastifyInstance>(fastify().register(function (instance, opts, next) { }, () => { }))
expectAssignable<FastifyInstance>(fastify().register(function (instance, opts, next) { }, { logLevel: 'info', prefix: 'foobar' }))

const testPluginCallback: FastifyPluginCallback = function (instance, opts, next) { }
expectAssignable<FastifyInstance>(fastify().register(testPluginCallback, {}))

const testPluginAsync: FastifyPluginAsync = async function (instance, opts) { }
expectAssignable<FastifyInstance>(fastify().register(testPluginAsync, {}))

expectAssignable<FastifyInstance>(fastify().register(function (instance, opts): Promise<void> { return Promise.resolve() }))
expectAssignable<FastifyInstance>(fastify().register(async function (instance, opts) { }, () => { }))
expectAssignable<FastifyInstance>(fastify().register(async function (instance, opts) { }, { logLevel: 'info', prefix: 'foobar' }))

expectError(fastify().register(function (instance, opts, next) { }, { logLevel: '' })) // must use a valid logLevel

const httpsServer = fastify({ https: {} });
expectType<FastifyInstance<https.Server, http.IncomingMessage, http.ServerResponse>>(httpsServer)

// Chainable
httpsServer
  .register(testPluginOpts)
  .after((_error) => { })
  .close(() => { })
  .ready((_error) => { })

// Thenable
expectAssignable<PromiseLike<undefined>>(httpsServer.after());
expectAssignable<PromiseLike<undefined>>(httpsServer.close());
expectAssignable<PromiseLike<undefined>>(httpsServer.ready());
expectAssignable<PromiseLike<undefined>>(httpsServer.register(testPluginOpts));
expectAssignable<PromiseLike<undefined>>(httpsServer.register(testPluginOptsWithType));
expectAssignable<PromiseLike<undefined>>(httpsServer.register(testPluginOptsWithTypeAsync));
expectAssignable<PromiseLike<undefined>>(httpsServer.register(testPluginOptsWithType, { prefix: "/test" }));
expectAssignable<PromiseLike<undefined>>(httpsServer.register(testPluginOptsWithTypeAsync, { prefix: "/test" }));

async function testAsync() {
  await httpsServer
    .register(testPluginOpts)
    .register(testPluginOpts)
}


Fastify

## Benchmarking
Benchmarking is important if you want to measure how a change can impact the performance of your application. We provide a simple way to benchmark your application from the point of view of a user and contributor. The setup allows you to automate benchmarks in different branches and on different Node.js versions.

The modules we'll use:
- [Autocannon](https://github.com/mcollina/autocannon): A HTTP/1.1 benchmarking tool written in node.
- [Branch-comparer](https://github.com/StarpTech/branch-comparer): Checkout multiple git branches, execute scripts and log the results.
- [Concurrently](https://github.com/kimmobrunfeldt/concurrently): Run commands concurrently.
- [Npx](https://github.com/zkat/npx) NPM package runner - We are using it to run scripts against different Node.js Versions and to execute local binaries. Shipped with npm@5.2.0.

## Simple

### Run the test in the current branch
```sh
npm run benchmark
```

### Run the test against different Node.js versions ✨
```sh
npx -p node@10 -- npm run benchmark
```

## Advanced

### Run the test in different branches
```sh
branchcmp --rounds 2 --script "npm run benchmark"
```

### Run the test in different branches against different Node.js versions ✨
```sh
branchcmp --rounds 2 --script "npm run benchmark"
```

### Compare current branch with master (Gitflow)
```sh
branchcmp --rounds 2 --gitflow --script "npm run benchmark"
```
or
```sh
npm run bench
```

### Run different examples

```sh
branchcmp --rounds 2 -s "node ./node_modules/concurrently -k -s first \"node ./examples/asyncawait.js\" \"node ./node_modules/autocannon -c 100 -d 5 -p 10 localhost:3000/\""
```


/* eslint-disable @typescript-eslint/no-explicit-any */

import { Buffer } from 'buffer'
import { RawServerBase, RawServerDefault, RawRequestDefaultExpression } from './utils'
import { FastifyRequest } from './request'
import { RouteGenericInterface } from './route'

type ContentTypeParserDoneFunction = (err: Error | null, body?: any) => void

/**
 * Body parser method that operatoes on request body
 */
export type FastifyBodyParser<
  RawBody extends string | Buffer,
  RawServer extends RawServerBase = RawServerDefault,
  RawRequest extends RawRequestDefaultExpression<RawServer> = RawRequestDefaultExpression<RawServer>,
  RouteGeneric extends RouteGenericInterface = RouteGenericInterface,
> = ((request: FastifyRequest<RouteGeneric, RawServer, RawRequest>, rawBody: RawBody, done: ContentTypeParserDoneFunction) => void)
| ((request: FastifyRequest<RouteGeneric, RawServer, RawRequest>, rawBody: RawBody) => Promise<any>)

/**
 * Content Type Parser method that operates on request content
 */
export type FastifyContentTypeParser<
  RawServer extends RawServerBase = RawServerDefault,
  RawRequest extends RawRequestDefaultExpression<RawServer> = RawRequestDefaultExpression<RawServer>,
  RouteGeneric extends RouteGenericInterface = RouteGenericInterface,
> = ((request: FastifyRequest<RouteGeneric, RawServer, RawRequest>, payload: RawRequest) => Promise<any>)
| ((request: FastifyRequest<RouteGeneric, RawServer, RawRequest>, payload: RawRequest, done: ContentTypeParserDoneFunction) => void)

/**
 * Natively, Fastify only supports 'application/json' and 'text/plain' content types. The default charset is utf-8. If you need to support different content types, you can use the addContentTypeParser API. The default JSON and/or plain text parser can be changed.
 */
export interface AddContentTypeParser<
  RawServer extends RawServerBase = RawServerDefault,
  RawRequest extends RawRequestDefaultExpression<RawServer> = RawRequestDefaultExpression<RawServer>
> {
  (
    contentType: string | string[],
    opts: {
      bodyLimit?: number;
    },
    parser: FastifyContentTypeParser<RawServer, RawRequest>
  ): void;
}

export interface AddContentTypeParser<
  RawServer extends RawServerBase = RawServerDefault,
  RawRequest extends RawRequestDefaultExpression<RawServer> = RawRequestDefaultExpression<RawServer>
> {
  (contentType: string | string[], parser: FastifyContentTypeParser<RawServer, RawRequest>): void;
}

export interface AddContentTypeParser<
  RawServer extends RawServerBase = RawServerDefault,
  RawRequest extends RawRequestDefaultExpression<RawServer> = RawRequestDefaultExpression<RawServer>
>{
  <parseAs extends string | Buffer>(
    contentType: string | string[],
    opts: {
      parseAs: parseAs extends Buffer ? 'buffer' : 'string';
      bodyLimit?: number;
    },
    parser: FastifyBodyParser<parseAs, RawServer, RawRequest>
  ): void;
}

/**
 * Checks for a type parser of a content type
 */
export type hasContentTypeParser = (contentType: string) => boolean


/*
 * Copyright (c) 2013 Adobe Systems Incorporated. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

/*jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4, maxerr: 50 */
/*global define */

/**
 * LiveDevServerManager Overview:
 *
 * The LiveDevServerManager allows extensions to register to be Live Development
 * servers. Servers are queried for their ability to serve a page in
 * order of descending priority by way their canServe methods.
 *
 * NOTE: This API is currently experimental and intented to be internal-only.
 * It is very likely that it will be changed in the near future and/or
 * removed entirely.
 *
 *   `LiveDevServerManager.getServer(localPath)`
 *
 * Returns highest priority server (BaseServer) that can serve the local file.
 *
 * A Live Development server must implement the BaseServer API. See
 * LiveDevelopment/Servers/BaseServer base class.
 */
define(function (require, exports, module) {
    "use strict";

    var _serverProviders   = [];

    /**
     * @private
     * Comparator to sort providers based on their priority
     * @param {number} a
     * @param {number} b
     */
    function _providerSort(a, b) {
        return b.priority - a.priority;
    }

    /**
     * Determines which provider can serve a file with a local path.
     *
     * @param {string} localPath A local path to file being served.
     * @return {?BaseServer} A server no null if no servers can serve the file
     */
    function getServer(localPath) {
        var provider, server, i;

        for (i = 0; i < _serverProviders.length; i++) {
            provider = _serverProviders[i];
            server = provider.create();

            if (server.canServe(localPath)) {
                return server;
            }
        }

        return null;
    }

    /**
     * The method by which a server registers itself. It returns an
     * object handler that can be used to remove that server from the list.
     *
     * @param {BaseServer|{create: function():BaseServer}} provider
     *  The provider to be registered, described below.
     * @param {number} priority
     *  A non-negative number used to break ties among providers for a
     *  particular url. Providers that register with a higher priority will
     *  have the opportunity to provide a given url before those with a
     *  lower priority. The higher the number, the higher the priority.
     * @return {{create: function():BaseServer, priority: number}}
     */
    function registerServer(provider, priority) {
        if (!provider.create) {
            console.error("Incompatible live development server provider");
            return;
        }

        var providerObj = {};

        providerObj.create = provider.create;
        providerObj.priority = priority || 0;

        _serverProviders.push(providerObj);
        _serverProviders.sort(_providerSort);

        return providerObj;
    }

    /**
     * Remove a server from the list of the registered providers.
     *
     * @param {{create: function():BaseServer}, priority: number}} provider
     * The provider to be removed.
     */
    function removeServer(provider) {
        var i;
        for (i = 0; i < _serverProviders.length; i++) {
            if (provider === _serverProviders[i]) {
                _serverProviders.splice(i, 1);
            }
        }
    }

    // Backwards compatibility
    exports.getProvider         = getServer;
    exports.registerProvider    = registerServer;

    // Define public API
    exports.getServer           = getServer;
    exports.registerServer      = registerServer;
    exports.removeServer        = removeServer;
});