fyrejet
Version:
Web Framework for node.js that strives to provide (almost) perfect compatibility with Express, while providing better performance, where you need it.
237 lines (208 loc) • 7.8 kB
JavaScript
const onChange = require('on-change')
const fastDecode = require('fast-decode-uri-component')
function next (middlewares, middlewaresArgsNum, req, res, index, routeIndex, routers = {}, defaultRoute, errorHandler, error) {
function nextRunErrLoop (errMiddlewareIndex) { // used for error handling
for (let n = index, o = middlewares.length; n < o; n++) {
const argsNum = middlewaresArgsNum[n]
if (argsNum >= 4) {
errMiddlewareIndex = n
return errMiddlewareIndex
}
}
}
if (index === 0) {
req.stepString = step.toString()
req.route = req.routesToProcess[0]
return paramOrNot(() => middlewares[0](req, res, step))
}
const middlewareArgsNum = middlewaresArgsNum[index]
if (!middlewares[index]) return defaultRoute(req, res)
function paramOrNot (cb) {
if (!req.currentRouteMiddlewareNum) {
req.currentRouteMiddlewareNum = 0
if (req.route.keys.length) { // we don't want to extract params at EVERY middleware and at routes with no keys
if (!req.rData_internal.urlProperVerified) {
res.statusCode = 400
res.statusMessage = 'Bad Request'
return res.end()
}
getParams(req.route)
}
}
++req.currentRouteMiddlewareNum
if ((req.route.handlers.length) === req.currentRouteMiddlewareNum) {
routeIndex++
delete req.currentRouteMiddlewareNum
}
return cb()
}
function getParams (route) {
let matches
const params = {}
if (Array.isArray(route.keys)) {
matches = route.pattern.exec(req.path)
if (matches === null) return
for (let j = 0; j < route.keys.length;) params[route.keys[j]] = matches[++j]
return furtherGetParams()
}
matches = route.pattern.exec(req.path)
if (matches === null) return
// params.length = route.keys.length
let j = 0
let finishMatchLoop = false
while (!finishMatchLoop) {
if (matches[j]) {
params[j] = matches[j]
j++
} else {
finishMatchLoop = true
params.length = j
}
}
Array.prototype.splice.call(params, 0, 1)
Object.keys(params).forEach(key => {
if (!params[key]) {
delete params[key]
}
})
delete params.length
return furtherGetParams()
function furtherGetParams () {
if (req.params['0']) {
let n = 0
let nEmptyNotReached = true
while (nEmptyNotReached) {
if (req.params[n]) {
n++
if (req.params[n] === 'undefined' || req.params[n] === 'null') delete onChange.target(req.params)[n]
} else {
nEmptyNotReached = false
for (let x = 0, y = route.keys.length; x < y; ++x) {
if (params[x]) {
onChange.target(req.params)[n] = params[x]
}
n++
}
break
}
}
if (req.app.mounted) return
}
if (route.starCatchAll) { // the http://example.com/* route ;)
onChange.target(req.params)['0'] = '/' + fastDecode(params['0'])
return
}
let prevParams = req.rData_internal.paramsPrev[req.rData_internal.paramsPrev.length - 1 || 0] || {}
prevParams = JSON.stringify(prevParams)
if (prevParams !== JSON.stringify(params)) req.rData_internal.paramsPrev.push(Object.assign({}, params))
Object.keys(params).forEach(key => {
if (!req.paramsUserDefined.includes(key)) {
onChange.target(req.params)[key] = params[key] !== undefined ? fastDecode(params[key]) : undefined
}
})
}
}
function step (err) {
let errMiddlewareIndex // together with runErrLoop this is used for error handling
function runErrLoop () {
for (let n = index, o = middlewaresArgsNum.length; n < o; n++) {
const argsNum = middlewaresArgsNum[n]
if (argsNum >= 4) {
errMiddlewareIndex = n
break
}
}
}
if (!(req.params === Object(req.params))) req.params = req.rData_internal.paramsPrev[req.rData_internal.paramsPrev.length - 1] || {}
if (req.routesToProcess[routeIndex]) req.rData_internal.lastPattern = req.routesToProcess[routeIndex].pattern
switch (err) {
case null:
case undefined:
return stepNormally()
case 'route':
if (req.currentRouteMiddlewareNum) {
const middlewaresPassed = req.currentRouteMiddlewareNum
const middlewaresTotal = req.routesToProcess[routeIndex].handlers.length
const middlewaresToSkip = middlewaresTotal - middlewaresPassed
index = index + middlewaresToSkip + 1
req.paramsCalled = {}
routeIndex++
delete req.currentRouteMiddlewareNum
return next(middlewares, middlewaresArgsNum, req, res, index, routeIndex, routers, defaultRoute, errorHandler)
}
return stepNormally()
case 'router':
if (req.currentRouteMiddlewareNum) {
delete req.currentRouteMiddlewareNum
}
req.paramsCalled = {}
return next(middlewares, middlewaresArgsNum, req, res, middlewares.length - 1, routeIndex, routers, defaultRoute, errorHandler)
default:
runErrLoop()
if (errMiddlewareIndex === index) {
errMiddlewareIndex = null
++index
runErrLoop()
}
if (errMiddlewareIndex) return next(middlewares, middlewaresArgsNum, req, res, errMiddlewareIndex, routeIndex, routers, defaultRoute, errorHandler, err)
return errorHandler(err, req, res)
}
function stepNormally () {
if (req.rData_internal.methodPrev !== req.method || req.rData_internal.urlPrev !== req.url) {
req.rData_internal.methodPrev = req.method
req.rData_internal.urlPrev = req.url
return req.app.getRouter().lookup(req, res, step)
}
let newIndex
for (let n = index + 1, o = middlewares.length; n < o; n++) {
const argsNum = middlewaresArgsNum[n]
if (argsNum < 4) {
newIndex = n
break
}
newIndex = n
}
for (; routeIndex < req.routesToProcess.length; routeIndex++) {
if (req.routesToProcess[routeIndex].handlers.includes(middlewares[newIndex])) {
index = newIndex
return next(middlewares, middlewaresArgsNum, req, res, index, routeIndex, routers, defaultRoute, errorHandler, error)
}
}
routeIndex = req.routesToProcess.length - 1
index = newIndex
return next(middlewares, middlewaresArgsNum, req, res, index, routeIndex, routers, defaultRoute, errorHandler, error)
}
}
req.route = req.routesToProcess[routeIndex]
req.next = step
if (!req.route) return res.defaultErrHandler()
return paramOrNot(finishStage)
function finishStage () {
try {
if (middlewares[index].id) {
const pattern = routers[middlewares[index].id]
if (pattern) {
req.rData_internal.urlPrevious.push(req.url)
const mountpath = middlewares[index].getRouter().getSequentialConfig().mountpath || pattern
req.url = req.url.replace(mountpath, '')
delete req.rData_internal.lastPattern
}
return middlewares[index].lookup(req, res, step)
}
if (middlewareArgsNum > 3) {
if (!error && req.method !== 'OPTIONS') {
return defaultRoute(req, res)
}
return middlewares[index](error, req, res, step)
}
return middlewares[index](req, res, step)
} catch (err) {
let errMiddlewareIndex
nextRunErrLoop(errMiddlewareIndex)
if (errMiddlewareIndex) return middlewares[errMiddlewareIndex](err, req, res, step)
return errorHandler(err, req, res)
}
}
}
module.exports = next