Constructor: Support for defaultHeaders was added with default secure options.
continuous-integration/appveyor/branch AppVeyor build succeeded Details

CSP: Added smart CSP support with nonce support as well. Can generate unique nonce values for each request.
CorsHandler: Started development of basic cors handler.
master
Jonatan Nilsson 2022-03-24 09:29:54 +00:00
parent e50e9f8a94
commit 7b682e8e95
8 changed files with 1430 additions and 55 deletions

View File

@ -1,2 +1 @@
start /B /WAIT /REALTIME node index.js
pause
start /B /WAIT /REALTIME node index.js

View File

@ -1,3 +1,4 @@
import crypto from 'crypto'
import assert from 'assert'
import Benchmark from 'benchmarkjs-pretty'
import { koaRouter1, koaRouter2 } from './router_koa.js'
@ -176,7 +177,89 @@ function TestObjectAssign() {
ctx.router2 = flaskaRouter2
}
function registerHeader(ctx) {
ctx.headers['Server'] = 'nginx/1.16.1'
ctx.headers['Date'] = 'Mon, 21 Mar 2022 07:26:01 GMT'
ctx.headers['Content-Type'] = 'application/json; charset=utf-8'
ctx.headers['Content-Length'] = '1646'
ctx.headers['Connection'] = 'keep-alive'
ctx.headers['vary'] = 'Origin'
ctx.headers['Link'] = '<http://kisildalur.is/api/categories?perPage=1250>; rel="current"; title="Page 1"'
ctx.headers['pagination_total'] = '7'
ctx.headers['X-Frame-Options'] = 'DENY'
ctx.headers['X-Content-Type-Options'] = 'nosniff'
}
function registerHeaderAlt(ctx) {
ctx.headers = {
'Server': 'nginx/1.16.1',
'Date': 'Mon, 21 Mar 2022 07:26:01 GMT',
'Content-Type': 'application/json; charset=utf-8',
'Content-Length': '1646',
'Connection': 'keep-alive',
'vary': 'Origin',
'Link': '<http://kisildalur.is/api/categories?perPage=1250>; rel="current"; title="Page 1"',
'pagination_total': '7',
'X-Frame-Options': 'DENY',
'X-Content-Type-Options': 'nosniff',
}
}
let baseHeaders = {
'Server': 'nginx/1.16.1',
'Date': 'Mon, 21 Mar 2022 07:26:01 GMT',
'Content-Type': 'application/json; charset=utf-8',
'Content-Length': '1646',
'Connection': 'keep-alive',
'vary': 'Origin',
'Link': '<http://kisildalur.is/api/categories?perPage=1250>; rel="current"; title="Page 1"',
'pagination_total': '7',
'X-Frame-Options': 'DENY',
'X-Content-Type-Options': 'nosniff',
}
let keys = Object.keys(baseHeaders)
return new Benchmark.default('test different method to initialize objects)')
.add('[HEADERS] Object.assign()', function() {
let ctx = {
headers: {}
}
Object.assign(ctx.headers, baseHeaders)
// assert.notStrictEqual(ctx.headers, baseHeaders)
})
.add('[HEADERS] ecmascript spread', function() {
let ctx = {
headers: {
...baseHeaders
}
}
// assert.notStrictEqual(ctx.headers, baseHeaders)
})
.add('[HEADERS] Basic clone lol', function() {
let ctx = {
headers: {}
}
for (let key of keys) {
ctx.headers[key] = baseHeaders[key]
}
// assert.notStrictEqual(ctx.headers, baseHeaders)
})
.add('[HEADERS] Use register function', function() {
let ctx = {
headers: {}
}
registerHeader(ctx)
// assert.notStrictEqual(ctx.headers, baseHeaders)
})
.add('[HEADERS] Use register function ALT', function() {
let ctx = {
headers: {}
}
registerHeaderAlt(ctx)
// assert.notStrictEqual(ctx.headers, baseHeaders)
})
/*
.add('Object.assign()', function() {
let ctx = {
req: req,
@ -196,7 +279,7 @@ function TestObjectAssign() {
register3(ctx)
ctx.log.info()
})
/*.add('Object.create() all props', function() {
.add('Object.create() all props', function() {
let ctx = Object.create({}, propMakerAlt)
ctx.log.info()
})
@ -212,7 +295,100 @@ function TestObjectAssign() {
let ctx = { }
Object.defineProperties(ctx, propMakerAlt)
ctx.log.info()
})*/
})
*/
.run()
.then(function() {}, function(e) {
console.error('error:', e)
process.exit(1)
})
}
function TestGenerateRandomString() {
return new Benchmark.default('test different method to generate random string)')
.add('crypto.randomBytes(16)', function() {
for (let i = 0; i < 25; i++) {
crypto.randomBytes(16).toString('base64')
}
})
.add('crypto.randomBytes(32)', function() {
for (let i = 0; i < 25; i++) {
crypto.randomBytes(32).toString('base64')
}
})
.add('random (22 characters long)', function() {
for (let i = 0; i < 25; i++) {
let out = Math.random().toString(36).substring(2, 24)
+ Math.random().toString(36).substring(2, 24)
}
})
.add('random (44 characters long)', function() {
for (let i = 0; i < 25; i++) {
let out = Math.random().toString(36).substring(2, 24)
+ Math.random().toString(36).substring(2, 24)
+ Math.random().toString(36).substring(2, 24)
+ Math.random().toString(36).substring(2, 24)
}
})
.run()
.then(function() {}, function(e) {
console.error('error:', e)
process.exit(1)
})
}
function TestArrayReduce() {
return new Benchmark.default('test different method to reduce array)')
.add('currIndex', function() {
const arr1 = new Array(100)
for (let i = 0; i < arr1.length; i++) {
arr1[i] = 'a'
}
let currIndex = arr1.length - 1
let out = ''
while (currIndex >= 0) {
out += arr1[currIndex]
currIndex--
}
})
.add('crypto.randomBytes(32)', function() {
const arr1 = new Array(100)
for (let i = 0; i < arr1.length; i++) {
arr1[i] = 'a'
}
let out = ''
while (arr1.length > 0) {
out += arr1.splice(0, 1)[0]
}
})
.run()
.then(function() {}, function(e) {
console.error('error:', e)
process.exit(1)
})
}
function TestStringCombination() {
let val1 = 'some'
let val2 = 'text'
let val3 = 'gose'
let val4 = 'here'
return new Benchmark.default('test different method to combine string)')
.add('ES6 with variable', function() {
let out = `Hello my friend ${val1} this goes to ${val2} all my homies ${val3} over at my ${val4} house`
return out
})
.add('String concatenation', function() {
let out = 'Hello my friend ' + val1 + ' this goes to ' + val2 + ' all my homies ' + val3 + ' over at my ' + val4 + ' house'
return out
})
.run()
.then(function() {}, function(e) {
console.error('error:', e)
@ -345,8 +521,8 @@ function TestLargeParamLargeUrlRoute() {
})
}
/*
TestSmallStaticRoute()
// TestObjectAssign()
// TestPromiseCreators()
.then(function() {
return TestSmallParamRoute()
@ -362,4 +538,12 @@ TestSmallStaticRoute()
})
.then(function() {
process.exit(0)
})
})*/
// TestObjectAssign()
// TestGenerateRandomString()
// TestArrayReduce()
TestStringCombination()
.then(function() {
process.exit(0)
})

View File

@ -1,8 +1,731 @@
{
"name": "benchmark",
"version": "1.0.0",
"lockfileVersion": 1,
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "benchmark",
"version": "1.0.0",
"license": "WTFPL",
"dependencies": {
"benchmarkjs-pretty": "^2.0.0",
"express": "^4.17.1",
"koa-router": "^8.0.8"
}
},
"node_modules/@types/benchmark": {
"version": "1.0.31",
"resolved": "https://registry.npmjs.org/@types/benchmark/-/benchmark-1.0.31.tgz",
"integrity": "sha512-F6fVNOkGEkSdo/19yWYOwVKGvzbTeWkR/XQYBKtGBQ9oGRjBN9f/L4aJI4sDcVPJO58Y1CJZN8va9V2BhrZapA=="
},
"node_modules/@types/chalk": {
"version": "0.4.31",
"resolved": "https://registry.npmjs.org/@types/chalk/-/chalk-0.4.31.tgz",
"integrity": "sha1-ox10JBprHtu5c8822XooloNKUfk="
},
"node_modules/accepts": {
"version": "1.3.7",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
"integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==",
"dependencies": {
"mime-types": "~2.1.24",
"negotiator": "0.6.2"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/ansi-styles": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
"dependencies": {
"color-convert": "^1.9.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/array-flatten": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
"integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
},
"node_modules/benchmark": {
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/benchmark/-/benchmark-2.1.4.tgz",
"integrity": "sha1-CfPeMckWQl1JjMLuVloOvzwqVik=",
"dependencies": {
"lodash": "^4.17.4",
"platform": "^1.3.3"
}
},
"node_modules/benchmarkjs-pretty": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/benchmarkjs-pretty/-/benchmarkjs-pretty-2.0.0.tgz",
"integrity": "sha512-t5a+ztdAuim1HPEbQwBELN9ugqe5WCSbjwPh79olqS+zgw44Bi/3qPz472LNPIfXlTernAo+meL8KULaXuWAeQ==",
"dependencies": {
"@types/benchmark": "^1.0.30",
"@types/chalk": "^0.4.31",
"benchmark": "^2.1.4",
"chalk": "^2.0.1"
}
},
"node_modules/body-parser": {
"version": "1.19.0",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz",
"integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==",
"dependencies": {
"bytes": "3.1.0",
"content-type": "~1.0.4",
"debug": "2.6.9",
"depd": "~1.1.2",
"http-errors": "1.7.2",
"iconv-lite": "0.4.24",
"on-finished": "~2.3.0",
"qs": "6.7.0",
"raw-body": "2.4.0",
"type-is": "~1.6.17"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/body-parser/node_modules/debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"dependencies": {
"ms": "2.0.0"
}
},
"node_modules/body-parser/node_modules/http-errors": {
"version": "1.7.2",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz",
"integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==",
"dependencies": {
"depd": "~1.1.2",
"inherits": "2.0.3",
"setprototypeof": "1.1.1",
"statuses": ">= 1.5.0 < 2",
"toidentifier": "1.0.0"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/body-parser/node_modules/inherits": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
},
"node_modules/body-parser/node_modules/ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
},
"node_modules/bytes": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
"integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/chalk": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
"dependencies": {
"ansi-styles": "^3.2.1",
"escape-string-regexp": "^1.0.5",
"supports-color": "^5.3.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/color-convert": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
"dependencies": {
"color-name": "1.1.3"
}
},
"node_modules/color-name": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
},
"node_modules/content-disposition": {
"version": "0.5.3",
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz",
"integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==",
"dependencies": {
"safe-buffer": "5.1.2"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/content-type": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
"integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/cookie": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz",
"integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/cookie-signature": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
"integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
},
"node_modules/debug": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
"deprecated": "Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)",
"dependencies": {
"ms": "^2.1.1"
}
},
"node_modules/depd": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
"integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/destroy": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
"integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
},
"node_modules/ee-first": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
"integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
},
"node_modules/encodeurl": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
"integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/escape-html": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
"integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
},
"node_modules/escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
"engines": {
"node": ">=0.8.0"
}
},
"node_modules/etag": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
"integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/express": {
"version": "4.17.1",
"resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz",
"integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==",
"dependencies": {
"accepts": "~1.3.7",
"array-flatten": "1.1.1",
"body-parser": "1.19.0",
"content-disposition": "0.5.3",
"content-type": "~1.0.4",
"cookie": "0.4.0",
"cookie-signature": "1.0.6",
"debug": "2.6.9",
"depd": "~1.1.2",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"finalhandler": "~1.1.2",
"fresh": "0.5.2",
"merge-descriptors": "1.0.1",
"methods": "~1.1.2",
"on-finished": "~2.3.0",
"parseurl": "~1.3.3",
"path-to-regexp": "0.1.7",
"proxy-addr": "~2.0.5",
"qs": "6.7.0",
"range-parser": "~1.2.1",
"safe-buffer": "5.1.2",
"send": "0.17.1",
"serve-static": "1.14.1",
"setprototypeof": "1.1.1",
"statuses": "~1.5.0",
"type-is": "~1.6.18",
"utils-merge": "1.0.1",
"vary": "~1.1.2"
},
"engines": {
"node": ">= 0.10.0"
}
},
"node_modules/express/node_modules/debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"dependencies": {
"ms": "2.0.0"
}
},
"node_modules/express/node_modules/ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
},
"node_modules/finalhandler": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
"integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==",
"dependencies": {
"debug": "2.6.9",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"on-finished": "~2.3.0",
"parseurl": "~1.3.3",
"statuses": "~1.5.0",
"unpipe": "~1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/finalhandler/node_modules/debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"dependencies": {
"ms": "2.0.0"
}
},
"node_modules/finalhandler/node_modules/ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
},
"node_modules/forwarded": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
"integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/fresh": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
"integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
"engines": {
"node": ">=4"
}
},
"node_modules/http-errors": {
"version": "1.7.3",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.3.tgz",
"integrity": "sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==",
"dependencies": {
"depd": "~1.1.2",
"inherits": "2.0.4",
"setprototypeof": "1.1.1",
"statuses": ">= 1.5.0 < 2",
"toidentifier": "1.0.0"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/iconv-lite": {
"version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
"dependencies": {
"safer-buffer": ">= 2.1.2 < 3"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
"node_modules/ipaddr.js": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
"integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
"engines": {
"node": ">= 0.10"
}
},
"node_modules/isarray": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
"integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
},
"node_modules/koa-compose": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/koa-compose/-/koa-compose-4.1.0.tgz",
"integrity": "sha512-8ODW8TrDuMYvXRwra/Kh7/rJo9BtOfPc6qO8eAfC80CnCvSjSl0bkRM24X6/XBBEyj0v1nRUQ1LyOy3dbqOWXw=="
},
"node_modules/koa-router": {
"version": "8.0.8",
"resolved": "https://registry.npmjs.org/koa-router/-/koa-router-8.0.8.tgz",
"integrity": "sha512-2rNF2cgu/EWi/NV8GlBE5+H/QBoaof83X6Z0dULmalkbt7W610/lyP2EOLVqVrUUFfjsVWL/Ju5TVBcGJDY9XQ==",
"dependencies": {
"debug": "^4.1.1",
"http-errors": "^1.7.3",
"koa-compose": "^4.1.0",
"methods": "^1.1.2",
"path-to-regexp": "1.x",
"urijs": "^1.19.2"
},
"engines": {
"node": ">= 8.0.0"
}
},
"node_modules/koa-router/node_modules/path-to-regexp": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz",
"integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==",
"dependencies": {
"isarray": "0.0.1"
}
},
"node_modules/lodash": {
"version": "4.17.15",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
"integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A=="
},
"node_modules/media-typer": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
"integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/merge-descriptors": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
"integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
},
"node_modules/methods": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
"integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mime": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
"bin": {
"mime": "cli.js"
},
"engines": {
"node": ">=4"
}
},
"node_modules/mime-db": {
"version": "1.43.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.43.0.tgz",
"integrity": "sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mime-types": {
"version": "2.1.26",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.26.tgz",
"integrity": "sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ==",
"dependencies": {
"mime-db": "1.43.0"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
},
"node_modules/negotiator": {
"version": "0.6.2",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
"integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/on-finished": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
"integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
"dependencies": {
"ee-first": "1.1.1"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/parseurl": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/path-to-regexp": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
"integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
},
"node_modules/platform": {
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/platform/-/platform-1.3.5.tgz",
"integrity": "sha512-TuvHS8AOIZNAlE77WUDiR4rySV/VMptyMfcfeoMgs4P8apaZM3JrnbzBiixKUv+XR6i+BXrQh8WAnjaSPFO65Q=="
},
"node_modules/proxy-addr": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz",
"integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==",
"dependencies": {
"forwarded": "~0.1.2",
"ipaddr.js": "1.9.1"
},
"engines": {
"node": ">= 0.10"
}
},
"node_modules/qs": {
"version": "6.7.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
"integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==",
"engines": {
"node": ">=0.6"
}
},
"node_modules/range-parser": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/raw-body": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz",
"integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==",
"dependencies": {
"bytes": "3.1.0",
"http-errors": "1.7.2",
"iconv-lite": "0.4.24",
"unpipe": "1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/raw-body/node_modules/http-errors": {
"version": "1.7.2",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz",
"integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==",
"dependencies": {
"depd": "~1.1.2",
"inherits": "2.0.3",
"setprototypeof": "1.1.1",
"statuses": ">= 1.5.0 < 2",
"toidentifier": "1.0.0"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/raw-body/node_modules/inherits": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
},
"node_modules/safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
},
"node_modules/safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
"node_modules/send": {
"version": "0.17.1",
"resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz",
"integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==",
"dependencies": {
"debug": "2.6.9",
"depd": "~1.1.2",
"destroy": "~1.0.4",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"fresh": "0.5.2",
"http-errors": "~1.7.2",
"mime": "1.6.0",
"ms": "2.1.1",
"on-finished": "~2.3.0",
"range-parser": "~1.2.1",
"statuses": "~1.5.0"
},
"engines": {
"node": ">= 0.8.0"
}
},
"node_modules/send/node_modules/debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"dependencies": {
"ms": "2.0.0"
}
},
"node_modules/send/node_modules/debug/node_modules/ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
},
"node_modules/send/node_modules/ms": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
"integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
},
"node_modules/serve-static": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz",
"integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==",
"dependencies": {
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"parseurl": "~1.3.3",
"send": "0.17.1"
},
"engines": {
"node": ">= 0.8.0"
}
},
"node_modules/setprototypeof": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz",
"integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw=="
},
"node_modules/statuses": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
"integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/supports-color": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
"dependencies": {
"has-flag": "^3.0.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/toidentifier": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
"integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==",
"engines": {
"node": ">=0.6"
}
},
"node_modules/type-is": {
"version": "1.6.18",
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
"integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
"dependencies": {
"media-typer": "0.3.0",
"mime-types": "~2.1.24"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/unpipe": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
"integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/urijs": {
"version": "1.19.2",
"resolved": "https://registry.npmjs.org/urijs/-/urijs-1.19.2.tgz",
"integrity": "sha512-s/UIq9ap4JPZ7H1EB5ULo/aOUbWqfDi7FKzMC2Nz+0Si8GiT1rIEaprt8hy3Vy2Ex2aJPpOQv4P4DuOZ+K1c6w=="
},
"node_modules/utils-merge": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
"integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=",
"engines": {
"node": ">= 0.4.0"
}
},
"node_modules/vary": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
"integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=",
"engines": {
"node": ">= 0.8"
}
}
},
"dependencies": {
"@types/benchmark": {
"version": "1.0.31",

View File

@ -1,4 +1,5 @@
import os from 'os'
import crypto from 'crypto'
import path from 'path'
import http from 'http'
import stream from 'stream'
@ -128,6 +129,28 @@ export function JsonHandler(org = {}) {
}
}
export function CorsHandler(opts = {}) {
const options = {
allowedMethods: opts.allowedMethods || 'GET,HEAD,PUT,POST,DELETE,PATCH',
allowedHeaders: opts.allowedHeaders,
openerPolicy: 'same-origin',
resourcePolicy: 'same-origin',
embedderPolicy: 'require-corp',
}
return function(ctx) {
let origin = ctx.req.headers['origin']
let reqHeaders = options.allowedHeaders || ctx.req.headers['access-control-request-headers']
ctx.headers['Access-Control-Allow-Origin'] = origin
ctx.headers['Access-Control-Allow-Methods'] = options.allowedMethods
if (reqHeaders && options.allowedHeaders !== false) {
ctx.headers['Access-Control-Allow-Headers'] = reqHeaders
}
ctx.status = 204
}
}
export function FormidableHandler(formidable, org = {}) {
let lastDateString = ''
let incrementor = 1
@ -420,7 +443,7 @@ export class FlaskaRouter {
* Flaska
*/
export class Flaska {
constructor(opts, orgHttp = http, orgStream = stream) {
constructor(opts = {}, orgHttp = http, orgStream = stream) {
this._before = []
this._beforeCompiled = null
this._beforeAsync = []
@ -430,10 +453,12 @@ export class Flaska {
this._afterAsync = []
this._afterAsyncCompiled = null
this._on404 = function(ctx) {
ctx.status = 404
ctx.body = {
status: 404,
message: statuses[404],
if (ctx.body == null && ctx.status !== 204) {
ctx.status = 404
ctx.body = {
status: 404,
message: statuses[404],
}
}
}
this._backuperror = this._onerror = function(err, ctx) {
@ -465,17 +490,91 @@ export class Flaska {
ctx.log.error(err)
}
let options = opts || {}
this.log = options.log || {
fatal: console.error.bind(console),
error: console.error.bind(console),
warn: console.log.bind(console),
info: console.log.bind(console),
debug: console.debug.bind(console),
trace: console.debug.bind(console),
log: console.log.bind(console),
let options = {
defaultHeaders: opts.defaultHeaders || {
'Server': 'Flaska',
'X-Content-Type-Options': 'nosniff',
'Content-Security-Policy': `default-src 'self'; style-src 'self' 'unsafe-inline'; img-src * data: blob:; object-src 'none'; frame-ancestors 'none'`,
'Cross-Origin-Opener-Policy': 'same-origin',
'Cross-Origin-Resource-Policy': 'same-origin',
'Cross-Origin-Embedder-Policy': 'require-corp',
},
log: opts.log || {
fatal: console.error.bind(console),
error: console.error.bind(console),
warn: console.log.bind(console),
info: console.log.bind(console),
debug: console.debug.bind(console),
trace: console.debug.bind(console),
log: console.log.bind(console),
},
nonce: opts.nonce || [],
nonceCacheLength: opts.nonceCacheLength || 25
}
if (!options.defaultHeaders && options.nonce.length) {
// throw error
}
let headerKeys = Object.keys(options.defaultHeaders)
let constructFunction = ''
if (options.nonce.length) {
this._nonces = new Array(options.nonceCacheLength)
this._noncesIndex = this._nonces.length - 1
for (let i = 0; i < this._nonces.length; i++) {
this._nonces[i] = crypto.randomBytes(16).toString('base64')
}
constructFunction += `
let nonce = this._nonces[this._noncesIndex] || crypto.randomBytes(16).toString('base64');
this._noncesIndex--;
ctx.state.nonce = nonce;
`
}
constructFunction += 'ctx.headers = {'
for (let key of headerKeys) {
if (key === 'Content-Security-Policy' && options.nonce.length) {
let groups = options.defaultHeaders[key].split(';')
for (let ni = 0; ni < options.nonce.length; ni++) {
let found = false
for (let x = 0; x < groups.length; x++) {
if (groups[x].trim().startsWith(options.nonce[ni])) {
groups[x] = groups[x].trimEnd() + ` 'nonce-$'`
found = true
break
}
}
if (!found) {
groups.push(` ${options.nonce[ni]} 'nonce-$'`)
}
}
groups = groups.join(';').replace(/\'/g, "\\'").split('$')
constructFunction += `'${key}': '${groups.join(`' + nonce + '`)}',`
} else {
constructFunction += `'${key}': '${options.defaultHeaders[key].replace(/\'/g, "\\'")}',`
}
}
constructFunction += '};'
// console.log(constructFunction)
if (options.nonce.length) {
this.before(new Function('crypto', 'ctx', constructFunction).bind(this, crypto))
this.after(new Function('crypto', 'ctx', `
this._noncesIndex = Math.max(this._noncesIndex, -1);
if (this._noncesIndex < this._nonces.length - 1) {
this._noncesIndex++;
this._nonces[this._noncesIndex] = crypto.randomBytes(16).toString('base64');
}
`).bind(this, crypto))
} else {
this.before(new Function('ctx', constructFunction).bind(this))
}
this.log = options.log
this.http = orgHttp
this.stream = orgStream
this.server = null

View File

@ -4,34 +4,250 @@ import { createCtx, fakeHttp } from './helper.mjs'
const faker = fakeHttp()
t.test('should be able to override the http', function() {
let flaska = new Flaska({}, faker)
assert.strictEqual(flaska.http, faker)
t.describe('#constructor', function() {
t.test('should be able to override the http', function() {
let flaska = new Flaska({}, faker)
assert.strictEqual(flaska.http, faker)
})
t.test('it should have all the common verbs', function() {
let flaska = new Flaska({}, faker)
assert.ok(flaska.get)
assert.strictEqual(typeof(flaska.get), 'function')
assert.ok(flaska.post)
assert.strictEqual(typeof(flaska.post), 'function')
assert.ok(flaska.put)
assert.strictEqual(typeof(flaska.put), 'function')
assert.ok(flaska.delete)
assert.strictEqual(typeof(flaska.delete), 'function')
assert.ok(flaska.options)
assert.strictEqual(typeof(flaska.options), 'function')
assert.ok(flaska.patch)
assert.strictEqual(typeof(flaska.patch), 'function')
})
t.test('the verbs GET and HEAD should be identical', function() {
let flaska = new Flaska({}, faker)
assert.ok(flaska.get)
assert.strictEqual(typeof(flaska.get), 'function')
assert.notOk(flaska.head)
assert.ok(flaska.routers['HEAD'])
assert.strictEqual(flaska.routers['GET'], flaska.routers['HEAD'])
})
t.test('should have before default header generator', function() {
let flaska = new Flaska({}, faker)
assert.strictEqual(flaska._before.length, 1)
let ctx = {}
flaska._before[0](ctx)
assert.deepStrictEqual(ctx.headers, {
'Server': 'Flaska',
'X-Content-Type-Options': 'nosniff',
'Content-Security-Policy': `default-src 'self'; style-src 'self' 'unsafe-inline'; img-src * data: blob:; object-src 'none'; frame-ancestors 'none'`,
'Cross-Origin-Opener-Policy': 'same-origin',
'Cross-Origin-Resource-Policy': 'same-origin',
'Cross-Origin-Embedder-Policy': 'require-corp',
})
assert.strictEqual(flaska._after.length, 0)
})
t.test('should have before ready setting headers on context if defaultHeaders is specified', function() {
const defaultHeaders = {
'Server': 'nginx/1.16.1',
'Date': 'Mon, 21 Mar\' 2022 07:26:01 GMT',
'Content-Type': 'applicat"ion/json; charset=utf-8',
'Content-Length': '1646',
'Connection': 'keep-alive',
'vary': 'Origin',
'Link': 'Link goes here',
'X-Frame-Options': 'DENY',
'X-Content-Type-Options': 'nosniff',
}
let flaska = new Flaska({
defaultHeaders: defaultHeaders,
}, faker)
assert.strictEqual(flaska._before.length, 1)
let ctx = {}
flaska._before[0](ctx)
assert.notStrictEqual(ctx.headers, defaultHeaders)
assert.deepStrictEqual(ctx.headers, defaultHeaders)
assert.strictEqual(flaska._after.length, 0)
})
})
t.test('it should have all the common verbs', function() {
let flaska = new Flaska({}, faker)
assert.ok(flaska.get)
assert.strictEqual(typeof(flaska.get), 'function')
assert.ok(flaska.post)
assert.strictEqual(typeof(flaska.post), 'function')
assert.ok(flaska.put)
assert.strictEqual(typeof(flaska.put), 'function')
assert.ok(flaska.delete)
assert.strictEqual(typeof(flaska.delete), 'function')
assert.ok(flaska.options)
assert.strictEqual(typeof(flaska.options), 'function')
assert.ok(flaska.patch)
assert.strictEqual(typeof(flaska.patch), 'function')
})
t.describe('#_nonce', function() {
t.test('should support nonce parameter with cached pre-filled entries', function() {
let flaska = new Flaska({
nonce: ['script-src', 'style-src'],
}, faker)
t.test('the verbs GET and HEAD should be identical', function() {
let flaska = new Flaska({}, faker)
assert.ok(flaska.get)
assert.strictEqual(typeof(flaska.get), 'function')
assert.notOk(flaska.head)
assert.ok(flaska.routers['HEAD'])
assert.strictEqual(flaska.routers['GET'], flaska.routers['HEAD'])
assert.ok(flaska._nonces)
assert.strictEqual(flaska._noncesIndex + 1, flaska._nonces.length)
assert.strictEqual(flaska._nonces.length, 25)
assert.ok(flaska._nonces[flaska._noncesIndex])
//Check they're all unique
let set = new Set()
flaska._nonces.forEach(function(entry) {
set.add(entry)
})
assert.strictEqual(set.size, flaska._nonces.length)
let ctx = createCtx()
assert.notOk(ctx.state.nonce)
let oldIndex = flaska._noncesIndex
flaska._before[0](ctx)
assert.ok(ctx.state.nonce)
assert.strictEqual(flaska._noncesIndex, oldIndex - 1)
assert.strictEqual(flaska._nonces[oldIndex], ctx.state.nonce)
assert.deepStrictEqual(ctx.headers, {
'Server': 'Flaska',
'X-Content-Type-Options': 'nosniff',
'Content-Security-Policy': `default-src 'self'; style-src 'self' 'unsafe-inline' 'nonce-${ctx.state.nonce}'; img-src * data: blob:; object-src 'none'; frame-ancestors 'none'; script-src 'nonce-${ctx.state.nonce}'`,
'Cross-Origin-Opener-Policy': 'same-origin',
'Cross-Origin-Resource-Policy': 'same-origin',
'Cross-Origin-Embedder-Policy': 'require-corp',
})
})
t.test('should always return nonce values even if it runs out in cache', function() {
let flaska = new Flaska({
nonce: ['script-src'],
nonceCacheLength: 5,
}, faker)
let ctx = createCtx()
for (let i = 0; i < 5; i++) {
let nextNonce = flaska._nonces[flaska._noncesIndex]
flaska._before[0](ctx)
assert.strictEqual(ctx.state.nonce, nextNonce)
assert.strictEqual(ctx.headers['Content-Security-Policy'], `default-src 'self'; style-src 'self' 'unsafe-inline'; img-src * data: blob:; object-src 'none'; frame-ancestors 'none'; script-src 'nonce-${ctx.state.nonce}'`)
}
assert.notOk(flaska._nonces[flaska._noncesIndex])
flaska._before[0](ctx)
assert.notOk(flaska._nonces[flaska._noncesIndex])
assert.ok(ctx.state.nonce)
for (let i = 0; i < flaska._nonces.length; i++) {
assert.notStrictEqual(ctx.state.nonce, flaska._nonces[i])
}
assert.strictEqual(ctx.headers['Content-Security-Policy'], `default-src 'self'; style-src 'self' 'unsafe-inline'; img-src * data: blob:; object-src 'none'; frame-ancestors 'none'; script-src 'nonce-${ctx.state.nonce}'`)
})
t.test('should have after that regenerates lost hashes', function() {
let flaska = new Flaska({
nonce: ['script-src'],
nonceCacheLength: 5,
}, faker)
let ctx = createCtx()
assert.strictEqual(flaska._after.length, 1)
//Check they're all unique
let set = new Set()
flaska._nonces.forEach(function(entry) {
set.add(entry)
})
assert.strictEqual(set.size, 5)
flaska._before[0](ctx)
flaska._before[0](ctx)
flaska._before[0](ctx)
assert.strictEqual(flaska._noncesIndex, 1)
flaska._after[0](ctx)
assert.strictEqual(flaska._noncesIndex, 2)
set.add(flaska._nonces[flaska._noncesIndex])
assert.strictEqual(set.size, 6)
flaska._after[0](ctx)
assert.strictEqual(flaska._noncesIndex, 3)
set.add(flaska._nonces[flaska._noncesIndex])
assert.strictEqual(set.size, 7)
flaska._after[0](ctx)
assert.strictEqual(flaska._noncesIndex, 4)
set.add(flaska._nonces[flaska._noncesIndex])
assert.strictEqual(set.size, 8)
flaska._after[0](ctx)
assert.strictEqual(flaska._noncesIndex, 4)
set.add(flaska._nonces[flaska._noncesIndex])
assert.strictEqual(set.size, 8)
})
t.test('after should not generate keys outside range', function() {
let flaska = new Flaska({
nonce: ['script-src'],
nonceCacheLength: 2,
}, faker)
let ctx = createCtx()
assert.strictEqual(flaska._after.length, 1)
//Check they're all unique
let set = new Set()
flaska._nonces.forEach(function(entry) {
set.add(entry)
})
assert.strictEqual(set.size, 2)
flaska._before[0](ctx)
flaska._before[0](ctx)
assert.strictEqual(flaska._noncesIndex, -1)
flaska._before[0](ctx)
assert.strictEqual(flaska._noncesIndex, -2)
set.add(ctx.state.nonce)
assert.strictEqual(set.size, 3)
flaska._before[0](ctx)
assert.strictEqual(flaska._noncesIndex, -3)
set.add(ctx.state.nonce)
assert.strictEqual(set.size, 4)
flaska._before[0](ctx)
assert.strictEqual(flaska._noncesIndex, -4)
set.add(ctx.state.nonce)
assert.strictEqual(set.size, 5)
assert.strictEqual(Object.keys(flaska._nonces).length, 2)
flaska._after[0](ctx)
assert.strictEqual(flaska._noncesIndex, 0)
set.add(flaska._nonces[flaska._noncesIndex])
assert.strictEqual(set.size, 6)
assert.strictEqual(Object.keys(flaska._nonces).length, 2)
flaska._after[0](ctx)
assert.strictEqual(flaska._noncesIndex, 1)
set.add(flaska._nonces[flaska._noncesIndex])
assert.strictEqual(set.size, 7)
assert.strictEqual(Object.keys(flaska._nonces).length, 2)
flaska._after[0](ctx)
assert.strictEqual(flaska._noncesIndex, 1)
set.add(flaska._nonces[flaska._noncesIndex])
assert.strictEqual(set.size, 7)
assert.strictEqual(Object.keys(flaska._nonces).length, 2)
})
})
t.describe('#log', function() {
@ -108,11 +324,6 @@ specialHandlers.forEach(function(type) {
})
t.describe('_on404', function() {
t.test('a valid function', function() {
let flaska = new Flaska({}, faker)
assert.strictEqual(typeof(flaska._on404), 'function')
})
t.test('default valid handling of context', function() {
let ctx = createCtx()
let flaska = new Flaska({}, faker)
@ -123,6 +334,28 @@ t.describe('_on404', function() {
message: 'Not Found',
})
})
t.test('should do nothing if body is not null', function() {
const assertBody = { a: 1 }
let ctx = createCtx()
let flaska = new Flaska({}, faker)
ctx.body = assertBody
flaska._on404(ctx)
assert.strictEqual(ctx.status, 200)
assert.strictEqual(ctx.body, assertBody)
})
t.test('should do nothing if body is null but status is 204', function() {
let ctx = createCtx()
let flaska = new Flaska({}, faker)
ctx.status = 204
ctx.body = null
flaska._on404(ctx)
assert.strictEqual(ctx.status, 204)
assert.strictEqual(ctx.body, null)
})
})
t.describe('_onerror', function() {
@ -317,8 +550,8 @@ t.describe('#before()', function() {
let flaska = new Flaska({}, faker)
assert.ok(flaska._before)
flaska.before(assertFunction)
assert.strictEqual(flaska._before.length, 1)
assert.strictEqual(flaska._before[0], assertFunction)
assert.strictEqual(flaska._before.length, 2)
assert.strictEqual(flaska._before[1], assertFunction)
})
})

View File

@ -153,6 +153,14 @@ t.describe('#requestStart()', function() {
assert.ok(ctx.query.get)
assert.ok(ctx.query.set)
assert.ok(ctx.query.delete)
assert.deepStrictEqual(ctx.headers, {
'Server': 'Flaska',
'X-Content-Type-Options': 'nosniff',
'Content-Security-Policy': `default-src 'self'; style-src 'self' 'unsafe-inline'; img-src * data: blob:; object-src 'none'; frame-ancestors 'none'`,
'Cross-Origin-Opener-Policy': 'same-origin',
'Cross-Origin-Resource-Policy': 'same-origin',
'Cross-Origin-Embedder-Policy': 'require-corp',
})
cb()
} catch (err) { cb(err) }
}
@ -163,6 +171,41 @@ t.describe('#requestStart()', function() {
}), createRes())
})
t.test('correctly adds default headers', function(cb) {
const assertError = new Error('test')
const defaultHeaders = {
'Test': 'Asdf',
'Herp': 'Derp',
}
let flaska = new Flaska({
defaultHeaders: defaultHeaders,
}, faker)
flaska.compile()
flaska.routers.test = {
match: function(path) {
throw assertError
}
}
flaska.requestEnd = function(err, ctx) {
if (err && err !== assertError) return cb(err)
try {
assert.ok(err)
assert.strictEqual(err, assertError)
assert.notStrictEqual(ctx.headers, defaultHeaders)
assert.deepStrictEqual(ctx.headers, defaultHeaders)
cb()
} catch (err) { cb(err) }
}
flaska.requestStart(createReq({
url: '/',
method: 'test',
}), createRes())
})
t.test('calls correct router with correct url and context if beforeAsync', function(cb) {
const assertError = new Error('test')
const assertMethod = 'test'

View File

@ -24,6 +24,7 @@ export function fakeHttp(inj1, inj2) {
export function createReq(def) {
return defaults(def, {
headers: {},
on: spy(),
})
}
@ -53,6 +54,7 @@ export function createCtx(def, endHandler) {
body: null,
type: null,
length: null,
headers: {},
log: {
error: spy(),
info: spy(),

View File

@ -2,7 +2,7 @@ import os from 'os'
import path from 'path'
import { Buffer } from 'buffer'
import { Eltro as t, assert, stub} from 'eltro'
import { QueryHandler, JsonHandler, FormidableHandler, HttpError } from '../flaska.mjs'
import { QueryHandler, JsonHandler, FormidableHandler, HttpError, CorsHandler } from '../flaska.mjs'
import { createCtx } from './helper.mjs'
import { finished } from 'stream'
import { setTimeout } from 'timers/promises'
@ -29,6 +29,98 @@ t.describe('#QueryHandler()', function() {
})
})
t.describe('#CorsHandler()', function() {
let corsHandler
let ctx
t.beforeEach(function() {
ctx = createCtx()
})
t.test('should return a handler', function() {
corsHandler = CorsHandler()
assert.strictEqual(typeof(corsHandler), 'function')
})
t.test('should set status and headers', function() {
const assertOrigin = 'http://my.site.here'
const assertRequestHeaders = 'asdf,foobar'
corsHandler = CorsHandler({
allowedOrigins: [assertOrigin],
})
ctx.method = 'OPTIONS'
ctx.req.headers['origin'] = assertOrigin
ctx.req.headers['access-control-request-method'] = 'GET'
ctx.req.headers['access-control-request-headers'] = assertRequestHeaders
assert.notOk(ctx.headers['Access-Control-Allow-Origin'])
assert.notOk(ctx.headers['Access-Control-Allow-Methods'])
assert.notOk(ctx.headers['Access-Control-Allow-Headers'])
corsHandler(ctx)
assert.strictEqual(ctx.headers['Access-Control-Allow-Origin'], assertOrigin)
assert.strictEqual(ctx.headers['Access-Control-Allow-Methods'], 'GET,HEAD,PUT,POST,DELETE,PATCH')
assert.strictEqual(ctx.headers['Access-Control-Allow-Headers'], assertRequestHeaders)
assert.strictEqual(ctx.status, 204)
})
t.test('should support custom allowed methods and headers', function() {
const assertOrigin = 'http://my.site.here'
const assertAllowedMethods = 'GET,HEAD'
const assertAllowedHeaders = 'test1,test2'
corsHandler = CorsHandler({
allowedOrigins: [assertOrigin],
allowedMethods: assertAllowedMethods,
allowedHeaders: assertAllowedHeaders,
})
ctx.method = 'OPTIONS'
ctx.req.headers['origin'] = assertOrigin
ctx.req.headers['access-control-request-method'] = 'GET'
ctx.req.headers['access-control-request-headers'] = 'asdfasdfasdfsfad'
assert.notOk(ctx.headers['Access-Control-Allow-Origin'])
assert.notOk(ctx.headers['Access-Control-Allow-Methods'])
assert.notOk(ctx.headers['Access-Control-Allow-Headers'])
corsHandler(ctx)
assert.strictEqual(ctx.headers['Access-Control-Allow-Origin'], assertOrigin)
assert.strictEqual(ctx.headers['Access-Control-Allow-Methods'], assertAllowedMethods)
assert.strictEqual(ctx.headers['Access-Control-Allow-Headers'], assertAllowedHeaders)
assert.strictEqual(ctx.status, 204)
})
t.test('should not set any allowed headers if allowedHeaders is explicitly false', function() {
const assertOrigin = 'http://my.site.here'
const assertAllowedMethods = 'GET,HEAD'
corsHandler = CorsHandler({
allowedOrigins: [assertOrigin],
allowedMethods: assertAllowedMethods,
allowedHeaders: false,
})
ctx.method = 'OPTIONS'
ctx.req.headers['origin'] = assertOrigin
ctx.req.headers['access-control-request-method'] = 'GET'
ctx.req.headers['access-control-request-headers'] = 'asdfasdfasdfsfad'
assert.notOk(ctx.headers['Access-Control-Allow-Origin'])
assert.notOk(ctx.headers['Access-Control-Allow-Methods'])
assert.notOk(ctx.headers['Access-Control-Allow-Headers'])
corsHandler(ctx)
assert.strictEqual(ctx.headers['Access-Control-Allow-Origin'], assertOrigin)
assert.strictEqual(ctx.headers['Access-Control-Allow-Methods'], assertAllowedMethods)
assert.strictEqual(ctx.headers['Access-Control-Allow-Headers'], undefined)
assert.strictEqual(ctx.status, 204)
})
})
t.describe('#JsonHandler()', function() {
let jsonHandler = JsonHandler()
let ctx