UNPKG

fastify

Version:

Fast and low overhead web framework, for Node.js

118 lines (97 loc) 3.1 kB
/* eslint-disable no-useless-return */ 'use strict' const pump = require('pump') const safeStringify = require('fast-safe-stringify') const validation = require('./validation') const serialize = validation.serialize function Reply (req, res, handle) { this.res = res this.handle = handle this._req = req this.sent = false this._serializer = null } /** * Instead of using directly res.end(), we are using setImmediate(…) * This because we have observed that with this technique we are faster at responding to the various requests, * since the setImmediate forwards the res.end at the end of the poll phase of libuv in the event loop. * So we can gather multiple requests and then handle all the replies in a different moment, * causing a general improvement of performances, ~+10%. */ Reply.prototype.send = function (payload) { if (this.sent) { throw new Error('Reply already sent') } if (!payload) { if (!this.res.statusCode) { this.res.statusCode = 204 } this.res.setHeader('Content-Length', '0') setImmediate(wrapReplyEnd, this, '') return } if (payload instanceof Error) { if (!this.res.statusCode || this.res.statusCode < 500) { this.res.statusCode = 500 } this._req.log.error(payload) setImmediate(wrapReplyEnd, this, safeStringify(payload)) return } if (typeof payload.then === 'function') { return payload.then(wrapReplySend(this)).catch(wrapReplySend(this)) } if (payload._readableState) { if (!this.res.getHeader('Content-Type')) { this.res.setHeader('Content-Type', 'application/octet-stream') } return pump(payload, this.res, wrapPumpCallback(this)) } if (!this.res.getHeader('Content-Type') || this.res.getHeader('Content-Type') === 'application/json') { this.res.setHeader('Content-Type', 'application/json') // Here we are assuming that the custom serializer is a json serializer const str = this._serializer ? this._serializer(payload) : serialize(this.handle, payload) if (!this.res.getHeader('Content-Length')) { this.res.setHeader('Content-Length', Buffer.byteLength(str)) } setImmediate(wrapReplyEnd, this, str) return } // All the code below must have a 'content-type' setted if (this._serializer) { setImmediate(wrapReplyEnd, this, this._serializer(payload)) return } setImmediate(wrapReplyEnd, this, payload) return } Reply.prototype.header = function (key, value) { this.res.setHeader(key, value) return this } Reply.prototype.code = function (code) { this.res.statusCode = code return this } Reply.prototype.serializer = function (fn) { this._serializer = fn return this } function wrapPumpCallback (reply) { return function pumpCallback (err) { if (err) { reply._req.log.error(err) setImmediate(wrapReplyEnd, reply) } } } function wrapReplyEnd (reply, payload) { reply.sent = true reply.res.end(payload) } function wrapReplySend (reply, payload) { return function send (payload) { return reply.send(payload) } } module.exports = Reply