UNPKG

@fastify/middie

Version:

Middleware engine for Fastify

186 lines (162 loc) 4.49 kB
'use strict' const reusify = require('reusify') const { pathToRegexp } = require('path-to-regexp') const FindMyWay = require('find-my-way') function middie (complete, options = {}) { const middlewares = [] const pool = reusify(Holder) const ignoreDuplicateSlashes = options.ignoreDuplicateSlashes === true const useSemicolonDelimiter = options.useSemicolonDelimiter === true const ignoreTrailingSlash = options.ignoreTrailingSlash === true const normalizationOptions = { ignoreDuplicateSlashes, useSemicolonDelimiter, ignoreTrailingSlash } return { use, run } function use (url, f) { if (f === undefined) { f = url url = null } let regexp if (url) { const pathRegExp = pathToRegexp(sanitizePrefixUrl(url), { end: false }) regexp = pathRegExp.regexp } if (Array.isArray(f)) { for (const val of f) { middlewares.push({ regexp, fn: val }) } } else { middlewares.push({ regexp, fn: f }) } return this } function run (req, res, ctx) { if (!middlewares.length) { complete(null, req, res, ctx) return } req.originalUrl = req.url const holder = pool.get() holder.req = req holder.res = res const sanitized = sanitizeUrl(req.url) holder.normalizedUrl = normalizePathForMatching(sanitized, normalizationOptions) holder.sanitizedUrl = sanitized holder.urlSuffix = req.url.slice(sanitized.length) holder.context = ctx holder.done() } function Holder () { this.next = null this.req = null this.res = null this.normalizedUrl = null this.sanitizedUrl = null this.urlSuffix = null this.context = null this.i = 0 const that = this this.done = function (err) { const req = that.req const res = that.res const normalizedUrl = that.normalizedUrl const sanitizedUrl = that.sanitizedUrl const urlSuffix = that.urlSuffix const context = that.context const i = that.i++ req.url = req.originalUrl if (res.finished === true || res.writableEnded === true) { that.req = null that.res = null that.normalizedUrl = null that.sanitizedUrl = null that.urlSuffix = null that.context = null that.i = 0 pool.release(that) return } if (err || middlewares.length === i) { complete(err, req, res, context) that.req = null that.res = null that.normalizedUrl = null that.sanitizedUrl = null that.urlSuffix = null that.context = null that.i = 0 pool.release(that) } else { const middleware = middlewares[i] const fn = middleware.fn const regexp = middleware.regexp if (regexp) { const result = regexp.exec(normalizedUrl) if (result) { const origResult = regexp.exec(sanitizedUrl) if (origResult) { req.url = sanitizedUrl.slice(origResult[0].length) if (ignoreDuplicateSlashes) { req.url = FindMyWay.removeDuplicateSlashes(req.url) } if (ignoreTrailingSlash) { req.url = FindMyWay.trimLastSlash(req.url) } } else { req.url = normalizedUrl.slice(result[0].length) } if (req.url[0] !== '/') { req.url = '/' + req.url } req.url = req.url + urlSuffix fn(req, res, that.done) } else { that.done() } } else { fn(req, res, that.done) } } } } } function sanitizeUrl (url) { for (let i = 0, len = url.length; i < len; i++) { const charCode = url.charCodeAt(i) if (charCode === 63 || charCode === 35) { return url.slice(0, i) } } return url } function sanitizePrefixUrl (url) { if (url === '/') return '' if (url[url.length - 1] === '/') return url.slice(0, -1) return url } function normalizePathForMatching (url, options) { let path = url if (options.ignoreDuplicateSlashes) { path = FindMyWay.removeDuplicateSlashes(path) } path = FindMyWay.sanitizeUrlPath(path, options.useSemicolonDelimiter) if (options.ignoreTrailingSlash) { path = FindMyWay.trimLastSlash(path) } return path } module.exports = middie