Comparing version 2.0.2 to 2.1.0
39
index.js
@@ -26,7 +26,7 @@ 'use strict' | ||
if (!args || !args.length) args = [3000] | ||
if (options.serverType === "uWebSockets") { | ||
if (options.serverType === 'uWebSockets') { | ||
var __address = {} | ||
server.__address = __address | ||
if (typeof args[args.length - 1] !== 'function') { | ||
server.serverType = options.serverType | ||
if (typeof args[args.length - 1] !== 'function') { | ||
args.push((socket) => { | ||
@@ -38,3 +38,3 @@ // stub function | ||
if (ipFamily !== 'IPv6' && ipFamily !== 'IPv4') ipFamily = 'IPv6' | ||
switch (args.length >= 3) { | ||
@@ -45,3 +45,3 @@ case true: | ||
server.__address.family = ipFamily | ||
break; | ||
break | ||
case false: | ||
@@ -55,3 +55,2 @@ server.__address.port = args[0] | ||
server.address = () => { | ||
return __address | ||
@@ -81,3 +80,3 @@ } | ||
}, | ||
uWebSockets: function() { | ||
uWebSockets: function () { | ||
if (server.keepAliveTimeout) return false | ||
@@ -92,15 +91,12 @@ return true | ||
}) | ||
if (!this.mounted && !req.rData_internal) { | ||
if (this.enabled('x-powered-by')) res.setHeader('X-Powered-By', 'Fyrejet') | ||
if (this.enabled('x-powered-by')) res.setHeader('X-Powered-By', 'Fyrejet') | ||
req.app = this | ||
res.app = req.app | ||
req.res = res | ||
res.req = req | ||
} | ||
try { | ||
this.getRouter().lookup(req, res, step) | ||
} catch (e) { | ||
return res.defaultErrHandler(e) | ||
} | ||
req.app = this | ||
res.app = req.app | ||
req.res = res | ||
res.req = req | ||
return this.getRouter().lookup(req, res, step) | ||
}, | ||
@@ -113,3 +109,3 @@ | ||
return server.address() | ||
} | ||
} | ||
return server.__address | ||
@@ -153,2 +149,3 @@ }, | ||
}) | ||
// this may be counterintuitive to you, but this is faster, since it allows to skip some phases in the event pool. This is prioritized. | ||
} else { | ||
@@ -167,3 +164,2 @@ server.on('request', (req, res) => { | ||
mixin(app, EventEmitter.prototype) | ||
@@ -231,3 +227,2 @@ app.request = Object.assign({}, req) | ||
/** | ||
@@ -234,0 +229,0 @@ * Expose the prototypes. |
@@ -41,3 +41,5 @@ 'use strict' | ||
res.defaultErrHandler = defErrHandlerBuilder(deh, res, req) | ||
function expressServe () { | ||
req.activateExpress = function() { | ||
let req = this | ||
let res = this.res | ||
Object.keys(req.app.response).forEach(key => { | ||
@@ -53,6 +55,3 @@ res[key] = req.app.response[key] | ||
Object.defineProperties(req, reqProperties) | ||
req.next = next | ||
req.rData_internal.initDone = true | ||
return next() | ||
return req | ||
} | ||
@@ -65,3 +64,4 @@ | ||
if (router.mountpath) req.rData_internal.tree.push(router.mountpath) | ||
req.rData_internal.tree.push(router.mountpath) | ||
if (req.rData_internal.initDone === true) { | ||
@@ -74,6 +74,9 @@ req.next = next | ||
req.next = next | ||
req.rData_internal.initDone = true | ||
if (!specialMode) { // check that the route is not marked as API-only, with disabled Express req & res additions. | ||
return expressServe() | ||
req.activateExpress() | ||
return next() | ||
} | ||
if (specialMode === 'properties as functions') { | ||
@@ -83,15 +86,9 @@ Object.keys(reqAdditionsPropsAsFns).forEach(key => { | ||
}) | ||
req.propFn = req.app.request.propFn | ||
Object.keys(req.app.response).forEach(key => { | ||
res[key] = req.app.response[key] | ||
}) // no change here, express response additions are non-problematic | ||
req.next = next | ||
} | ||
// if we are here, it means we got into special route mode ;) | ||
req.rData_internal.initDone = true | ||
// disabled for now, future release should include ability to re-enable express compatibility | ||
// req.rFuncs = { | ||
// activateExpress: expressServe | ||
// } | ||
if (!res.send) res.send = res.sendLite | ||
@@ -98,0 +95,0 @@ |
@@ -68,3 +68,4 @@ 'use strict' | ||
if (!contentType) contentType = TYPE_PLAIN | ||
} else if (typeof data === 'object') { | ||
} | ||
else if (typeof data === 'object') { | ||
if (data instanceof Buffer) { | ||
@@ -74,3 +75,2 @@ if (!contentType) contentType = TYPE_OCTET | ||
if (!contentType) contentType = TYPE_OCTET | ||
// NOTE: we exceptionally handle the response termination for streams | ||
@@ -77,0 +77,0 @@ preEnd(res, contentType, code) |
@@ -75,3 +75,3 @@ 'use strict' | ||
req.range = function range (size, options) { | ||
var range = this.get('Range') | ||
var range = this.headers.range | ||
if (!range) return | ||
@@ -125,3 +125,3 @@ return parseRange(size, range, options) | ||
// single value, but this is to be safe. | ||
var header = this.get('X-Forwarded-Proto') || proto | ||
var header = this.headers['x-forwarded-proto'] || proto | ||
var index = header.indexOf(',') | ||
@@ -134,3 +134,3 @@ return index !== -1 | ||
req.propFn.secure = function secure () { | ||
const proto = typeof this.protocol !== 'function' ? this.protocol : this.protocol() | ||
const proto = req.propFn.protocol.apply(this) | ||
return proto === 'https' | ||
@@ -156,3 +156,3 @@ } | ||
req.propFn.subdomains = function subdomains () { | ||
var hostname = typeof this.hostname !== 'function' ? this.hostname : this.hostname() | ||
var hostname = req.propFn.hostname.apply(this) | ||
@@ -233,6 +233,6 @@ if (!hostname) return [] | ||
var trust = this.app.get('trust proxy fn') | ||
var host = this.get('X-Forwarded-Host') | ||
var host = this.headers['x-forwarded-host'] | ||
if (!host || !trust(this.connection.remoteAddress, 0)) { | ||
host = this.get('Host') | ||
host = this.headers.host | ||
} else if (host.indexOf(',') !== -1) { | ||
@@ -274,4 +274,4 @@ // Note: X-Forwarded-Host is normally only ever a | ||
return fresh(this.headers, { | ||
etag: res.get('ETag'), | ||
'last-modified': res.get('Last-Modified') | ||
etag: res.getHeader('ETag'), | ||
'last-modified': res.getHeader('Last-Modified') | ||
}) | ||
@@ -284,3 +284,3 @@ } | ||
req.propFn.stale = function stale () { | ||
const fresh = typeof this.fresh !== 'function' ? this.fresh : this.fresh() | ||
const fresh = req.propFn.fresh.apply(this) | ||
return !fresh | ||
@@ -290,4 +290,4 @@ } | ||
req.propFn.xhr = function xhr () { | ||
var val = this.get('X-Requested-With') || '' | ||
var val = this.headers['x-requested-with'] || '' | ||
return val.toLowerCase() === 'xmlhttprequest' | ||
} |
@@ -37,5 +37,5 @@ 'use strict' | ||
res.links = function (links) { | ||
var link = this.get('Link') || '' | ||
var link = this.getHeader('Link') || '' | ||
if (link) link += ', ' | ||
return this.set('Link', link + Object.keys(links).map(function (rel) { | ||
return this.setHeader('Link', link + Object.keys(links).map(function (rel) { | ||
return '<' + links[rel] + '>; rel="' + rel + '"' | ||
@@ -46,39 +46,39 @@ }).join(', ')) | ||
res.send = function send (body) { | ||
function sendDeprecateTwoArgs(args, that) { | ||
if (typeof args[0] !== 'number' && typeof args[1] === 'number') { | ||
// res.send(body, status) backwards compat | ||
deprecate('res.send(body, status): Use res.status(status).send(body) instead') | ||
that.statusCode = args[1] | ||
return | ||
} | ||
// allow status / body | ||
deprecate('res.send(status, body): Use res.status(status).send(body) instead') | ||
that.statusCode = args[0] | ||
chunk = args[1] | ||
return | ||
} | ||
var chunk = body | ||
var encoding | ||
var req = this.req | ||
var type | ||
if (typeof chunk === 'number' || arguments.length === 2) { | ||
// disambiguate res.send(status) and res.send(status, num) | ||
if (arguments.length === 1) { | ||
// res.send(status) will set status message as text string | ||
if (!this.getHeader('Content-Type')) { | ||
this.setHeader('Content-Type', 'text/plain') | ||
} | ||
// settings | ||
var app = this.app | ||
// allow status / body | ||
if (arguments.length === 2) { | ||
// res.send(body, status) backwards compat | ||
if (typeof arguments[0] !== 'number' && typeof arguments[1] === 'number') { | ||
deprecate('res.send(body, status): Use res.status(status).send(body) instead') | ||
this.statusCode = arguments[1] | ||
} else { | ||
deprecate('res.send(status, body): Use res.status(status).send(body) instead') | ||
this.statusCode = arguments[0] | ||
chunk = arguments[1] | ||
deprecate('res.send(status): Use res.sendStatus(status) instead') | ||
this.statusCode = chunk | ||
chunk = statuses[chunk] | ||
} | ||
else sendDeprecateTwoArgs(arguments, this) | ||
} | ||
// disambiguate res.send(status) and res.send(status, num) | ||
if (typeof chunk === 'number' && arguments.length === 1) { | ||
// res.send(status) will set status message as text string | ||
if (!this.get('Content-Type')) { | ||
this.type('txt') | ||
} | ||
deprecate('res.send(status): Use res.sendStatus(status) instead') | ||
this.statusCode = chunk | ||
chunk = statuses[chunk] | ||
} | ||
switch (typeof chunk) { | ||
// string defaulting to html | ||
case 'string': | ||
if (!this.get('Content-Type')) { | ||
this.type('html') | ||
if (!this.getHeader('Content-Type')) { | ||
this.setHeader('Content-Type', 'text/html') | ||
} | ||
@@ -88,2 +88,5 @@ break | ||
case 'number': | ||
this.setHeader('Content-Type', 'application/json') | ||
chunk = chunk.toString() | ||
break | ||
case 'object': | ||
@@ -93,20 +96,29 @@ if (chunk === null) { | ||
break | ||
} | ||
else if (Buffer.isBuffer(chunk)) { | ||
if (!this.get('Content-Type')) { | ||
this.type('bin') | ||
} else if (Buffer.isBuffer(chunk)) { | ||
if (!this.getHeader('Content-Type')) { | ||
this.setHeader('Content-Type', 'application/octet-stream') | ||
} | ||
break | ||
} | ||
return this.json(chunk) | ||
} | ||
return resJson.apply(this, [chunk]) // no need to go through checks for deprecated functionality | ||
} | ||
return resSend.apply(this, [chunk]) | ||
} | ||
function resSend(chunk) { // split to avoid deprecation checks when returning from resJson or res.json | ||
var req = this.req | ||
var type | ||
// settings | ||
var app = this.app | ||
var encoding | ||
// write strings in utf-8 | ||
if (typeof chunk === 'string') { | ||
encoding = 'utf8' | ||
type = this.get('Content-Type') | ||
// reflect this in content-type | ||
if (typeof type === 'string') { | ||
this.set('Content-Type', setCharset(type, 'utf-8')) | ||
if (typeof (type = this.getHeader('Content-Type')) === 'string') { | ||
this.setHeader('Content-Type', setCharset(type, 'utf-8')) | ||
} | ||
@@ -118,2 +130,8 @@ } | ||
if (chunk !== undefined) { | ||
var etag | ||
// determine if ETag should be generated | ||
var etagFn = app.get('etag fn') | ||
var generateETag = etagFn && !req.rData_internal.noEtag && !this.getHeader('ETag') | ||
if (Buffer.isBuffer(chunk)) { | ||
@@ -131,23 +149,14 @@ // get length of Buffer | ||
} | ||
if (!this.__serverType === 'uWebSocket') this.set('Content-Length', len) | ||
var etag | ||
// determine if ETag should be generated | ||
var etagFn = app.get('etag fn') | ||
var generateETag = !this.get('ETag') && typeof etagFn === 'function' && !req.rData_internal.noEtag | ||
// populate ETag | ||
if (generateETag && (etag = etagFn(chunk, encoding) ) ) { // unlike express, we don't need to check if there is len, because we know it for certain, since we placed this if block in a different place | ||
this.set('ETag', etag) | ||
if (generateETag && (etag = etagFn(chunk, encoding))) { // unlike express, we don't need to check if there is len, because we know it for certain, since we placed this if block in a different place | ||
this.setHeader('ETag', etag) | ||
} | ||
} | ||
// freshness | ||
const fresh = typeof req.fresh !== 'function' ? req.fresh : req.fresh() | ||
if (fresh) this.statusCode = 304 | ||
const fresh = req.propFn.fresh.apply(req) | ||
// strip irrelevant headers | ||
if (this.statusCode === 204 || this.statusCode === 304) { | ||
if ((fresh && (this.statusCode = 304) ) || this.statusCode === 204 || this.statusCode === 304) { | ||
this.removeHeader('Content-Type') | ||
@@ -159,10 +168,9 @@ this.removeHeader('Content-Length') | ||
if (req.method === 'HEAD') { | ||
// skip body for HEAD | ||
this.end() | ||
if (req.rData_internal.method !== 'HEAD') { | ||
// respond | ||
this.end(chunk, encoding) | ||
return this | ||
} | ||
// respond | ||
this.end(chunk, encoding) | ||
// skip body for HEAD. | ||
this.end() | ||
return this | ||
@@ -187,3 +195,7 @@ | ||
} | ||
return resJson.apply(this, [val]) | ||
} | ||
function resJson(val) { // this is split, so we can avoid checks for deprecated functionality | ||
// settings | ||
@@ -197,7 +209,6 @@ var app = this.app | ||
// content-type | ||
if (!this.get('Content-Type')) { | ||
if (!this.getHeader('Content-Type')) { | ||
this.set('Content-Type', 'application/json') | ||
} | ||
return this.send(body) | ||
return resSend.apply(this, [body]) | ||
} | ||
@@ -230,4 +241,4 @@ | ||
// content-type | ||
if (!this.get('Content-Type')) { | ||
this.set('X-Content-Type-Options', 'nosniff') | ||
if (!this.getHeader('Content-Type')) { | ||
this.setHeader('X-Content-Type-Options', 'nosniff') | ||
this.set('Content-Type', 'application/json') | ||
@@ -243,3 +254,3 @@ } | ||
if (typeof callback === 'string' && callback.length !== 0) { | ||
this.set('X-Content-Type-Options', 'nosniff') | ||
this.setHeader('X-Content-Type-Options', 'nosniff') | ||
this.set('Content-Type', 'text/javascript') | ||
@@ -389,3 +400,2 @@ | ||
: type | ||
return this.set('Content-Type', ct) | ||
@@ -422,3 +432,3 @@ } | ||
next(err) | ||
return this | ||
@@ -432,3 +442,3 @@ } | ||
this.set('Content-Disposition', contentDisposition(filename)) | ||
this.setHeader('Content-Disposition', contentDisposition(filename)) | ||
@@ -439,3 +449,3 @@ return this | ||
res.append = function append (field, val) { | ||
var prev = this.get(field) | ||
var prev = this.getHeader(field) | ||
var value = val | ||
@@ -473,3 +483,3 @@ | ||
return this | ||
} | ||
} | ||
@@ -479,3 +489,3 @@ for (var key in field) { | ||
} | ||
return this | ||
@@ -574,12 +584,11 @@ } | ||
this.statusCode = status | ||
this.set('Content-Length', Buffer.byteLength(body)) | ||
this.setHeader('Content-Length', Buffer.byteLength(body)) | ||
if (this.req.method === 'HEAD') { | ||
if (this.req.rData_internal.method === 'HEAD') { | ||
this.end() | ||
return this | ||
} | ||
this.end(body) | ||
return this | ||
} | ||
@@ -586,0 +595,0 @@ |
@@ -20,5 +20,3 @@ 'use strict' | ||
if (!middleware) { | ||
if (!res.finished) return defaultRoute(req, res) | ||
return | ||
return defaultRoute(req, res) | ||
} | ||
@@ -28,3 +26,3 @@ | ||
if (routeIndex < req.routesToProcess.length) { | ||
req.currentRouteMiddlewareNum = req.currentRouteMiddlewareNum ? req.currentRouteMiddlewareNum + 1 : 1 | ||
req.currentRouteMiddlewareNum = req.currentRouteMiddlewareNum ? ++req.currentRouteMiddlewareNum : 1 | ||
const curRoute = req.route = req.routesToProcess[routeIndex] | ||
@@ -57,3 +55,3 @@ if (curRoute.keys.length && curRoute.handlers[0] === middleware) { // we don't want to extract params at EVERY middleware and at routes with no keys | ||
return furtherGetParams() | ||
} | ||
} | ||
if (route.keys.length) { | ||
@@ -65,4 +63,4 @@ if (Array.isArray(route.keys)) { | ||
return furtherGetParams() | ||
} | ||
} | ||
matches = route.pattern.exec(req.path) | ||
@@ -90,6 +88,6 @@ if (matches === null) return | ||
delete params.length | ||
return furtherGetParams() | ||
} | ||
function furtherGetParams() { | ||
function furtherGetParams () { | ||
if (req.params['0']) { | ||
@@ -120,6 +118,6 @@ let n = 0 | ||
} | ||
let prevParams = req.paramsPrev[req.paramsPrev.length - 1 || 0] || {} | ||
let prevParams = req.rData_internal.paramsPrev[req.rData_internal.paramsPrev.length - 1 || 0] || {} | ||
prevParams = JSON.stringify(prevParams) | ||
if (prevParams !== JSON.stringify(params)) req.paramsPrev.push(Object.assign({}, params)) | ||
if (prevParams !== JSON.stringify(params)) req.rData_internal.paramsPrev.push(Object.assign({}, params)) | ||
Object.keys(params).forEach(key => { | ||
@@ -145,5 +143,5 @@ if (!req.paramsUserDefined.includes(key)) { | ||
if (typeof req.params !== 'object' && Array.isArray(req.paramsPrev)) req.params = req.paramsPrev[req.paramsPrev.length - 1] || {} | ||
if (middlewares.length && req.routesToProcess[routeIndex] && !middlewares[index].finisher) { | ||
req.lastPattern = req.routesToProcess[routeIndex].pattern | ||
if (typeof req.params !== 'object') req.params = req.rData_internal.paramsPrev[req.rData_internal.paramsPrev.length - 1] || {} | ||
if (req.routesToProcess[routeIndex]) { | ||
req.rData_internal.lastPattern = req.routesToProcess[routeIndex].pattern | ||
} | ||
@@ -159,3 +157,2 @@ if (!err) return stepNormally() | ||
req.paramsCalled = {} | ||
// req.routesToProcess.splice(0,1) | ||
routeIndex++ | ||
@@ -176,3 +173,3 @@ delete req.currentRouteMiddlewareNum | ||
errMiddlewareIndex = null | ||
index = index + 1 | ||
++index | ||
runErrLoop() | ||
@@ -206,3 +203,2 @@ } | ||
} | ||
// req.routesToProcess.splice(0,1) | ||
routeIndex++ | ||
@@ -212,3 +208,2 @@ } | ||
} | ||
// req.routesToProcess = req.routesToProcess.slice(req.routesToProcess.length - 1) | ||
routeIndex = req.routesToProcess.length - 1 | ||
@@ -222,5 +217,2 @@ index = newIndex | ||
try { | ||
if (index > 0 && req.paramsPrev && typeof req.params !== 'object') { | ||
req.params = req.paramsPrev[req.paramsPrev.length - 1 || 0] || {} | ||
} | ||
paramOrNot() | ||
@@ -237,3 +229,3 @@ req.curMiddleware = middleware | ||
delete req.lastPattern | ||
delete req.rData_internal.lastPattern | ||
} | ||
@@ -244,4 +236,3 @@ | ||
const argsNum = middlewareArgsNum | ||
if (argsNum >= 4) { | ||
if (middlewareArgsNum > 3) { | ||
if (!error && req.method !== 'OPTIONS') { | ||
@@ -255,3 +246,3 @@ return defaultRoute(req, res) | ||
let errMiddlewareIndex | ||
index = index + 1 | ||
++index | ||
nextRunErrLoop(errMiddlewareIndex) | ||
@@ -258,0 +249,0 @@ if (errMiddlewareIndex) { |
@@ -39,3 +39,2 @@ 'use strict' | ||
switch (search) { | ||
@@ -54,4 +53,3 @@ case undefined: | ||
} | ||
} | ||
} |
@@ -27,5 +27,2 @@ 'use strict' | ||
} | ||
if (config.cacheSize === undefined) { | ||
config.cacheSize = 1000 | ||
} | ||
if (config.id === undefined) { | ||
@@ -38,2 +35,3 @@ config.id = (Date.now().toString(36) + Math.random().toString(36).substr(2, 5)).toUpperCase() | ||
const cache = hasCache ? new LRU(config.cacheSize) : null | ||
const router = new Trouter(config) | ||
@@ -76,3 +74,3 @@ router.id = config.id | ||
router.lookup = (req, res, step) => { | ||
function init() { | ||
function init () { | ||
if (!req.rData_internal) { | ||
@@ -89,9 +87,9 @@ req.originalUrl = req.url // if there is no req.rData_internal, it means there is no req.originalUrl either | ||
} | ||
res.locals = Object.create(null) // res.locals is rather convenient at times, so we leave it even for API routes | ||
res.locals = Object.create(null) // res.locals is rather convenient at times, so we leave it on even for API routes | ||
req.paramsCalled = {} // needed for app.param | ||
return | ||
} | ||
if (req.lastPattern) { | ||
if (req.rData_internal.lastPattern) { | ||
const reqUrlCopy = req.rData_internal.url | ||
req.rData_internal.url = req.rData_internal.url.replace(req.lastPattern, '') || reqUrlCopy | ||
req.rData_internal.url = req.rData_internal.url.replace(req.rData_internal.lastPattern, '') || reqUrlCopy | ||
} | ||
@@ -105,3 +103,2 @@ } | ||
if (hasCache) { | ||
const reqCacheKey = req.method + req.path | ||
@@ -111,24 +108,16 @@ match = cache.get(reqCacheKey) | ||
match = router.find(req.method, req.path, req, res) | ||
match.routesLength = match.sRoutes.length // LRU cache is incapable of storing Array of objects or Array-like objects... | ||
match.routes = Object.assign({}, match.sRoutes) // But it is capable of storing Objects with numbered access without length property ;) | ||
if (!match.specialMode) match.specialMode = config.specialMode | ||
// so we naturally store length property separately | ||
// this might be complicated and counter-intuitive approach, but using other cache will reduce performance | ||
match.routes = match.sRoutes // But it is capable of storing Objects with numbered access without length property ;) | ||
cache.set(reqCacheKey, match) | ||
} | ||
match.routes.length = match.routesLength | ||
match.routes = Array.from(match.routes) | ||
return routeFurther(req, res, step, match) | ||
} | ||
// route validation | ||
match = router.find(req.method, req.path, req, res) | ||
if (!match.specialMode) match.specialMode = config.specialMode | ||
match.routes = match.sRoutes | ||
return routeFurther(req, res, step, match) | ||
function routeFurther(req, res, step, match) { | ||
if (match.handlers.length > 0) { | ||
if (!req.rData_internal.specialMode) req.rData_internal.specialMode = match.specialMode | ||
function routeFurther (req, res, step, match) { | ||
if (match.handlers.length) { | ||
if (!req.rData_internal.specialMode) req.rData_internal.specialMode = match.specialMode || config.specialMode | ||
if (!req.rData_internal.noEtag) req.rData_internal.noEtag = match.noEtag | ||
@@ -147,5 +136,5 @@ | ||
if (step && req.stepString && step.toString() !== req.stepString) { | ||
// req.paramsPrev.push(req.params) | ||
// req.rData_internal.paramsPrev.push(req.params) | ||
fn = function (err) { | ||
req.params = req.paramsPrev[req.paramsPrev.length - 2] || req.paramsPrev[req.paramsPrev.length - 1] || req.params | ||
req.params = req.rData_internal.paramsPrev[req.rData_internal.paramsPrev.length - 2] || req.rData_internal.paramsPrev[req.rData_internal.paramsPrev.length - 1] || req.params | ||
@@ -170,7 +159,6 @@ try { | ||
} | ||
fn.finisher = true | ||
middlewares.push(fn) | ||
} | ||
req.routesToProcess = Array.from(match.routes) | ||
req.routesToProcess = match.routes | ||
@@ -182,6 +170,5 @@ if (!req.params) { | ||
return next(middlewares, middlewaresArgsNum, req, res, 0, 0, routers, config.defaultRoute, config.errorHandler, null) | ||
} | ||
} | ||
return config.defaultRoute(req, res) // if we haven't found routes. Should not occur, since init middleware is also a route | ||
return config.defaultRoute(req, res) // if we haven't found routes. | ||
} | ||
@@ -195,7 +182,11 @@ } | ||
function makeReqParams(req) { | ||
if (!req.paramsPrev) req.paramsPrev = [] | ||
function makeReqParams (req) { | ||
if (!req.rData_internal.paramsPrev) req.rData_internal.paramsPrev = [] | ||
req.params = {} | ||
req.paramsUserDefined = [] | ||
req.params = onChange(req.params, function (path, value, previousValue, name) { | ||
if (typeof req.params !== 'object') { | ||
req.params = req.rData_internal.paramsPrev[req.rData_internal.paramsPrev.length - 1 || 0] || {} | ||
return | ||
} | ||
req.paramsUserDefined.push(path) | ||
@@ -202,0 +193,0 @@ }, { |
@@ -140,3 +140,3 @@ 'use strict' | ||
tmp = arr[i] | ||
if (tmp.method.length === 0 || tmp.method === method || isHEAD && tmp.method === 'GET') { | ||
if (tmp.method.length === 0 || tmp.method === method || (isHEAD && tmp.method === 'GET')) { | ||
const test = tmp.pattern.exec(url) | ||
@@ -164,5 +164,4 @@ | ||
} | ||
const z = { handlers, handlersArgsNum, url, sRoutes: routes, specialMode, noEtag } | ||
return z | ||
const routeData = { handlers, handlersArgsNum, url, sRoutes: routes, specialMode, noEtag } | ||
return routeData | ||
} | ||
@@ -169,0 +168,0 @@ } |
@@ -84,2 +84,3 @@ var Buffer = require('safe-buffer').Buffer | ||
case false: | ||
fn = null | ||
break | ||
@@ -86,0 +87,0 @@ case 'strong': |
@@ -49,10 +49,8 @@ | ||
} | ||
return | ||
return | ||
} | ||
return reqWrapper.push(chunk) | ||
}) | ||
} | ||
else if (!res.finished) { | ||
} else if (!res.finished) { | ||
handler(reqWrapper, resWrapper) | ||
@@ -91,3 +89,3 @@ } | ||
constructor (uRequest) { | ||
super(); | ||
super() | ||
const q = uRequest.getQuery() | ||
@@ -101,5 +99,5 @@ this.req = uRequest | ||
this.headers = {} | ||
uRequest.forEach((header, value) => { | ||
this.headers[header] = value | ||
this.headers[header] = value | ||
}) | ||
@@ -111,3 +109,3 @@ } | ||
forEach(this.headers, (header, value) => { | ||
raw.push(header, value) | ||
raw.push(header, value) | ||
}) | ||
@@ -121,4 +119,4 @@ return raw | ||
_read(size) { | ||
return this.slice(0,size) | ||
_read (size) { | ||
return this.slice(0, size) | ||
} | ||
@@ -128,3 +126,3 @@ } | ||
function writeAllHeaders () { | ||
//this.res.writeHeader('Date', this.server._date) | ||
// this.res.writeHeader('Date', this.server._date) | ||
forEach(this.__headers, ([name, value]) => { | ||
@@ -151,3 +149,3 @@ this.res.writeHeader(name, value) | ||
this.res.onAborted(() => { | ||
this.__isAborted = true | ||
this.finished = true | ||
}) | ||
@@ -157,3 +155,3 @@ | ||
this.__isWritable = true | ||
if (!this.__isAborted) writeAllHeaders.call(this) | ||
if (!this.finished) writeAllHeaders.call(this) | ||
}) | ||
@@ -163,3 +161,6 @@ } | ||
setHeader (name, value) { | ||
this.__headers[toLowerCase(name)] = [name, toString(value)] | ||
let filterRegEx = new RegExp(`^${name},`) | ||
let toSet = toString(value) | ||
toSet = toSet.replace(filterRegEx, '') | ||
this.__headers[toLowerCase(name)] = [name, toSet] | ||
} | ||
@@ -188,3 +189,3 @@ | ||
write (data) { | ||
if (this.__isAborted) { | ||
if (this.finished) { | ||
return | ||
@@ -196,3 +197,3 @@ } | ||
writeHead (statusCode) { | ||
if (this.__isAborted) { | ||
if (this.finished) { | ||
return | ||
@@ -216,3 +217,3 @@ } | ||
end (data = '') { | ||
if (this.__isAborted) return | ||
if (this.finished) return | ||
this.res.writeStatus(`${this.statusCode} ${this.statusMessage}`) | ||
@@ -232,7 +233,6 @@ | ||
} | ||
} | ||
catch(e) { | ||
module.exports = function() { | ||
} catch (e) { | ||
module.exports = function () { | ||
console.log('uWebSockets.js is not available.') | ||
} | ||
} |
{ | ||
"name": "fyrejet", | ||
"version": "2.0.2", | ||
"version": "2.1.0", | ||
"description": "Web Framework for node.js that strives to provide (almost) perfect compatibility with Express, while providing better performance, where you need it.", | ||
@@ -46,3 +46,3 @@ "main": "index.js", | ||
"content-type": "~1.0.4", | ||
"cookie": "0.4.0", | ||
"cookie": "^0.4.1", | ||
"cookie-signature": "1.0.6", | ||
@@ -65,8 +65,8 @@ "debug": "2.6.9", | ||
"proxy-addr": "~2.0.5", | ||
"qs": "6.7.0", | ||
"qs": "^6.9.4", | ||
"range-parser": "~1.2.1", | ||
"safe-buffer": "5.1.2", | ||
"safe-buffer": "^5.2.1", | ||
"send": "0.17.1", | ||
"serve-static": "1.14.1", | ||
"setprototypeof": "1.1.1", | ||
"setprototypeof": "^1.2.0", | ||
"statuses": "~1.5.0", | ||
@@ -73,0 +73,0 @@ "type-is": "~1.6.18", |
107
README.md
@@ -277,2 +277,57 @@ | ||
#### Using Express functions in API mode | ||
API mode does not fully disable Express functions and properties, they are still stored and can be made available yet again. This may be useful, if you do not know yet at the start of routing, whether you will need Express's functionality. | ||
Each request's `req` and `res` is extended with a copy of the Fyrejet function, available as `req.app` and `res.app` respectively. This function also has many attached objects and other functions, such as `app.request` and `app.response`. | ||
Starting with v2.1.0, you can do the following: | ||
```js | ||
app.get('/hi', (req, res, next) => { | ||
req.activateExpress() | ||
return res.send(req.hostname) | ||
}, 'api') | ||
``` | ||
This will, however, lead to the same performance penalties the normal routing mode leads to, albeit at a later stage, when you are certain you need Express functionality. | ||
Alternatively, in order to call an Express function you'd need to follow this example (**NOT FULLY TESTED**): | ||
```js | ||
app.get('/hi', (req, res, next) => { | ||
Object.keys(req.app.response).forEach(key => { | ||
res[key] = req.app.response[key] | ||
}) // at this point all res functions are available. Doesn't mean they work properly ;) | ||
Object.keys(req.app.request).forEach(key => { | ||
req[key] = req.app.request[key] | ||
}) // at this point all res FUNCTIONS are available. Doesn't mean they work properly ;) | ||
req.propFn = req.app.request.propFn // this is NOT just for convenience at this point. | ||
// to access, say, req.hostname, we would then do the following: | ||
let hostname = req.propFn.hostname.apply(req) | ||
return res.send(hostname) // localhost. This is express's res.send here | ||
// if you need to use API mode's default res.send (from Restana), you can: | ||
// res.send = res.sendLite | ||
}, 'api') | ||
``` | ||
This is *somewhat* like the Properties as Functions mode. However, this is **NOT FULLY** tested, **NOR OFFICIALLY SUPPORTED**. | ||
#### Caveats | ||
@@ -397,22 +452,45 @@ | ||
Second-best result out of a series of 5 is used. | ||
Second-best result out of a series of 5 is used. | ||
Results: | ||
1) 25738.07 req/s (85.7% faster than express) | ||
1) 26002.70 req/s (85.9% faster than express) | ||
2) 34101.83 req/s (146.1% faster than express) | ||
2) 34942.53 req/s (149.8% faster than express) | ||
3) 23141.63 req/s (66.9% faster than express) | ||
3) 24919.97 req/s (78.1% faster than express) | ||
4) 21471.72 req/s (53.6% faster than express) | ||
4) 22792.33 req/s (62.9% faster than express) | ||
5) 13857.42 req/s (baseline) | ||
5) 13989.85 req/s (baseline) | ||
The CPU package temperature was ensured to be 45-47 degrees Celsium at the start of each round. | ||
### Clustering | ||
Be aware that uWS generally doesn't perform on MacOS as well as on Linux. | ||
Be aware that `uWebSockets.js` generally doesn't perform on MacOS and FreeBSD as well as on Linux. It also does not clusterize on non-Linux platforms, [as it depends on certain kernel features](https://github.com/uNetworking/uWebSockets.js/issues/214#issuecomment-547589050). This only affects `uWebSockets.js` (and, by extenstion, `fyrejet.uwsCompat`). However, Fyrejet itself has no problems with Node.JS clustering, as demonstrated by the table before. | ||
```sh | ||
# in terminal 1 or whatever pleases your soul <3 | ||
node ./performance/fyrejet-route-cluster.js 2 | ||
# 2 is the number of worker processes to use | ||
# you can also use express-route-cluster.js, which will run on port 4005 | ||
# in terminal 2 | ||
wrk -t8 -c64 -d5s 'http://localhost:4004/hi' | ||
``` | ||
#### Overall results with clustering | ||
| № of workers | Express, req/s | Fyrejet, req/s | % difference in favor of Fyrejet | | ||
| ------------ | -------------- | -------------- | -------------------------------- | | ||
| 1 | 13893.07 | 22563.44 | 62.4 | | ||
| 2 | 25067.27 | 40751.20 | 62.5 | | ||
| 3 | 33958.38 | 56950.06 | 67.7 | | ||
| 4 | 44578.20 | 72082.12 | 61.7 | | ||
## Run tests | ||
@@ -426,1 +504,16 @@ | ||
## Donations | ||
Are welcome. | ||
Currently, you can use PayPal: | ||
https://paypal.me/schamberg97 | ||
## Support | ||
In order to get support, you can file an issue in this repository. If you need commercial support, please write on schamberg.nicholas@gmail.com |
115666
516
2870
+ Addedcookie@0.4.2(transitive)
+ Addedqs@6.14.0(transitive)
- Removedcookie@0.4.0(transitive)
Updatedcookie@^0.4.1
Updatedqs@^6.9.4
Updatedsafe-buffer@^5.2.1
Updatedsetprototypeof@^1.2.0