Compare commits
No commits in common. "master" and "v0.9.9" have entirely different histories.
45 changed files with 1556 additions and 6359 deletions
|
@ -1,44 +0,0 @@
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- master
|
|
||||||
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
deploy:
|
|
||||||
runs-on: arch
|
|
||||||
steps:
|
|
||||||
- name: Check out repository code
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
- name: Install dependencies
|
|
||||||
run: time pnpm install
|
|
||||||
- name: Run Tests
|
|
||||||
run: pnpm run test --ignore-only
|
|
||||||
- name: Deply if new version
|
|
||||||
run: |
|
|
||||||
echo ""
|
|
||||||
echo "------------------------------------"
|
|
||||||
echo ""
|
|
||||||
CURR_VER="$(cat package.json | jq -r .name)_v$(cat package.json | jq -r .version)"
|
|
||||||
CURR_NAME="$(cat package.json | jq -r .name) v$(cat package.json | jq -r .version)"
|
|
||||||
|
|
||||||
echo "Checking https://git.nfp.is/api/v1/repos/${{ github.repository }}/releases for name ${CURR_NAME}"
|
|
||||||
|
|
||||||
if curl -s -X GET -H "Authorization: token ${{ secrets.deploytoken }}" https://git.nfp.is/api/v1/repos/${{ github.repository }}/releases | grep -o "\"name\":\"${CURR_NAME}\"" > /dev/null; then
|
|
||||||
echo "Skipping ${{ github.job }} since $CURR_NAME already exists";
|
|
||||||
exit;
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "New release ${CURR_VER} found, beginning deployment"
|
|
||||||
|
|
||||||
echo "Creating ${CURR_VER} release on forgejo"
|
|
||||||
curl \
|
|
||||||
-X POST \
|
|
||||||
-H "Authorization: token ${{ secrets.deploytoken }}" \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
https://git.nfp.is/api/v1/repos/${{ github.repository }}/releases \
|
|
||||||
-d "{\"tag_name\":\"${CURR_VER}\",\"name\":\"${CURR_NAME}\",\"body\":\"Automatic release from Appveyor from ${{ github.sha }} :\n\n${{ github.event.head_commit.message }}\"}" | jq
|
|
||||||
|
|
||||||
echo "//registry.npmjs.org/:_authToken=${{ secrets.npmtoken }}" > ~/.npmrc
|
|
||||||
echo "Publishing new version to npm"
|
|
||||||
npm publish
|
|
200
README.md
200
README.md
|
@ -1,198 +1,2 @@
|
||||||
# Flaska
|
# bottle-node
|
||||||
Flaska is a micro web-framework for node. It is designed to be fast, simple and lightweight, and is distributed as a single file module with no dependencies.
|
Bottle is a micro web-framework for node. It is designed to be fast, simple and lightweight, and is distributed as a single file module with no dependencies.
|
||||||
|
|
||||||
Heavily inspired by koa and koa-router it takes liberties of implementing most of the common functionality required for most sites without sacrificing anything. And the fact that the footprint for this is much smaller than koa while also being more powerful and faster shows the testament that is flaska.
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
```
|
|
||||||
$ npm install flaska
|
|
||||||
```
|
|
||||||
|
|
||||||
## Hello Flaska
|
|
||||||
|
|
||||||
```js
|
|
||||||
import { Flaska } from '../flaska.mjs'
|
|
||||||
const flaska = new Flaska()
|
|
||||||
|
|
||||||
flaska.get('/', function(ctx) {
|
|
||||||
ctx.body = 'Hello Flaska';
|
|
||||||
})
|
|
||||||
|
|
||||||
// flaska.listen(3000);
|
|
||||||
flaska.listenAsync(3000).then(function() {
|
|
||||||
console.log('listening on port 3000')
|
|
||||||
}, function(err) {
|
|
||||||
console.error('Error listening:', err)
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Router/Handlers
|
|
||||||
|
|
||||||
Flaska supports all the common verbs out of the box:
|
|
||||||
|
|
||||||
* `flaska.get(url, [middlewares], handler)`
|
|
||||||
* `flaska.post(url, [middlewares], handler)`
|
|
||||||
* `flaska.put(url, [middlewares], handler)`
|
|
||||||
* `flaska.delete(url, [middlewares], handler)`
|
|
||||||
* `flaska.options(url, [middlewares], handler)`
|
|
||||||
* `flaska.patch(url, [middlewares], handler)`
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
```
|
|
||||||
flaska.get('/path/to/url', async function(ctx) {
|
|
||||||
// Perform action
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
In addition, each route can have none, one or many middlewares:
|
|
||||||
|
|
||||||
```
|
|
||||||
flaska.get('/handlequery', QueryHandler(), async function(ctx) {
|
|
||||||
// Perform action
|
|
||||||
})
|
|
||||||
|
|
||||||
flaska.get('/query/and/json', [QueryHandler(), JsonHandler()], async function(ctx) {
|
|
||||||
// Perform action
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
You can also run global middlewares at the start or end of every request:
|
|
||||||
|
|
||||||
```
|
|
||||||
import { Flaska, QueryHandler, JsonHandler } from '../flaska.mjs'
|
|
||||||
const flaska = new Flaska()
|
|
||||||
|
|
||||||
flaska.before(QueryHandler())
|
|
||||||
flaska.before(JsonHandler())
|
|
||||||
flaska.beforeAsync(MyLoadDatabase())
|
|
||||||
|
|
||||||
flaska.after(function(ctx) {
|
|
||||||
ctx.log.info('Request finished.')
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Context, Request and Response
|
|
||||||
|
|
||||||
Each handler receives a Flaska `Context` object that encapsulates an incoming
|
|
||||||
http message and the corresponding response to that message. For those familiar
|
|
||||||
with koa will be famililar with this concept. `ctx` is often used as the parameter
|
|
||||||
name for the context object.
|
|
||||||
|
|
||||||
```js
|
|
||||||
app.before(function(ctx) {
|
|
||||||
ctx.log.info('Got request')
|
|
||||||
})
|
|
||||||
app.beforeAsync(async function(ctx) {
|
|
||||||
// Do some database fetching maybe
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
The context `ctx` that gets generated includes the following properties for the incoming request:
|
|
||||||
|
|
||||||
* `log`: Log handler.
|
|
||||||
* `req`: The Request's [IncomingMessage](https://nodejs.org/api/http.html#http_class_http_incomingmessage).
|
|
||||||
* `res`: The Response's [ServerResponse](https://nodejs.org/api/http.html#http_class_http_serverresponse).
|
|
||||||
* `method`: The HTTP method ('GET', 'POST', etc.).
|
|
||||||
* `url`: The full URL path.
|
|
||||||
* `search`: The URL search.
|
|
||||||
* `state`: Anonymous object for user-defined data.
|
|
||||||
* `query`: Map containing the key/value for the url search.
|
|
||||||
|
|
||||||
The following context specific variables can be specified by you the user to instruct Flaska on what to return in response:
|
|
||||||
* `status`: The status code to return in response (default 200).
|
|
||||||
* `body`: The body contents to return in response (default null).
|
|
||||||
* `type`: The content-type for the header (default null).
|
|
||||||
* `length`: The length of the body in bytes (default null).
|
|
||||||
|
|
||||||
More on this next.
|
|
||||||
|
|
||||||
## `ctx.body`
|
|
||||||
|
|
||||||
At the end of each request, flaska will read `ctx.body` and determine the best course of action based on the type of content is being sent.
|
|
||||||
|
|
||||||
### Javascript object
|
|
||||||
|
|
||||||
In cases where the response body is a normal object, Flaska will automatically `JSON.stringify` it,
|
|
||||||
set the `Content-Type` to `application/json` and set the total length in `Content-Length`. This
|
|
||||||
is provided for you at no expense on your behalf so you don't have to worry about it:
|
|
||||||
|
|
||||||
```
|
|
||||||
flaska.get('/api/test', function(ctx) {
|
|
||||||
ctx.body = {
|
|
||||||
message: 'Hello world',
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### pipe
|
|
||||||
|
|
||||||
In cases where the response body is a pipe object (detected from the existance of `.pipe` property), flaska will automatically pipe it for you. In addition, if a file stream is used, it will read the extension of the file being streamed and automatically fill in the mime-type for you in the `Content-Type` header.
|
|
||||||
|
|
||||||
```
|
|
||||||
flaska.get('/test.png', function(ctx) {
|
|
||||||
ctx.body = fs.createReadStream('./test/test.png')
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
Flaska will automatically close the file stream for you so you don't have to worry about that.
|
|
||||||
|
|
||||||
### FileResponse
|
|
||||||
|
|
||||||
Alternatively, if you want proper file support, I recommend using FileResponse object:
|
|
||||||
|
|
||||||
```
|
|
||||||
import { FileResponse } from '../flaska.mjs'
|
|
||||||
|
|
||||||
flaska.get('/test.txt', function(ctx) {
|
|
||||||
return fs.stat('./test/test.txt').then(function(stat) {
|
|
||||||
ctx.body = new FileResponse('./test/test.txt', stat)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
This performs a real file stream support, uses pipes and supports all the HTTP shenanigans when it comes to dealing with files, including sending proper etag header, supporting partial response and lots of other things. This is one of the few libraries that actually implements full HTTP partial and etag support in a proper way, almost all other have one or two quirks that don't follow the spec properly.
|
|
||||||
|
|
||||||
### String
|
|
||||||
|
|
||||||
In other instances, Flaska will `.toString()` the body and send it in response with the specified type or default to `text/plain` if unspecified.
|
|
||||||
|
|
||||||
```
|
|
||||||
flaska.get('/file.html', function(ctx) {
|
|
||||||
ctx.body = `
|
|
||||||
<!doctype html>
|
|
||||||
<html lang="en">
|
|
||||||
<body>
|
|
||||||
<h1>Hello world</h1>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
`
|
|
||||||
ctx.type = 'text/html; charset=utf-8'
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
The `Context` object also provides shortcuts for methods on its `request` and `response`. In the prior
|
|
||||||
examples, `ctx.type` can be used instead of `ctx.response.type` and `ctx.accepts` can be used
|
|
||||||
instead of `ctx.request.accepts`.
|
|
||||||
|
|
||||||
|
|
||||||
## Built-in middlewares and handlers
|
|
||||||
|
|
||||||
Flaska comes with a few middlewares out of the box.
|
|
||||||
|
|
||||||
* `QueryHandler()`
|
|
||||||
|
|
||||||
Parse the search query and create a map with key->value in `ctx.query`.
|
|
||||||
|
|
||||||
* `JsonHandler()`
|
|
||||||
|
|
||||||
Parse incoming request body as json and store it in `ctx.req.body`.
|
|
||||||
|
|
||||||
* `CorsHandler()`
|
|
||||||
|
|
||||||
A secure implementation for handling CORS requests.
|
|
||||||
|
|
||||||
* `FormidableHandler()`
|
|
||||||
|
|
||||||
Provides a wrapper to handle an incoming file upload using `Formidable@1`.
|
|
||||||
|
|
|
@ -52,12 +52,12 @@ on_success:
|
||||||
echo "Release already exists, nothing to do.";
|
echo "Release already exists, nothing to do.";
|
||||||
else
|
else
|
||||||
echo "Creating release on gitea"
|
echo "Creating release on gitea"
|
||||||
curl \
|
RELEASE_RESULT=$(curl \
|
||||||
-X POST \
|
-X POST \
|
||||||
-H "Authorization: token $deploytoken" \
|
-H "Authorization: token $deploytoken" \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
https://git.nfp.is/api/v1/repos/$APPVEYOR_REPO_NAME/releases \
|
https://git.nfp.is/api/v1/repos/$APPVEYOR_REPO_NAME/releases \
|
||||||
-d "{\"tag_name\":\"v${CURR_VER}\",\"name\":\"v${CURR_VER}\",\"body\":\"Automatic release from Appveyor from ${APPVEYOR_REPO_COMMIT} :\n\n${APPVEYOR_REPO_COMMIT_MESSAGE}\"}"
|
-d "{\"tag_name\":\"v${CURR_VER}\",\"name\":\"v${CURR_VER}\",\"body\":\"Automatic release from Appveyor from ${APPVEYOR_REPO_COMMIT} :\n\n${APPVEYOR_REPO_COMMIT_MESSAGE}\"}")
|
||||||
echo '//registry.npmjs.org/:_authToken=${npmtoken}' > ~/.npmrc
|
echo '//registry.npmjs.org/:_authToken=${npmtoken}' > ~/.npmrc
|
||||||
echo "Publishing new version to npm"
|
echo "Publishing new version to npm"
|
||||||
npm publish
|
npm publish
|
||||||
|
|
|
@ -1,116 +0,0 @@
|
||||||
import { printTree } from "./utils.mjs"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Child {
|
|
||||||
* char: 'a',
|
|
||||||
* children: [Child, Child],
|
|
||||||
* path: null / 'full/path',
|
|
||||||
* count: 0
|
|
||||||
* }
|
|
||||||
*/
|
|
||||||
function Child(char) {
|
|
||||||
this.char = char
|
|
||||||
this.children = []
|
|
||||||
this.path = null
|
|
||||||
this.count = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildChild(i, arr) {
|
|
||||||
let letter = new Child(arr[0][i])
|
|
||||||
let consume = []
|
|
||||||
if (arr[0].length === i + 1) {
|
|
||||||
letter.path = arr[0]
|
|
||||||
letter.count += 1
|
|
||||||
} else {
|
|
||||||
consume = [arr[0]]
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let y = 1; y < arr.length; y++) {
|
|
||||||
if (arr[y][i] !== letter.char) break
|
|
||||||
consume.push(arr[y])
|
|
||||||
}
|
|
||||||
|
|
||||||
letter.count += consume.length
|
|
||||||
while (consume.length) {
|
|
||||||
letter.children.push(buildChild(i + 1, consume))
|
|
||||||
consume.splice(0, letter.children[letter.children.length - 1].count)
|
|
||||||
}
|
|
||||||
return letter
|
|
||||||
}
|
|
||||||
|
|
||||||
export function buildTree(all) {
|
|
||||||
let paths = Array.from(new Set(all)).sort()
|
|
||||||
let builder = []
|
|
||||||
while (paths.length) {
|
|
||||||
builder.push(buildChild(0, paths))
|
|
||||||
paths.splice(0, builder[builder.length - 1].count)
|
|
||||||
}
|
|
||||||
printTree(builder)
|
|
||||||
return builder
|
|
||||||
}
|
|
||||||
|
|
||||||
function IfTreeBranch(branches, indent = 0) {
|
|
||||||
let output = ''
|
|
||||||
let indentation = ''.padStart(indent * 2)
|
|
||||||
|
|
||||||
for (let i = 0; i < branches.length; i++) {
|
|
||||||
let branch = branches[i]
|
|
||||||
output += `${i > 0 ? 'else ': ''}if (str.charCodeAt(${indent}) === ${branch.char.charCodeAt(0)}) { // ${branch.char}`
|
|
||||||
|
|
||||||
if (branch.path) {
|
|
||||||
output += '\n' + indentation + ` if (str.length === ${branch.path.length}) {`
|
|
||||||
output += '\n' + indentation + ` return "${branch.path}"`
|
|
||||||
output += '\n' + indentation + ` }`
|
|
||||||
}
|
|
||||||
if (branch.children.length) {
|
|
||||||
if (branch.path) {
|
|
||||||
output += ' else '
|
|
||||||
} else {
|
|
||||||
output += '\n' + indentation + ' '
|
|
||||||
}
|
|
||||||
output += IfTreeBranch(branch.children, indent + 1)
|
|
||||||
}
|
|
||||||
output += '\n' + indentation + '} '
|
|
||||||
}
|
|
||||||
return output
|
|
||||||
}
|
|
||||||
|
|
||||||
export function compileTreeIntoIfs(tree) {
|
|
||||||
let output = IfTreeBranch(tree)
|
|
||||||
output += '\nreturn null'
|
|
||||||
return new Function('str', output)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function IfTreeBranchBuffer(branches, indent = 0) {
|
|
||||||
let output = ''
|
|
||||||
let indentation = ''.padStart(indent * 2)
|
|
||||||
|
|
||||||
for (let i = 0; i < branches.length; i++) {
|
|
||||||
let branch = branches[i]
|
|
||||||
output += `${i > 0 ? 'else ': ''}if (uint[${indent}] === ${branch.char.charCodeAt(0)}) { // ${branch.char}`
|
|
||||||
|
|
||||||
if (branch.path) {
|
|
||||||
output += '\n' + indentation + ` if (uint.length === ${branch.path.length}) {`
|
|
||||||
output += '\n' + indentation + ` return "${branch.path}"`
|
|
||||||
output += '\n' + indentation + ` }`
|
|
||||||
}
|
|
||||||
if (branch.children.length) {
|
|
||||||
if (branch.path) {
|
|
||||||
output += ' else '
|
|
||||||
} else {
|
|
||||||
output += '\n' + indentation + ' '
|
|
||||||
}
|
|
||||||
output += IfTreeBranchBuffer(branch.children, indent + 1)
|
|
||||||
}
|
|
||||||
output += '\n' + indentation + '} '
|
|
||||||
}
|
|
||||||
return output
|
|
||||||
}
|
|
||||||
|
|
||||||
export function compileTreeIntoIfsWithBuffer(tree) {
|
|
||||||
let output = `var uint = Buffer.from(str)\n`
|
|
||||||
output += IfTreeBranchBuffer(tree)
|
|
||||||
output += '\nreturn null'
|
|
||||||
return new Function('str', output)
|
|
||||||
}
|
|
|
@ -1,39 +0,0 @@
|
||||||
export function printTree(children, indent = 0) {
|
|
||||||
if (!children.length) return
|
|
||||||
|
|
||||||
for (let child of children) {
|
|
||||||
console.log(child.char.padStart(1 + indent * 2))
|
|
||||||
printTree(child.children, indent + 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function printIfNotEightyOne(fn) {
|
|
||||||
let opt = %GetOptimizationStatus(fn)
|
|
||||||
if (opt === 81) return
|
|
||||||
printCurrentStatus()
|
|
||||||
}
|
|
||||||
|
|
||||||
export function printCurrentStatus(fn) {
|
|
||||||
let opt = %GetOptimizationStatus(fn)
|
|
||||||
console.log(`${opt.toString(2).padStart(17, '0').split('').join(' ')} (${opt})`)
|
|
||||||
}
|
|
||||||
export function printStatusHelperText() {
|
|
||||||
console.log(`┬ ┬ ┬ ┬ ┬ ┬ ┬ ┬ ┬ ┬ ┬ ┬ ┬ ┬ ┬ ┬ ┬
|
|
||||||
│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └─╸ is function
|
|
||||||
│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └───╸ is never optimized
|
|
||||||
│ │ │ │ │ │ │ │ │ │ │ │ │ │ └─────╸ is always optimized
|
|
||||||
│ │ │ │ │ │ │ │ │ │ │ │ │ └───────╸ is maybe deoptimized
|
|
||||||
│ │ │ │ │ │ │ │ │ │ │ │ └─────────╸ is optimized
|
|
||||||
│ │ │ │ │ │ │ │ │ │ │ └───────────╸ is optimized by TurboFan
|
|
||||||
│ │ │ │ │ │ │ │ │ │ └─────────────╸ is interpreted
|
|
||||||
│ │ │ │ │ │ │ │ │ └───────────────╸ is marked for optimization
|
|
||||||
│ │ │ │ │ │ │ │ └─────────────────╸ is marked for concurrent optimization
|
|
||||||
│ │ │ │ │ │ │ └───────────────────╸ is optimizing concurrently
|
|
||||||
│ │ │ │ │ │ └─────────────────────╸ is executing
|
|
||||||
│ │ │ │ │ └───────────────────────╸ topmost frame is turbo fanned
|
|
||||||
│ │ │ │ └─────────────────────────╸ lite mode
|
|
||||||
│ │ │ └───────────────────────────╸ marked for deoptimization
|
|
||||||
│ │ └─────────────────────────────╸ baseline
|
|
||||||
│ └───────────────────────────────╸ topmost frame is interpreted
|
|
||||||
└─────────────────────────────────╸ topmost frame is baseline`)
|
|
||||||
}
|
|
|
@ -6,15 +6,6 @@ export const overrideDummy = function(override) {
|
||||||
|
|
||||||
export const dummy = function() { callItem() }
|
export const dummy = function() { callItem() }
|
||||||
|
|
||||||
export const testRoutes = [
|
|
||||||
// '/api/articles/:id/file/:fileId',
|
|
||||||
'/',
|
|
||||||
// '/api/articles',
|
|
||||||
// '/api/articles/:id/file',
|
|
||||||
// '/api/articles/:id',
|
|
||||||
'/::rest',
|
|
||||||
]
|
|
||||||
|
|
||||||
export const allRoutes = [
|
export const allRoutes = [
|
||||||
'/',
|
'/',
|
||||||
'/api/articles',
|
'/api/articles',
|
||||||
|
@ -32,22 +23,6 @@ export const allRoutes = [
|
||||||
'/api/pages/:id/articles/public',
|
'/api/pages/:id/articles/public',
|
||||||
'/api/staff',
|
'/api/staff',
|
||||||
'/api/staff/:id',
|
'/api/staff/:id',
|
||||||
'/::rest',
|
|
||||||
]
|
|
||||||
|
|
||||||
export const allRoutesGoldenBalance = [
|
|
||||||
'/',
|
|
||||||
'/a/:a/aaaaaaaaaaaaaaaaaaaaaaaaaaaaa/:aa',
|
|
||||||
'/b/:b/bbbbbbbbbbbbbbbbbbbbbbbbbbbbb/:bb',
|
|
||||||
'/c/:c/ccccccccccccccccccccccccccccc/:cc',
|
|
||||||
'/d/:d/ddddddddddddddddddddddddddddd/:dd',
|
|
||||||
'/e/:e/eeeeeeeeeeeeeeeeeeeeeeeeeeeee/:ee',
|
|
||||||
'/f/:f/fffffffffffffffffffffffffffff/:ff',
|
|
||||||
'/g/:g/ggggggggggggggggggggggggggggg/:gg',
|
|
||||||
'/h/:h/hhhhhhhhhhhhhhhhhhhhhhhhhhhhh/:hh',
|
|
||||||
'/i/:i/iiiiiiiiiiiiiiiiiiiiiiiiiiiii/:ii',
|
|
||||||
'/j/:j/jjjjjjjjjjjjjjjjjjjjjjjjjjjjj/:jj',
|
|
||||||
'/::rest',
|
|
||||||
]
|
]
|
||||||
|
|
||||||
export const allManyRoutes = [
|
export const allManyRoutes = [
|
||||||
|
@ -62,7 +37,7 @@ export const allManyRoutes = [
|
||||||
'/api/categories/:categoryId/properties',
|
'/api/categories/:categoryId/properties',
|
||||||
'/api/categories/:categoryId/values/:props',
|
'/api/categories/:categoryId/values/:props',
|
||||||
'/api/categories/:categoryId',
|
'/api/categories/:categoryId',
|
||||||
//'/api/categories/:categoryId/products/:productId',
|
'/api/categories/:categoryId/products/:productId',
|
||||||
'/api/categories/:categoryId/products/:productId',
|
'/api/categories/:categoryId/products/:productId',
|
||||||
'/api/customers',
|
'/api/customers',
|
||||||
'/api/customers/:id',
|
'/api/customers/:id',
|
||||||
|
@ -86,7 +61,7 @@ export const allManyRoutes = [
|
||||||
'/api/products/:id',
|
'/api/products/:id',
|
||||||
'/api/products/:id/movement',
|
'/api/products/:id/movement',
|
||||||
'/api/products/:id/sub_products/:productId',
|
'/api/products/:id/sub_products/:productId',
|
||||||
//'/api/products/:id/sub_products/:productId',
|
'/api/products/:id/sub_products/:productId',
|
||||||
'/api/products/code/:code',
|
'/api/products/code/:code',
|
||||||
'/api/products/property/:propertyId',
|
'/api/products/property/:propertyId',
|
||||||
'/api/properties',
|
'/api/properties',
|
||||||
|
@ -109,5 +84,4 @@ export const allManyRoutes = [
|
||||||
'/api/works/public',
|
'/api/works/public',
|
||||||
'/api/staff',
|
'/api/staff',
|
||||||
'/api/staff/:id',
|
'/api/staff/:id',
|
||||||
'/::rest',
|
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,148 +0,0 @@
|
||||||
import { summary, run, bench } from 'mitata';
|
|
||||||
|
|
||||||
function printCurrentStatus(fn) {
|
|
||||||
let opt = %GetOptimizationStatus(fn)
|
|
||||||
console.log(`${opt.toString(2).padStart(17, '0').split('').join(' ')} (${opt}) ${fn.name}`)
|
|
||||||
}
|
|
||||||
function printStatusHelperText() {
|
|
||||||
console.log(`┬ ┬ ┬ ┬ ┬ ┬ ┬ ┬ ┬ ┬ ┬ ┬ ┬ ┬ ┬ ┬ ┬
|
|
||||||
│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └─╸ is function
|
|
||||||
│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └───╸ is never optimized
|
|
||||||
│ │ │ │ │ │ │ │ │ │ │ │ │ │ └─────╸ is always optimized
|
|
||||||
│ │ │ │ │ │ │ │ │ │ │ │ │ └───────╸ is maybe deoptimized
|
|
||||||
│ │ │ │ │ │ │ │ │ │ │ │ └─────────╸ is optimized
|
|
||||||
│ │ │ │ │ │ │ │ │ │ │ └───────────╸ is optimized by TurboFan
|
|
||||||
│ │ │ │ │ │ │ │ │ │ └─────────────╸ is interpreted
|
|
||||||
│ │ │ │ │ │ │ │ │ └───────────────╸ is marked for optimization
|
|
||||||
│ │ │ │ │ │ │ │ └─────────────────╸ is marked for concurrent optimization
|
|
||||||
│ │ │ │ │ │ │ └───────────────────╸ is optimizing concurrently
|
|
||||||
│ │ │ │ │ │ └─────────────────────╸ is executing
|
|
||||||
│ │ │ │ │ └───────────────────────╸ topmost frame is turbo fanned
|
|
||||||
│ │ │ │ └─────────────────────────╸ lite mode
|
|
||||||
│ │ │ └───────────────────────────╸ marked for deoptimization
|
|
||||||
│ │ └─────────────────────────────╸ baseline
|
|
||||||
│ └───────────────────────────────╸ topmost frame is interpreted
|
|
||||||
└─────────────────────────────────╸ topmost frame is baseline`)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Warmup (de-optimize `bench()` calls)
|
|
||||||
bench('noop', () => { });
|
|
||||||
bench('noop2', () => { });
|
|
||||||
|
|
||||||
function ifSingle(a) {
|
|
||||||
if (a[0] === 97 && a[1] === 97 && a[2] === 97 && a[3] === 97 && a.length === 4) {
|
|
||||||
return 100
|
|
||||||
}
|
|
||||||
return 10
|
|
||||||
}
|
|
||||||
|
|
||||||
function ifChain(a) {
|
|
||||||
if (a[0] === 97)
|
|
||||||
if (a[1] === 97)
|
|
||||||
if (a[2] === 97)
|
|
||||||
if (a[3] === 97)
|
|
||||||
if (a.length === 4) {
|
|
||||||
return 100
|
|
||||||
}
|
|
||||||
return 10
|
|
||||||
}
|
|
||||||
|
|
||||||
function ifSingleOpt(a) {
|
|
||||||
if (a.length >= 4 && a[0] === 97 && a[1] === 97 && a[2] === 97 && a[3] === 97 && a.length === 4) {
|
|
||||||
return 100
|
|
||||||
}
|
|
||||||
return 10
|
|
||||||
}
|
|
||||||
|
|
||||||
function ifChainOpt(a) {
|
|
||||||
if (a.length >= 4)
|
|
||||||
if (a[0] === 97)
|
|
||||||
if (a[1] === 97)
|
|
||||||
if (a[2] === 97)
|
|
||||||
if (a[3] === 97)
|
|
||||||
if (a.length === 4) {
|
|
||||||
return 100
|
|
||||||
}
|
|
||||||
return 10
|
|
||||||
}
|
|
||||||
|
|
||||||
function ifSingleOptAlt(a) {
|
|
||||||
if (a.length >= 4 && a[0] == 97 && a[1] == 97 && a[2] == 97 && a[3] == 97 && a.length == 4) {
|
|
||||||
return 100
|
|
||||||
}
|
|
||||||
return 10
|
|
||||||
}
|
|
||||||
|
|
||||||
function ifChainOptAlt(a) {
|
|
||||||
if (a.length >= 4)
|
|
||||||
if (a[0] == 97)
|
|
||||||
if (a[1] == 97)
|
|
||||||
if (a[2] == 97)
|
|
||||||
if (a[3] == 97)
|
|
||||||
if (a.length == 4) {
|
|
||||||
return 100
|
|
||||||
}
|
|
||||||
return 10
|
|
||||||
}
|
|
||||||
|
|
||||||
let paths = [
|
|
||||||
[97, 97, 97, 97],
|
|
||||||
[97, 97, 97, 97, 97],
|
|
||||||
[97, 97, 96, 97],
|
|
||||||
[97, 96, 97, 97],
|
|
||||||
[96, 97, 97, 97],
|
|
||||||
[],
|
|
||||||
[98],
|
|
||||||
[97, 97, 97],
|
|
||||||
[97, 97],
|
|
||||||
[97],
|
|
||||||
[97, 97, 96],
|
|
||||||
[97, 96],
|
|
||||||
[96],
|
|
||||||
]
|
|
||||||
|
|
||||||
let paths2 = [
|
|
||||||
[97, 97, 97, 97],
|
|
||||||
[97, 97, 97, 97, 97],
|
|
||||||
[97, 97, 96, 97],
|
|
||||||
[97, 96, 97, 97],
|
|
||||||
[96, 97, 97, 97],
|
|
||||||
[97, 97, 97, 97],
|
|
||||||
[97, 97, 97, 97, 97],
|
|
||||||
[97, 97, 96, 97],
|
|
||||||
[97, 96, 97, 97],
|
|
||||||
[96, 97, 97, 97],
|
|
||||||
[97, 97, 97, 97, 97, 97],
|
|
||||||
[97, 97, 97, 97, 96, 97],
|
|
||||||
[97, 97, 97, 97, 97, 96],
|
|
||||||
]
|
|
||||||
|
|
||||||
let func1 = [ifSingle, ifChain, ifSingleOpt, ifChainOpt, ifSingleOptAlt, ifChainOptAlt];
|
|
||||||
for (let fun of func1) {
|
|
||||||
console.log('-- begin', fun.name)
|
|
||||||
for (var i = 0; i < 1000000; i++) {
|
|
||||||
paths.map(fun)
|
|
||||||
}
|
|
||||||
printCurrentStatus(fun);
|
|
||||||
}
|
|
||||||
printStatusHelperText()
|
|
||||||
|
|
||||||
|
|
||||||
summary(() => {
|
|
||||||
func1.forEach(function(fun) {
|
|
||||||
bench(fun.name + ' first', function() {
|
|
||||||
return paths.map(fun)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
func1.forEach(function(fun) {
|
|
||||||
bench(fun.name + ' second', function() {
|
|
||||||
return paths2.map(fun)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
run().then(function() {
|
|
||||||
for (let fun of func1) {
|
|
||||||
printCurrentStatus(fun);
|
|
||||||
}
|
|
||||||
printStatusHelperText()
|
|
||||||
});
|
|
|
@ -1,85 +0,0 @@
|
||||||
import { printCurrentStatus, printStatusHelperText } from "./compiler/utils.mjs";
|
|
||||||
|
|
||||||
function ifSingle(a) {
|
|
||||||
if (a[0] === 97 && a[1] === 97 && a[2] === 97 && a[3] === 97 && a.length === 4) {
|
|
||||||
return 100
|
|
||||||
}
|
|
||||||
return 10
|
|
||||||
}
|
|
||||||
|
|
||||||
function ifChain(a) {
|
|
||||||
if (a[0] === 97)
|
|
||||||
if (a[1] === 97)
|
|
||||||
if (a[2] === 97)
|
|
||||||
if (a[3] === 97)
|
|
||||||
if (a.length === 4) {
|
|
||||||
return 100
|
|
||||||
}
|
|
||||||
return 10
|
|
||||||
}
|
|
||||||
|
|
||||||
function ifSingleOptimized(a) {
|
|
||||||
if (a.length >= 4 && a[0] === 97 && a[1] === 97 && a[2] === 97 && a[3] === 97 && a.length === 4) {
|
|
||||||
return 100
|
|
||||||
}
|
|
||||||
return 10
|
|
||||||
}
|
|
||||||
|
|
||||||
function ifChainOptimized(a) {
|
|
||||||
if (a.length >= 4)
|
|
||||||
if (a[0] === 97)
|
|
||||||
if (a[1] === 97)
|
|
||||||
if (a[2] === 97)
|
|
||||||
if (a[3] === 97)
|
|
||||||
if (a.length === 4) {
|
|
||||||
return 100
|
|
||||||
}
|
|
||||||
return 10
|
|
||||||
}
|
|
||||||
|
|
||||||
function ifSingleOptimizedAlt(a) {
|
|
||||||
if (a.length >= 4 && a[0] == 97 && a[1] == 97 && a[2] == 97 && a[3] == 97 && a.length == 4) {
|
|
||||||
return 100
|
|
||||||
}
|
|
||||||
return 10
|
|
||||||
}
|
|
||||||
|
|
||||||
function ifChainOptimizedAlt(a) {
|
|
||||||
if (a.length >= 4)
|
|
||||||
if (a[0] == 97)
|
|
||||||
if (a[1] == 97)
|
|
||||||
if (a[2] == 97)
|
|
||||||
if (a[3] == 97)
|
|
||||||
if (a.length == 4) {
|
|
||||||
return 100
|
|
||||||
}
|
|
||||||
return 10
|
|
||||||
}
|
|
||||||
|
|
||||||
let paths = [
|
|
||||||
[97, 97, 97, 97],
|
|
||||||
[97, 97, 97, 97, 97],
|
|
||||||
[97, 97, 96, 97],
|
|
||||||
[97, 96, 97, 97],
|
|
||||||
[96, 97, 97, 97],
|
|
||||||
[],
|
|
||||||
[98],
|
|
||||||
[97, 97, 97],
|
|
||||||
[97, 97],
|
|
||||||
[97],
|
|
||||||
[97, 97, 96],
|
|
||||||
[97, 96],
|
|
||||||
[96],
|
|
||||||
]
|
|
||||||
|
|
||||||
let func1 = [ifSingle, ifChain, ifSingleOptimized, ifChainOptimized, ifSingleOptimizedAlt, ifChainOptimizedAlt];
|
|
||||||
for (let fun of func1) {
|
|
||||||
console.log('-- begin', fun.name)
|
|
||||||
for (var i = 0; i < 1000000; i++) {
|
|
||||||
paths.map(x => fun(x))
|
|
||||||
}
|
|
||||||
printCurrentStatus(fun);
|
|
||||||
}
|
|
||||||
printStatusHelperText()
|
|
||||||
|
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
start /B /WAIT /REALTIME node index.js
|
start /B /WAIT /REALTIME node index.js
|
||||||
|
pause
|
|
@ -1,4 +1,3 @@
|
||||||
import crypto from 'crypto'
|
|
||||||
import assert from 'assert'
|
import assert from 'assert'
|
||||||
import Benchmark from 'benchmarkjs-pretty'
|
import Benchmark from 'benchmarkjs-pretty'
|
||||||
import { koaRouter1, koaRouter2 } from './router_koa.js'
|
import { koaRouter1, koaRouter2 } from './router_koa.js'
|
||||||
|
@ -177,89 +176,7 @@ function TestObjectAssign() {
|
||||||
ctx.router2 = flaskaRouter2
|
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)')
|
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() {
|
.add('Object.assign()', function() {
|
||||||
let ctx = {
|
let ctx = {
|
||||||
req: req,
|
req: req,
|
||||||
|
@ -279,7 +196,7 @@ function TestObjectAssign() {
|
||||||
register3(ctx)
|
register3(ctx)
|
||||||
ctx.log.info()
|
ctx.log.info()
|
||||||
})
|
})
|
||||||
.add('Object.create() all props', function() {
|
/*.add('Object.create() all props', function() {
|
||||||
let ctx = Object.create({}, propMakerAlt)
|
let ctx = Object.create({}, propMakerAlt)
|
||||||
ctx.log.info()
|
ctx.log.info()
|
||||||
})
|
})
|
||||||
|
@ -295,100 +212,7 @@ function TestObjectAssign() {
|
||||||
let ctx = { }
|
let ctx = { }
|
||||||
Object.defineProperties(ctx, propMakerAlt)
|
Object.defineProperties(ctx, propMakerAlt)
|
||||||
ctx.log.info()
|
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()
|
.run()
|
||||||
.then(function() {}, function(e) {
|
.then(function() {}, function(e) {
|
||||||
console.error('error:', e)
|
console.error('error:', e)
|
||||||
|
@ -521,8 +345,8 @@ function TestLargeParamLargeUrlRoute() {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
TestSmallStaticRoute()
|
TestSmallStaticRoute()
|
||||||
|
// TestObjectAssign()
|
||||||
// TestPromiseCreators()
|
// TestPromiseCreators()
|
||||||
.then(function() {
|
.then(function() {
|
||||||
return TestSmallParamRoute()
|
return TestSmallParamRoute()
|
||||||
|
@ -536,14 +360,6 @@ TestSmallStaticRoute()
|
||||||
.then(function() {
|
.then(function() {
|
||||||
return TestLargeParamLargeUrlRoute()
|
return TestLargeParamLargeUrlRoute()
|
||||||
})
|
})
|
||||||
.then(function() {
|
|
||||||
process.exit(0)
|
|
||||||
})*/
|
|
||||||
|
|
||||||
// TestObjectAssign()
|
|
||||||
// TestGenerateRandomString()
|
|
||||||
// TestArrayReduce()
|
|
||||||
TestStringCombination()
|
|
||||||
.then(function() {
|
.then(function() {
|
||||||
process.exit(0)
|
process.exit(0)
|
||||||
})
|
})
|
|
@ -1,106 +0,0 @@
|
||||||
import { summary, run, bench } from 'mitata';
|
|
||||||
|
|
||||||
function printCurrentStatus(fn) {
|
|
||||||
let opt = %GetOptimizationStatus(fn)
|
|
||||||
console.log(`${opt.toString(2).padStart(17, '0').split('').join(' ')} (${opt}) ${fn.name}`)
|
|
||||||
}
|
|
||||||
function printStatusHelperText() {
|
|
||||||
console.log(`┬ ┬ ┬ ┬ ┬ ┬ ┬ ┬ ┬ ┬ ┬ ┬ ┬ ┬ ┬ ┬ ┬
|
|
||||||
│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └─╸ is function
|
|
||||||
│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └───╸ is never optimized
|
|
||||||
│ │ │ │ │ │ │ │ │ │ │ │ │ │ └─────╸ is always optimized
|
|
||||||
│ │ │ │ │ │ │ │ │ │ │ │ │ └───────╸ is maybe deoptimized
|
|
||||||
│ │ │ │ │ │ │ │ │ │ │ │ └─────────╸ is optimized
|
|
||||||
│ │ │ │ │ │ │ │ │ │ │ └───────────╸ is optimized by TurboFan
|
|
||||||
│ │ │ │ │ │ │ │ │ │ └─────────────╸ is interpreted
|
|
||||||
│ │ │ │ │ │ │ │ │ └───────────────╸ is marked for optimization
|
|
||||||
│ │ │ │ │ │ │ │ └─────────────────╸ is marked for concurrent optimization
|
|
||||||
│ │ │ │ │ │ │ └───────────────────╸ is optimizing concurrently
|
|
||||||
│ │ │ │ │ │ └─────────────────────╸ is executing
|
|
||||||
│ │ │ │ │ └───────────────────────╸ topmost frame is turbo fanned
|
|
||||||
│ │ │ │ └─────────────────────────╸ lite mode
|
|
||||||
│ │ │ └───────────────────────────╸ marked for deoptimization
|
|
||||||
│ │ └─────────────────────────────╸ baseline
|
|
||||||
│ └───────────────────────────────╸ topmost frame is interpreted
|
|
||||||
└─────────────────────────────────╸ topmost frame is baseline`)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Warmup (de-optimize `bench()` calls)
|
|
||||||
bench('noop', () => { });
|
|
||||||
bench('noop2', () => { });
|
|
||||||
|
|
||||||
function mapGetAndCheck(map, t) {
|
|
||||||
let x = map.get(t)
|
|
||||||
if (x) return x
|
|
||||||
return t
|
|
||||||
}
|
|
||||||
|
|
||||||
function mapCheckBeforeGet(map, t) {
|
|
||||||
if (map.has(t)) {
|
|
||||||
return map.get(t)
|
|
||||||
}
|
|
||||||
return t
|
|
||||||
}
|
|
||||||
|
|
||||||
let paths = [
|
|
||||||
'a',
|
|
||||||
'aa',
|
|
||||||
'aaa',
|
|
||||||
'aaaa',
|
|
||||||
'aaaaa',
|
|
||||||
'aaaaaa',
|
|
||||||
'aaaaaaa',
|
|
||||||
'aaaaaaaa',
|
|
||||||
]
|
|
||||||
let tests = [
|
|
||||||
'a',
|
|
||||||
'aa',
|
|
||||||
'aaa',
|
|
||||||
'aaaa',
|
|
||||||
'aaaaa',
|
|
||||||
'aaaaaa',
|
|
||||||
'aaaaaaa',
|
|
||||||
'aaaaaaaa',
|
|
||||||
'b',
|
|
||||||
'bb',
|
|
||||||
'bbb',
|
|
||||||
'bbbb',
|
|
||||||
'bbbbb',
|
|
||||||
'bbbbbb',
|
|
||||||
'bbbbbbb',
|
|
||||||
'bbbbbbbb',
|
|
||||||
'c',
|
|
||||||
'cc',
|
|
||||||
'ccc',
|
|
||||||
'cccc',
|
|
||||||
'ccccc',
|
|
||||||
'cccccc',
|
|
||||||
'ccccccc',
|
|
||||||
'cccccccc',
|
|
||||||
]
|
|
||||||
|
|
||||||
let map = new Map(paths.map(x => [x, { a: x }]))
|
|
||||||
|
|
||||||
let func1 = [mapGetAndCheck, mapCheckBeforeGet];
|
|
||||||
for (let fun of func1) {
|
|
||||||
console.log('-- begin', fun.name)
|
|
||||||
for (var i = 0; i < 1000000; i++) {
|
|
||||||
tests.map(t => fun(map, t))
|
|
||||||
}
|
|
||||||
printCurrentStatus(fun);
|
|
||||||
}
|
|
||||||
printStatusHelperText()
|
|
||||||
|
|
||||||
summary(() => {
|
|
||||||
func1.forEach(function(fun) {
|
|
||||||
bench(fun.name, function() {
|
|
||||||
return paths.map(t => fun(map, t))
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
run().then(function() {
|
|
||||||
for (let fun of func1) {
|
|
||||||
printCurrentStatus(fun);
|
|
||||||
}
|
|
||||||
printStatusHelperText()
|
|
||||||
});
|
|
|
@ -1,25 +0,0 @@
|
||||||
import { getExternalKeys } from '@mapl/compiler';
|
|
||||||
import { compileRouter as compileRouterContent } from '@mapl/router';
|
|
||||||
const PATH = "__req_p";
|
|
||||||
const REQ = '__req';
|
|
||||||
const PARAMS = `${REQ}_ps`;
|
|
||||||
|
|
||||||
export function compileRouter(root) {
|
|
||||||
const state = {
|
|
||||||
contentBuilder: [],
|
|
||||||
declarationBuilders: [],
|
|
||||||
externalValues: [],
|
|
||||||
|
|
||||||
compileItem: (item, state, hasParam) => {
|
|
||||||
state.contentBuilder.push(`return [f${state.externalValues.push(item)},${hasParam ? PARAMS : '[]'}];`);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
compileRouterContent(root, state);
|
|
||||||
|
|
||||||
// eslint-disable-next-line
|
|
||||||
return Function(
|
|
||||||
...getExternalKeys(state),
|
|
||||||
`return (${PATH})=>{${state.contentBuilder.join('')}return null;}`
|
|
||||||
)(...state.externalValues);
|
|
||||||
}
|
|
|
@ -1,31 +0,0 @@
|
||||||
import { createRouter, insertItem } from '@mapl/router'
|
|
||||||
import { compileRouter } from './mapl_compiler.mjs'
|
|
||||||
import { printTime } from './utils.mjs'
|
|
||||||
import * as consts from './const.js'
|
|
||||||
|
|
||||||
let testPaths = consts.allManyRoutes // consts.allManyRoutes
|
|
||||||
|
|
||||||
/*testPaths = [
|
|
||||||
'/api/works/:id/lock',
|
|
||||||
'/api/staff/:id',
|
|
||||||
// '/::rest',
|
|
||||||
]*/
|
|
||||||
|
|
||||||
testPaths = testPaths.map(x => x.replace(/::[^\/]+/g, '**').replace(/:[^\/]+/g, '*'))
|
|
||||||
|
|
||||||
const r = createRouter();
|
|
||||||
for (let route of testPaths) {
|
|
||||||
let cleaned = route.replace(/:[^\/]+/g, '*')
|
|
||||||
insertItem(r, cleaned, () => { })
|
|
||||||
}
|
|
||||||
|
|
||||||
let s1 = process.hrtime.bigint()
|
|
||||||
let s2 = process.hrtime.bigint()
|
|
||||||
|
|
||||||
compileRouter(r);
|
|
||||||
|
|
||||||
let s3 = process.hrtime.bigint()
|
|
||||||
|
|
||||||
let time = s3 - s2 - (s2 - s1)
|
|
||||||
|
|
||||||
printTime(time)
|
|
597
benchmark/package-lock.json
generated
Normal file
597
benchmark/package-lock.json
generated
Normal file
|
@ -0,0 +1,597 @@
|
||||||
|
{
|
||||||
|
"name": "benchmark",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"lockfileVersion": 1,
|
||||||
|
"requires": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@types/benchmark": {
|
||||||
|
"version": "1.0.31",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/benchmark/-/benchmark-1.0.31.tgz",
|
||||||
|
"integrity": "sha512-F6fVNOkGEkSdo/19yWYOwVKGvzbTeWkR/XQYBKtGBQ9oGRjBN9f/L4aJI4sDcVPJO58Y1CJZN8va9V2BhrZapA=="
|
||||||
|
},
|
||||||
|
"@types/chalk": {
|
||||||
|
"version": "0.4.31",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/chalk/-/chalk-0.4.31.tgz",
|
||||||
|
"integrity": "sha1-ox10JBprHtu5c8822XooloNKUfk="
|
||||||
|
},
|
||||||
|
"accepts": {
|
||||||
|
"version": "1.3.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
|
||||||
|
"integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==",
|
||||||
|
"requires": {
|
||||||
|
"mime-types": "~2.1.24",
|
||||||
|
"negotiator": "0.6.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"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==",
|
||||||
|
"requires": {
|
||||||
|
"color-convert": "^1.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"array-flatten": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
|
||||||
|
"integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
|
||||||
|
},
|
||||||
|
"benchmark": {
|
||||||
|
"version": "2.1.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/benchmark/-/benchmark-2.1.4.tgz",
|
||||||
|
"integrity": "sha1-CfPeMckWQl1JjMLuVloOvzwqVik=",
|
||||||
|
"requires": {
|
||||||
|
"lodash": "^4.17.4",
|
||||||
|
"platform": "^1.3.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"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==",
|
||||||
|
"requires": {
|
||||||
|
"@types/benchmark": "^1.0.30",
|
||||||
|
"@types/chalk": "^0.4.31",
|
||||||
|
"benchmark": "^2.1.4",
|
||||||
|
"chalk": "^2.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"body-parser": {
|
||||||
|
"version": "1.19.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz",
|
||||||
|
"integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==",
|
||||||
|
"requires": {
|
||||||
|
"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"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"debug": {
|
||||||
|
"version": "2.6.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||||
|
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||||
|
"requires": {
|
||||||
|
"ms": "2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"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==",
|
||||||
|
"requires": {
|
||||||
|
"depd": "~1.1.2",
|
||||||
|
"inherits": "2.0.3",
|
||||||
|
"setprototypeof": "1.1.1",
|
||||||
|
"statuses": ">= 1.5.0 < 2",
|
||||||
|
"toidentifier": "1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"inherits": {
|
||||||
|
"version": "2.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
|
||||||
|
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
|
||||||
|
},
|
||||||
|
"ms": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||||
|
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"bytes": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg=="
|
||||||
|
},
|
||||||
|
"chalk": {
|
||||||
|
"version": "2.4.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
|
||||||
|
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
|
||||||
|
"requires": {
|
||||||
|
"ansi-styles": "^3.2.1",
|
||||||
|
"escape-string-regexp": "^1.0.5",
|
||||||
|
"supports-color": "^5.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"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==",
|
||||||
|
"requires": {
|
||||||
|
"color-name": "1.1.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"color-name": {
|
||||||
|
"version": "1.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
|
||||||
|
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
|
||||||
|
},
|
||||||
|
"content-disposition": {
|
||||||
|
"version": "0.5.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz",
|
||||||
|
"integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==",
|
||||||
|
"requires": {
|
||||||
|
"safe-buffer": "5.1.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"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=="
|
||||||
|
},
|
||||||
|
"cookie": {
|
||||||
|
"version": "0.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz",
|
||||||
|
"integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg=="
|
||||||
|
},
|
||||||
|
"cookie-signature": {
|
||||||
|
"version": "1.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
|
||||||
|
"integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
|
||||||
|
},
|
||||||
|
"debug": {
|
||||||
|
"version": "4.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
|
||||||
|
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
|
||||||
|
"requires": {
|
||||||
|
"ms": "^2.1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"depd": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
|
||||||
|
"integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
|
||||||
|
},
|
||||||
|
"destroy": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
|
||||||
|
"integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
|
||||||
|
},
|
||||||
|
"ee-first": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
||||||
|
"integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
|
||||||
|
},
|
||||||
|
"encodeurl": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
|
||||||
|
"integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
|
||||||
|
},
|
||||||
|
"escape-html": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
|
||||||
|
"integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
|
||||||
|
},
|
||||||
|
"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="
|
||||||
|
},
|
||||||
|
"etag": {
|
||||||
|
"version": "1.8.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
|
||||||
|
"integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
|
||||||
|
},
|
||||||
|
"express": {
|
||||||
|
"version": "4.17.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz",
|
||||||
|
"integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==",
|
||||||
|
"requires": {
|
||||||
|
"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"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"debug": {
|
||||||
|
"version": "2.6.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||||
|
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||||
|
"requires": {
|
||||||
|
"ms": "2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ms": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||||
|
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"finalhandler": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==",
|
||||||
|
"requires": {
|
||||||
|
"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"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"debug": {
|
||||||
|
"version": "2.6.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||||
|
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||||
|
"requires": {
|
||||||
|
"ms": "2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ms": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||||
|
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"forwarded": {
|
||||||
|
"version": "0.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
|
||||||
|
"integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ="
|
||||||
|
},
|
||||||
|
"fresh": {
|
||||||
|
"version": "0.5.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
|
||||||
|
"integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
|
||||||
|
},
|
||||||
|
"has-flag": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
|
||||||
|
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0="
|
||||||
|
},
|
||||||
|
"http-errors": {
|
||||||
|
"version": "1.7.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.3.tgz",
|
||||||
|
"integrity": "sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==",
|
||||||
|
"requires": {
|
||||||
|
"depd": "~1.1.2",
|
||||||
|
"inherits": "2.0.4",
|
||||||
|
"setprototypeof": "1.1.1",
|
||||||
|
"statuses": ">= 1.5.0 < 2",
|
||||||
|
"toidentifier": "1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"iconv-lite": {
|
||||||
|
"version": "0.4.24",
|
||||||
|
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
||||||
|
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
|
||||||
|
"requires": {
|
||||||
|
"safer-buffer": ">= 2.1.2 < 3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"inherits": {
|
||||||
|
"version": "2.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||||
|
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
|
||||||
|
},
|
||||||
|
"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=="
|
||||||
|
},
|
||||||
|
"isarray": {
|
||||||
|
"version": "0.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
|
||||||
|
"integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
|
||||||
|
},
|
||||||
|
"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=="
|
||||||
|
},
|
||||||
|
"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==",
|
||||||
|
"requires": {
|
||||||
|
"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"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"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==",
|
||||||
|
"requires": {
|
||||||
|
"isarray": "0.0.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"lodash": {
|
||||||
|
"version": "4.17.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
|
||||||
|
"integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A=="
|
||||||
|
},
|
||||||
|
"media-typer": {
|
||||||
|
"version": "0.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
|
||||||
|
"integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
|
||||||
|
},
|
||||||
|
"merge-descriptors": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
|
||||||
|
"integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
|
||||||
|
},
|
||||||
|
"methods": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
|
||||||
|
"integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4="
|
||||||
|
},
|
||||||
|
"mime": {
|
||||||
|
"version": "1.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
|
||||||
|
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="
|
||||||
|
},
|
||||||
|
"mime-db": {
|
||||||
|
"version": "1.43.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.43.0.tgz",
|
||||||
|
"integrity": "sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ=="
|
||||||
|
},
|
||||||
|
"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==",
|
||||||
|
"requires": {
|
||||||
|
"mime-db": "1.43.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ms": {
|
||||||
|
"version": "2.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||||
|
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||||
|
},
|
||||||
|
"negotiator": {
|
||||||
|
"version": "0.6.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
|
||||||
|
"integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw=="
|
||||||
|
},
|
||||||
|
"on-finished": {
|
||||||
|
"version": "2.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
|
||||||
|
"integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
|
||||||
|
"requires": {
|
||||||
|
"ee-first": "1.1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"parseurl": {
|
||||||
|
"version": "1.3.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
|
||||||
|
"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="
|
||||||
|
},
|
||||||
|
"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="
|
||||||
|
},
|
||||||
|
"platform": {
|
||||||
|
"version": "1.3.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/platform/-/platform-1.3.5.tgz",
|
||||||
|
"integrity": "sha512-TuvHS8AOIZNAlE77WUDiR4rySV/VMptyMfcfeoMgs4P8apaZM3JrnbzBiixKUv+XR6i+BXrQh8WAnjaSPFO65Q=="
|
||||||
|
},
|
||||||
|
"proxy-addr": {
|
||||||
|
"version": "2.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz",
|
||||||
|
"integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==",
|
||||||
|
"requires": {
|
||||||
|
"forwarded": "~0.1.2",
|
||||||
|
"ipaddr.js": "1.9.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"qs": {
|
||||||
|
"version": "6.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
|
||||||
|
"integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ=="
|
||||||
|
},
|
||||||
|
"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=="
|
||||||
|
},
|
||||||
|
"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==",
|
||||||
|
"requires": {
|
||||||
|
"bytes": "3.1.0",
|
||||||
|
"http-errors": "1.7.2",
|
||||||
|
"iconv-lite": "0.4.24",
|
||||||
|
"unpipe": "1.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"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==",
|
||||||
|
"requires": {
|
||||||
|
"depd": "~1.1.2",
|
||||||
|
"inherits": "2.0.3",
|
||||||
|
"setprototypeof": "1.1.1",
|
||||||
|
"statuses": ">= 1.5.0 < 2",
|
||||||
|
"toidentifier": "1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"inherits": {
|
||||||
|
"version": "2.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
|
||||||
|
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"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=="
|
||||||
|
},
|
||||||
|
"safer-buffer": {
|
||||||
|
"version": "2.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||||
|
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
||||||
|
},
|
||||||
|
"send": {
|
||||||
|
"version": "0.17.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz",
|
||||||
|
"integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==",
|
||||||
|
"requires": {
|
||||||
|
"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"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"debug": {
|
||||||
|
"version": "2.6.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||||
|
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||||
|
"requires": {
|
||||||
|
"ms": "2.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"ms": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||||
|
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"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=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"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==",
|
||||||
|
"requires": {
|
||||||
|
"encodeurl": "~1.0.2",
|
||||||
|
"escape-html": "~1.0.3",
|
||||||
|
"parseurl": "~1.3.3",
|
||||||
|
"send": "0.17.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"setprototypeof": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw=="
|
||||||
|
},
|
||||||
|
"statuses": {
|
||||||
|
"version": "1.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
|
||||||
|
"integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow="
|
||||||
|
},
|
||||||
|
"supports-color": {
|
||||||
|
"version": "5.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
|
||||||
|
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
|
||||||
|
"requires": {
|
||||||
|
"has-flag": "^3.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"toidentifier": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw=="
|
||||||
|
},
|
||||||
|
"type-is": {
|
||||||
|
"version": "1.6.18",
|
||||||
|
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
|
||||||
|
"integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
|
||||||
|
"requires": {
|
||||||
|
"media-typer": "0.3.0",
|
||||||
|
"mime-types": "~2.1.24"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"unpipe": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
|
||||||
|
"integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
|
||||||
|
},
|
||||||
|
"urijs": {
|
||||||
|
"version": "1.19.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/urijs/-/urijs-1.19.2.tgz",
|
||||||
|
"integrity": "sha512-s/UIq9ap4JPZ7H1EB5ULo/aOUbWqfDi7FKzMC2Nz+0Si8GiT1rIEaprt8hy3Vy2Ex2aJPpOQv4P4DuOZ+K1c6w=="
|
||||||
|
},
|
||||||
|
"utils-merge": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
|
||||||
|
"integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
|
||||||
|
},
|
||||||
|
"vary": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
||||||
|
"integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,10 +11,8 @@
|
||||||
"author": "",
|
"author": "",
|
||||||
"license": "WTFPL",
|
"license": "WTFPL",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@mapl/router": "^0.0.16",
|
|
||||||
"benchmarkjs-pretty": "^2.0.0",
|
"benchmarkjs-pretty": "^2.0.0",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"koa-router": "^8.0.8",
|
"koa-router": "^8.0.8"
|
||||||
"mitata": "^1.0.10"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,221 +0,0 @@
|
||||||
import { summary, run, bench } from 'mitata';
|
|
||||||
|
|
||||||
// Warmup (de-optimize `bench()` calls)
|
|
||||||
bench('noop', () => { });
|
|
||||||
bench('noop2', () => { });
|
|
||||||
|
|
||||||
// Example benchmark
|
|
||||||
/*summary(() => {
|
|
||||||
const fn = () => null
|
|
||||||
const fnAlt = function() {}
|
|
||||||
function fnBasic() {}
|
|
||||||
const fnAltWithReturn = function() { return null }
|
|
||||||
function fnBasicWithReturn() { return null }
|
|
||||||
|
|
||||||
bench('Reject with uncached error handler', async () => await Promise.reject().catch(() => null));
|
|
||||||
bench('Reject with cached error handler', async () => await Promise.reject().catch(fn));
|
|
||||||
|
|
||||||
bench('TT Reject with uncached error handler', () => Promise.reject().catch(() => null));
|
|
||||||
bench('TT Reject with cached error handler', () => Promise.reject().catch(fnAlt));
|
|
||||||
bench('TT Reject with cached basic error handler', () => Promise.reject().catch(fnBasic));
|
|
||||||
bench('TT Reject with cached error handler with return', () => Promise.reject().catch(fnAltWithReturn));
|
|
||||||
bench('TT Reject with cached basic error handler with return ', () => Promise.reject().catch(fnBasicWithReturn));
|
|
||||||
});*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
summary(() => {
|
|
||||||
function triple () {
|
|
||||||
const bla = Math.round(Math.random()) ? true : null
|
|
||||||
return bla === null
|
|
||||||
}
|
|
||||||
function doubleNull () {
|
|
||||||
const bla = Math.round(Math.random()) ? true : null
|
|
||||||
return bla == null
|
|
||||||
}
|
|
||||||
function doubleUndefined () {
|
|
||||||
const bla = Math.round(Math.random()) ? true : undefined
|
|
||||||
return bla == null
|
|
||||||
}
|
|
||||||
bench('null === null', triple);
|
|
||||||
bench('null == null', doubleNull);
|
|
||||||
bench('undefined == null', doubleUndefined);
|
|
||||||
|
|
||||||
bench('2x null === null', triple);
|
|
||||||
bench('2x null == null', doubleNull);
|
|
||||||
bench('2x undefined == null', doubleUndefined);
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
summary(() => {
|
|
||||||
function triple () {
|
|
||||||
const bla = false ? true : null
|
|
||||||
return bla === null
|
|
||||||
}
|
|
||||||
function doubleNull () {
|
|
||||||
const bla = false ? true : null
|
|
||||||
return bla == null
|
|
||||||
}
|
|
||||||
function doubleUndefined () {
|
|
||||||
const bla = false ? true : undefined
|
|
||||||
return bla == null
|
|
||||||
}
|
|
||||||
bench('const null === null', triple);
|
|
||||||
bench('const null == null', doubleNull);
|
|
||||||
bench('const undefined == null', doubleUndefined);
|
|
||||||
|
|
||||||
bench('const 2x null === null', triple);
|
|
||||||
bench('const 2x null == null', doubleNull);
|
|
||||||
bench('const 2x undefined == null', doubleUndefined);
|
|
||||||
})*/
|
|
||||||
|
|
||||||
function printCurrentStatus(fn) {
|
|
||||||
let opt = %GetOptimizationStatus(fn)
|
|
||||||
console.log(`${fn.toString()}
|
|
||||||
${opt.toString(2).padStart(12, '0').split('').join(' ')}`)
|
|
||||||
}
|
|
||||||
function printTree() {
|
|
||||||
console.log(`┬ ┬ ┬ ┬ ┬ ┬ ┬ ┬ ┬ ┬ ┬ ┬
|
|
||||||
│ │ │ │ │ │ │ │ │ │ │ └─╸ is function
|
|
||||||
│ │ │ │ │ │ │ │ │ │ └───╸ is never optimized
|
|
||||||
│ │ │ │ │ │ │ │ │ └─────╸ is always optimized
|
|
||||||
│ │ │ │ │ │ │ │ └───────╸ is maybe deoptimized
|
|
||||||
│ │ │ │ │ │ │ └─────────╸ is optimized
|
|
||||||
│ │ │ │ │ │ └───────────╸ is optimized by TurboFan
|
|
||||||
│ │ │ │ │ └─────────────╸ is interpreted
|
|
||||||
│ │ │ │ └───────────────╸ is marked for optimization
|
|
||||||
│ │ │ └─────────────────╸ is marked for concurrent optimization
|
|
||||||
│ │ └───────────────────╸ is optimizing concurrently
|
|
||||||
│ └─────────────────────╸ is executing
|
|
||||||
└───────────────────────╸ topmost frame is turbo fanned`)
|
|
||||||
}
|
|
||||||
|
|
||||||
let func1
|
|
||||||
let func2
|
|
||||||
let func3
|
|
||||||
|
|
||||||
await (async function () {
|
|
||||||
return
|
|
||||||
const fn1 = async () => await Promise.reject().catch(() => null) === null ? 1 : 0;
|
|
||||||
const fn2 = async () => await Promise.reject().catch(() => {}) == null ? 1 : 0;
|
|
||||||
const fn3 = async () => await Promise.reject().catch(() => null) == null ? 1 : 0;
|
|
||||||
const noop = () => null;
|
|
||||||
const fn4 = async () => await Promise.reject().catch(noop) == null ? 1 : 0;
|
|
||||||
const fn5 = async () => await Promise.reject().catch(noop) === null ? 1 : 0;
|
|
||||||
|
|
||||||
|
|
||||||
const fn6 = () => Promise.reject().catch(() => null).then(x => x === null ? 1 : 0);
|
|
||||||
const fn7 = () => Promise.reject().catch(() => {}).then(x => x == null ? 1 : 0);
|
|
||||||
const fn8 = () => Promise.reject().catch(() => null).then(x => x == null ? 1 : 0);
|
|
||||||
const fn9 = () => Promise.reject().catch(noop).then(x => x == null ? 1 : 0);
|
|
||||||
const fn10 = () => Promise.reject().catch(noop).then(x => x === null ? 1 : 0);
|
|
||||||
|
|
||||||
func1 = [fn1, fn2, fn3, fn4, fn5, fn6, fn7, fn8, fn9, fn10];
|
|
||||||
for (let fun of func1) {
|
|
||||||
%OptimizeFunctionOnNextCall(fun);
|
|
||||||
}
|
|
||||||
for (var i = 0; i < 100000; i++) {
|
|
||||||
for (let fun of func1) {
|
|
||||||
await fun()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (let fun of func1) {
|
|
||||||
printCurrentStatus(fun);
|
|
||||||
}
|
|
||||||
printTree()
|
|
||||||
|
|
||||||
summary(() => {
|
|
||||||
bench('Null with ===', fn1);
|
|
||||||
bench('Undefined with ==', fn2);
|
|
||||||
bench('Null with ==', fn3);
|
|
||||||
bench('Cached Null with ==', fn4);
|
|
||||||
bench('Cached Null with ===', fn5);
|
|
||||||
|
|
||||||
|
|
||||||
bench('[sync] Null with ===', fn6);
|
|
||||||
bench('[sync] Undefined with ==', fn7);
|
|
||||||
bench('[sync] Null with ==', fn8);
|
|
||||||
bench('[sync] Cached Null with ==', fn9);
|
|
||||||
bench('[sync] Cached Null with ===', fn10);
|
|
||||||
});
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
await (async function () {
|
|
||||||
const alt1 = function () { (Math.round(Math.random()) ? null : 1) ?? 0 };
|
|
||||||
const alt2 = function () { (Math.round(Math.random()) ? null : 1) || 0 };
|
|
||||||
const alt3 = function () { (Math.round(Math.random()) ? undefined : 1) ?? 0 };
|
|
||||||
const alt4 = function () { (Math.round(Math.random()) ? undefined : 1) || 0 };
|
|
||||||
|
|
||||||
let check = new Array(100).fill(0).map(() => Math.round(Math.random()) ? null : 1)
|
|
||||||
let check2 = new Array(100).fill(0).map(() => Math.round(Math.random()) ? undefined : 1)
|
|
||||||
|
|
||||||
const alt5 = function () { let out = 0; for (let x of check) { out += x ?? 0 } return out };
|
|
||||||
const alt6 = function () { let out = 0; for (let x of check) { out += x || 0 } return out };
|
|
||||||
const alt7 = function () { let out = 0; for (let x of check2) { out += x ?? 0 } return out };
|
|
||||||
const alt8 = function () { let out = 0; for (let x of check2) { out += x || 0 } return out };
|
|
||||||
|
|
||||||
func2 = [alt1, alt2, alt3, alt4, alt5, alt6, alt7, alt8];
|
|
||||||
for (let fun of func2) {
|
|
||||||
%OptimizeFunctionOnNextCall(fun);
|
|
||||||
}
|
|
||||||
for (var i = 0; i < 100000; i++) {
|
|
||||||
for (let fun of func2) {
|
|
||||||
fun()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (let fun of func2) {
|
|
||||||
printCurrentStatus(fun);
|
|
||||||
}
|
|
||||||
printTree()
|
|
||||||
|
|
||||||
summary(() => {
|
|
||||||
bench('null/1 ?? 0', alt1);
|
|
||||||
bench('null/1 || 0', alt2);
|
|
||||||
bench('undefined/1 ?? 0', alt3);
|
|
||||||
bench('undefined/1 || 0', alt4);
|
|
||||||
});
|
|
||||||
|
|
||||||
summary(() => {
|
|
||||||
bench('arr null/1 ?? null', alt5);
|
|
||||||
bench('arr null/1 || null', alt6);
|
|
||||||
bench('arr und/1 ?? null', alt7);
|
|
||||||
bench('arr und/1 || null', alt8);
|
|
||||||
});
|
|
||||||
})()
|
|
||||||
|
|
||||||
|
|
||||||
await (async function () {
|
|
||||||
const alt1 = function () { let out = 0; for (let x = 0; x < 100; x++) { out += (Math.round(Math.random()) ? null : 1) ?? 0 } return out };
|
|
||||||
const alt2 = function () { let out = 0; for (let x = 0; x < 100; x++) { out += (Math.round(Math.random()) ? null : 1) || 0 } return out };
|
|
||||||
const alt3 = function () { let out = 0; for (let x = 0; x < 100; x++) { out += (Math.round(Math.random()) ? undefined : 1) ?? 0 } return out };
|
|
||||||
const alt4 = function () { let out = 0; for (let x = 0; x < 100; x++) { out += (Math.round(Math.random()) ? undefined : 1) || 0 } return out };
|
|
||||||
|
|
||||||
func3 = [alt1, alt2, alt3, alt4];
|
|
||||||
for (let fun of func3) {
|
|
||||||
%OptimizeFunctionOnNextCall(fun);
|
|
||||||
}
|
|
||||||
for (var i = 0; i < 100000; i++) {
|
|
||||||
for (let fun of func3) {
|
|
||||||
fun()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (let fun of func3) {
|
|
||||||
printCurrentStatus(fun);
|
|
||||||
}
|
|
||||||
printTree()
|
|
||||||
|
|
||||||
summary(() => {
|
|
||||||
bench('loop null/1 ?? 0', alt1);
|
|
||||||
bench('loop null/1 || 0', alt2);
|
|
||||||
bench('loop und/1 ?? 0', alt3);
|
|
||||||
bench('loop und/1 || 0', alt4);
|
|
||||||
});
|
|
||||||
})()
|
|
||||||
|
|
||||||
// Start the benchmark
|
|
||||||
run().then(() => {
|
|
||||||
for (let fun of func3) {
|
|
||||||
printCurrentStatus(fun);
|
|
||||||
}
|
|
||||||
printTree()
|
|
||||||
});
|
|
|
@ -1,46 +0,0 @@
|
||||||
import crypto from 'crypto'
|
|
||||||
import Benchmark from 'benchmarkjs-pretty'
|
|
||||||
|
|
||||||
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 (11 characters long)', function() {
|
|
||||||
for (let i = 0; i < 25; i++) {
|
|
||||||
let out = Math.random().toString(36).substring(2, 14)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.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)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
TestGenerateRandomString()
|
|
||||||
.then(function() {
|
|
||||||
process.exit(0)
|
|
||||||
})
|
|
|
@ -1,97 +0,0 @@
|
||||||
import { summary, run, bench } from 'mitata';
|
|
||||||
import { createRouter, insertItem } from '@mapl/router'
|
|
||||||
import { compileRouter } from './mapl_compiler.mjs'
|
|
||||||
import { compilePaths } from "./router_v2.mjs"
|
|
||||||
import { compilePaths as mainCompiler } from "../router_v2.mjs"
|
|
||||||
import { FlaskaRouter as FlaskaRouterBuffer } from "../flaska_buffer.mjs"
|
|
||||||
import { FlaskaRouter as FlaskaRouterFast } from "../flaska_fast.mjs"
|
|
||||||
import * as consts from './const.js'
|
|
||||||
|
|
||||||
function printCurrentStatus(fn) {
|
|
||||||
// console.log(`--- checking optimizations status on ${fn.name} ---`)
|
|
||||||
// let opt = %GetOptimizationStatus(fn)
|
|
||||||
// console.log(`${opt.toString(2).padStart(17, '0').split('').join(' ')} (${opt}) ${fn.name}`)
|
|
||||||
}
|
|
||||||
function printStatusHelperText() {
|
|
||||||
console.log(`┬ ┬ ┬ ┬ ┬ ┬ ┬ ┬ ┬ ┬ ┬ ┬ ┬ ┬ ┬ ┬ ┬
|
|
||||||
│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └─╸ is function
|
|
||||||
│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └───╸ is never optimized
|
|
||||||
│ │ │ │ │ │ │ │ │ │ │ │ │ │ └─────╸ is always optimized
|
|
||||||
│ │ │ │ │ │ │ │ │ │ │ │ │ └───────╸ is maybe deoptimized
|
|
||||||
│ │ │ │ │ │ │ │ │ │ │ │ └─────────╸ is optimized
|
|
||||||
│ │ │ │ │ │ │ │ │ │ │ └───────────╸ is optimized by TurboFan
|
|
||||||
│ │ │ │ │ │ │ │ │ │ └─────────────╸ is interpreted
|
|
||||||
│ │ │ │ │ │ │ │ │ └───────────────╸ is marked for optimization
|
|
||||||
│ │ │ │ │ │ │ │ └─────────────────╸ is marked for concurrent optimization
|
|
||||||
│ │ │ │ │ │ │ └───────────────────╸ is optimizing concurrently
|
|
||||||
│ │ │ │ │ │ └─────────────────────╸ is executing
|
|
||||||
│ │ │ │ │ └───────────────────────╸ topmost frame is turbo fanned
|
|
||||||
│ │ │ │ └─────────────────────────╸ lite mode
|
|
||||||
│ │ │ └───────────────────────────╸ marked for deoptimization
|
|
||||||
│ │ └─────────────────────────────╸ baseline
|
|
||||||
│ └───────────────────────────────╸ topmost frame is interpreted
|
|
||||||
└─────────────────────────────────╸ topmost frame is baseline`)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Warmup (de-optimize `bench()` calls)
|
|
||||||
bench('noop', () => { });
|
|
||||||
bench('noop2', () => { });
|
|
||||||
|
|
||||||
let paths = consts.allRoutes.map(x => ({ path: x }))
|
|
||||||
let tests = paths.map(p => ([p.path.replace(/:[^/]+/g, '_'), p]))
|
|
||||||
let testStrings = tests.map(x => x[0])
|
|
||||||
let testStringsMapl = testStrings.map(x => x.slice(1))
|
|
||||||
let func = [[testStrings, mainCompiler(paths)]]
|
|
||||||
|
|
||||||
let flaskaRouterBuffer = new FlaskaRouterBuffer()
|
|
||||||
flaskaRouterBuffer.paths = paths.slice()
|
|
||||||
flaskaRouterBuffer.compile()
|
|
||||||
func.push([testStrings, flaskaRouterBuffer.match])
|
|
||||||
|
|
||||||
let flaskaRouterFast = new FlaskaRouterFast()
|
|
||||||
flaskaRouterFast.paths = paths.slice()
|
|
||||||
flaskaRouterFast.compile()
|
|
||||||
func.push([testStrings, flaskaRouterFast.match])
|
|
||||||
|
|
||||||
let maplPaths = paths.map(x => x.path.replace(/::[^\/]+/g, '**').replace(/:[^\/]+/g, '*'))
|
|
||||||
const maplRouter = createRouter();
|
|
||||||
for (let route of maplPaths) {
|
|
||||||
insertItem(maplRouter, route, { path: route })
|
|
||||||
}
|
|
||||||
let maplMatcher = compileRouter(maplRouter)
|
|
||||||
|
|
||||||
func.push([testStringsMapl, maplMatcher])
|
|
||||||
|
|
||||||
for (let [tests, fun] of func) {
|
|
||||||
console.log(`--- warming up ${fun.name || 'mapl'} ---`)
|
|
||||||
for (var i = 0; i < 100000; i++) {
|
|
||||||
tests.forEach(fun)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (let [tests, fun] of func) {
|
|
||||||
console.log(`--- Sanity checking ${fun.name || 'mapl'} ---`)
|
|
||||||
for (let a of tests) {
|
|
||||||
// console.log(a, fun(a))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
console.log('--- sleeping ---')
|
|
||||||
await new Promise(res => setTimeout(res, 1000))
|
|
||||||
for (let [_, org] of func) {
|
|
||||||
printCurrentStatus(org);
|
|
||||||
}
|
|
||||||
printStatusHelperText()
|
|
||||||
|
|
||||||
summary(() => {
|
|
||||||
func.forEach(function([tests, fun]) {
|
|
||||||
// console.log(tests, fun, tests.map(fun))
|
|
||||||
bench(fun.name || 'mapl', function() {
|
|
||||||
return tests.map(fun)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
run().then(function() {
|
|
||||||
for (let [_, check] of func) {
|
|
||||||
printCurrentStatus(check);
|
|
||||||
}
|
|
||||||
printStatusHelperText()
|
|
||||||
});
|
|
|
@ -1,517 +0,0 @@
|
||||||
const Debug = false
|
|
||||||
|
|
||||||
export function printTree(children, indent = 0) {
|
|
||||||
if (!children.length || !Debug) return
|
|
||||||
|
|
||||||
for (let child of children) {
|
|
||||||
console.log(''.padStart(indent * 2) + (child.char || '*')
|
|
||||||
+ ' ' + (child.isParams ? `{ params = ${child.isParams} }` : '')
|
|
||||||
+ (child.isFullParams ? `{ fullParams = ${child.isFullParams} }` : '')
|
|
||||||
+ ' ' + (child.path ? `{ handler = ${child.path.path} }` : ''))
|
|
||||||
printTree(child.children, indent + 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class RouterError extends Error {
|
|
||||||
constructor(route1, route2, ...params) {
|
|
||||||
// Pass remaining arguments (including vendor specific ones) to parent constructor
|
|
||||||
super(...params);
|
|
||||||
|
|
||||||
// Maintains proper stack trace for where our error was thrown (only available on V8)
|
|
||||||
if (Error.captureStackTrace) {
|
|
||||||
Error.captureStackTrace(this, RouterError);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.name = "RouterError";
|
|
||||||
this.routeA = route1
|
|
||||||
this.routeB = route2
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function Child(split, x, i) {
|
|
||||||
this.path = null
|
|
||||||
this.isParams = split[x].isParams ? split[x].word : null
|
|
||||||
this.isFullParams = split[x].isFullParams ? split[x].word : null
|
|
||||||
this.paramVarName = split[x].paramVarName ?? null
|
|
||||||
this.char = !this.isParams && !this.isFullParams ? split[x].word[i] || '/' : null
|
|
||||||
this.count = 0
|
|
||||||
this.children = []
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildChild(x, i, splitPaths) {
|
|
||||||
let splitPath = splitPaths[0]
|
|
||||||
let letter = new Child(splitPath.split, x, i)
|
|
||||||
|
|
||||||
let consume = []
|
|
||||||
if (splitPath.split.length === x + 1
|
|
||||||
&& (splitPath.split[x].isParams
|
|
||||||
|| splitPath.split[x].isFullParams
|
|
||||||
|| splitPath.split[x].word.length === i + 1)) {
|
|
||||||
letter.path = splitPath.entry
|
|
||||||
letter.count += 1
|
|
||||||
} else {
|
|
||||||
consume = [splitPath]
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let y = 1; y < splitPaths.length; y++) {
|
|
||||||
let checkPath = splitPaths[y]
|
|
||||||
if (!checkPath.split[x]
|
|
||||||
|| checkPath.split[x].isParams !== splitPath.split[x].isParams
|
|
||||||
|| checkPath.split[x].isFullParams !== splitPath.split[x].isFullParams
|
|
||||||
|| !checkPath.split[x].isParams
|
|
||||||
&& !checkPath.split[x].isFullParams
|
|
||||||
&& (checkPath.split[x].word[i] || '/') !== letter.char) break
|
|
||||||
consume.push(checkPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
letter.count += consume.length
|
|
||||||
if (splitPath.split[x].word.length === i || splitPath.split[x].isParams || splitPath.split[x].isFullParams) {
|
|
||||||
x++
|
|
||||||
i = -1
|
|
||||||
}
|
|
||||||
while (consume.length) {
|
|
||||||
letter.children.push(buildChild(x, i + 1, consume))
|
|
||||||
consume.splice(0, letter.children[letter.children.length - 1].count)
|
|
||||||
}
|
|
||||||
return letter
|
|
||||||
}
|
|
||||||
|
|
||||||
export function buildTree(splitPaths) {
|
|
||||||
let builder = []
|
|
||||||
while (splitPaths.length) {
|
|
||||||
builder.push(buildChild(0, 0, splitPaths))
|
|
||||||
splitPaths.splice(0, builder[builder.length - 1].count)
|
|
||||||
}
|
|
||||||
return builder
|
|
||||||
}
|
|
||||||
|
|
||||||
const regParamPrefix = /^::?/
|
|
||||||
const regCleanNonAschii = /(?![a-zA-Z_])./g
|
|
||||||
const regCleanRest = /_+/g
|
|
||||||
|
|
||||||
function splitAndSortPaths(paths, separateStatic = true) {
|
|
||||||
let staticPaths = new Map()
|
|
||||||
let paramsPaths = []
|
|
||||||
let collator = new Intl.Collator('en', { sensitivity: 'accent' });
|
|
||||||
|
|
||||||
paths.forEach(function(entry) {
|
|
||||||
if (entry.path[0] !== '/') throw new RouterError(entry, null, 'Specified route was missing forward slash at start')
|
|
||||||
|
|
||||||
// Collect static paths separately
|
|
||||||
if (entry.path.indexOf('/:') < 0 && separateStatic) {
|
|
||||||
return staticPaths.set(entry.path, {
|
|
||||||
path: entry,
|
|
||||||
params: {}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Collect params path separately
|
|
||||||
paramsPaths.push({
|
|
||||||
split: entry.path.slice(1).split(/\//g).map(function(word) {
|
|
||||||
let actualWord = word.replace(regParamPrefix, '')
|
|
||||||
return {
|
|
||||||
word: actualWord,
|
|
||||||
isParams: word[0] === ':' && word[1] !== ':',
|
|
||||||
isFullParams: word[0] === ':' && word[1] === ':',
|
|
||||||
paramVarName: word[0] === ':'
|
|
||||||
? actualWord.replace(regCleanNonAschii, '_').replace(regCleanRest, '_')
|
|
||||||
: null
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
entry,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
paramsPaths.sort(function(aGroup, bGroup) {
|
|
||||||
let length = Math.max(aGroup.split.length, bGroup.split.length)
|
|
||||||
for (let x = 0; x < length; x++) {
|
|
||||||
let a = aGroup.split[x]
|
|
||||||
let b = bGroup.split[x]
|
|
||||||
if (!a) return -1
|
|
||||||
if (!b) return 1
|
|
||||||
// Full params go last
|
|
||||||
if (a.isFullParams && b.isFullParams) throw new RouterError(aGroup.entry, bGroup.entry, 'Two full path routes found on same level')
|
|
||||||
if (a.isFullParams) return 1
|
|
||||||
if (b.isFullParams) return -1
|
|
||||||
// Params go second last
|
|
||||||
if (a.isParams && !b.isParams) return 1
|
|
||||||
if (!a.isParams && b.isParams) return -1
|
|
||||||
// otherwise sort alphabetically if not identical
|
|
||||||
if (a.word !== b.word) return collator.compare(a.word, b.word)
|
|
||||||
}
|
|
||||||
throw new RouterError(aGroup, bGroup, 'Two identical paths were found')
|
|
||||||
})
|
|
||||||
|
|
||||||
return {
|
|
||||||
staticPaths,
|
|
||||||
paramsPaths,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const SlashCode = '/'.charCodeAt(0)
|
|
||||||
|
|
||||||
function getIndex(offset, additions, params) {
|
|
||||||
return (offset + additions)
|
|
||||||
+ (params.length
|
|
||||||
? ' + ' + params.map(a => `offset${a[1]}`).join(' + ')
|
|
||||||
: '')
|
|
||||||
}
|
|
||||||
|
|
||||||
function treeIntoCompiledTreeBufferReturnPath(indentString, paths, branch, params, alt) {
|
|
||||||
let pathIndex = paths.indexOf(branch.path)
|
|
||||||
if (pathIndex < 0) {
|
|
||||||
throw new RouterError(branch.path, null, 'InternalError: Specified path was not found in paths')
|
|
||||||
}
|
|
||||||
let output = ''
|
|
||||||
output += '\n' + indentString + `return {`
|
|
||||||
output += '\n' + indentString + ` path: paths[${pathIndex}],`
|
|
||||||
if (params.length) {
|
|
||||||
output += '\n' + indentString + ` params: {`
|
|
||||||
for (let param of params) {
|
|
||||||
if (alt === 0) {
|
|
||||||
output += '\n' + indentString + ` ${param[0]}: s${param[1]}.toString(),`
|
|
||||||
} else if (alt === 1 || alt === 3) {
|
|
||||||
output += '\n' + indentString + ` ${param[0]}: s${param[1]},`
|
|
||||||
} else if (alt === 2) {
|
|
||||||
output += '\n' + indentString + ` ${param[0]}: textDecoder.decode(s${param[1]}),`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
output += '\n' + indentString + ` },`
|
|
||||||
} else {
|
|
||||||
output += '\n' + indentString + ` params: {},`
|
|
||||||
}
|
|
||||||
output += '\n' + indentString + `}`
|
|
||||||
return output
|
|
||||||
}
|
|
||||||
|
|
||||||
function treeIntoCompiledTreeBufferBranch(paths, branches, indent = 0, params = [], alt = 0) {
|
|
||||||
let output = ''
|
|
||||||
let indentation = ''.padStart((indent - params.length) * 2)
|
|
||||||
let addEndBracket = true
|
|
||||||
|
|
||||||
for (let i = 0; i < branches.length; i++) {
|
|
||||||
let branch = branches[i]
|
|
||||||
if (i > 0) {
|
|
||||||
if (!branch.isParams && !branch.isFullParams) {
|
|
||||||
output += ' else '
|
|
||||||
} else {
|
|
||||||
// output += '} //'
|
|
||||||
output += '\n' + indentation
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!branch.isParams && !branch.isFullParams) {
|
|
||||||
output += `if (buf[${getIndex(indent, 0, params)}] === ${branch.char.charCodeAt(0)}) { // ${branch.char}`
|
|
||||||
|
|
||||||
if (branch.path) {
|
|
||||||
output += '\n' + indentation + ` if (buf.length === ${getIndex(indent, 1, params)}) {`
|
|
||||||
output += treeIntoCompiledTreeBufferReturnPath(indentation + ' ', paths, branch, params, alt)
|
|
||||||
output += '\n' + indentation + ` }`
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
addEndBracket = false
|
|
||||||
let paramVarName = (params.length + 1) + '_' + branch.paramVarName
|
|
||||||
if (alt === 1 || alt === 3) {
|
|
||||||
output += `let s${paramVarName} = str.slice(${getIndex(indent, 0, params)}${branch.isFullParams ? '' : `, buf.indexOf(${SlashCode}, ${getIndex(indent, 0, params)}) >>> 0`})`
|
|
||||||
} else {
|
|
||||||
output += `let s${paramVarName} = buf.slice(${getIndex(indent, 0, params)}${branch.isFullParams ? '' : `, buf.indexOf(${SlashCode}, ${getIndex(indent, 0, params)}) >>> 0`})`
|
|
||||||
}
|
|
||||||
output += '\n' + indentation + `let offset${paramVarName} = s${paramVarName}.length`
|
|
||||||
output += '\n' + indentation
|
|
||||||
params.push([branch.isParams || branch.isFullParams, paramVarName])
|
|
||||||
|
|
||||||
if (branch.isFullParams) {
|
|
||||||
output += treeIntoCompiledTreeBufferReturnPath(indentation, paths, branch, params, alt)
|
|
||||||
} else if (branch.path) {
|
|
||||||
output += '\n' + indentation + `if (buf.length === ${getIndex(indent, 0, params)}) {`
|
|
||||||
output += treeIntoCompiledTreeBufferReturnPath(indentation + ' ', paths, branch, params, alt)
|
|
||||||
output += '\n' + indentation + `}`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (branch.children.length) {
|
|
||||||
if (branch.path) {
|
|
||||||
output += ' else '
|
|
||||||
} else {
|
|
||||||
output += '\n' + indentation + ' '
|
|
||||||
}
|
|
||||||
output += treeIntoCompiledTreeBufferBranch(paths, branch.children, indent + 1, params.slice())
|
|
||||||
}
|
|
||||||
if (addEndBracket) {
|
|
||||||
output += '\n' + indentation + '} '
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return output
|
|
||||||
}
|
|
||||||
|
|
||||||
export function treeIntoCompiledTreeBuffer(paths, tree) {
|
|
||||||
let output = 'return function RBuffer(paths, static, str) {'
|
|
||||||
output += '\n let checkStatic = static.get(str)'
|
|
||||||
output += '\n if(checkStatic) {'
|
|
||||||
output += '\n return checkStatic'
|
|
||||||
output += '\n }'
|
|
||||||
output += '\n var buf = Buffer.from(str)'
|
|
||||||
output += '\n ' + treeIntoCompiledTreeBufferBranch(paths, tree, 1)
|
|
||||||
output += '\n return null'
|
|
||||||
output += '\n}'
|
|
||||||
if (Debug) {
|
|
||||||
console.log(output)
|
|
||||||
}
|
|
||||||
return new Function(output)()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function treeIntoCompiledTreeStringReturnPath(indentString, paths, branch, params) {
|
|
||||||
let pathIndex = paths.indexOf(branch.path)
|
|
||||||
if (pathIndex < 0) {
|
|
||||||
throw new RouterError(branch.path, null, 'InternalError: Specified path was not found in paths')
|
|
||||||
}
|
|
||||||
let output = ''
|
|
||||||
output += '\n' + indentString + `return {`
|
|
||||||
output += '\n' + indentString + ` path: paths[${pathIndex}],`
|
|
||||||
if (params.length) {
|
|
||||||
output += '\n' + indentString + ` params: {`
|
|
||||||
for (let param of params) {
|
|
||||||
output += '\n' + indentString + ` ${param[0]}: s${param[1]},`
|
|
||||||
}
|
|
||||||
output += '\n' + indentString + ` },`
|
|
||||||
} else {
|
|
||||||
output += '\n' + indentString + ` params: {},`
|
|
||||||
}
|
|
||||||
output += '\n' + indentString + `}`
|
|
||||||
return output
|
|
||||||
}
|
|
||||||
|
|
||||||
function treeIntoCompiledTreeStringBranch(paths, branches, indent = 0, params = []) {
|
|
||||||
let output = ''
|
|
||||||
let indentation = ''.padStart((indent - params.length) * 2)
|
|
||||||
let addEndBracket = true
|
|
||||||
|
|
||||||
for (let i = 0; i < branches.length; i++) {
|
|
||||||
let branch = branches[i]
|
|
||||||
if (i > 0) {
|
|
||||||
if (!branch.isParams && !branch.isFullParams) {
|
|
||||||
output += ' else '
|
|
||||||
} else {
|
|
||||||
// output += '} //'
|
|
||||||
output += '\n' + indentation
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!branch.isParams && !branch.isFullParams) {
|
|
||||||
output += `if (str.charCodeAt(${getIndex(indent, 0, params)}) === ${branch.char.charCodeAt(0)}) { // ${branch.char}`
|
|
||||||
|
|
||||||
if (branch.path) {
|
|
||||||
output += '\n' + indentation + ` if (str.length === ${getIndex(indent, 1, params)}) {`
|
|
||||||
output += treeIntoCompiledTreeStringReturnPath(indentation + ' ', paths, branch, params)
|
|
||||||
output += '\n' + indentation + ` }`
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
addEndBracket = false
|
|
||||||
let paramVarName = (params.length + 1) + '_' + branch.paramVarName
|
|
||||||
output += `let s${paramVarName} = str.slice(${getIndex(indent, 0, params)}${branch.isFullParams ? '' : `, str.indexOf('/', ${getIndex(indent, 0, params)}) >>> 0`})`
|
|
||||||
output += '\n' + indentation + `let offset${paramVarName} = s${paramVarName}.length`
|
|
||||||
output += '\n' + indentation
|
|
||||||
params.push([branch.isParams || branch.isFullParams, paramVarName])
|
|
||||||
|
|
||||||
if (branch.isFullParams) {
|
|
||||||
output += treeIntoCompiledTreeStringReturnPath(indentation, paths, branch, params)
|
|
||||||
} else if (branch.path) {
|
|
||||||
output += '\n' + indentation + `if (str.length === ${getIndex(indent, 0, params)}) {`
|
|
||||||
output += treeIntoCompiledTreeStringReturnPath(indentation + ' ', paths, branch, params)
|
|
||||||
output += '\n' + indentation + `}`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (branch.children.length) {
|
|
||||||
if (branch.path) {
|
|
||||||
output += ' else '
|
|
||||||
} else {
|
|
||||||
output += '\n' + indentation + ' '
|
|
||||||
}
|
|
||||||
output += treeIntoCompiledTreeStringBranch(paths, branch.children, indent + 1, params.slice())
|
|
||||||
}
|
|
||||||
if (addEndBracket) {
|
|
||||||
output += '\n' + indentation + '} '
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return output
|
|
||||||
}
|
|
||||||
|
|
||||||
function treeIntoCompiledTreeStringBranchIndices(paths, branches, indent = 0, params = []) {
|
|
||||||
let output = ''
|
|
||||||
let indentation = ''.padStart((indent - params.length) * 2)
|
|
||||||
let addEndBracket = true
|
|
||||||
|
|
||||||
for (let i = 0; i < branches.length; i++) {
|
|
||||||
let branch = branches[i]
|
|
||||||
if (i > 0) {
|
|
||||||
if (!branch.isParams && !branch.isFullParams) {
|
|
||||||
output += ' else '
|
|
||||||
} else {
|
|
||||||
// output += '} //'
|
|
||||||
output += '\n' + indentation
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!branch.isParams && !branch.isFullParams) {
|
|
||||||
output += `if (str[${getIndex(indent, 0, params)}] === '${branch.char}') { // ${branch.char}`
|
|
||||||
|
|
||||||
if (branch.path) {
|
|
||||||
output += '\n' + indentation + ` if (str.length === ${getIndex(indent, 1, params)}) {`
|
|
||||||
output += treeIntoCompiledTreeStringReturnPath(indentation + ' ', paths, branch, params)
|
|
||||||
output += '\n' + indentation + ` }`
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
addEndBracket = false
|
|
||||||
let paramVarName = (params.length + 1) + '_' + branch.paramVarName
|
|
||||||
output += `let s${paramVarName} = str.slice(${getIndex(indent, 0, params)}${branch.isFullParams ? '' : `, str.indexOf('/', ${getIndex(indent, 0, params)}) >>> 0`})`
|
|
||||||
output += '\n' + indentation + `let offset${paramVarName} = s${paramVarName}.length`
|
|
||||||
output += '\n' + indentation
|
|
||||||
params.push([branch.isParams || branch.isFullParams, paramVarName])
|
|
||||||
|
|
||||||
if (branch.isFullParams) {
|
|
||||||
output += treeIntoCompiledTreeStringReturnPath(indentation, paths, branch, params)
|
|
||||||
} else if (branch.path) {
|
|
||||||
output += '\n' + indentation + `if (str.length === ${getIndex(indent, 0, params)}) {`
|
|
||||||
output += treeIntoCompiledTreeStringReturnPath(indentation + ' ', paths, branch, params)
|
|
||||||
output += '\n' + indentation + `}`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (branch.children.length) {
|
|
||||||
if (branch.path) {
|
|
||||||
output += ' else '
|
|
||||||
} else {
|
|
||||||
output += '\n' + indentation + ' '
|
|
||||||
}
|
|
||||||
output += treeIntoCompiledTreeStringBranch(paths, branch.children, indent + 1, params.slice())
|
|
||||||
}
|
|
||||||
if (addEndBracket) {
|
|
||||||
output += '\n' + indentation + '} '
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return output
|
|
||||||
}
|
|
||||||
|
|
||||||
export function treeIntoCompiledTreeString(paths, tree) {
|
|
||||||
let output = 'return function RString(paths, static, str) {'
|
|
||||||
output += '\n let checkStatic = static.get(str)'
|
|
||||||
output += '\n if(checkStatic) {'
|
|
||||||
output += '\n return checkStatic'
|
|
||||||
output += '\n }'
|
|
||||||
output += '\n ' + treeIntoCompiledTreeStringBranch(paths, tree, 1)
|
|
||||||
output += '\n return null'
|
|
||||||
output += '\n}'
|
|
||||||
if (Debug) {
|
|
||||||
console.log(output)
|
|
||||||
}
|
|
||||||
return new Function(output)()
|
|
||||||
}
|
|
||||||
|
|
||||||
export function treeIntoCompiledTreeStringIndices(paths, tree) {
|
|
||||||
let output = 'return function RStringIndices(paths, static, str) {'
|
|
||||||
output += '\n let checkStatic = static.get(str)'
|
|
||||||
output += '\n if(checkStatic) {'
|
|
||||||
output += '\n return checkStatic'
|
|
||||||
output += '\n }'
|
|
||||||
output += '\n ' + treeIntoCompiledTreeStringBranchIndices(paths, tree, 1)
|
|
||||||
output += '\n return null'
|
|
||||||
output += '\n}'
|
|
||||||
if (Debug) {
|
|
||||||
console.log(output)
|
|
||||||
}
|
|
||||||
return new Function(output)()
|
|
||||||
}
|
|
||||||
|
|
||||||
export function treeIntoCompiledTreeBufferAltOne(paths, tree) {
|
|
||||||
let output = 'return function RBufferStrSlice(paths, static, str) {'
|
|
||||||
output += '\n let checkStatic = static.get(str)'
|
|
||||||
output += '\n if(checkStatic) {'
|
|
||||||
output += '\n return checkStatic'
|
|
||||||
output += '\n }'
|
|
||||||
output += '\n var buf = Buffer.from(str)'
|
|
||||||
output += '\n ' + treeIntoCompiledTreeBufferBranch(paths, tree, 1, [], 1)
|
|
||||||
output += '\n return null'
|
|
||||||
output += '\n}'
|
|
||||||
if (Debug) {
|
|
||||||
console.log(output)
|
|
||||||
}
|
|
||||||
return new Function(output)()
|
|
||||||
}
|
|
||||||
|
|
||||||
export function treeIntoCompiledTreeBufferAltTwo(paths, tree) {
|
|
||||||
let output = 'return function RTextEncoderDecoder(textEncoder, textDecoder, paths, static, str) {'
|
|
||||||
output += '\n let checkStatic = static.get(str)'
|
|
||||||
output += '\n if(checkStatic) {'
|
|
||||||
output += '\n return checkStatic'
|
|
||||||
output += '\n }'
|
|
||||||
output += '\n var buf = textEncoder.encode(str)'
|
|
||||||
output += '\n ' + treeIntoCompiledTreeBufferBranch(paths, tree, 1, [], 2)
|
|
||||||
output += '\n return null'
|
|
||||||
output += '\n}'
|
|
||||||
if (Debug) {
|
|
||||||
console.log(output)
|
|
||||||
}
|
|
||||||
return new Function(output)()
|
|
||||||
}
|
|
||||||
|
|
||||||
export function treeIntoCompiledTreeBufferAltThree(paths, tree) {
|
|
||||||
let output = 'return function RTextEncoderStrSlice(textEncoder, paths, static, str) {'
|
|
||||||
output += '\n let checkStatic = static.get(str)'
|
|
||||||
output += '\n if(checkStatic) {'
|
|
||||||
output += '\n return checkStatic'
|
|
||||||
output += '\n }'
|
|
||||||
output += '\n var buf = textEncoder.encode(str)'
|
|
||||||
output += '\n ' + treeIntoCompiledTreeBufferBranch(paths, tree, 1, [], 3)
|
|
||||||
output += '\n return null'
|
|
||||||
output += '\n}'
|
|
||||||
if (Debug) {
|
|
||||||
console.log(output)
|
|
||||||
}
|
|
||||||
return new Function(output)()
|
|
||||||
}
|
|
||||||
|
|
||||||
export function compilePaths(paths) {
|
|
||||||
let splitPaths = splitAndSortPaths(paths)
|
|
||||||
let tree = buildTree(splitPaths.paramsPaths.slice())
|
|
||||||
printTree(tree, 0)
|
|
||||||
let compiled = treeIntoCompiledTreeBuffer(paths, tree)
|
|
||||||
let compiledString = treeIntoCompiledTreeString(paths, tree)
|
|
||||||
let compiledStringIndices = treeIntoCompiledTreeStringIndices(paths, tree)
|
|
||||||
|
|
||||||
let compiledTextOne = treeIntoCompiledTreeBufferAltOne(paths, tree)
|
|
||||||
|
|
||||||
// let compiledTextTwo = treeIntoCompiledTreeBufferAltTwo(paths, tree)
|
|
||||||
// let compiledTextThree = treeIntoCompiledTreeBufferAltThree(paths, tree)
|
|
||||||
return [
|
|
||||||
[compiled, compiled.bind(null, paths, splitPaths.staticPaths)],
|
|
||||||
[compiledTextOne, compiledTextOne.bind(null, paths, splitPaths.staticPaths)],
|
|
||||||
// [compiledTextTwo, compiledTextTwo.bind(null, new TextEncoder(), new TextDecoder(), paths, splitPaths.staticPaths)],
|
|
||||||
// [compiledTextThree, compiledTextThree.bind(null, new TextEncoder(), paths, splitPaths.staticPaths)],
|
|
||||||
[compiledString, compiledString.bind(null, paths, splitPaths.staticPaths)],
|
|
||||||
[compiledStringIndices, compiledStringIndices.bind(null, paths, splitPaths.staticPaths)],
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
export function compilePathsBuffer(paths) {
|
|
||||||
let splitPaths = splitAndSortPaths(paths)
|
|
||||||
let tree = buildTree(splitPaths.paramsPaths.slice())
|
|
||||||
printTree(tree, 0)
|
|
||||||
let compiled = treeIntoCompiledTreeBuffer(paths, tree)
|
|
||||||
let compiledString = treeIntoCompiledTreeString(paths, tree)
|
|
||||||
let compiledStringIndices = treeIntoCompiledTreeStringIndices(paths, tree)
|
|
||||||
|
|
||||||
let compiledTextOne = treeIntoCompiledTreeBufferAltOne(paths, tree)
|
|
||||||
|
|
||||||
// let compiledTextTwo = treeIntoCompiledTreeBufferAltTwo(paths, tree)
|
|
||||||
// let compiledTextThree = treeIntoCompiledTreeBufferAltThree(paths, tree)
|
|
||||||
return [
|
|
||||||
[compiled, compiled.bind(null, paths, splitPaths.staticPaths)],
|
|
||||||
[compiledTextOne, compiledTextOne.bind(null, paths, splitPaths.staticPaths)],
|
|
||||||
// [compiledTextTwo, compiledTextTwo.bind(null, new TextEncoder(), new TextDecoder(), paths, splitPaths.staticPaths)],
|
|
||||||
// [compiledTextThree, compiledTextThree.bind(null, new TextEncoder(), paths, splitPaths.staticPaths)],
|
|
||||||
[compiledString, compiledString.bind(null, paths, splitPaths.staticPaths)],
|
|
||||||
[compiledStringIndices, compiledStringIndices.bind(null, paths, splitPaths.staticPaths)],
|
|
||||||
]
|
|
||||||
}
|
|
|
@ -1,16 +0,0 @@
|
||||||
import { compilePaths } from "../router_v2.mjs"
|
|
||||||
import { printTime } from './utils.mjs'
|
|
||||||
import * as consts from './const.js'
|
|
||||||
|
|
||||||
let paths = consts.allManyRoutes.map(x => ({ path: x }))
|
|
||||||
|
|
||||||
let s1 = process.hrtime.bigint()
|
|
||||||
let s2 = process.hrtime.bigint()
|
|
||||||
|
|
||||||
compilePaths(paths)
|
|
||||||
|
|
||||||
let s3 = process.hrtime.bigint()
|
|
||||||
|
|
||||||
let time = s3 - s2 - (s2 - s1)
|
|
||||||
|
|
||||||
printTime(time)
|
|
|
@ -1,117 +0,0 @@
|
||||||
import { summary, run, bench } from 'mitata';
|
|
||||||
|
|
||||||
// Warmup (de-optimize `bench()` calls)
|
|
||||||
bench('noop', () => { });
|
|
||||||
bench('noop2', () => { });
|
|
||||||
|
|
||||||
function padStart(length) {
|
|
||||||
return ''.padStart(length * 2)
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = [
|
|
||||||
'/',
|
|
||||||
'/api/articles',
|
|
||||||
'/api/articles/:id/file',
|
|
||||||
'/api/articles/:id',
|
|
||||||
'/api/articles/public',
|
|
||||||
'/api/articles/public/:id',
|
|
||||||
'/api/categories',
|
|
||||||
'/api/categories/:categoryId/products',
|
|
||||||
'/api/categories/:categoryId/properties',
|
|
||||||
'/api/categories/:categoryId/values/:props',
|
|
||||||
'/api/categories/:categoryId',
|
|
||||||
//'/api/categories/:categoryId/products/:productId',
|
|
||||||
'/api/categories/:categoryId/products/:productId',
|
|
||||||
'/api/customers',
|
|
||||||
'/api/customers/:id',
|
|
||||||
'/api/customers/kennitala/:kennitala',
|
|
||||||
'/api/customers/public/kennitala/:kennitala',
|
|
||||||
'/api/customers/search/:search',
|
|
||||||
'/api/file',
|
|
||||||
'/api/file/:id',
|
|
||||||
'/api/media',
|
|
||||||
'/api/media/:id',
|
|
||||||
'/api/orderitem',
|
|
||||||
'/api/orderitem/:id',
|
|
||||||
'/api/orders',
|
|
||||||
'/api/orders/:orderId',
|
|
||||||
'/api/orders/:orderId/sell',
|
|
||||||
'/api/pages',
|
|
||||||
'/api/pages/:pageId',
|
|
||||||
'/api/pages/:pageId/articles',
|
|
||||||
'/api/pages/:pageId/articles/public',
|
|
||||||
'/api/products',
|
|
||||||
'/api/products/:id',
|
|
||||||
'/api/products/:id/movement',
|
|
||||||
'/api/products/:id/sub_products/:productId',
|
|
||||||
//'/api/products/:id/sub_products/:productId',
|
|
||||||
'/api/products/code/:code',
|
|
||||||
'/api/products/property/:propertyId',
|
|
||||||
'/api/properties',
|
|
||||||
'/api/properties/:id',
|
|
||||||
'/api/sales',
|
|
||||||
'/api/sales/:id',
|
|
||||||
'/api/stockitem',
|
|
||||||
'/api/stockitem/:id',
|
|
||||||
'/api/stocks',
|
|
||||||
'/api/stocks/:id',
|
|
||||||
'/api/stocks/:id/commit',
|
|
||||||
'/api/test',
|
|
||||||
'/api/test/auth',
|
|
||||||
'/api/test/error',
|
|
||||||
'/api/workentries',
|
|
||||||
'/api/workentries/:id',
|
|
||||||
'/api/works',
|
|
||||||
'/api/works/:id',
|
|
||||||
'/api/works/:id/lock',
|
|
||||||
'/api/works/public',
|
|
||||||
'/api/staff',
|
|
||||||
'/api/staff/:id',
|
|
||||||
'/::rest',
|
|
||||||
]
|
|
||||||
|
|
||||||
function arrIncludes(data) {
|
|
||||||
let out = new Array()
|
|
||||||
for (let item of data) {
|
|
||||||
if (out.includes(item)) { break }
|
|
||||||
out.push(item)
|
|
||||||
}
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
function setAdd(data) {
|
|
||||||
let s = new Set()
|
|
||||||
for (let item of data) {
|
|
||||||
let size = s.size
|
|
||||||
if (s.add(item).size === size) { break }
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
let func = [arrIncludes, setAdd];
|
|
||||||
for (let fun of func) {
|
|
||||||
console.log(`--- warming up ${fun.name || 'mapl'} ---`)
|
|
||||||
for (var i = 0; i < 100; i++) {
|
|
||||||
fun(data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await new Promise(res => setTimeout(res, 3000))
|
|
||||||
|
|
||||||
summary(() => {
|
|
||||||
for (let i = 20; i >= 0; i--) {
|
|
||||||
const dataSet = data.slice(0, data.length - i)
|
|
||||||
|
|
||||||
func.forEach(function(fun) {
|
|
||||||
bench(`${dataSet.length} items: ${fun.name}`, function() {
|
|
||||||
return fun(dataSet)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
// console.log(tests, fun, tests.map(fun))
|
|
||||||
/*bench(fun.name, function() {
|
|
||||||
return fun(data)
|
|
||||||
})*/
|
|
||||||
})
|
|
||||||
|
|
||||||
run();
|
|
|
@ -1,80 +0,0 @@
|
||||||
import { summary, run, bench } from 'mitata';
|
|
||||||
|
|
||||||
// Warmup (de-optimize `bench()` calls)
|
|
||||||
bench('noop', () => { });
|
|
||||||
bench('noop2', () => { });
|
|
||||||
|
|
||||||
// Example benchmark
|
|
||||||
summary(() => {
|
|
||||||
const dataset = new Array(100).fill(0).map(
|
|
||||||
() => new Array(10).fill(0).map(() => String.fromCharCode(97 + Math.round(Math.random() * 22))).join('')
|
|
||||||
);
|
|
||||||
console.log(dataset)
|
|
||||||
|
|
||||||
const fn1 = (str) => (str[0] === 'c' ? 1 : 0) + (str[1] === 'c' ? 1 : 0) + (str[2] === 'c' ? 1 : 0);
|
|
||||||
fn1('a');
|
|
||||||
fn1('b');
|
|
||||||
fn1('c');
|
|
||||||
fn1('d');
|
|
||||||
// optimizeNextInvocation(fn1);
|
|
||||||
bench('Char check', () => dataset.map(fn1))
|
|
||||||
|
|
||||||
const fn2 = (str) => (str.charCodeAt(0) === 99 ? 1 : 0) + (str.charCodeAt(1) === 99 ? 1 : 0) + (str.charCodeAt(2) === 99 ? 1 : 0);
|
|
||||||
fn2('a');
|
|
||||||
fn2('b');
|
|
||||||
fn2('c');
|
|
||||||
fn2('d');
|
|
||||||
// optimizeNextInvocation(fn2);
|
|
||||||
bench('Char code check', () => dataset.map(fn2));
|
|
||||||
|
|
||||||
|
|
||||||
bench('2x Char check', () => dataset.map(fn1))
|
|
||||||
bench('2x Char code check', () => dataset.map(fn2));
|
|
||||||
});
|
|
||||||
|
|
||||||
// Example benchmark
|
|
||||||
summary(() => {
|
|
||||||
let paths = [
|
|
||||||
'test1/',
|
|
||||||
'test/',
|
|
||||||
'test3/',
|
|
||||||
'test/',
|
|
||||||
'test5/',
|
|
||||||
'something/',
|
|
||||||
'else/',
|
|
||||||
'goes/',
|
|
||||||
'here/',
|
|
||||||
'too/',
|
|
||||||
]
|
|
||||||
|
|
||||||
function fastCheck(str) {
|
|
||||||
if (str.charCodeAt(0) === 116) {
|
|
||||||
if (str.charCodeAt(1) === 101) {
|
|
||||||
if (str.charCodeAt(2) === 115) {
|
|
||||||
if (str.charCodeAt(3) === 116) {
|
|
||||||
if (str.charCodeAt(4) === 47) {
|
|
||||||
if (str.indexOf('/', 5) === -1) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
let r = new RegExp('^test/$')
|
|
||||||
function regexCheck(str) {
|
|
||||||
return r.test(str)
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(paths.map(fastCheck))
|
|
||||||
console.log(paths.map(regexCheck))
|
|
||||||
|
|
||||||
bench('fastCheck', () => paths.map(fastCheck))
|
|
||||||
bench('regexCheck', () => paths.map(regexCheck));
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
run();
|
|
|
@ -1,121 +0,0 @@
|
||||||
import { buildTree, compileTreeIntoIfs, compileTreeIntoIfsWithBuffer } from "./compiler/compiler.mjs"
|
|
||||||
import { summary, run, bench } from 'mitata';
|
|
||||||
import { printCurrentStatus, printStatusHelperText } from "./compiler/utils.mjs";
|
|
||||||
import { FlaskaRouter } from "../flaska.mjs";
|
|
||||||
|
|
||||||
// Warmup (de-optimize `bench()` calls)
|
|
||||||
bench('noop', () => { });
|
|
||||||
bench('noop2', () => { });
|
|
||||||
|
|
||||||
let paths = [
|
|
||||||
'/fsdafasfa',
|
|
||||||
'/ymreklhmse',
|
|
||||||
'/h34nmlaeknmgl',
|
|
||||||
'/asdgsdagas',
|
|
||||||
'/ahaewhweaaa',
|
|
||||||
'/adshashaea',
|
|
||||||
'/sdafasfsadfasdfas',
|
|
||||||
//'/gdfsfgsfdsgsdrgsregsergsregersgserersgsergersg',
|
|
||||||
//'/test5afdlkasdflksad flsakdf lsakf asdlkfa lsdkfasdlkdfalklk',
|
|
||||||
]
|
|
||||||
let pathsBla = [
|
|
||||||
'/test5afdlkasdflksad flsakdf lsakf asdlkfa lsdkfasdlkdfalklk',
|
|
||||||
'/test5afdlkasdflksad flsakdf lsakf asdlkfa lsdkfasdlkdfalklk',
|
|
||||||
'/test5afdlkasdflksad flsakdf lsakf asdlkfa lsdkfasdlkdfalklk',
|
|
||||||
'/test5afdlkasdflksad flsakdf lsakf asdlkfa lsdkfasdlkdfalklk',
|
|
||||||
'/test5afdlkasdflksad flsakdf lsakf asdlkfa lsdkfasdlkdfalklk',
|
|
||||||
'/test5afdlkasdflksad flsakdf lsakf asdlkfa lsdkfasdlkdfalklk',
|
|
||||||
'/test5afdlkasdflksad flsakdf lsakf asdlkfa lsdkfasdlkdfalklk',
|
|
||||||
'/test5afdlkasdflksad flsakdf lsakf asdlkfa lsdkfasdlkdfalklk',
|
|
||||||
'/test5afdlkasdflksad flsakdf lsakf asdlkfa lsdkfasdlkdfalklk',
|
|
||||||
'/test5afdlkasdflksad flsakdf lsakf asdlkfa lsdkfasdlkdfalklk',
|
|
||||||
]
|
|
||||||
let pathsBuffer = paths.map(x => new Uint8Array(Buffer.from(x)))
|
|
||||||
|
|
||||||
let tree = buildTree(paths)
|
|
||||||
let noop = function() { }
|
|
||||||
|
|
||||||
const ifTree = compileTreeIntoIfs(tree)
|
|
||||||
// console.log(ifTree.toString())
|
|
||||||
const ifTreeBuffer = compileTreeIntoIfsWithBuffer(tree)
|
|
||||||
// console.log(ifTreeBuffer.toString())
|
|
||||||
const flaskaRouter = new FlaskaRouter()
|
|
||||||
for (let path of paths) {
|
|
||||||
flaskaRouter.addRoute(path, noop)
|
|
||||||
}
|
|
||||||
|
|
||||||
const m = new Map(paths.map(x => [x, x]))
|
|
||||||
function mapFlat(str) {
|
|
||||||
return m.get(str) ?? null
|
|
||||||
}
|
|
||||||
|
|
||||||
function toBuffer(str) {
|
|
||||||
return Buffer.from(str)
|
|
||||||
}
|
|
||||||
function toUint(str) {
|
|
||||||
return new Uint8Array(Buffer.from(str))
|
|
||||||
}
|
|
||||||
function toManualArray(str) {
|
|
||||||
let length = str.length
|
|
||||||
let out = new Array(length)
|
|
||||||
for (let i = 0; i < length; i++) {
|
|
||||||
out[i] = str.charCodeAt(i)
|
|
||||||
}
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
function allocBufferUnsafe(str) {
|
|
||||||
return Buffer.allocUnsafe(str.length)
|
|
||||||
}
|
|
||||||
function allocBufferSafe(str) {
|
|
||||||
return Buffer.alloc(str.length)
|
|
||||||
}
|
|
||||||
function allocUint8(str) {
|
|
||||||
return new Uint8Array(str.length)
|
|
||||||
}
|
|
||||||
function toManualArraySplitMap(str) {
|
|
||||||
return str.split('').map(x => x.charCodeAt(0))
|
|
||||||
}
|
|
||||||
|
|
||||||
let func1 = [mapFlat, toBuffer, toUint, toManualArray, toManualArraySplitMap, allocBufferUnsafe, allocBufferSafe, allocUint8, ifTree];
|
|
||||||
for (var i = 0; i < 100000; i++) {
|
|
||||||
for (let fun of func1) {
|
|
||||||
paths.map(fun)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (var i = 0; i < 100000; i++) {
|
|
||||||
for (let path of paths) {
|
|
||||||
flaskaRouter.match(path)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (let fun of func1) {
|
|
||||||
printCurrentStatus(fun);
|
|
||||||
}
|
|
||||||
printCurrentStatus(flaskaRouter.match)
|
|
||||||
printStatusHelperText()
|
|
||||||
|
|
||||||
summary(() => {
|
|
||||||
bench('if tree', function() {
|
|
||||||
return paths.map(ifTree)
|
|
||||||
})
|
|
||||||
bench('if tree buffer edition', function() {
|
|
||||||
return paths.map(ifTreeBuffer)
|
|
||||||
});
|
|
||||||
bench('flaskarouter', function() {
|
|
||||||
return paths.map(flaskaRouter.match.bind(flaskaRouter))
|
|
||||||
});
|
|
||||||
/*bench('map edition', function() {
|
|
||||||
return paths.map(mapFlat)
|
|
||||||
});
|
|
||||||
bench('if tree pre buffer edition', function() {
|
|
||||||
return pathsBuffer.map(ifTreeBuffer)
|
|
||||||
});
|
|
||||||
bench('toBuffer', () => pathsBla.map(toBuffer));
|
|
||||||
bench('toUint', () => pathsBla.map(toUint));
|
|
||||||
bench('toManualArraySplitMap', () => pathsBla.map(toManualArraySplitMap))
|
|
||||||
bench('toManualArray', () => pathsBla.map(toManualArray))*/
|
|
||||||
bench('allocBufferUnsafe', () => pathsBla.map(allocBufferUnsafe))
|
|
||||||
bench('allocBufferSafe', () => pathsBla.map(allocBufferSafe))
|
|
||||||
bench('allocUint8', () => pathsBla.map(allocUint8))
|
|
||||||
})
|
|
||||||
|
|
||||||
run();
|
|
|
@ -1,115 +0,0 @@
|
||||||
import { printCurrentStatus, printStatusHelperText } from "./compiler/utils.mjs";
|
|
||||||
import { summary, run, bench } from 'mitata';
|
|
||||||
|
|
||||||
// Do warmup
|
|
||||||
bench('noop', () => { })
|
|
||||||
bench('noop2', () => { })
|
|
||||||
|
|
||||||
// Do checks with arrays
|
|
||||||
let arr = ['hi', 'there', 'nagna', 'randomlongstring', 'small']
|
|
||||||
arr = arr.map(x => [x, { path: x }])
|
|
||||||
let paths = arr.map(x => x[1])
|
|
||||||
|
|
||||||
// Do checks with objects
|
|
||||||
let obj = {};
|
|
||||||
let map = new Map()
|
|
||||||
for (let i = 0, l = arr.length; i < l; ++i) {
|
|
||||||
obj[arr[i][0]] = arr[i][1]
|
|
||||||
map.set(arr[i][0], arr[i][1])
|
|
||||||
};
|
|
||||||
let objHas1 = (obj, item) => { let x = obj[item]; if (x) { return x } return null };
|
|
||||||
let objHas2 = (obj, item) => { if (item in obj) { return obj[item] } return null }
|
|
||||||
let mapGet = (map, item) => { let x = map.get(item); if (x) { return x }; return null };
|
|
||||||
|
|
||||||
|
|
||||||
// Generate a function which compares the input string to all strings in the list
|
|
||||||
let basicIfChain = Function(`
|
|
||||||
return (paths, str) => {
|
|
||||||
${arr.map((item, i) => 'if (str === "' + item[0] + '") { return paths[' + i + '] }').join('\n ')}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
`)();
|
|
||||||
let basicIfChainAlt = Function(`
|
|
||||||
return (str) => {
|
|
||||||
${arr.map((item, i) => 'if (str === "' + item[0] + '") { return "' + item[0] + '" }').join('\n ')}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
`)();
|
|
||||||
function basicIfChainNoFunc(str) {
|
|
||||||
if (str === "hi") { return "hi" }
|
|
||||||
if (str === "there") { return "there" }
|
|
||||||
if (str === "nagna") { return "nagna" }
|
|
||||||
if (str === "randomlongstring") { return "randomlongstring" }
|
|
||||||
if (str === "small") { return "small" }
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
function addTwo(a, b) {
|
|
||||||
return a + b
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(`
|
|
||||||
return (str) => {
|
|
||||||
${arr.map((item, i) => 'if (str === "' + item[0] + '") { return "' + item[0] + '" }').join('\n ')}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
`)
|
|
||||||
|
|
||||||
let func = [
|
|
||||||
['basicIfChainNoFunc', basicIfChainNoFunc, basicIfChainNoFunc],
|
|
||||||
['basicIfChainAlt', basicIfChainAlt, basicIfChainAlt],
|
|
||||||
['objHas1', objHas1, objHas1.bind(null, obj)],
|
|
||||||
['objHas2', objHas2, objHas2.bind(null, obj)],
|
|
||||||
['mapGet', mapGet, mapGet.bind(null, map)],
|
|
||||||
['basicIfChain', basicIfChain, basicIfChain.bind(null, paths)],
|
|
||||||
]
|
|
||||||
|
|
||||||
for (let [name, __, fun] of func) {
|
|
||||||
console.log(`--- Sanity checking ${name} ---`)
|
|
||||||
for (let a of arr) {
|
|
||||||
console.log(a[0], fun(a[0]))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate strings
|
|
||||||
let newArr = [];
|
|
||||||
for (let i = 0; i < 100000; i++)
|
|
||||||
newArr.push(Math.random() < 0.5 ? `${Math.random()}` : arr[Math.round(Math.random() * 4)].slice(0, -1 >>> 0));
|
|
||||||
|
|
||||||
console.log(`--- warming up addTwo ---`)
|
|
||||||
for (let i = 0; i < 100; i++) {
|
|
||||||
console.log(`--- run ${i} ---`)
|
|
||||||
for (let num of newArr) {
|
|
||||||
addTwo(num.length, num.length + 1)
|
|
||||||
}
|
|
||||||
await new Promise(res => setTimeout(res, 1000))
|
|
||||||
printCurrentStatus(addTwo);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
for (let [name, org, fun] of func) {
|
|
||||||
console.log(`--- warming up ${name} ---`)
|
|
||||||
for (let i = 0; i < 100; i++) {
|
|
||||||
console.log(`--- run ${i} ---`)
|
|
||||||
newArr.forEach(fun)
|
|
||||||
await new Promise(res => setTimeout(res, 1000))
|
|
||||||
printCurrentStatus(org);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('--- sleeping ---')
|
|
||||||
await new Promise(res => setTimeout(res, 1000))
|
|
||||||
|
|
||||||
|
|
||||||
for (let [_, org] of func) {
|
|
||||||
printCurrentStatus(org);
|
|
||||||
}
|
|
||||||
printStatusHelperText()
|
|
||||||
|
|
||||||
|
|
||||||
summary(() => {
|
|
||||||
func.forEach(([name, _, fun]) => {
|
|
||||||
bench(name, () => newArr.map(fun));
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
run();
|
|
|
@ -1,44 +0,0 @@
|
||||||
import { summary, run, bench } from 'mitata';
|
|
||||||
import { FlaskaRouter } from "../flaska.mjs";
|
|
||||||
|
|
||||||
// Warmup (de-optimize `bench()` calls)
|
|
||||||
bench('noop', () => { });
|
|
||||||
bench('noop2', () => { });
|
|
||||||
|
|
||||||
function padStart(length) {
|
|
||||||
return ''.padStart(length * 2)
|
|
||||||
}
|
|
||||||
|
|
||||||
const spaces = ''.padStart(256)
|
|
||||||
|
|
||||||
function strSlice(length) {
|
|
||||||
return spaces.slice(0, length)
|
|
||||||
}
|
|
||||||
|
|
||||||
function strSubstring(length) {
|
|
||||||
return spaces.substring(0, length)
|
|
||||||
}
|
|
||||||
|
|
||||||
const testData = new Array(100000)
|
|
||||||
for (let i = 0; i < testData.length; i++) {
|
|
||||||
testData[i] = Math.round(Math.random() * 200)
|
|
||||||
}
|
|
||||||
|
|
||||||
let func = [padStart, strSlice, strSubstring];
|
|
||||||
for (let fun of func) {
|
|
||||||
console.log(`--- warming up ${fun.name || 'mapl'} ---`)
|
|
||||||
for (var i = 0; i < 100; i++) {
|
|
||||||
testData.map(fun)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
summary(() => {
|
|
||||||
func.forEach(function(fun) {
|
|
||||||
// console.log(tests, fun, tests.map(fun))
|
|
||||||
bench(fun.name, function() {
|
|
||||||
return testData.map(fun)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
run();
|
|
|
@ -1,33 +0,0 @@
|
||||||
import assert from 'assert'
|
|
||||||
import { compilePaths } from "./router_v2.mjs"
|
|
||||||
import * as consts from './const.js'
|
|
||||||
|
|
||||||
let paths = [
|
|
||||||
{ path: '/aa/aa', },
|
|
||||||
{ path: '/aa/:blabla', },
|
|
||||||
{ path: '/::rest', },
|
|
||||||
]
|
|
||||||
|
|
||||||
// paths = consts.allManyRoutes.map(x => ({ path: x }))
|
|
||||||
|
|
||||||
let tests = [
|
|
||||||
['/', paths[5]],
|
|
||||||
['/aa', paths[5]],
|
|
||||||
['/aa/aa', paths[0]],
|
|
||||||
['/aa/_', paths[1]],
|
|
||||||
['/aa/_/aa', paths[2]],
|
|
||||||
['/aa/_/ab', paths[3]],
|
|
||||||
['/aa/_/bb', paths[4]],
|
|
||||||
]
|
|
||||||
|
|
||||||
tests = paths.map(p => ([p.path.replace(/:[^/]+/g, '_'), p]))
|
|
||||||
|
|
||||||
let func = compilePaths(paths)
|
|
||||||
for (let [_, fun] of func) {
|
|
||||||
console.log(`--- ${fun.name} ---`)
|
|
||||||
for (let test of tests) {
|
|
||||||
let check = fun(test[0])
|
|
||||||
console.log(test[0], check)
|
|
||||||
assert.strictEqual(check.path, test[1])
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,15 +0,0 @@
|
||||||
export function printTime (t) {
|
|
||||||
let time = Number(t)
|
|
||||||
let units = ['n', 'μ', 'm', 'c', 's']
|
|
||||||
let unit = units[0]
|
|
||||||
let unitPower = 1
|
|
||||||
for (let i = 0; i < units.length; i++) {
|
|
||||||
let power = Math.pow(10, (i + 1) * 3)
|
|
||||||
if (power * 2 > time) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
unitPower = power
|
|
||||||
unit = units[i]
|
|
||||||
}
|
|
||||||
console.log(t, '=', Number((time / unitPower).toFixed(2)), unit)
|
|
||||||
}
|
|
1048
flaska.mjs
1048
flaska.mjs
File diff suppressed because one or more lines are too long
26
package.json
26
package.json
|
@ -1,24 +1,27 @@
|
||||||
{
|
{
|
||||||
"name": "flaska",
|
"name": "flaska",
|
||||||
"version": "1.4.0",
|
"version": "0.9.9",
|
||||||
"description": "Flaska is a micro web-framework for node. It is designed to be fast, simple and lightweight, and is distributed as a single file module with no dependencies.",
|
"description": "Flaska is a micro web-framework for node. It is designed to be fast, simple and lightweight, and is distributed as a single file module with no dependencies.",
|
||||||
"main": "flaska.mjs",
|
"main": "flaska.mjs",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "eltro -r dot",
|
"test": "eltro",
|
||||||
"test:watch": "eltro -r dot -w test"
|
"test:watch": "npm-watch test"
|
||||||
},
|
},
|
||||||
"watch": {
|
"watch": {
|
||||||
"test": {
|
"test": {
|
||||||
"patterns": [
|
"patterns": [
|
||||||
"./"
|
"test/*",
|
||||||
|
"flaska.mjs"
|
||||||
],
|
],
|
||||||
"extensions": "mjs"
|
"extensions": "mjs",
|
||||||
|
"quiet": true,
|
||||||
|
"inherit": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git+https://git.nfp.is/TheThing/flaska.git"
|
"url": "git+https://github.com/nfp-projects/bottle-node.git"
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"web",
|
"web",
|
||||||
|
@ -32,18 +35,13 @@
|
||||||
"author": "Jonatan Nilsson",
|
"author": "Jonatan Nilsson",
|
||||||
"license": "WTFPL",
|
"license": "WTFPL",
|
||||||
"bugs": {
|
"bugs": {
|
||||||
"url": "https://git.nfp.is/TheThing/flaska/issues"
|
"url": "https://github.com/nfp-projects/bottle-node/issues"
|
||||||
},
|
},
|
||||||
"homepage": "https://git.nfp.is/TheThing/flaska/#readme",
|
"homepage": "https://github.com/nfp-projects/bottle-node#readme",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"eltro": "^1.3.2",
|
"eltro": "^1.3.1",
|
||||||
"formidable": "^1.2.2"
|
"formidable": "^1.2.2"
|
||||||
},
|
},
|
||||||
"pnpm": {
|
|
||||||
"allowedDeprecatedVersions": {
|
|
||||||
"formidable": "1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"files": [
|
"files": [
|
||||||
"flaska.mjs",
|
"flaska.mjs",
|
||||||
"README.md",
|
"README.md",
|
||||||
|
|
16
test.mjs
Normal file
16
test.mjs
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
import { Flaska } from './flaska.mjs'
|
||||||
|
|
||||||
|
const port = 51024
|
||||||
|
const flaska = new Flaska({}, )
|
||||||
|
|
||||||
|
flaska.get('/', function(ctx) {
|
||||||
|
ctx.body = { status: true }
|
||||||
|
})
|
||||||
|
|
||||||
|
flaska.get('/error', function(ctx) {
|
||||||
|
process.exit(1)
|
||||||
|
})
|
||||||
|
|
||||||
|
flaska.listen(port, function() {
|
||||||
|
console.log('listening on port', port)
|
||||||
|
})
|
|
@ -1,7 +1,4 @@
|
||||||
import fs from 'fs/promises'
|
|
||||||
import path from 'path'
|
|
||||||
import http from 'http'
|
import http from 'http'
|
||||||
import stream from 'stream'
|
|
||||||
import { URL } from 'url'
|
import { URL } from 'url'
|
||||||
import { defaults } from './helper.mjs'
|
import { defaults } from './helper.mjs'
|
||||||
|
|
||||||
|
@ -10,7 +7,7 @@ export default function Client(port, opts) {
|
||||||
this.prefix = `http://localhost:${port}`
|
this.prefix = `http://localhost:${port}`
|
||||||
}
|
}
|
||||||
|
|
||||||
Client.prototype.customRequest = function(method = 'GET', path, body, options = {}) {
|
Client.prototype.customRequest = function(method = 'GET', path, body, options) {
|
||||||
if (path.slice(0, 4) !== 'http') {
|
if (path.slice(0, 4) !== 'http') {
|
||||||
path = this.prefix + path
|
path = this.prefix + path
|
||||||
}
|
}
|
||||||
|
@ -29,54 +26,31 @@ Client.prototype.customRequest = function(method = 'GET', path, body, options =
|
||||||
headers: {},
|
headers: {},
|
||||||
}))
|
}))
|
||||||
|
|
||||||
if (options.agent) {
|
const req = http.request(opts)
|
||||||
opts.agent = options.agent
|
if (body) {
|
||||||
|
req.write(body)
|
||||||
}
|
}
|
||||||
|
|
||||||
// opts.agent = agent
|
req.on('error', reject)
|
||||||
|
|
||||||
const req = http.request(opts)
|
|
||||||
|
|
||||||
req.on('error', (err) => {
|
|
||||||
reject(err)
|
|
||||||
})
|
|
||||||
req.on('timeout', function() {
|
req.on('timeout', function() {
|
||||||
req.destroy()
|
|
||||||
reject(new Error(`Request ${method} ${path} timed out`))
|
reject(new Error(`Request ${method} ${path} timed out`))
|
||||||
})
|
})
|
||||||
req.on('response', res => {
|
req.on('response', res => {
|
||||||
let output = ''
|
|
||||||
if (options.toPipe) {
|
|
||||||
return stream.pipeline(res, options.toPipe, function(err) {
|
|
||||||
if (err) {
|
|
||||||
return reject(err)
|
|
||||||
}
|
|
||||||
resolve()
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
res.setEncoding('utf8')
|
res.setEncoding('utf8')
|
||||||
|
let output = ''
|
||||||
|
|
||||||
res.on('data', function (chunk) {
|
res.on('data', function (chunk) {
|
||||||
output += chunk.toString()
|
output += chunk.toString()
|
||||||
})
|
})
|
||||||
}
|
|
||||||
|
|
||||||
res.on('end', function () {
|
res.on('end', function () {
|
||||||
if (options.getRaw) {
|
|
||||||
output = {
|
|
||||||
status: res.statusCode,
|
|
||||||
data: output,
|
|
||||||
headers: res.headers,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (!output) return resolve(null)
|
if (!output) return resolve(null)
|
||||||
try {
|
try {
|
||||||
output = JSON.parse(output)
|
output = JSON.parse(output)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return reject(new Error(`${e.message} while decoding: ${output}`))
|
return reject(new Error(`${e.message} while decoding: ${output}`))
|
||||||
}
|
}
|
||||||
}
|
if (output.status && typeof(output.status) === 'number') {
|
||||||
if (!options.getRaw && output.status && typeof(output.status) === 'number') {
|
|
||||||
let err = new Error(`Request failed [${output.status}]: ${output.message}`)
|
let err = new Error(`Request failed [${output.status}]: ${output.message}`)
|
||||||
err.body = output
|
err.body = output
|
||||||
return reject(err)
|
return reject(err)
|
||||||
|
@ -84,14 +58,6 @@ Client.prototype.customRequest = function(method = 'GET', path, body, options =
|
||||||
resolve(output)
|
resolve(output)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
if (opts.returnRequest) {
|
|
||||||
return resolve(req)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (body) {
|
|
||||||
req.write(body)
|
|
||||||
}
|
|
||||||
req.end()
|
req.end()
|
||||||
return req
|
return req
|
||||||
})
|
})
|
||||||
|
@ -126,47 +92,27 @@ const random = (length = 8) => {
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
Client.prototype.upload = function(url, files, method = 'POST', body = {}, overrideType = null) {
|
Client.prototype.upload = function(url, file, method = 'POST', body = {}) {
|
||||||
const boundary = `---------${random(32)}`
|
|
||||||
const crlf = '\r\n'
|
|
||||||
let upload = files
|
|
||||||
|
|
||||||
if (typeof(upload) === 'string') {
|
|
||||||
upload = {
|
|
||||||
file: files
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let keys = Object.keys(upload)
|
|
||||||
let uploadBody = []
|
|
||||||
|
|
||||||
return Promise.all(keys.map(key => {
|
|
||||||
let file = upload[key]
|
|
||||||
return fs.readFile(file).then(data => {
|
return fs.readFile(file).then(data => {
|
||||||
|
const crlf = '\r\n'
|
||||||
const filename = path.basename(file)
|
const filename = path.basename(file)
|
||||||
|
const boundary = `---------${random(32)}`
|
||||||
|
|
||||||
uploadBody.push(Buffer.from(
|
const multipartBody = Buffer.concat([
|
||||||
`${crlf}--${boundary}${crlf}`
|
Buffer.from(
|
||||||
+ `Content-Disposition: form-data; name="${key}"; filename="${filename}"`
|
`${crlf}--${boundary}${crlf}` +
|
||||||
+ (overrideType ? crlf + `Content-Type: ${overrideType}`: '')
|
`Content-Disposition: form-data; name="file"; filename="${filename}"` + crlf + crlf
|
||||||
+ crlf
|
),
|
||||||
+ crlf
|
data,
|
||||||
))
|
|
||||||
uploadBody.push(data)
|
|
||||||
})
|
|
||||||
}))
|
|
||||||
.then(() => {
|
|
||||||
uploadBody.push(
|
|
||||||
Buffer.concat(Object.keys(body).map(function(key) {
|
Buffer.concat(Object.keys(body).map(function(key) {
|
||||||
return Buffer.from(''
|
return Buffer.from(''
|
||||||
+ `${crlf}--${boundary}${crlf}`
|
+ `${crlf}--${boundary}${crlf}`
|
||||||
+ `Content-Disposition: form-data; name="${key}"` + crlf + crlf
|
+ `Content-Disposition: form-data; name="${key}"` + crlf + crlf
|
||||||
+ JSON.stringify(body[key])
|
+ JSON.stringify(body[key])
|
||||||
)
|
)
|
||||||
}))
|
})),
|
||||||
)
|
Buffer.from(`${crlf}--${boundary}--`),
|
||||||
uploadBody.push(Buffer.from(`${crlf}--${boundary}--`))
|
])
|
||||||
|
|
||||||
let multipartBody = Buffer.concat(uploadBody)
|
|
||||||
|
|
||||||
return this.customRequest(method, url, multipartBody, {
|
return this.customRequest(method, url, multipartBody, {
|
||||||
timeout: 5000,
|
timeout: 5000,
|
||||||
|
|
|
@ -1,498 +0,0 @@
|
||||||
import { Eltro as t, assert, stub } from 'eltro'
|
|
||||||
import { FileResponse } from '../flaska.mjs'
|
|
||||||
import { createCtx } from './helper.mjs'
|
|
||||||
|
|
||||||
t.test('should add path and stat', function() {
|
|
||||||
const assertPath = 'some/path/here'
|
|
||||||
const assertStat = { a: 1 }
|
|
||||||
|
|
||||||
let fileRes = new FileResponse(assertPath, assertStat)
|
|
||||||
|
|
||||||
assert.strictEqual(fileRes.filepath, assertPath)
|
|
||||||
assert.strictEqual(fileRes.stat, assertStat)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.describe('CreateReader()', function() {
|
|
||||||
t.describe('return fileReader', function() {
|
|
||||||
const assertPath = '/some/path/here.png'
|
|
||||||
const assertBody = { a: 1 }
|
|
||||||
const assertSize = 12498
|
|
||||||
const assertmTime = new Date('2021-04-02T11:22:33.777')
|
|
||||||
const roundedModified = new Date('2021-04-02T11:22:33')
|
|
||||||
const assertIno = 3569723027
|
|
||||||
const assertEtag = `"${assertIno}-${assertSize}-${assertmTime.getTime()}"`
|
|
||||||
const stat = {
|
|
||||||
size: assertSize,
|
|
||||||
mtime: assertmTime,
|
|
||||||
ino: assertIno,
|
|
||||||
}
|
|
||||||
|
|
||||||
let tests = [
|
|
||||||
[
|
|
||||||
'if no range',
|
|
||||||
function() {},
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'if range start is higher than end',
|
|
||||||
function(headers) { headers['range'] = 'bytes=2000-1000' },
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'if pre-condition if-match passes',
|
|
||||||
function(headers) { headers['if-match'] = assertEtag },
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'if pre-condition if-unmodified-since passes',
|
|
||||||
function(headers) { headers['if-unmodified-since'] = roundedModified.toUTCString(); },
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'if both pre-condition pass',
|
|
||||||
function(headers) { headers['if-unmodified-since'] = new Date(roundedModified.getTime() + 1000).toUTCString(); headers['if-match'] = assertEtag },
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'if range is specified but if-range etag does not match',
|
|
||||||
function(headers) {
|
|
||||||
headers['if-range'] = `"${assertIno}-${assertSize}-${assertmTime.getTime() + 1}"`
|
|
||||||
headers['range'] = 'bytes=1000-2000'
|
|
||||||
}
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'if range is specified but if-range modified by is older',
|
|
||||||
function(headers) {
|
|
||||||
headers['if-range'] = `${new Date(roundedModified.getTime() - 1000).toUTCString()}`
|
|
||||||
headers['range'] = 'bytes=1000-2000'
|
|
||||||
}
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'if range is specified but if-range is garbage',
|
|
||||||
function(headers) {
|
|
||||||
headers['if-range'] = `asdf`
|
|
||||||
headers['range'] = 'bytes=1000-2000'
|
|
||||||
}
|
|
||||||
],
|
|
||||||
]
|
|
||||||
|
|
||||||
tests.forEach(function(test){
|
|
||||||
t.test(test[0], function() {
|
|
||||||
|
|
||||||
const stubCreateReadStream = stub().returns(assertBody)
|
|
||||||
let ctx = createCtx()
|
|
||||||
test[1](ctx.req.headers)
|
|
||||||
ctx.body = new FileResponse(assertPath, stat)
|
|
||||||
let checkBody = ctx.body.handleRequest(ctx, { createReadStream: stubCreateReadStream })
|
|
||||||
|
|
||||||
assert.strictEqual(checkBody, assertBody)
|
|
||||||
assert.strictEqual(stubCreateReadStream.firstCall[0], assertPath)
|
|
||||||
assert.deepStrictEqual(stubCreateReadStream.firstCall[1], {})
|
|
||||||
assert.strictEqual(ctx.headers['Etag'], assertEtag)
|
|
||||||
assert.strictEqual(ctx.headers['Last-Modified'], assertmTime.toUTCString())
|
|
||||||
assert.strictEqual(ctx.headers['Content-Length'], assertSize)
|
|
||||||
assert.strictEqual(ctx.type, 'image/png')
|
|
||||||
assert.notOk(ctx.headers['Content-Range'])
|
|
||||||
assert.strictEqual(ctx.status, 200)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
t.test('return fileReader with proper length with range', function() {
|
|
||||||
const assertPath = '/some/path/here.jpg'
|
|
||||||
const assertBody = { a: 1 }
|
|
||||||
const assertSize = 2000
|
|
||||||
const assertmTime = new Date('2021-04-03T11:22:33.777')
|
|
||||||
const assertIno = 3569723027
|
|
||||||
const stat = {
|
|
||||||
size: assertSize,
|
|
||||||
mtime: assertmTime,
|
|
||||||
ino: assertIno,
|
|
||||||
}
|
|
||||||
const stubCreateReadStream = stub().returns(assertBody)
|
|
||||||
let ctx = createCtx()
|
|
||||||
ctx.req.headers['range'] = 'bytes=0-1023'
|
|
||||||
ctx.body = new FileResponse(assertPath, stat)
|
|
||||||
let checkBody = ctx.body.handleRequest(ctx, { createReadStream: stubCreateReadStream })
|
|
||||||
|
|
||||||
assert.strictEqual(checkBody, assertBody)
|
|
||||||
assert.strictEqual(stubCreateReadStream.firstCall[0], assertPath)
|
|
||||||
assert.strictEqual(stubCreateReadStream.firstCall[1].start, 0)
|
|
||||||
assert.strictEqual(stubCreateReadStream.firstCall[1].end, 1023)
|
|
||||||
assert.strictEqual(ctx.headers['Etag'], `"${assertIno}-${assertSize}-${assertmTime.getTime()}"`)
|
|
||||||
assert.strictEqual(ctx.headers['Last-Modified'], assertmTime.toUTCString())
|
|
||||||
assert.strictEqual(ctx.headers['Content-Length'], 1024)
|
|
||||||
assert.strictEqual(ctx.type, 'image/jpeg')
|
|
||||||
assert.strictEqual(ctx.headers['Content-Range'], `0-1023/${assertSize}`)
|
|
||||||
assert.strictEqual(ctx.status, 206)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.test('should not call createReadStream if HEAD request', function() {
|
|
||||||
const assertPath = '/some/path/here.png'
|
|
||||||
const assertNotBody = { a: 1 }
|
|
||||||
const assertSize = 12498
|
|
||||||
const assertmTime = new Date('2021-04-02T11:22:33.777')
|
|
||||||
const roundedModified = new Date('2021-04-02T11:22:33')
|
|
||||||
const assertIno = 3569723027
|
|
||||||
const assertEtag = `"${assertIno}-${assertSize}-${assertmTime.getTime()}"`
|
|
||||||
const stat = {
|
|
||||||
size: assertSize,
|
|
||||||
mtime: assertmTime,
|
|
||||||
ino: assertIno,
|
|
||||||
}
|
|
||||||
|
|
||||||
let tests = [
|
|
||||||
[
|
|
||||||
'if no range',
|
|
||||||
function() {},
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'if range start is higher than end',
|
|
||||||
function(headers) { headers['range'] = 'bytes=2000-1000' },
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'if pre-condition if-match passes',
|
|
||||||
function(headers) { headers['if-match'] = assertEtag },
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'if pre-condition if-unmodified-since passes',
|
|
||||||
function(headers) { headers['if-unmodified-since'] = roundedModified.toUTCString(); },
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'if both pre-condition pass',
|
|
||||||
function(headers) { headers['if-unmodified-since'] = new Date(roundedModified.getTime() + 1000).toUTCString(); headers['if-match'] = assertEtag },
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'if range is specified but if-range etag does not match',
|
|
||||||
function(headers) {
|
|
||||||
headers['if-range'] = `"${assertIno}-${assertSize}-${assertmTime.getTime() + 1}"`
|
|
||||||
headers['range'] = 'bytes=1000-2000'
|
|
||||||
}
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'if range is specified but if-range modified by is older',
|
|
||||||
function(headers) {
|
|
||||||
headers['if-range'] = `${new Date(roundedModified.getTime() - 1000).toUTCString()}`
|
|
||||||
headers['range'] = 'bytes=1000-2000'
|
|
||||||
}
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'if range is specified but if-range is garbage',
|
|
||||||
function(headers) {
|
|
||||||
headers['if-range'] = `asdf`
|
|
||||||
headers['range'] = 'bytes=1000-2000'
|
|
||||||
}
|
|
||||||
],
|
|
||||||
]
|
|
||||||
|
|
||||||
tests.forEach(function(test){
|
|
||||||
const stubCreateReadStream = stub().returns(assertNotBody)
|
|
||||||
let ctx = createCtx({
|
|
||||||
method: 'HEAD',
|
|
||||||
})
|
|
||||||
test[1](ctx.req.headers)
|
|
||||||
ctx.body = new FileResponse(assertPath, stat)
|
|
||||||
let checkBody = ctx.body.handleRequest(ctx, { createReadStream: stubCreateReadStream })
|
|
||||||
|
|
||||||
assert.strictEqual(checkBody, null)
|
|
||||||
assert.notOk(stubCreateReadStream.called)
|
|
||||||
assert.strictEqual(ctx.headers['Etag'], assertEtag)
|
|
||||||
assert.strictEqual(ctx.headers['Last-Modified'], assertmTime.toUTCString())
|
|
||||||
assert.strictEqual(ctx.headers['Content-Length'], assertSize)
|
|
||||||
assert.strictEqual(ctx.type, 'image/png')
|
|
||||||
assert.notOk(ctx.headers['Content-Range'])
|
|
||||||
assert.strictEqual(ctx.status, 200)
|
|
||||||
})
|
|
||||||
|
|
||||||
const testStart = [0, 1000, 1999]
|
|
||||||
testStart.forEach(function(start) {
|
|
||||||
const stubCreateReadStream = stub()
|
|
||||||
let ctx = createCtx({
|
|
||||||
method: 'HEAD',
|
|
||||||
})
|
|
||||||
ctx.req.headers['range'] = 'bytes=' + start + '-'
|
|
||||||
ctx.body = new FileResponse('file.png', stat)
|
|
||||||
let checkBody = ctx.body.handleRequest(ctx, { createReadStream: stubCreateReadStream })
|
|
||||||
|
|
||||||
assert.strictEqual(checkBody, null)
|
|
||||||
assert.notOk(stubCreateReadStream.called)
|
|
||||||
assert.strictEqual(ctx.headers['Content-Length'], assertSize - start)
|
|
||||||
assert.strictEqual(ctx.headers['Content-Range'], `${start}-${assertSize - 1}/${assertSize}`)
|
|
||||||
assert.strictEqual(ctx.status, 206)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
t.test('return fileReader with proper length with range and if-range passes', function() {
|
|
||||||
const assertPath = '/some/path/here.jpg'
|
|
||||||
const assertBody = { a: 1 }
|
|
||||||
const assertSize = 2000
|
|
||||||
const assertmTime = new Date('2021-04-03T11:22:33.777')
|
|
||||||
const roundedModified = new Date('2021-04-03T11:22:33')
|
|
||||||
const assertIno = 3569723027
|
|
||||||
const stat = {
|
|
||||||
size: assertSize,
|
|
||||||
mtime: assertmTime,
|
|
||||||
ino: assertIno,
|
|
||||||
}
|
|
||||||
const assertEtag = `"${assertIno}-${assertSize}-${assertmTime.getTime()}"`
|
|
||||||
let tests = [
|
|
||||||
function(headers) { headers['if-range'] = roundedModified.toUTCString() },
|
|
||||||
function(headers) { headers['if-range'] = assertEtag },
|
|
||||||
]
|
|
||||||
tests.forEach(function(check, i) {
|
|
||||||
const stubCreateReadStream = stub().returns(assertBody)
|
|
||||||
let ctx = createCtx()
|
|
||||||
ctx.req.headers['range'] = 'bytes=0-1023'
|
|
||||||
check(ctx.req.headers)
|
|
||||||
ctx.body = new FileResponse(assertPath, stat)
|
|
||||||
let checkBody = ctx.body.handleRequest(ctx, { createReadStream: stubCreateReadStream })
|
|
||||||
|
|
||||||
assert.strictEqual(checkBody, assertBody)
|
|
||||||
assert.strictEqual(stubCreateReadStream.firstCall[0], assertPath)
|
|
||||||
assert.strictEqual(stubCreateReadStream.firstCall[1].start, 0, `start missing in test ${i + 1}`)
|
|
||||||
assert.strictEqual(stubCreateReadStream.firstCall[1].end, 1023)
|
|
||||||
assert.strictEqual(ctx.headers['Etag'], `"${assertIno}-${assertSize}-${assertmTime.getTime()}"`)
|
|
||||||
assert.strictEqual(ctx.headers['Last-Modified'], assertmTime.toUTCString())
|
|
||||||
assert.strictEqual(ctx.headers['Content-Length'], 1024)
|
|
||||||
assert.strictEqual(ctx.type, 'image/jpeg')
|
|
||||||
assert.strictEqual(ctx.headers['Content-Range'], `0-1023/${assertSize}`)
|
|
||||||
assert.strictEqual(ctx.status, 206)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
t.test('return fileReader with proper start if only start is specified', function() {
|
|
||||||
const assertSize = 2000
|
|
||||||
const stat = {
|
|
||||||
size: assertSize,
|
|
||||||
mtime: new Date('2021-04-03T11:22:33.777'),
|
|
||||||
ino: 111,
|
|
||||||
}
|
|
||||||
|
|
||||||
const testStart = [0, 1000, 1999]
|
|
||||||
testStart.forEach(function(start) {
|
|
||||||
const stubCreateReadStream = stub()
|
|
||||||
let ctx = createCtx()
|
|
||||||
ctx.req.headers['range'] = 'bytes=' + start + '-'
|
|
||||||
ctx.body = new FileResponse('file.png', stat)
|
|
||||||
ctx.body.handleRequest(ctx, { createReadStream: stubCreateReadStream })
|
|
||||||
|
|
||||||
assert.strictEqual(stubCreateReadStream.firstCall[1].start, start)
|
|
||||||
assert.strictEqual(stubCreateReadStream.firstCall[1].end, assertSize - 1)
|
|
||||||
assert.strictEqual(ctx.headers['Content-Length'], assertSize - start)
|
|
||||||
assert.strictEqual(ctx.headers['Content-Range'], `${start}-${assertSize - 1}/${assertSize}`)
|
|
||||||
assert.strictEqual(ctx.status, 206)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
t.test('should default to end if end overflows but start is valid', function() {
|
|
||||||
const assertSize = 2000
|
|
||||||
const stat = {
|
|
||||||
size: assertSize,
|
|
||||||
mtime: new Date('2021-04-03T11:22:33.777'),
|
|
||||||
ino: 111,
|
|
||||||
}
|
|
||||||
|
|
||||||
const testEnd = [2000, 3000, 99999]
|
|
||||||
testEnd.forEach(function(end) {
|
|
||||||
const stubCreateReadStream = stub()
|
|
||||||
let ctx = createCtx()
|
|
||||||
ctx.req.headers['range'] = 'bytes=1000-' + end
|
|
||||||
ctx.body = new FileResponse('file.png', stat)
|
|
||||||
ctx.body.handleRequest(ctx, { createReadStream: stubCreateReadStream })
|
|
||||||
|
|
||||||
assert.strictEqual(stubCreateReadStream.firstCall[1].start, 1000)
|
|
||||||
assert.strictEqual(stubCreateReadStream.firstCall[1].end, 1999)
|
|
||||||
assert.strictEqual(ctx.headers['Content-Length'], 1000)
|
|
||||||
assert.strictEqual(ctx.headers['Content-Range'], `1000-${assertSize - 1}/${assertSize}`)
|
|
||||||
assert.strictEqual(ctx.status, 206)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
t.test('should throw 416 if start is out of range', function() {
|
|
||||||
const stubCreateReadStream = stub()
|
|
||||||
let tests = [1000, 2000, 9999]
|
|
||||||
|
|
||||||
tests.forEach(function(start) {
|
|
||||||
let ctx = createCtx()
|
|
||||||
ctx.body = new FileResponse('file.png', {
|
|
||||||
size: 1000,
|
|
||||||
mtime: new Date('2021-04-03T11:22:33.777'),
|
|
||||||
ino: 111,
|
|
||||||
})
|
|
||||||
ctx.req.headers['range'] = `bytes=${start}-`
|
|
||||||
|
|
||||||
assert.throws(function() {
|
|
||||||
ctx.body.handleRequest(ctx, { createReadStream: stubCreateReadStream })
|
|
||||||
}, function(err) {
|
|
||||||
assert.strictEqual(err.status, 416)
|
|
||||||
assert.match(err.message, new RegExp(1000))
|
|
||||||
assert.match(err.message, new RegExp(start))
|
|
||||||
|
|
||||||
assert.ok(ctx.headers['Etag'])
|
|
||||||
assert.notOk(stubCreateReadStream.called)
|
|
||||||
assert.notOk(ctx.headers['Last-Modified'])
|
|
||||||
assert.notOk(ctx.headers['Content-Length'])
|
|
||||||
assert.notOk(ctx.type)
|
|
||||||
assert.notOk(ctx.headers['Content-Range'])
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
t.test('should return 304 if etag is found', function() {
|
|
||||||
const assertPath = '/some/path/here.png'
|
|
||||||
const assertNotBody = { a: 1 }
|
|
||||||
const assertSize = 12498
|
|
||||||
const assertmTime = new Date('2021-04-02T11:22:33.777')
|
|
||||||
const assertIno = 3569723027
|
|
||||||
const stat = {
|
|
||||||
size: assertSize,
|
|
||||||
mtime: assertmTime,
|
|
||||||
ino: assertIno,
|
|
||||||
}
|
|
||||||
const assertEtag = `"${assertIno}-${assertSize}-${assertmTime.getTime()}"`
|
|
||||||
|
|
||||||
let tests = [
|
|
||||||
assertEtag,
|
|
||||||
`"asdf", "herp", ${assertEtag}`,
|
|
||||||
`"asdf", ${assertEtag}, "bla"`,
|
|
||||||
`${assertEtag}, "hello world"`,
|
|
||||||
`"asdf","herp",${assertEtag}`,
|
|
||||||
`"asdf",${assertEtag},"bla"`,
|
|
||||||
`${assertEtag},"hello world"`,
|
|
||||||
]
|
|
||||||
tests.forEach(function(check) {
|
|
||||||
const stubCreateReadStream = stub().returns(assertNotBody)
|
|
||||||
let ctx = createCtx()
|
|
||||||
ctx.req.headers['if-none-match'] = check
|
|
||||||
ctx.body = new FileResponse(assertPath, stat)
|
|
||||||
let checkBody = ctx.body.handleRequest(ctx, { createReadStream: stubCreateReadStream })
|
|
||||||
|
|
||||||
assert.strictEqual(checkBody, null)
|
|
||||||
assert.strictEqual(ctx.status, 304)
|
|
||||||
assert.strictEqual(ctx.headers['Etag'], `"${assertIno}-${assertSize}-${assertmTime.getTime()}"`)
|
|
||||||
|
|
||||||
assert.notOk(stubCreateReadStream.called)
|
|
||||||
|
|
||||||
assert.notOk(ctx.headers['Last-Modified'])
|
|
||||||
assert.notOk(ctx.headers['Content-Length'])
|
|
||||||
assert.notOk(ctx.type)
|
|
||||||
assert.notOk(ctx.headers['Content-Range'])
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
t.test('should return 304 if-last-modified is found', function() {
|
|
||||||
const assertPath = '/some/path/here.png'
|
|
||||||
const assertNotBody = { a: 1 }
|
|
||||||
const assertSize = 12498
|
|
||||||
const assertmTime = new Date('2021-04-02T11:22:33.777')
|
|
||||||
const assertIno = 3569723027
|
|
||||||
const stat = {
|
|
||||||
size: assertSize,
|
|
||||||
mtime: assertmTime,
|
|
||||||
ino: assertIno,
|
|
||||||
}
|
|
||||||
|
|
||||||
let tests = [
|
|
||||||
assertmTime.toUTCString(),
|
|
||||||
'Fri, 02 Apr 2021 11:22:33 GMT',
|
|
||||||
'Fri, 02 Apr 2021 11:22:34 GMT',
|
|
||||||
'Fri, 02 Apr 2022 11:22:32 GMT',
|
|
||||||
]
|
|
||||||
|
|
||||||
tests.forEach(function(check) {
|
|
||||||
const stubCreateReadStream = stub().returns(assertNotBody)
|
|
||||||
let ctx = createCtx()
|
|
||||||
ctx.body = new FileResponse(assertPath, stat)
|
|
||||||
ctx.req.headers['if-modified-since'] = assertmTime.toUTCString()
|
|
||||||
let checkBody = ctx.body.handleRequest(ctx, { createReadStream: stubCreateReadStream })
|
|
||||||
|
|
||||||
assert.strictEqual(checkBody, null)
|
|
||||||
assert.strictEqual(ctx.status, 304)
|
|
||||||
assert.strictEqual(ctx.headers['Etag'], `"${assertIno}-${assertSize}-${assertmTime.getTime()}"`)
|
|
||||||
|
|
||||||
assert.notOk(stubCreateReadStream.called)
|
|
||||||
assert.notOk(ctx.headers['Last-Modified'])
|
|
||||||
assert.notOk(ctx.headers['Content-Length'])
|
|
||||||
assert.notOk(ctx.type)
|
|
||||||
assert.notOk(ctx.headers['Content-Range'])
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
t.test('should return 200 if etag or modified-by is not found', function() {
|
|
||||||
const assertPath = '/some/path/here.png'
|
|
||||||
const assertBody = { a: 1 }
|
|
||||||
const assertSize = 12498
|
|
||||||
const assertmTime = new Date('2021-04-02T11:22:33.777')
|
|
||||||
const assertIno = 3569723027
|
|
||||||
const stat = {
|
|
||||||
size: assertSize,
|
|
||||||
mtime: assertmTime,
|
|
||||||
ino: assertIno,
|
|
||||||
}
|
|
||||||
const assertEtag = `"${assertIno}-${assertSize}-${assertmTime.getTime()}"`
|
|
||||||
const roundedModified = new Date('2021-04-02T11:22:33')
|
|
||||||
|
|
||||||
let tests = [
|
|
||||||
function(headers) { headers['if-none-match'] = `"${assertIno}-${assertSize}-${assertmTime.getTime() + 1}"`; },
|
|
||||||
function(headers) { headers['if-none-match'] = `"${assertIno}-${assertSize}-${assertmTime.getTime() - 1}"`; },
|
|
||||||
function(headers) { headers['if-none-match'] = `"asdf"`; },
|
|
||||||
function(headers) { headers['if-none-match'] = `"asdf"`; headers['if-modified-since'] = roundedModified.toUTCString(); },
|
|
||||||
function(headers) { headers['if-modified-since'] = new Date(roundedModified.getTime() - 1).toUTCString(); },
|
|
||||||
function(headers) { headers['if-modified-since'] = 'asdfs' },
|
|
||||||
]
|
|
||||||
tests.forEach(function(check, i) {
|
|
||||||
const stubCreateReadStream = stub().returns(assertBody)
|
|
||||||
let ctx = createCtx()
|
|
||||||
check(ctx.req.headers)
|
|
||||||
ctx.body = new FileResponse(assertPath, stat)
|
|
||||||
let checkBody = ctx.body.handleRequest(ctx, { createReadStream: stubCreateReadStream })
|
|
||||||
|
|
||||||
assert.strictEqual(checkBody, assertBody, 'missing body in test ' + (i + 1))
|
|
||||||
assert.strictEqual(stubCreateReadStream.firstCall[0], assertPath)
|
|
||||||
assert.deepStrictEqual(stubCreateReadStream.firstCall[1], {})
|
|
||||||
assert.strictEqual(ctx.headers['Etag'], assertEtag)
|
|
||||||
assert.strictEqual(ctx.headers['Last-Modified'], assertmTime.toUTCString())
|
|
||||||
assert.strictEqual(ctx.headers['Content-Length'], assertSize)
|
|
||||||
assert.strictEqual(ctx.type, 'image/png')
|
|
||||||
assert.notOk(ctx.headers['Content-Range'])
|
|
||||||
assert.strictEqual(ctx.status, 200)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
t.test('should return 412 if precondition fails', function() {
|
|
||||||
const stubCreateReadStream = stub()
|
|
||||||
const assertmTime = new Date('2021-04-02T11:22:33.777')
|
|
||||||
const roundedModified = new Date('2021-04-02T11:22:33')
|
|
||||||
const assertIno = 3569723027
|
|
||||||
const assertSize = 12498
|
|
||||||
const assertEtag = `"${assertIno}-${assertSize}-${assertmTime.getTime()}"`
|
|
||||||
const stat = {
|
|
||||||
size: assertSize,
|
|
||||||
mtime: assertmTime,
|
|
||||||
ino: assertIno,
|
|
||||||
}
|
|
||||||
let tests = [
|
|
||||||
function(headers) { headers['if-match'] = `"${assertIno}-${assertSize}-${assertmTime.getTime() + 1}"` },
|
|
||||||
function(headers) { headers['if-match'] = `"${assertIno}-${assertSize}-${assertmTime.getTime() - 1}"` },
|
|
||||||
function(headers) { headers['if-match'] = `asdf` },
|
|
||||||
function(headers) { headers['if-unmodified-since'] = new Date(roundedModified.getTime() - 1).toUTCString(); },
|
|
||||||
function(headers) { headers['if-unmodified-since'] = 'asdf'; },
|
|
||||||
function(headers) { headers['if-match'] = assertEtag; headers['if-unmodified-since'] = 'asdf'; },
|
|
||||||
function(headers) { headers['if-match'] = assertEtag; headers['if-unmodified-since'] = new Date(roundedModified.getTime() - 1000).toUTCString(); },
|
|
||||||
function(headers) { headers['if-match'] = '"bla"'; headers['if-unmodified-since'] = roundedModified.toUTCString(); },
|
|
||||||
]
|
|
||||||
tests.forEach(function(check) {
|
|
||||||
let ctx = createCtx()
|
|
||||||
ctx.body = new FileResponse('file.png', stat)
|
|
||||||
check(ctx.req.headers)
|
|
||||||
|
|
||||||
assert.throws(function() {
|
|
||||||
ctx.body.handleRequest(ctx, { createReadStream: stubCreateReadStream })
|
|
||||||
}, function(err) {
|
|
||||||
assert.strictEqual(err.status, 412)
|
|
||||||
assert.notOk(stubCreateReadStream.called)
|
|
||||||
assert.notOk(ctx.headers['Last-Modified'])
|
|
||||||
assert.notOk(ctx.headers['Content-Length'])
|
|
||||||
assert.notOk(ctx.type)
|
|
||||||
assert.notOk(ctx.headers['Content-Range'])
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
|
@ -1,10 +1,9 @@
|
||||||
import { Eltro as t, assert, stub, spy } from 'eltro'
|
import { Eltro as t, assert, stub, spy } from 'eltro'
|
||||||
import { Flaska, HttpError } from '../flaska.mjs'
|
import { Flaska } from '../flaska.mjs'
|
||||||
import { createCtx, fakeHttp } from './helper.mjs'
|
import { createCtx, fakeHttp } from './helper.mjs'
|
||||||
|
|
||||||
const faker = fakeHttp()
|
const faker = fakeHttp()
|
||||||
|
|
||||||
t.describe('#constructor', function() {
|
|
||||||
t.test('should be able to override the http', function() {
|
t.test('should be able to override the http', function() {
|
||||||
let flaska = new Flaska({}, faker)
|
let flaska = new Flaska({}, faker)
|
||||||
assert.strictEqual(flaska.http, faker)
|
assert.strictEqual(flaska.http, faker)
|
||||||
|
@ -26,272 +25,6 @@ t.describe('#constructor', function() {
|
||||||
assert.strictEqual(typeof(flaska.patch), 'function')
|
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.deepEqual(
|
|
||||||
Object.keys(ctx.headers).sort(),
|
|
||||||
['Server','X-Content-Type-Options','Content-Security-Policy','Cross-Origin-Opener-Policy','Cross-Origin-Resource-Policy','Cross-Origin-Embedder-Policy','Date'].sort()
|
|
||||||
)
|
|
||||||
|
|
||||||
assert.strictEqual(ctx.headers['Server'], 'Flaska')
|
|
||||||
assert.strictEqual(ctx.headers['X-Content-Type-Options'], 'nosniff')
|
|
||||||
assert.strictEqual(ctx.headers['Content-Security-Policy'], `default-src 'self'; style-src 'self' 'unsafe-inline'; img-src * data: blob:; font-src 'self' data:; object-src 'none'; frame-ancestors 'none'`)
|
|
||||||
assert.strictEqual(ctx.headers['Cross-Origin-Opener-Policy'], 'same-origin')
|
|
||||||
assert.strictEqual(ctx.headers['Cross-Origin-Resource-Policy'], 'same-origin')
|
|
||||||
assert.strictEqual(ctx.headers['Cross-Origin-Embedder-Policy'], 'require-corp')
|
|
||||||
assert.ok(new Date(ctx.headers['Date']).getDate())
|
|
||||||
|
|
||||||
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',
|
|
||||||
'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)
|
|
||||||
|
|
||||||
let keys = Object.keys(defaultHeaders)
|
|
||||||
|
|
||||||
assert.strictEqual(Object.keys(ctx.headers).length, keys.length + 1)
|
|
||||||
for (let key of keys) {
|
|
||||||
assert.strictEqual(ctx.headers[key], defaultHeaders[key])
|
|
||||||
}
|
|
||||||
assert.ok(ctx.headers['Date'])
|
|
||||||
|
|
||||||
assert.strictEqual(flaska._after.length, 0)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.test('should have before ready setting headers on context if appendHeaders is specified', function() {
|
|
||||||
const appendHeaders = {
|
|
||||||
'Server': 'nginx/1.16.1',
|
|
||||||
'Herp': 'Derp',
|
|
||||||
}
|
|
||||||
let flaska = new Flaska({
|
|
||||||
appendHeaders: appendHeaders,
|
|
||||||
}, faker)
|
|
||||||
assert.strictEqual(flaska._before.length, 1)
|
|
||||||
|
|
||||||
let ctx = {}
|
|
||||||
|
|
||||||
flaska._before[0](ctx)
|
|
||||||
|
|
||||||
assert.deepEqual(
|
|
||||||
Object.keys(ctx.headers).sort(),
|
|
||||||
['Server', 'Herp', 'X-Content-Type-Options','Content-Security-Policy','Cross-Origin-Opener-Policy','Cross-Origin-Resource-Policy','Cross-Origin-Embedder-Policy','Date'].sort()
|
|
||||||
)
|
|
||||||
|
|
||||||
assert.notStrictEqual(ctx.headers['Server'], 'Flaska')
|
|
||||||
assert.strictEqual(ctx.headers['Server'], appendHeaders.Server)
|
|
||||||
assert.strictEqual(ctx.headers['Herp'], 'Derp')
|
|
||||||
assert.strictEqual(ctx.headers['X-Content-Type-Options'], 'nosniff')
|
|
||||||
assert.strictEqual(ctx.headers['Content-Security-Policy'], `default-src 'self'; style-src 'self' 'unsafe-inline'; img-src * data: blob:; font-src 'self' data:; object-src 'none'; frame-ancestors 'none'`)
|
|
||||||
assert.strictEqual(ctx.headers['Cross-Origin-Opener-Policy'], 'same-origin')
|
|
||||||
assert.strictEqual(ctx.headers['Cross-Origin-Resource-Policy'], 'same-origin')
|
|
||||||
assert.strictEqual(ctx.headers['Cross-Origin-Embedder-Policy'], 'require-corp')
|
|
||||||
assert.ok(new Date(ctx.headers['Date']).getDate())
|
|
||||||
|
|
||||||
assert.strictEqual(flaska._after.length, 0)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
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.strictEqual(ctx.headers['Server'], 'Flaska')
|
|
||||||
assert.strictEqual(ctx.headers['X-Content-Type-Options'], 'nosniff')
|
|
||||||
assert.strictEqual(ctx.headers['Content-Security-Policy'], `default-src 'self'; style-src 'self' 'unsafe-inline' 'nonce-${ctx.state.nonce}'; img-src * data: blob:; font-src 'self' data:; object-src 'none'; frame-ancestors 'none'; script-src 'nonce-${ctx.state.nonce}'`)
|
|
||||||
assert.strictEqual(ctx.headers['Cross-Origin-Opener-Policy'], 'same-origin')
|
|
||||||
assert.strictEqual(ctx.headers['Cross-Origin-Resource-Policy'], 'same-origin')
|
|
||||||
assert.strictEqual(ctx.headers['Cross-Origin-Embedder-Policy'], 'require-corp')
|
|
||||||
assert.ok(new Date(ctx.headers['Date']).getDate())
|
|
||||||
})
|
|
||||||
|
|
||||||
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:; font-src 'self' data:; 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:; font-src 'self' data:; 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() {
|
t.describe('#log', function() {
|
||||||
t.test('default have a logger valid', function() {
|
t.test('default have a logger valid', function() {
|
||||||
let flaska = new Flaska({}, faker)
|
let flaska = new Flaska({}, faker)
|
||||||
|
@ -366,6 +99,11 @@ specialHandlers.forEach(function(type) {
|
||||||
})
|
})
|
||||||
|
|
||||||
t.describe('_on404', function() {
|
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() {
|
t.test('default valid handling of context', function() {
|
||||||
let ctx = createCtx()
|
let ctx = createCtx()
|
||||||
let flaska = new Flaska({}, faker)
|
let flaska = new Flaska({}, faker)
|
||||||
|
@ -376,28 +114,6 @@ t.describe('_on404', function() {
|
||||||
message: 'Not Found',
|
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() {
|
t.describe('_onerror', function() {
|
||||||
|
@ -419,68 +135,6 @@ t.describe('_onerror', function() {
|
||||||
message: 'Internal Server Error',
|
message: 'Internal Server Error',
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
t.test('default valid handling of HttpError', function() {
|
|
||||||
const assertStatus = 431
|
|
||||||
const assertBody = { a: 1 }
|
|
||||||
const assertError = new HttpError(assertStatus, 'should not be seen', assertBody)
|
|
||||||
|
|
||||||
let ctx = createCtx()
|
|
||||||
let flaska = new Flaska({}, faker)
|
|
||||||
flaska._onerror(assertError, ctx)
|
|
||||||
assert.strictEqual(ctx.log.error.callCount, 1)
|
|
||||||
assert.strictEqual(ctx.log.error.firstCall[0], assertError)
|
|
||||||
assert.strictEqual(ctx.status, assertStatus)
|
|
||||||
assert.strictEqual(ctx.body, assertBody)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.test('default valid handling of HttpError with no body', function() {
|
|
||||||
const assertStatus = 413
|
|
||||||
const assertError = new HttpError(assertStatus, 'should not be seen')
|
|
||||||
|
|
||||||
let ctx = createCtx()
|
|
||||||
let flaska = new Flaska({}, faker)
|
|
||||||
flaska._onerror(assertError, ctx)
|
|
||||||
assert.strictEqual(ctx.log.error.callCount, 1)
|
|
||||||
assert.strictEqual(ctx.log.error.firstCall[0], assertError)
|
|
||||||
assert.strictEqual(ctx.status, assertStatus)
|
|
||||||
assert.deepStrictEqual(ctx.body, {
|
|
||||||
status: assertStatus,
|
|
||||||
message: 'Payload Too Large',
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
t.test('default valid handling of HttpError with missing status message', function() {
|
|
||||||
const assertStatus = 432
|
|
||||||
const assertError = new HttpError(assertStatus, 'should not be seen')
|
|
||||||
|
|
||||||
let ctx = createCtx()
|
|
||||||
let flaska = new Flaska({}, faker)
|
|
||||||
flaska._onerror(assertError, ctx)
|
|
||||||
assert.strictEqual(ctx.log.error.callCount, 1)
|
|
||||||
assert.strictEqual(ctx.log.error.firstCall[0], assertError)
|
|
||||||
assert.strictEqual(ctx.status, assertStatus)
|
|
||||||
assert.deepStrictEqual(ctx.body, {
|
|
||||||
status: assertStatus,
|
|
||||||
message: 'Internal Server Error',
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
t.describe('_onreqerror', function() {
|
|
||||||
t.test('a valid function', function() {
|
|
||||||
let flaska = new Flaska({}, faker)
|
|
||||||
assert.strictEqual(typeof(flaska._onreqerror), 'function')
|
|
||||||
})
|
|
||||||
|
|
||||||
t.test('default valid handling of aborted', function() {
|
|
||||||
const assertError = new Error('aborted')
|
|
||||||
let flaska = new Flaska({}, faker)
|
|
||||||
let ctx = createCtx()
|
|
||||||
flaska._onreqerror(assertError, ctx)
|
|
||||||
assert.strictEqual(ctx.log.info.callCount, 0)
|
|
||||||
assert.strictEqual(ctx.log.error.callCount, 0)
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
t.describe('#devMode()', function() {
|
t.describe('#devMode()', function() {
|
||||||
|
@ -526,54 +180,6 @@ t.describe('#devMode()', function() {
|
||||||
assert.match(ctx.body.message, new RegExp(assertErrorMessage))
|
assert.match(ctx.body.message, new RegExp(assertErrorMessage))
|
||||||
assert.ok(ctx.body.stack)
|
assert.ok(ctx.body.stack)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.test('default valid handling of HttpError', function() {
|
|
||||||
const assertStatus = 431
|
|
||||||
const assertBody = { a: 1 }
|
|
||||||
const assertError = new HttpError(assertStatus, 'should not be seen', assertBody)
|
|
||||||
|
|
||||||
let ctx = createCtx()
|
|
||||||
let flaska = new Flaska({}, faker)
|
|
||||||
flaska.devMode()
|
|
||||||
flaska._onerror(assertError, ctx)
|
|
||||||
assert.strictEqual(ctx.log.error.callCount, 1)
|
|
||||||
assert.strictEqual(ctx.log.error.firstCall[0], assertError)
|
|
||||||
assert.strictEqual(ctx.status, assertStatus)
|
|
||||||
assert.strictEqual(ctx.body, assertBody)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.test('default valid handling of HttpError with no body', function() {
|
|
||||||
const assertStatus = 413
|
|
||||||
const assertErrorMessage = 'A day'
|
|
||||||
const assertError = new HttpError(assertStatus, assertErrorMessage)
|
|
||||||
|
|
||||||
let ctx = createCtx()
|
|
||||||
let flaska = new Flaska({}, faker)
|
|
||||||
flaska.devMode()
|
|
||||||
flaska._onerror(assertError, ctx)
|
|
||||||
assert.strictEqual(ctx.log.error.callCount, 1)
|
|
||||||
assert.strictEqual(ctx.log.error.firstCall[0], assertError)
|
|
||||||
assert.strictEqual(ctx.status, assertStatus)
|
|
||||||
assert.strictEqual(ctx.body.status, assertStatus)
|
|
||||||
assert.match(ctx.body.message, /Payload Too Large/)
|
|
||||||
assert.match(ctx.body.message, new RegExp(assertErrorMessage))
|
|
||||||
})
|
|
||||||
|
|
||||||
t.test('default valid handling of HttpError with missing status message', function() {
|
|
||||||
const assertStatus = 432
|
|
||||||
const assertErrorMessage = 'Jet Coaster Ride'
|
|
||||||
const assertError = new HttpError(assertStatus, assertErrorMessage)
|
|
||||||
|
|
||||||
let ctx = createCtx()
|
|
||||||
let flaska = new Flaska({}, faker)
|
|
||||||
flaska.devMode()
|
|
||||||
flaska._onerror(assertError, ctx)
|
|
||||||
assert.strictEqual(ctx.log.error.callCount, 1)
|
|
||||||
assert.strictEqual(ctx.log.error.firstCall[0], assertError)
|
|
||||||
assert.strictEqual(ctx.status, assertStatus)
|
|
||||||
assert.strictEqual(ctx.body.status, assertStatus)
|
|
||||||
assert.match(ctx.body.message, /Internal Server Error/)
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
t.describe('#before()', function() {
|
t.describe('#before()', function() {
|
||||||
|
@ -592,8 +198,8 @@ t.describe('#before()', function() {
|
||||||
let flaska = new Flaska({}, faker)
|
let flaska = new Flaska({}, faker)
|
||||||
assert.ok(flaska._before)
|
assert.ok(flaska._before)
|
||||||
flaska.before(assertFunction)
|
flaska.before(assertFunction)
|
||||||
assert.strictEqual(flaska._before.length, 2)
|
assert.strictEqual(flaska._before.length, 1)
|
||||||
assert.strictEqual(flaska._before[1], assertFunction)
|
assert.strictEqual(flaska._before[0], assertFunction)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -735,16 +341,16 @@ t.describe('#compile()', function() {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
t.describe('#runHandlers()', function() {
|
t.describe('#handleMiddleware()', function() {
|
||||||
t.test('should work with empty array', function() {
|
t.test('should work with empty array', function() {
|
||||||
let flaska = new Flaska({}, faker)
|
let flaska = new Flaska({}, faker)
|
||||||
flaska.runHandlers({}, [], 0)
|
flaska.handleMiddleware({}, [], 0)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.test('should work with correct index', function() {
|
t.test('should work with correct index', function() {
|
||||||
let checkIsTrue = false
|
let checkIsTrue = false
|
||||||
let flaska = new Flaska({}, faker)
|
let flaska = new Flaska({}, faker)
|
||||||
flaska.runHandlers({}, [
|
flaska.handleMiddleware({}, [
|
||||||
function() { throw new Error('should not be thrown') },
|
function() { throw new Error('should not be thrown') },
|
||||||
function() { throw new Error('should not be thrown') },
|
function() { throw new Error('should not be thrown') },
|
||||||
function() { throw new Error('should not be thrown') },
|
function() { throw new Error('should not be thrown') },
|
||||||
|
@ -757,7 +363,7 @@ t.describe('#runHandlers()', function() {
|
||||||
const assertCtx = createCtx({ a: 1 })
|
const assertCtx = createCtx({ a: 1 })
|
||||||
let checkCounter = 0
|
let checkCounter = 0
|
||||||
let flaska = new Flaska({}, faker)
|
let flaska = new Flaska({}, faker)
|
||||||
flaska.runHandlers(assertCtx, [
|
flaska.handleMiddleware(assertCtx, [
|
||||||
function(ctx) { assert.strictEqual(ctx, assertCtx); checkCounter++ },
|
function(ctx) { assert.strictEqual(ctx, assertCtx); checkCounter++ },
|
||||||
function(ctx) { assert.strictEqual(ctx, assertCtx); checkCounter++ },
|
function(ctx) { assert.strictEqual(ctx, assertCtx); checkCounter++ },
|
||||||
function(ctx) { assert.strictEqual(ctx, assertCtx); checkCounter++ },
|
function(ctx) { assert.strictEqual(ctx, assertCtx); checkCounter++ },
|
||||||
|
@ -771,7 +377,7 @@ t.describe('#runHandlers()', function() {
|
||||||
const assertCtx = createCtx({ a: 1 })
|
const assertCtx = createCtx({ a: 1 })
|
||||||
let checkCounter = 0
|
let checkCounter = 0
|
||||||
let flaska = new Flaska({}, faker)
|
let flaska = new Flaska({}, faker)
|
||||||
let result = flaska.runHandlers(assertCtx, [
|
let result = flaska.handleMiddleware(assertCtx, [
|
||||||
function(ctx) { assert.strictEqual(ctx, assertCtx); assert.strictEqual(checkCounter, 0); checkCounter++ },
|
function(ctx) { assert.strictEqual(ctx, assertCtx); assert.strictEqual(checkCounter, 0); checkCounter++ },
|
||||||
function(ctx) { assert.strictEqual(ctx, assertCtx); assert.strictEqual(checkCounter, 1); checkCounter++ },
|
function(ctx) { assert.strictEqual(ctx, assertCtx); assert.strictEqual(checkCounter, 1); checkCounter++ },
|
||||||
function(ctx) { return new Promise(function(res) { assert.strictEqual(ctx, assertCtx); assert.strictEqual(checkCounter, 2); checkCounter++; res() }) },
|
function(ctx) { return new Promise(function(res) { assert.strictEqual(ctx, assertCtx); assert.strictEqual(checkCounter, 2); checkCounter++; res() }) },
|
||||||
|
@ -793,19 +399,19 @@ t.describe('#runHandlers()', function() {
|
||||||
const assertError = { a: 1 }
|
const assertError = { a: 1 }
|
||||||
let checkCounter = 0
|
let checkCounter = 0
|
||||||
let flaska = new Flaska({}, faker)
|
let flaska = new Flaska({}, faker)
|
||||||
let err = await assert.isRejected(flaska.runHandlers({}, [
|
let err = await assert.isRejected(flaska.handleMiddleware({}, [
|
||||||
function() { },
|
function() { },
|
||||||
function() { return new Promise(function(res, rej) { rej(assertError) }) },
|
function() { return new Promise(function(res, rej) { rej(assertError) }) },
|
||||||
function() { throw new Error('should not be seen') },
|
function() { throw new Error('should not be seen') },
|
||||||
], 0))
|
], 0))
|
||||||
assert.strictEqual(err, assertError)
|
assert.strictEqual(err, assertError)
|
||||||
err = await assert.isRejected(flaska.runHandlers({}, [
|
err = await assert.isRejected(flaska.handleMiddleware({}, [
|
||||||
function() { },
|
function() { },
|
||||||
function() { return Promise.reject(assertError) },
|
function() { return Promise.reject(assertError) },
|
||||||
function() { throw new Error('should not be seen') },
|
function() { throw new Error('should not be seen') },
|
||||||
], 0))
|
], 0))
|
||||||
assert.strictEqual(err, assertError)
|
assert.strictEqual(err, assertError)
|
||||||
err = await assert.isRejected(flaska.runHandlers({}, [
|
err = await assert.isRejected(flaska.handleMiddleware({}, [
|
||||||
function() { },
|
function() { },
|
||||||
function() { return Promise.resolve() },
|
function() { return Promise.resolve() },
|
||||||
function() { throw assertError },
|
function() { throw assertError },
|
||||||
|
@ -945,7 +551,6 @@ t.describe('#listenAsync()', function() {
|
||||||
checkIp = ip
|
checkIp = ip
|
||||||
cb()
|
cb()
|
||||||
})
|
})
|
||||||
|
|
||||||
let flaska = new Flaska({}, testFaker)
|
let flaska = new Flaska({}, testFaker)
|
||||||
assert.ok(flaska.requestStart)
|
assert.ok(flaska.requestStart)
|
||||||
flaska.requestStart = function() {
|
flaska.requestStart = function() {
|
||||||
|
@ -957,7 +562,7 @@ t.describe('#listenAsync()', function() {
|
||||||
assert.strictEqual(checkIp, assertIp)
|
assert.strictEqual(checkIp, assertIp)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.test('call http and listen correctly if only port specified', async function() {
|
t.test('call http correctly if only port specified', async function() {
|
||||||
const assertPort = 325897235
|
const assertPort = 325897235
|
||||||
let checkPort = null
|
let checkPort = null
|
||||||
let checkIp = null
|
let checkIp = null
|
||||||
|
@ -978,55 +583,6 @@ t.describe('#listenAsync()', function() {
|
||||||
assert.strictEqual(checkIp, '::')
|
assert.strictEqual(checkIp, '::')
|
||||||
})
|
})
|
||||||
|
|
||||||
t.test('call http and listenAsync correctly if supported', async function() {
|
|
||||||
const assertPort = 4632
|
|
||||||
const assertIp = 'asdf'
|
|
||||||
const assertReturns = { a: 1 }
|
|
||||||
const stubListenAsync = stub().resolves(assertReturns)
|
|
||||||
|
|
||||||
let flaska = new Flaska({}, {
|
|
||||||
createServer: function() {
|
|
||||||
return {
|
|
||||||
listenAsync: stubListenAsync,
|
|
||||||
on: stub(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
assert.ok(flaska.requestStart)
|
|
||||||
flaska.requestStart = function() {
|
|
||||||
checkInternalThis = this
|
|
||||||
checkIsTrue = true
|
|
||||||
}
|
|
||||||
let res = await flaska.listenAsync(assertPort, assertIp)
|
|
||||||
assert.strictEqual(res, assertReturns)
|
|
||||||
assert.strictEqual(stubListenAsync.firstCall[0], assertPort)
|
|
||||||
assert.strictEqual(stubListenAsync.firstCall[1], assertIp)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.test('call http and listenAsync correctly if supported and ip is null', async function() {
|
|
||||||
const assertPort = 325897235
|
|
||||||
const assertReturns = { a: 1 }
|
|
||||||
const stubListenAsync = stub().resolves(assertReturns)
|
|
||||||
|
|
||||||
let flaska = new Flaska({}, {
|
|
||||||
createServer: function() {
|
|
||||||
return {
|
|
||||||
listenAsync: stubListenAsync,
|
|
||||||
on: stub(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
assert.ok(flaska.requestStart)
|
|
||||||
flaska.requestStart = function() {
|
|
||||||
checkInternalThis = this
|
|
||||||
checkIsTrue = true
|
|
||||||
}
|
|
||||||
let res = await flaska.listenAsync(assertPort)
|
|
||||||
assert.strictEqual(res, assertReturns)
|
|
||||||
assert.strictEqual(stubListenAsync.firstCall[0], assertPort)
|
|
||||||
assert.strictEqual(stubListenAsync.firstCall[1], '::')
|
|
||||||
})
|
|
||||||
|
|
||||||
t.test('register requestStart if no async', async function() {
|
t.test('register requestStart if no async', async function() {
|
||||||
let checkIsTrue = false
|
let checkIsTrue = false
|
||||||
let checkInternalThis = null
|
let checkInternalThis = null
|
||||||
|
@ -1072,23 +628,10 @@ t.describe('#closeAsync()', function() {
|
||||||
assert.strictEqual(err, assertError)
|
assert.strictEqual(err, assertError)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.test('it should call closeAllConnections', async function() {
|
|
||||||
const assertError = new Error('Pirate Fight')
|
|
||||||
let flaska = new Flaska()
|
|
||||||
flaska.server = {
|
|
||||||
close: stub(),
|
|
||||||
closeAllConnections: stub(),
|
|
||||||
}
|
|
||||||
flaska.server.closeAllConnections.throws(assertError)
|
|
||||||
let err = await assert.isRejected(flaska.closeAsync())
|
|
||||||
assert.strictEqual(err, assertError)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.test('should otherwise work', async function() {
|
t.test('should otherwise work', async function() {
|
||||||
let flaska = new Flaska()
|
let flaska = new Flaska()
|
||||||
flaska.server = {
|
flaska.server = {
|
||||||
close: stub(),
|
close: stub()
|
||||||
closeAllConnections: stub(),
|
|
||||||
}
|
}
|
||||||
flaska.server.close.returnWith(function(cb) {
|
flaska.server.close.returnWith(function(cb) {
|
||||||
cb(null, { a: 1 })
|
cb(null, { a: 1 })
|
||||||
|
|
0
test/flaska.formidable.test.mjs
Normal file
0
test/flaska.formidable.test.mjs
Normal file
|
@ -19,11 +19,13 @@ t.describe('#requestStart()', function() {
|
||||||
flaska.onreserror(onResError)
|
flaska.onreserror(onResError)
|
||||||
flaska.requestEnded = onEnded
|
flaska.requestEnded = onEnded
|
||||||
|
|
||||||
flaska.requestEnd = cb.finish(function(err, ctx) {
|
flaska.requestEnd = function(err, ctx) {
|
||||||
|
try {
|
||||||
assert.ok(err)
|
assert.ok(err)
|
||||||
assert.strictEqual(assertReq.on.callCount, 1)
|
assert.strictEqual(assertReq.on.callCount, 2)
|
||||||
assert.strictEqual(assertRes.on.callCount, 2)
|
assert.strictEqual(assertRes.on.callCount, 2)
|
||||||
|
|
||||||
|
|
||||||
assert.strictEqual(assertRes.on.firstCall[0], 'error')
|
assert.strictEqual(assertRes.on.firstCall[0], 'error')
|
||||||
assert.strictEqual(typeof(assertRes.on.firstCall[1]), 'function')
|
assert.strictEqual(typeof(assertRes.on.firstCall[1]), 'function')
|
||||||
assertRes.on.firstCall[1](assertErrorTwo, ctx)
|
assertRes.on.firstCall[1](assertErrorTwo, ctx)
|
||||||
|
@ -44,7 +46,18 @@ t.describe('#requestStart()', function() {
|
||||||
assert.strictEqual(onReqError.callCount, 1)
|
assert.strictEqual(onReqError.callCount, 1)
|
||||||
assert.strictEqual(onReqError.firstCall[0], assertErrorOne)
|
assert.strictEqual(onReqError.firstCall[0], assertErrorOne)
|
||||||
assert.strictEqual(onReqError.firstCall[1], ctx)
|
assert.strictEqual(onReqError.firstCall[1], ctx)
|
||||||
})
|
|
||||||
|
assert.strictEqual(assertReq.on.secondCall[0], 'aborted')
|
||||||
|
assert.strictEqual(typeof(assertReq.on.secondCall[1]), 'function')
|
||||||
|
assert.notStrictEqual(ctx.aborted, false)
|
||||||
|
assertReq.on.secondCall[1]()
|
||||||
|
assert.strictEqual(ctx.aborted, true)
|
||||||
|
|
||||||
|
// Test abort and close
|
||||||
|
|
||||||
|
cb()
|
||||||
|
} catch (err) { cb(err) }
|
||||||
|
}
|
||||||
flaska._beforeCompiled = function(ctx) {
|
flaska._beforeCompiled = function(ctx) {
|
||||||
throw new Error()
|
throw new Error()
|
||||||
}
|
}
|
||||||
|
@ -58,13 +71,18 @@ t.describe('#requestStart()', function() {
|
||||||
const assertRes = createRes({ b: 2 })
|
const assertRes = createRes({ b: 2 })
|
||||||
let flaska = new Flaska({}, faker)
|
let flaska = new Flaska({}, faker)
|
||||||
|
|
||||||
flaska.requestEnd = cb.finish(function(err, ctx) {
|
flaska.requestEnd = function(err, ctx) {
|
||||||
|
if (err && err !== assertError) return cb(err)
|
||||||
|
|
||||||
|
try {
|
||||||
assert.ok(err)
|
assert.ok(err)
|
||||||
assert.strictEqual(err, assertError)
|
assert.strictEqual(err, assertError)
|
||||||
assert.deepStrictEqual(ctx.state, {})
|
assert.deepStrictEqual(ctx.state, {})
|
||||||
assert.strictEqual(ctx.req, assertReq)
|
assert.strictEqual(ctx.req, assertReq)
|
||||||
assert.strictEqual(ctx.res, assertRes)
|
assert.strictEqual(ctx.res, assertRes)
|
||||||
})
|
cb()
|
||||||
|
} catch (err) { cb(err) }
|
||||||
|
}
|
||||||
flaska._beforeCompiled = function(ctx) {
|
flaska._beforeCompiled = function(ctx) {
|
||||||
assert.strictEqual(ctx.req, assertReq)
|
assert.strictEqual(ctx.req, assertReq)
|
||||||
assert.strictEqual(ctx.res, assertRes)
|
assert.strictEqual(ctx.res, assertRes)
|
||||||
|
@ -87,13 +105,18 @@ t.describe('#requestStart()', function() {
|
||||||
return Promise.resolve().then(function() { return Promise.reject(assertError) })
|
return Promise.resolve().then(function() { return Promise.reject(assertError) })
|
||||||
}
|
}
|
||||||
|
|
||||||
flaska.requestEnd = cb.finish(function(err, ctx) {
|
flaska.requestEnd = function(err, ctx) {
|
||||||
|
if (err && err !== assertError) return cb(err)
|
||||||
|
|
||||||
|
try {
|
||||||
assert.ok(err)
|
assert.ok(err)
|
||||||
assert.strictEqual(err, assertError)
|
assert.strictEqual(err, assertError)
|
||||||
assert.deepStrictEqual(ctx.state, {})
|
assert.deepStrictEqual(ctx.state, {})
|
||||||
assert.strictEqual(ctx.req, assertReq)
|
assert.strictEqual(ctx.req, assertReq)
|
||||||
assert.strictEqual(ctx.res, assertRes)
|
assert.strictEqual(ctx.res, assertRes)
|
||||||
})
|
cb()
|
||||||
|
} catch (err) { cb(err) }
|
||||||
|
}
|
||||||
|
|
||||||
flaska.requestStart(assertReq, assertRes)
|
flaska.requestStart(assertReq, assertRes)
|
||||||
})
|
})
|
||||||
|
@ -115,8 +138,10 @@ t.describe('#requestStart()', function() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
flaska.requestEnd = cb.finish(function(err, ctx) {
|
flaska.requestEnd = function(err, ctx) {
|
||||||
|
if (err && err !== assertError) return cb(err)
|
||||||
|
|
||||||
|
try {
|
||||||
assert.ok(err)
|
assert.ok(err)
|
||||||
assert.strictEqual(err, assertError)
|
assert.strictEqual(err, assertError)
|
||||||
assert.strictEqual(ctx.url, assertPath)
|
assert.strictEqual(ctx.url, assertPath)
|
||||||
|
@ -131,19 +156,9 @@ t.describe('#requestStart()', function() {
|
||||||
assert.ok(ctx.query.get)
|
assert.ok(ctx.query.get)
|
||||||
assert.ok(ctx.query.set)
|
assert.ok(ctx.query.set)
|
||||||
assert.ok(ctx.query.delete)
|
assert.ok(ctx.query.delete)
|
||||||
assert.deepEqual(
|
cb()
|
||||||
Object.keys(ctx.headers).sort(),
|
} catch (err) { cb(err) }
|
||||||
['Server','X-Content-Type-Options','Content-Security-Policy','Cross-Origin-Opener-Policy','Cross-Origin-Resource-Policy','Cross-Origin-Embedder-Policy','Date'].sort()
|
}
|
||||||
)
|
|
||||||
|
|
||||||
assert.strictEqual(ctx.headers['Server'], 'Flaska')
|
|
||||||
assert.strictEqual(ctx.headers['X-Content-Type-Options'], 'nosniff')
|
|
||||||
assert.strictEqual(ctx.headers['Content-Security-Policy'], `default-src 'self'; style-src 'self' 'unsafe-inline'; img-src * data: blob:; font-src 'self' data:; object-src 'none'; frame-ancestors 'none'`)
|
|
||||||
assert.strictEqual(ctx.headers['Cross-Origin-Opener-Policy'], 'same-origin')
|
|
||||||
assert.strictEqual(ctx.headers['Cross-Origin-Resource-Policy'], 'same-origin')
|
|
||||||
assert.strictEqual(ctx.headers['Cross-Origin-Embedder-Policy'], 'require-corp')
|
|
||||||
assert.ok(new Date(ctx.headers['Date']).getDate())
|
|
||||||
})
|
|
||||||
|
|
||||||
flaska.requestStart(createReq({
|
flaska.requestStart(createReq({
|
||||||
url: assertPath + assertSearch,
|
url: assertPath + assertSearch,
|
||||||
|
@ -151,52 +166,14 @@ t.describe('#requestStart()', function() {
|
||||||
}), createRes())
|
}), 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 = cb.finish(function(err, ctx) {
|
|
||||||
assert.ok(err)
|
|
||||||
assert.strictEqual(err, assertError)
|
|
||||||
let keys = Object.keys(defaultHeaders)
|
|
||||||
assert.strictEqual(Object.keys(ctx.headers).length, keys.length + 1)
|
|
||||||
for (let key of keys) {
|
|
||||||
assert.strictEqual(ctx.headers[key], defaultHeaders[key])
|
|
||||||
}
|
|
||||||
assert.ok(ctx.headers['Date'])
|
|
||||||
})
|
|
||||||
|
|
||||||
flaska.requestStart(createReq({
|
|
||||||
url: '/',
|
|
||||||
method: 'test',
|
|
||||||
}), createRes())
|
|
||||||
})
|
|
||||||
|
|
||||||
t.test('calls correct router with correct url and context if beforeAsync', function(cb) {
|
t.test('calls correct router with correct url and context if beforeAsync', function(cb) {
|
||||||
const assertError = new Error('test')
|
const assertError = new Error('test')
|
||||||
const assertMethod = 'test'
|
const assertMethod = 'test'
|
||||||
const assertPath = '/test/me'
|
const assertPath = '/test/me'
|
||||||
const assertSearch = '?asdf=test'
|
const assertSearch = '?asdf=test'
|
||||||
let calledBefore = false
|
|
||||||
let flaska = new Flaska({}, faker)
|
let flaska = new Flaska({}, faker)
|
||||||
flaska.compile()
|
flaska.compile()
|
||||||
flaska._beforeAsyncCompiled = function() {
|
flaska._beforeAsyncCompiled = function() { return Promise.resolve() }
|
||||||
calledBefore = true
|
|
||||||
return Promise.resolve()
|
|
||||||
}
|
|
||||||
|
|
||||||
flaska.routers.test = {
|
flaska.routers.test = {
|
||||||
match: function(path) {
|
match: function(path) {
|
||||||
|
@ -205,14 +182,18 @@ t.describe('#requestStart()', function() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
flaska.requestEnd = cb.finish(function(err, ctx) {
|
flaska.requestEnd = function(err, ctx) {
|
||||||
|
if (err && err !== assertError) return cb(err)
|
||||||
|
|
||||||
|
try {
|
||||||
assert.ok(err)
|
assert.ok(err)
|
||||||
assert.ok(calledBefore)
|
|
||||||
assert.strictEqual(err, assertError)
|
assert.strictEqual(err, assertError)
|
||||||
assert.strictEqual(ctx.url, assertPath)
|
assert.strictEqual(ctx.url, assertPath)
|
||||||
assert.strictEqual(ctx.search, assertSearch)
|
assert.strictEqual(ctx.search, assertSearch)
|
||||||
assert.strictEqual(ctx.method, assertMethod)
|
assert.strictEqual(ctx.method, assertMethod)
|
||||||
})
|
cb()
|
||||||
|
} catch (err) { cb(err) }
|
||||||
|
}
|
||||||
|
|
||||||
flaska.requestStart(createReq({
|
flaska.requestStart(createReq({
|
||||||
url: assertPath + assertSearch,
|
url: assertPath + assertSearch,
|
||||||
|
@ -220,7 +201,7 @@ t.describe('#requestStart()', function() {
|
||||||
}), createRes())
|
}), createRes())
|
||||||
})
|
})
|
||||||
|
|
||||||
t.test('calls handlers correctly', function(cb) {
|
t.test('calls handleMiddleware correctly', function(cb) {
|
||||||
const assertError = new Error('test')
|
const assertError = new Error('test')
|
||||||
const assertMiddles = [1, 2]
|
const assertMiddles = [1, 2]
|
||||||
const assertParams = { a: 1, b: 2 }
|
const assertParams = { a: 1, b: 2 }
|
||||||
|
@ -231,24 +212,30 @@ t.describe('#requestStart()', function() {
|
||||||
|
|
||||||
flaska.routers.GET.match = function() {
|
flaska.routers.GET.match = function() {
|
||||||
return {
|
return {
|
||||||
path: { handlers: assertMiddles, },
|
handler: function() {},
|
||||||
|
middlewares: assertMiddles,
|
||||||
params: assertParams,
|
params: assertParams,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
flaska.runHandlers = function(ctx, middles, index) {
|
flaska.handleMiddleware = function(ctx, middles, index) {
|
||||||
assert.strictEqual(index, 0)
|
assert.strictEqual(index, 0)
|
||||||
assert.strictEqual(middles, assertMiddles)
|
assert.strictEqual(middles, assertMiddles)
|
||||||
checkMiddleCtx = ctx
|
checkMiddleCtx = ctx
|
||||||
throw assertError
|
throw assertError
|
||||||
}
|
}
|
||||||
|
|
||||||
flaska.requestEnd = cb.finish(function(err, ctx) {
|
flaska.requestEnd = function(err, ctx) {
|
||||||
|
if (err && err !== assertError) return cb(err)
|
||||||
|
|
||||||
|
try {
|
||||||
assert.ok(err)
|
assert.ok(err)
|
||||||
assert.ok(ctx)
|
assert.ok(ctx)
|
||||||
assert.strictEqual(err, assertError)
|
assert.strictEqual(err, assertError)
|
||||||
assert.strictEqual(ctx, checkMiddleCtx)
|
assert.strictEqual(ctx, checkMiddleCtx)
|
||||||
assert.strictEqual(ctx.params, assertParams)
|
assert.strictEqual(ctx.params, assertParams)
|
||||||
})
|
cb()
|
||||||
|
} catch (err) { cb(err) }
|
||||||
|
}
|
||||||
|
|
||||||
flaska.requestStart(createReq({
|
flaska.requestStart(createReq({
|
||||||
url: '',
|
url: '',
|
||||||
|
@ -269,12 +256,17 @@ t.describe('#requestStart()', function() {
|
||||||
flaska.get('/:id', handler)
|
flaska.get('/:id', handler)
|
||||||
flaska.compile()
|
flaska.compile()
|
||||||
|
|
||||||
flaska.requestEnd = cb.finish(function(err, ctx) {
|
flaska.requestEnd = function(err, ctx) {
|
||||||
|
if (err && err !== assertError) return cb(err)
|
||||||
|
|
||||||
|
try {
|
||||||
assert.ok(err)
|
assert.ok(err)
|
||||||
assert.ok(ctx)
|
assert.ok(ctx)
|
||||||
assert.strictEqual(err, assertError)
|
assert.strictEqual(err, assertError)
|
||||||
assert.strictEqual(ctx, checkHandlerCtx)
|
assert.strictEqual(ctx, checkHandlerCtx)
|
||||||
})
|
cb()
|
||||||
|
} catch (err) { cb(err) }
|
||||||
|
}
|
||||||
|
|
||||||
flaska.requestStart(createReq({
|
flaska.requestStart(createReq({
|
||||||
url: '/test',
|
url: '/test',
|
||||||
|
@ -294,12 +286,16 @@ t.describe('#requestStart()', function() {
|
||||||
flaska.get('/test', function() { throw new Error('should not be called') })
|
flaska.get('/test', function() { throw new Error('should not be called') })
|
||||||
flaska.compile()
|
flaska.compile()
|
||||||
|
|
||||||
flaska.requestEnd = cb.finish(function(err, ctx) {
|
flaska.requestEnd = function(err, ctx) {
|
||||||
assert.notOk(err)
|
if (err) return cb(err)
|
||||||
|
|
||||||
|
try {
|
||||||
assert.ok(ctx)
|
assert.ok(ctx)
|
||||||
assert.strictEqual(on404Error.callCount, 1)
|
assert.strictEqual(on404Error.callCount, 1)
|
||||||
assert.strictEqual(on404Error.firstCall[0], ctx)
|
assert.strictEqual(on404Error.firstCall[0], ctx)
|
||||||
})
|
cb()
|
||||||
|
} catch (err) { cb(err) }
|
||||||
|
}
|
||||||
|
|
||||||
flaska.requestStart(createReq({
|
flaska.requestStart(createReq({
|
||||||
url: '/nope',
|
url: '/nope',
|
||||||
|
@ -320,11 +316,17 @@ t.describe('#requestStart()', function() {
|
||||||
flaska.get('/test', function() { throw new Error('should not be called') })
|
flaska.get('/test', function() { throw new Error('should not be called') })
|
||||||
flaska.compile()
|
flaska.compile()
|
||||||
|
|
||||||
flaska.requestEnd = cb.finish(function(err, ctx) {
|
flaska.requestEnd = function(err, ctx) {
|
||||||
assert.strictEqual(err, assertError)
|
if (err && err !== assertError) return cb(err)
|
||||||
|
|
||||||
|
try {
|
||||||
|
assert.ok(err)
|
||||||
assert.ok(ctx)
|
assert.ok(ctx)
|
||||||
assert.strictEqual(ctx, checkCtx)
|
assert.strictEqual(ctx, checkCtx)
|
||||||
})
|
assert.strictEqual(err, assertError)
|
||||||
|
cb()
|
||||||
|
} catch (err) { cb(err) }
|
||||||
|
}
|
||||||
|
|
||||||
flaska.requestStart(createReq({
|
flaska.requestStart(createReq({
|
||||||
url: '/nope',
|
url: '/nope',
|
||||||
|
@ -344,11 +346,17 @@ t.describe('#requestStart()', function() {
|
||||||
flaska.get('/test', function() { throw new Error('should not be called') })
|
flaska.get('/test', function() { throw new Error('should not be called') })
|
||||||
flaska.compile()
|
flaska.compile()
|
||||||
|
|
||||||
flaska.requestEnd = cb.finish(function(err, ctx) {
|
flaska.requestEnd = function(err, ctx) {
|
||||||
assert.strictEqual(err, assertError)
|
if (err && err !== assertError) return cb(err)
|
||||||
|
|
||||||
|
try {
|
||||||
|
assert.ok(err)
|
||||||
assert.ok(ctx)
|
assert.ok(ctx)
|
||||||
assert.strictEqual(ctx, checkCtx)
|
assert.strictEqual(ctx, checkCtx)
|
||||||
})
|
assert.strictEqual(err, assertError)
|
||||||
|
cb()
|
||||||
|
} catch (err) { cb(err) }
|
||||||
|
}
|
||||||
|
|
||||||
flaska.requestStart(createReq({
|
flaska.requestStart(createReq({
|
||||||
url: '/nope',
|
url: '/nope',
|
||||||
|
@ -368,11 +376,17 @@ t.describe('#requestStart()', function() {
|
||||||
flaska.get('/test', middles, function() { throw new Error('should not be called') })
|
flaska.get('/test', middles, function() { throw new Error('should not be called') })
|
||||||
flaska.compile()
|
flaska.compile()
|
||||||
|
|
||||||
flaska.requestEnd = cb.finish(function(err, ctx) {
|
flaska.requestEnd = function(err, ctx) {
|
||||||
assert.strictEqual(err, assertError)
|
if (err && err !== assertError) return cb(err)
|
||||||
|
|
||||||
|
try {
|
||||||
|
assert.ok(err)
|
||||||
assert.ok(ctx)
|
assert.ok(ctx)
|
||||||
assert.strictEqual(ctx, checkCtx)
|
assert.strictEqual(ctx, checkCtx)
|
||||||
})
|
assert.strictEqual(err, assertError)
|
||||||
|
cb()
|
||||||
|
} catch (err) { cb(err) }
|
||||||
|
}
|
||||||
|
|
||||||
flaska.requestStart(createReq({
|
flaska.requestStart(createReq({
|
||||||
url: '/test',
|
url: '/test',
|
||||||
|
@ -390,12 +404,18 @@ t.describe('#requestStart()', function() {
|
||||||
let flaska = new Flaska({}, faker)
|
let flaska = new Flaska({}, faker)
|
||||||
flaska.get('/::path', handler)
|
flaska.get('/::path', handler)
|
||||||
flaska.compile()
|
flaska.compile()
|
||||||
|
flaska.handleMiddleware = function() {
|
||||||
|
throw new Error('should not be called')
|
||||||
|
}
|
||||||
|
|
||||||
flaska.requestEnd = cb.finish(function(err, ctx) {
|
flaska.requestEnd = function(err, ctx) {
|
||||||
|
try {
|
||||||
assert.notOk(err)
|
assert.notOk(err)
|
||||||
assert.ok(ctx)
|
assert.ok(ctx)
|
||||||
assert.strictEqual(ctx.body, assertBody)
|
assert.strictEqual(ctx.body, assertBody)
|
||||||
})
|
cb()
|
||||||
|
} catch (err) { cb(err) }
|
||||||
|
}
|
||||||
|
|
||||||
flaska.requestStart(createReq({
|
flaska.requestStart(createReq({
|
||||||
url: '/test/something/here',
|
url: '/test/something/here',
|
||||||
|
@ -403,7 +423,7 @@ t.describe('#requestStart()', function() {
|
||||||
}), createRes())
|
}), createRes())
|
||||||
})
|
})
|
||||||
|
|
||||||
t.test('calls runHandlers correctly if is promise', function(cb) {
|
t.test('calls handleMiddleware correctly if is promise', function(cb) {
|
||||||
const assertError = new Error('test')
|
const assertError = new Error('test')
|
||||||
const assertMiddles = [1]
|
const assertMiddles = [1]
|
||||||
|
|
||||||
|
@ -412,19 +432,24 @@ t.describe('#requestStart()', function() {
|
||||||
|
|
||||||
flaska.routers.GET.match = function() {
|
flaska.routers.GET.match = function() {
|
||||||
return {
|
return {
|
||||||
path: { handlers: function() {}, },
|
handler: function() {},
|
||||||
middlewares: assertMiddles,
|
middlewares: assertMiddles,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
flaska.runHandlers = function() {
|
flaska.handleMiddleware = function() {
|
||||||
return Promise.resolve().then(function() { return Promise.reject(assertError) })
|
return Promise.resolve().then(function() { return Promise.reject(assertError) })
|
||||||
}
|
}
|
||||||
|
|
||||||
flaska.requestEnd = cb.finish(function(err, ctx) {
|
flaska.requestEnd = function(err, ctx) {
|
||||||
|
if (err && err !== assertError) return cb(err)
|
||||||
|
|
||||||
|
try {
|
||||||
assert.ok(err)
|
assert.ok(err)
|
||||||
assert.ok(ctx)
|
assert.ok(ctx)
|
||||||
assert.strictEqual(err, assertError)
|
assert.strictEqual(err, assertError)
|
||||||
})
|
cb()
|
||||||
|
} catch (err) { cb(err) }
|
||||||
|
}
|
||||||
|
|
||||||
flaska.requestStart(createReq({
|
flaska.requestStart(createReq({
|
||||||
url: '',
|
url: '',
|
||||||
|
@ -445,12 +470,17 @@ t.describe('#requestStart()', function() {
|
||||||
flaska.get('/:id', [function() { return Promise.resolve() }], handler)
|
flaska.get('/:id', [function() { return Promise.resolve() }], handler)
|
||||||
flaska.compile()
|
flaska.compile()
|
||||||
|
|
||||||
flaska.requestEnd = cb.finish(function(err, ctx) {
|
flaska.requestEnd = function(err, ctx) {
|
||||||
|
if (err && err !== assertError) return cb(err)
|
||||||
|
|
||||||
|
try {
|
||||||
assert.ok(err)
|
assert.ok(err)
|
||||||
assert.ok(ctx)
|
assert.ok(ctx)
|
||||||
assert.strictEqual(err, assertError)
|
assert.strictEqual(err, assertError)
|
||||||
assert.strictEqual(ctx, checkHandlerCtx)
|
assert.strictEqual(ctx, checkHandlerCtx)
|
||||||
})
|
cb()
|
||||||
|
} catch (err) { cb(err) }
|
||||||
|
}
|
||||||
|
|
||||||
flaska.requestStart(createReq({
|
flaska.requestStart(createReq({
|
||||||
url: '/test',
|
url: '/test',
|
||||||
|
@ -471,12 +501,17 @@ t.describe('#requestStart()', function() {
|
||||||
flaska.get('/:id', [function() { return Promise.resolve() }], handler)
|
flaska.get('/:id', [function() { return Promise.resolve() }], handler)
|
||||||
flaska.compile()
|
flaska.compile()
|
||||||
|
|
||||||
flaska.requestEnd = cb.finish(function(err, ctx) {
|
flaska.requestEnd = function(err, ctx) {
|
||||||
|
if (err && err !== assertError) return cb(err)
|
||||||
|
|
||||||
|
try {
|
||||||
assert.ok(err)
|
assert.ok(err)
|
||||||
assert.ok(ctx)
|
assert.ok(ctx)
|
||||||
assert.strictEqual(err, assertError)
|
assert.strictEqual(err, assertError)
|
||||||
assert.strictEqual(ctx, checkHandlerCtx)
|
assert.strictEqual(ctx, checkHandlerCtx)
|
||||||
})
|
cb()
|
||||||
|
} catch (err) { cb(err) }
|
||||||
|
}
|
||||||
|
|
||||||
flaska.requestStart(createReq({
|
flaska.requestStart(createReq({
|
||||||
url: '/test',
|
url: '/test',
|
||||||
|
@ -505,12 +540,15 @@ t.describe('#requestStart()', function() {
|
||||||
flaska.get('/::path', [middle], handler)
|
flaska.get('/::path', [middle], handler)
|
||||||
flaska.compile()
|
flaska.compile()
|
||||||
|
|
||||||
flaska.requestEnd = cb.finish(function(err, ctx) {
|
flaska.requestEnd = function(err, ctx) {
|
||||||
|
try {
|
||||||
assert.notOk(err)
|
assert.notOk(err)
|
||||||
assert.ok(ctx)
|
assert.ok(ctx)
|
||||||
assert.strictEqual(ctx.body, assertBody)
|
assert.strictEqual(ctx.body, assertBody)
|
||||||
assert.strictEqual(ctx.state, assertState)
|
assert.strictEqual(ctx.state, assertState)
|
||||||
})
|
cb()
|
||||||
|
} catch (err) { cb(err) }
|
||||||
|
}
|
||||||
|
|
||||||
flaska.requestStart(createReq({
|
flaska.requestStart(createReq({
|
||||||
url: '/test/something/here',
|
url: '/test/something/here',
|
||||||
|
@ -533,12 +571,17 @@ t.describe('#requestStart()', function() {
|
||||||
flaska.get('/:id', handler)
|
flaska.get('/:id', handler)
|
||||||
flaska.compile()
|
flaska.compile()
|
||||||
|
|
||||||
flaska.requestEnd = cb.finish(function(err, ctx) {
|
flaska.requestEnd = function(err, ctx) {
|
||||||
|
if (err && err !== assertError) return cb(err)
|
||||||
|
|
||||||
|
try {
|
||||||
assert.ok(err)
|
assert.ok(err)
|
||||||
assert.ok(ctx)
|
assert.ok(ctx)
|
||||||
assert.strictEqual(err, assertError)
|
assert.strictEqual(err, assertError)
|
||||||
assert.strictEqual(ctx, checkHandlerCtx)
|
assert.strictEqual(ctx, checkHandlerCtx)
|
||||||
})
|
cb()
|
||||||
|
} catch (err) { cb(err) }
|
||||||
|
}
|
||||||
|
|
||||||
flaska.requestStart(createReq({
|
flaska.requestStart(createReq({
|
||||||
url: '/test',
|
url: '/test',
|
||||||
|
@ -559,11 +602,18 @@ t.describe('#requestStart()', function() {
|
||||||
flaska.get('/::path', [], handler)
|
flaska.get('/::path', [], handler)
|
||||||
flaska.compile()
|
flaska.compile()
|
||||||
|
|
||||||
flaska.requestEnd = cb.finish(function(err, ctx) {
|
flaska.handleMiddleware = function() {
|
||||||
|
throw new Error('should not be called')
|
||||||
|
}
|
||||||
|
|
||||||
|
flaska.requestEnd = function(err, ctx) {
|
||||||
|
try {
|
||||||
assert.notOk(err)
|
assert.notOk(err)
|
||||||
assert.ok(ctx)
|
assert.ok(ctx)
|
||||||
assert.strictEqual(ctx.body, assertBody)
|
assert.strictEqual(ctx.body, assertBody)
|
||||||
})
|
cb()
|
||||||
|
} catch (err) { cb(err) }
|
||||||
|
}
|
||||||
|
|
||||||
flaska.requestStart(createReq({
|
flaska.requestStart(createReq({
|
||||||
url: '/test/something/here',
|
url: '/test/something/here',
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { Eltro as t, spy, assert, stub} from 'eltro'
|
import { Eltro as t, spy, assert} from 'eltro'
|
||||||
import { FileResponse, Flaska, FlaskaRouter } from '../flaska.mjs'
|
import { Flaska, FlaskaRouter } from '../flaska.mjs'
|
||||||
import { fakeHttp, createCtx } from './helper.mjs'
|
import { fakeHttp, createCtx } from './helper.mjs'
|
||||||
|
|
||||||
const fakerHttp = fakeHttp()
|
const fakerHttp = fakeHttp()
|
||||||
|
@ -12,17 +12,21 @@ t.describe('#requestEnd()', function() {
|
||||||
const assertStatus = 501
|
const assertStatus = 501
|
||||||
// Calculated manually just in case
|
// Calculated manually just in case
|
||||||
const assertBodyLength = 7
|
const assertBodyLength = 7
|
||||||
let onFinish = cb.finish(function(body) {
|
let onFinish = function(body) {
|
||||||
|
try {
|
||||||
assert.strictEqual(ctx.status, assertStatus)
|
assert.strictEqual(ctx.status, assertStatus)
|
||||||
assert.strictEqual(ctx.body, assertBody)
|
assert.strictEqual(ctx.body, assertBody)
|
||||||
assert.strictEqual(ctx.headers['Content-Type'], 'application/json; charset=utf-8')
|
assert.strictEqual(ctx.res.statusCode, assertStatus)
|
||||||
assert.strictEqual(ctx.headers['Content-Length'], assertBodyLength)
|
assert.strictEqual(ctx.res.setHeader.callCount, 2)
|
||||||
|
assert.strictEqual(ctx.res.setHeader.firstCall[0], 'Content-Type')
|
||||||
|
assert.strictEqual(ctx.res.setHeader.firstCall[1], 'application/json; charset=utf-8')
|
||||||
|
assert.strictEqual(ctx.res.setHeader.secondCall[0], 'Content-Length')
|
||||||
|
assert.strictEqual(ctx.res.setHeader.secondCall[1], assertBodyLength)
|
||||||
assert.ok(body)
|
assert.ok(body)
|
||||||
assert.strictEqual(body, '{"a":1}')
|
assert.strictEqual(body, '{"a":1}')
|
||||||
assert.ok(ctx.res.writeHead.called)
|
cb()
|
||||||
assert.strictEqual(ctx.res.writeHead.firstCall[0], ctx.status)
|
} catch (err) { cb(err) }
|
||||||
assert.strictEqual(ctx.res.writeHead.firstCall[1], ctx.headers)
|
}
|
||||||
})
|
|
||||||
const ctx = createCtx({}, onFinish)
|
const ctx = createCtx({}, onFinish)
|
||||||
|
|
||||||
let flaska = new Flaska({}, fakerHttp, fakeStream)
|
let flaska = new Flaska({}, fakerHttp, fakeStream)
|
||||||
|
@ -40,11 +44,14 @@ t.describe('#requestEnd()', function() {
|
||||||
const assertErrorNotSeen = new Error('should not be seen')
|
const assertErrorNotSeen = new Error('should not be seen')
|
||||||
const assertError = new Error('test')
|
const assertError = new Error('test')
|
||||||
|
|
||||||
let onFinish = cb.finish(function(body) {
|
let onFinish = function(body) {
|
||||||
|
try {
|
||||||
assert.strictEqual(ctx.status, 500)
|
assert.strictEqual(ctx.status, 500)
|
||||||
assert.strictEqual(ctx.body.status, 500)
|
assert.strictEqual(ctx.body.status, 500)
|
||||||
assert.strictEqual(ctx.body.message, 'Internal Server Error')
|
assert.strictEqual(ctx.body.message, 'Internal Server Error')
|
||||||
})
|
cb()
|
||||||
|
} catch (err) { cb(err) }
|
||||||
|
}
|
||||||
const ctx = createCtx({}, onFinish)
|
const ctx = createCtx({}, onFinish)
|
||||||
|
|
||||||
let flaska = new Flaska({}, fakerHttp, fakeStream)
|
let flaska = new Flaska({}, fakerHttp, fakeStream)
|
||||||
|
@ -55,118 +62,27 @@ t.describe('#requestEnd()', function() {
|
||||||
flaska.requestEnd(assertErrorNotSeen, ctx)
|
flaska.requestEnd(assertErrorNotSeen, ctx)
|
||||||
})
|
})
|
||||||
|
|
||||||
let testMethods = ['GET', 'HEAD']
|
|
||||||
|
|
||||||
testMethods.forEach(function(method) {
|
|
||||||
t.describe(method, function() {
|
|
||||||
t.test('call res and end correctly when dealing with objects', function(cb) {
|
t.test('call res and end correctly when dealing with objects', function(cb) {
|
||||||
const assertStatus = 202
|
const assertStatus = 202
|
||||||
// Calculated manually just in case
|
// Calculated manually just in case
|
||||||
const assertBodyLength = 7
|
const assertBodyLength = 7
|
||||||
const assertBody = { a: 1 }
|
const assertBody = { a: 1 }
|
||||||
let onFinish = cb.finish(function(body) {
|
let onFinish = function(body) {
|
||||||
|
try {
|
||||||
assert.strictEqual(ctx.status, assertStatus)
|
assert.strictEqual(ctx.status, assertStatus)
|
||||||
assert.strictEqual(ctx.body, assertBody)
|
assert.strictEqual(ctx.body, assertBody)
|
||||||
assert.strictEqual(ctx.headers['Content-Type'], 'application/json; charset=utf-8')
|
assert.strictEqual(ctx.res.statusCode, assertStatus)
|
||||||
assert.strictEqual(ctx.headers['Content-Length'], assertBodyLength)
|
assert.strictEqual(ctx.res.setHeader.callCount, 2)
|
||||||
|
assert.strictEqual(ctx.res.setHeader.firstCall[0], 'Content-Type')
|
||||||
if (method === 'GET') {
|
assert.strictEqual(ctx.res.setHeader.firstCall[1], 'application/json; charset=utf-8')
|
||||||
|
assert.strictEqual(ctx.res.setHeader.secondCall[0], 'Content-Length')
|
||||||
|
assert.strictEqual(ctx.res.setHeader.secondCall[1], assertBodyLength)
|
||||||
assert.ok(body)
|
assert.ok(body)
|
||||||
assert.strictEqual(body, '{"a":1}')
|
assert.strictEqual(body, '{"a":1}')
|
||||||
} else {
|
cb()
|
||||||
assert.notOk(body)
|
} catch (err) { cb(err) }
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.ok(ctx.res.writeHead.called)
|
|
||||||
assert.strictEqual(ctx.res.writeHead.firstCall[0], ctx.status)
|
|
||||||
assert.strictEqual(ctx.res.writeHead.firstCall[1], ctx.headers)
|
|
||||||
})
|
|
||||||
const ctx = createCtx({
|
const ctx = createCtx({
|
||||||
method: method,
|
|
||||||
status: assertStatus,
|
|
||||||
}, onFinish)
|
|
||||||
ctx.body = assertBody
|
|
||||||
|
|
||||||
let flaska = new Flaska({}, fakerHttp, fakeStream)
|
|
||||||
flaska.requestEnd(null, ctx)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.test('call res and end correctly when dealing with buffers', function(cb) {
|
|
||||||
const assertStatus = 202
|
|
||||||
// Calculated manually just in case
|
|
||||||
const assertBodyLength = 11
|
|
||||||
const assertBody = Buffer.from('hello world')
|
|
||||||
let onFinish = cb.finish(function(body) {
|
|
||||||
assert.strictEqual(ctx.status, assertStatus)
|
|
||||||
assert.strictEqual(ctx.body, assertBody)
|
|
||||||
assert.strictEqual(ctx.headers['Content-Type'], 'application/octet-stream')
|
|
||||||
assert.strictEqual(ctx.headers['Content-Length'], assertBodyLength)
|
|
||||||
|
|
||||||
if (method === 'GET') {
|
|
||||||
assert.ok(body)
|
|
||||||
assert.strictEqual(body, assertBody)
|
|
||||||
} else {
|
|
||||||
assert.notOk(body)
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.ok(ctx.res.writeHead.called)
|
|
||||||
assert.strictEqual(ctx.res.writeHead.firstCall[0], ctx.status)
|
|
||||||
assert.strictEqual(ctx.res.writeHead.firstCall[1], ctx.headers)
|
|
||||||
})
|
|
||||||
const ctx = createCtx({
|
|
||||||
method: method,
|
|
||||||
status: assertStatus,
|
|
||||||
}, onFinish)
|
|
||||||
ctx.body = assertBody
|
|
||||||
|
|
||||||
let flaska = new Flaska({}, fakerHttp, fakeStream)
|
|
||||||
flaska.requestEnd(null, ctx)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.test('call res and end correctly when dealing with null and no status override', function(cb) {
|
|
||||||
// Calculated manually just in case
|
|
||||||
const assertBodyLength = 0
|
|
||||||
const assertBody = null
|
|
||||||
let onFinish = cb.finish(function(body) {
|
|
||||||
assert.strictEqual(ctx.status, 204)
|
|
||||||
assert.strictEqual(ctx.body, assertBody)
|
|
||||||
assert.notOk(ctx.headers['Content-Type'])
|
|
||||||
assert.notOk(ctx.headers['Content-Length'])
|
|
||||||
|
|
||||||
assert.strictEqual(body, undefined)
|
|
||||||
|
|
||||||
assert.ok(ctx.res.writeHead.called)
|
|
||||||
assert.strictEqual(ctx.res.writeHead.firstCall[0], ctx.status)
|
|
||||||
assert.strictEqual(ctx.res.writeHead.firstCall[1], ctx.headers)
|
|
||||||
})
|
|
||||||
const ctx = createCtx({
|
|
||||||
method: method,
|
|
||||||
status: 200,
|
|
||||||
}, onFinish)
|
|
||||||
ctx.body = assertBody
|
|
||||||
|
|
||||||
let flaska = new Flaska({}, fakerHttp, fakeStream)
|
|
||||||
flaska.requestEnd(null, ctx)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.test('call res and end correctly when dealing with null and status override', function(cb) {
|
|
||||||
const assertStatus = 202
|
|
||||||
// Calculated manually just in case
|
|
||||||
const assertBody = null
|
|
||||||
let onFinish = cb.finish(function(body) {
|
|
||||||
assert.strictEqual(ctx.status, assertStatus)
|
|
||||||
assert.strictEqual(ctx.body, assertBody)
|
|
||||||
assert.notOk(ctx.headers['Content-Type'])
|
|
||||||
assert.strictEqual(ctx.headers['Content-Length'], 0)
|
|
||||||
|
|
||||||
assert.strictEqual(body, undefined)
|
|
||||||
|
|
||||||
assert.ok(ctx.res.writeHead.called)
|
|
||||||
assert.strictEqual(ctx.res.writeHead.firstCall[0], ctx.status)
|
|
||||||
assert.strictEqual(ctx.res.writeHead.firstCall[1], ctx.headers)
|
|
||||||
})
|
|
||||||
const ctx = createCtx({
|
|
||||||
method: method,
|
|
||||||
status: assertStatus,
|
status: assertStatus,
|
||||||
}, onFinish)
|
}, onFinish)
|
||||||
ctx.body = assertBody
|
ctx.body = assertBody
|
||||||
|
@ -180,25 +96,22 @@ t.describe('#requestEnd()', function() {
|
||||||
// Calculated manually just in case
|
// Calculated manually just in case
|
||||||
const assertBodyLength = 4
|
const assertBodyLength = 4
|
||||||
const assertBody = 'eða'
|
const assertBody = 'eða'
|
||||||
let onFinish = cb.finish(function(body) {
|
let onFinish = function(body) {
|
||||||
|
try {
|
||||||
assert.strictEqual(ctx.status, assertStatus)
|
assert.strictEqual(ctx.status, assertStatus)
|
||||||
assert.strictEqual(ctx.body, assertBody)
|
assert.strictEqual(ctx.body, assertBody)
|
||||||
assert.strictEqual(ctx.headers['Content-Type'], 'text/plain; charset=utf-8')
|
assert.strictEqual(ctx.res.statusCode, assertStatus)
|
||||||
assert.strictEqual(ctx.headers['Content-Length'], assertBodyLength)
|
assert.strictEqual(ctx.res.setHeader.callCount, 2)
|
||||||
|
assert.strictEqual(ctx.res.setHeader.firstCall[0], 'Content-Type')
|
||||||
if (method === 'GET') {
|
assert.strictEqual(ctx.res.setHeader.firstCall[1], 'text/plain; charset=utf-8')
|
||||||
|
assert.strictEqual(ctx.res.setHeader.secondCall[0], 'Content-Length')
|
||||||
|
assert.strictEqual(ctx.res.setHeader.secondCall[1], assertBodyLength)
|
||||||
assert.ok(body)
|
assert.ok(body)
|
||||||
assert.strictEqual(body, assertBody)
|
assert.strictEqual(body, assertBody)
|
||||||
} else {
|
cb()
|
||||||
assert.notOk(body)
|
} catch (err) { cb(err) }
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.ok(ctx.res.writeHead.called)
|
|
||||||
assert.strictEqual(ctx.res.writeHead.firstCall[0], ctx.status)
|
|
||||||
assert.strictEqual(ctx.res.writeHead.firstCall[1], ctx.headers)
|
|
||||||
})
|
|
||||||
const ctx = createCtx({
|
const ctx = createCtx({
|
||||||
method: method,
|
|
||||||
status: assertStatus,
|
status: assertStatus,
|
||||||
}, onFinish)
|
}, onFinish)
|
||||||
ctx.body = assertBody
|
ctx.body = assertBody
|
||||||
|
@ -212,24 +125,22 @@ t.describe('#requestEnd()', function() {
|
||||||
// Calculated manually just in case
|
// Calculated manually just in case
|
||||||
const assertBodyLength = 7
|
const assertBodyLength = 7
|
||||||
const assertBody = 4214124
|
const assertBody = 4214124
|
||||||
let onFinish = cb.finish(function(body) {
|
let onFinish = function(body) {
|
||||||
|
try {
|
||||||
assert.strictEqual(ctx.status, assertStatus)
|
assert.strictEqual(ctx.status, assertStatus)
|
||||||
assert.strictEqual(ctx.body, assertBody)
|
assert.strictEqual(ctx.body, assertBody)
|
||||||
assert.strictEqual(ctx.headers['Content-Type'], 'text/plain; charset=utf-8')
|
assert.strictEqual(ctx.res.statusCode, assertStatus)
|
||||||
assert.strictEqual(ctx.headers['Content-Length'], assertBodyLength)
|
assert.strictEqual(ctx.res.setHeader.callCount, 2)
|
||||||
|
assert.strictEqual(ctx.res.setHeader.firstCall[0], 'Content-Type')
|
||||||
if (method === 'GET') {
|
assert.strictEqual(ctx.res.setHeader.firstCall[1], 'text/plain; charset=utf-8')
|
||||||
|
assert.strictEqual(ctx.res.setHeader.secondCall[0], 'Content-Length')
|
||||||
|
assert.strictEqual(ctx.res.setHeader.secondCall[1], assertBodyLength)
|
||||||
assert.ok(body)
|
assert.ok(body)
|
||||||
assert.strictEqual(body, assertBody.toString())
|
assert.strictEqual(body, assertBody.toString())
|
||||||
} else {
|
cb()
|
||||||
assert.notOk(body)
|
} catch (err) { cb(err) }
|
||||||
}
|
}
|
||||||
assert.ok(ctx.res.writeHead.called)
|
|
||||||
assert.strictEqual(ctx.res.writeHead.firstCall[0], ctx.status)
|
|
||||||
assert.strictEqual(ctx.res.writeHead.firstCall[1], ctx.headers)
|
|
||||||
})
|
|
||||||
const ctx = createCtx({
|
const ctx = createCtx({
|
||||||
method: method,
|
|
||||||
status: assertStatus,
|
status: assertStatus,
|
||||||
}, onFinish)
|
}, onFinish)
|
||||||
ctx.body = assertBody
|
ctx.body = assertBody
|
||||||
|
@ -238,181 +149,20 @@ t.describe('#requestEnd()', function() {
|
||||||
flaska.requestEnd(null, ctx)
|
flaska.requestEnd(null, ctx)
|
||||||
})
|
})
|
||||||
|
|
||||||
if (method === 'GET') {
|
|
||||||
t.test('call handleRequest if body contains it', function(cb) {
|
|
||||||
const assertContentType = 'test/test'
|
|
||||||
const assertContentLength = 1241241
|
|
||||||
const assertHandle = stub().returnWith(function(checkCtx, secondary) {
|
|
||||||
assert.notOk(secondary)
|
|
||||||
assert.strictEqual(checkCtx, ctx)
|
|
||||||
assert.notOk(ctx.res.writeHead.called)
|
|
||||||
ctx.type = assertContentType
|
|
||||||
ctx.headers['Content-Length'] = assertContentLength
|
|
||||||
return assertBody
|
|
||||||
})
|
|
||||||
|
|
||||||
let body = new FileResponse()
|
|
||||||
let assertBody = { pipe: function() {} }
|
|
||||||
let onFinish = cb.finish(function(source, target, callback) {
|
|
||||||
assert.ok(assertHandle.called)
|
|
||||||
assert.ok(ctx.res.writeHead)
|
|
||||||
assert.strictEqual(ctx.body, assertBody)
|
|
||||||
assert.strictEqual(source, assertBody)
|
|
||||||
assert.strictEqual(target, ctx.res)
|
|
||||||
assert.strictEqual(typeof(callback), 'function')
|
|
||||||
assert.ok(ctx.res.writeHead.called)
|
|
||||||
assert.strictEqual(ctx.res.writeHead.firstCall[0], ctx.status)
|
|
||||||
assert.strictEqual(ctx.res.writeHead.firstCall[1], ctx.headers)
|
|
||||||
assert.strictEqual(ctx.headers['Content-Type'], assertContentType)
|
|
||||||
assert.strictEqual(ctx.headers['Content-Length'], assertContentLength)
|
|
||||||
})
|
|
||||||
const ctx = createCtx({
|
|
||||||
})
|
|
||||||
ctx.body = body
|
|
||||||
fakeStream.pipeline = onFinish
|
|
||||||
body.handleRequest = assertHandle
|
|
||||||
|
|
||||||
let flaska = new Flaska({}, fakerHttp, fakeStream)
|
|
||||||
|
|
||||||
try {
|
|
||||||
flaska.requestEnd(null, ctx)
|
|
||||||
} catch (err) {
|
|
||||||
cb(err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.test('call pipeline correctly when dealing with pipe', function(cb) {
|
|
||||||
const assertStatus = 211
|
|
||||||
const assertType = 'herp/derp'
|
|
||||||
const assertBody = { pipe: function() {} }
|
|
||||||
|
|
||||||
let onFinish = cb.finish(function(source, target, callback) {
|
|
||||||
assert.strictEqual(ctx.status, assertStatus)
|
|
||||||
assert.strictEqual(ctx.headers['Content-Type'], assertType)
|
|
||||||
assert.strictEqual(source, assertBody)
|
|
||||||
assert.strictEqual(target, ctx.res)
|
|
||||||
assert.strictEqual(typeof(callback), 'function')
|
|
||||||
assert.ok(ctx.res.writeHead.called)
|
|
||||||
assert.strictEqual(ctx.res.writeHead.firstCall[0], ctx.status)
|
|
||||||
assert.strictEqual(ctx.res.writeHead.firstCall[1], ctx.headers)
|
|
||||||
})
|
|
||||||
const ctx = createCtx({
|
|
||||||
status: assertStatus,
|
|
||||||
})
|
|
||||||
fakeStream.pipeline = onFinish
|
|
||||||
|
|
||||||
ctx.body = assertBody
|
|
||||||
ctx.type = assertType
|
|
||||||
let flaska = new Flaska({}, fakerHttp, fakeStream)
|
|
||||||
flaska.requestEnd(null, ctx)
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
t.test('call handleRequest if body contains it', function(cb) {
|
|
||||||
const assertContentType = 'test/test'
|
|
||||||
const assertContentLength = 1241241
|
|
||||||
const assertHandle = stub().returnWith(function(checkCtx, secondary) {
|
|
||||||
assert.notOk(secondary)
|
|
||||||
assert.strictEqual(checkCtx, ctx)
|
|
||||||
assert.notOk(ctx.res.writeHead.called)
|
|
||||||
ctx.type = assertContentType
|
|
||||||
ctx.headers['Content-Length'] = assertContentLength
|
|
||||||
return null
|
|
||||||
})
|
|
||||||
|
|
||||||
let body = new FileResponse()
|
|
||||||
let onFinish = cb.finish(function(body) {
|
|
||||||
assert.ok(assertHandle.called)
|
|
||||||
assert.ok(ctx.res.writeHead)
|
|
||||||
assert.strictEqual(ctx.body, null)
|
|
||||||
assert.notOk(body)
|
|
||||||
assert.ok(ctx.res.writeHead.called)
|
|
||||||
assert.strictEqual(ctx.res.writeHead.firstCall[0], ctx.status)
|
|
||||||
assert.strictEqual(ctx.res.writeHead.firstCall[1], ctx.headers)
|
|
||||||
assert.strictEqual(ctx.headers['Content-Type'], assertContentType)
|
|
||||||
assert.strictEqual(ctx.headers['Content-Length'], assertContentLength)
|
|
||||||
})
|
|
||||||
|
|
||||||
const ctx = createCtx({
|
|
||||||
method: method,
|
|
||||||
}, onFinish)
|
|
||||||
ctx.body = body
|
|
||||||
body.handleRequest = assertHandle
|
|
||||||
|
|
||||||
let flaska = new Flaska({}, fakerHttp, fakeStream)
|
|
||||||
|
|
||||||
try {
|
|
||||||
flaska.requestEnd(null, ctx)
|
|
||||||
} catch (err) {
|
|
||||||
cb(err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.test('call pipeline correctly when dealing with pipe', function(cb) {
|
|
||||||
const assertStatus = 211
|
|
||||||
const assertType = 'herp/derp'
|
|
||||||
const assertBody = { pipe: function() {}, destroy: stub() }
|
|
||||||
|
|
||||||
let onFinish = cb.finish(function(body) {
|
|
||||||
assert.strictEqual(ctx.status, assertStatus)
|
|
||||||
assert.strictEqual(ctx.headers['Content-Type'], assertType)
|
|
||||||
assert.notOk(ctx.headers['Content-Length'])
|
|
||||||
assert.notOk(body)
|
|
||||||
|
|
||||||
assert.ok(assertBody.destroy.called)
|
|
||||||
assert.ok(ctx.res.writeHead.called)
|
|
||||||
assert.strictEqual(ctx.res.writeHead.firstCall[0], ctx.status)
|
|
||||||
assert.strictEqual(ctx.res.writeHead.firstCall[1], ctx.headers)
|
|
||||||
})
|
|
||||||
const ctx = createCtx({
|
|
||||||
method: method,
|
|
||||||
status: assertStatus,
|
|
||||||
}, onFinish)
|
|
||||||
|
|
||||||
ctx.body = assertBody
|
|
||||||
ctx.type = assertType
|
|
||||||
let flaska = new Flaska({}, fakerHttp, fakeStream)
|
|
||||||
flaska.requestEnd(null, ctx)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
t.test('call _onerror with error if handleRequest throws error', function() {
|
|
||||||
let body = new FileResponse()
|
|
||||||
const assertError = new Error('Secrets of the Goddess')
|
|
||||||
let assertBody = { a : 1 }
|
|
||||||
const ctx = createCtx({
|
|
||||||
status: 204,
|
|
||||||
})
|
|
||||||
ctx.body = body
|
|
||||||
const assertHandle = stub().throws(assertError)
|
|
||||||
body.handleRequest = assertHandle
|
|
||||||
|
|
||||||
let flaska = new Flaska({}, fakerHttp, fakeStream)
|
|
||||||
flaska._onerror = stub().returnWith(function(err, checkCtx) {
|
|
||||||
assert.strictEqual(err, assertError)
|
|
||||||
checkCtx.body = assertBody
|
|
||||||
})
|
|
||||||
flaska.requestEnd(null, ctx)
|
|
||||||
assert.ok(assertHandle.called)
|
|
||||||
assert.ok(flaska._onerror.called)
|
|
||||||
assert.strictEqual(flaska._onerror.firstCall[0], assertError)
|
|
||||||
assert.ok(ctx.res.writeHead)
|
|
||||||
assert.strictEqual(ctx.body, assertBody)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.test('call res and end correctly when dealing with custom type', function(cb) {
|
t.test('call res and end correctly when dealing with custom type', function(cb) {
|
||||||
const assertStatus = 209
|
const assertStatus = 209
|
||||||
const assertBody = 'test'
|
const assertBody = 'test'
|
||||||
const assertType = 'something/else'
|
const assertType = 'something/else'
|
||||||
let onFinish = cb.finish(function(body) {
|
let onFinish = function(body) {
|
||||||
|
try {
|
||||||
assert.strictEqual(ctx.status, assertStatus)
|
assert.strictEqual(ctx.status, assertStatus)
|
||||||
assert.strictEqual(ctx.headers['Content-Type'], assertType)
|
assert.strictEqual(ctx.res.statusCode, assertStatus)
|
||||||
|
assert.strictEqual(ctx.res.setHeader.firstCall[0], 'Content-Type')
|
||||||
|
assert.strictEqual(ctx.res.setHeader.firstCall[1], assertType)
|
||||||
assert.strictEqual(body, assertBody)
|
assert.strictEqual(body, assertBody)
|
||||||
assert.ok(ctx.res.writeHead.called)
|
cb()
|
||||||
assert.strictEqual(ctx.res.writeHead.firstCall[0], ctx.status)
|
} catch (err) { cb(err) }
|
||||||
assert.strictEqual(ctx.res.writeHead.firstCall[1], ctx.headers)
|
}
|
||||||
})
|
|
||||||
const ctx = createCtx({
|
const ctx = createCtx({
|
||||||
status: assertStatus,
|
status: assertStatus,
|
||||||
body: assertBody,
|
body: assertBody,
|
||||||
|
@ -423,16 +173,45 @@ t.describe('#requestEnd()', function() {
|
||||||
flaska.requestEnd(null, ctx)
|
flaska.requestEnd(null, ctx)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.test('call pipeline correctly when dealing with pipe', function(cb) {
|
||||||
|
const assertStatus = 211
|
||||||
|
const assertType = 'herp/derp'
|
||||||
|
const assertBody = { pipe: function() {} }
|
||||||
|
|
||||||
|
let onFinish = function(source, target, callback) {
|
||||||
|
try {
|
||||||
|
assert.strictEqual(ctx.status, assertStatus)
|
||||||
|
assert.strictEqual(ctx.res.statusCode, assertStatus)
|
||||||
|
assert.strictEqual(ctx.res.setHeader.firstCall[0], 'Content-Type')
|
||||||
|
assert.strictEqual(ctx.res.setHeader.firstCall[1], assertType)
|
||||||
|
assert.strictEqual(source, assertBody)
|
||||||
|
assert.strictEqual(target, ctx.res)
|
||||||
|
assert.strictEqual(typeof(callback), 'function')
|
||||||
|
cb()
|
||||||
|
} catch (err) { cb(err) }
|
||||||
|
}
|
||||||
|
const ctx = createCtx({
|
||||||
|
status: assertStatus,
|
||||||
|
})
|
||||||
|
fakeStream.pipeline = onFinish
|
||||||
|
|
||||||
|
ctx.body = assertBody
|
||||||
|
ctx.type = assertType
|
||||||
|
let flaska = new Flaska({}, fakerHttp, fakeStream)
|
||||||
|
flaska.requestEnd(null, ctx)
|
||||||
|
})
|
||||||
|
|
||||||
t.test('call pipe should have default type', function(cb) {
|
t.test('call pipe should have default type', function(cb) {
|
||||||
let onFinish = cb.finish(function(source, target) {
|
let onFinish = function(source, target) {
|
||||||
assert.strictEqual(ctx.status, 200)
|
try {
|
||||||
assert.strictEqual(ctx.headers['Content-Type'], 'application/octet-stream')
|
assert.strictEqual(ctx.res.statusCode, 200)
|
||||||
|
assert.strictEqual(ctx.res.setHeader.firstCall[0], 'Content-Type')
|
||||||
|
assert.strictEqual(ctx.res.setHeader.firstCall[1], 'application/octet-stream')
|
||||||
assert.strictEqual(source, ctx.body)
|
assert.strictEqual(source, ctx.body)
|
||||||
assert.strictEqual(target, ctx.res)
|
assert.strictEqual(target, ctx.res)
|
||||||
assert.ok(ctx.res.writeHead.called)
|
cb()
|
||||||
assert.strictEqual(ctx.res.writeHead.firstCall[0], ctx.status)
|
} catch (err) { cb(err) }
|
||||||
assert.strictEqual(ctx.res.writeHead.firstCall[1], ctx.headers)
|
}
|
||||||
})
|
|
||||||
const ctx = createCtx({})
|
const ctx = createCtx({})
|
||||||
ctx.body = { pipe: function() {} }
|
ctx.body = { pipe: function() {} }
|
||||||
fakeStream.pipeline = onFinish
|
fakeStream.pipeline = onFinish
|
||||||
|
@ -441,33 +220,6 @@ t.describe('#requestEnd()', function() {
|
||||||
flaska.requestEnd(null, ctx)
|
flaska.requestEnd(null, ctx)
|
||||||
})
|
})
|
||||||
|
|
||||||
let tests = [
|
|
||||||
['png', 'image/png'],
|
|
||||||
['jpg', 'image/jpeg'],
|
|
||||||
['bmp', 'image/bmp'],
|
|
||||||
['js', 'text/javascript'],
|
|
||||||
]
|
|
||||||
|
|
||||||
tests.forEach(function(test) {
|
|
||||||
t.test(`call pipe with file extension ${test[0]} should mimetype ${test[1]}`, function(cb) {
|
|
||||||
let onFinish = cb.finish(function(source, target) {
|
|
||||||
assert.strictEqual(ctx.status, 200)
|
|
||||||
assert.strictEqual(ctx.headers['Content-Type'], test[1])
|
|
||||||
assert.strictEqual(source, ctx.body)
|
|
||||||
assert.strictEqual(target, ctx.res)
|
|
||||||
assert.ok(ctx.res.writeHead.called)
|
|
||||||
assert.strictEqual(ctx.res.writeHead.firstCall[0], ctx.status)
|
|
||||||
assert.strictEqual(ctx.res.writeHead.firstCall[1], ctx.headers)
|
|
||||||
})
|
|
||||||
const ctx = createCtx({})
|
|
||||||
ctx.body = { pipe: function() {}, path: './bla/test/temp.' + test[0] }
|
|
||||||
fakeStream.pipeline = onFinish
|
|
||||||
|
|
||||||
let flaska = new Flaska({}, fakerHttp, fakeStream)
|
|
||||||
flaska.requestEnd(null, ctx)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
t.test('should return immediately if req is finished', function() {
|
t.test('should return immediately if req is finished', function() {
|
||||||
let onFinish = function() {
|
let onFinish = function() {
|
||||||
throw new Error('should not be called')
|
throw new Error('should not be called')
|
||||||
|
@ -486,14 +238,15 @@ t.describe('#requestEnd()', function() {
|
||||||
const assertStatus = status
|
const assertStatus = status
|
||||||
const assertNotBody = 'test'
|
const assertNotBody = 'test'
|
||||||
const assertNotType = 'something/else'
|
const assertNotType = 'something/else'
|
||||||
let onFinish = cb.finish(function(body) {
|
let onFinish = function(body) {
|
||||||
|
try {
|
||||||
assert.strictEqual(ctx.status, assertStatus)
|
assert.strictEqual(ctx.status, assertStatus)
|
||||||
|
assert.strictEqual(ctx.res.statusCode, assertStatus)
|
||||||
assert.strictEqual(ctx.res.setHeader.callCount, 0)
|
assert.strictEqual(ctx.res.setHeader.callCount, 0)
|
||||||
assert.ok(ctx.res.writeHead.called)
|
|
||||||
assert.strictEqual(ctx.res.writeHead.firstCall[0], ctx.status)
|
|
||||||
assert.strictEqual(ctx.res.writeHead.firstCall[1], ctx.headers)
|
|
||||||
assert.notOk(body)
|
assert.notOk(body)
|
||||||
})
|
cb()
|
||||||
|
} catch (err) { cb(err) }
|
||||||
|
}
|
||||||
const ctx = createCtx({
|
const ctx = createCtx({
|
||||||
status: assertStatus,
|
status: assertStatus,
|
||||||
body: assertNotBody,
|
body: assertNotBody,
|
||||||
|
@ -507,38 +260,6 @@ t.describe('#requestEnd()', function() {
|
||||||
})
|
})
|
||||||
|
|
||||||
t.describe('#requestEnded()', function() {
|
t.describe('#requestEnded()', function() {
|
||||||
t.test('calls destroy if body is pipe and is not closed', function() {
|
|
||||||
const ctx = createCtx({
|
|
||||||
body: {
|
|
||||||
pipe: {},
|
|
||||||
closed: false,
|
|
||||||
destroy: spy(),
|
|
||||||
},
|
|
||||||
})
|
|
||||||
let flaska = new Flaska({}, fakerHttp)
|
|
||||||
flaska._afterCompiled = function() {}
|
|
||||||
|
|
||||||
flaska.requestEnded(ctx)
|
|
||||||
|
|
||||||
assert.ok(ctx.body.destroy.called)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.test('not call destroy if body is pipe but is closed', function() {
|
|
||||||
const ctx = createCtx({
|
|
||||||
body: {
|
|
||||||
pipe: {},
|
|
||||||
closed: true,
|
|
||||||
destroy: spy(),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
let flaska = new Flaska({}, fakerHttp)
|
|
||||||
flaska._afterCompiled = function() {}
|
|
||||||
|
|
||||||
flaska.requestEnded(ctx)
|
|
||||||
|
|
||||||
assert.notOk(ctx.body.destroy.called)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.test('calls afterCompiled correctly', function() {
|
t.test('calls afterCompiled correctly', function() {
|
||||||
const assertError = new Error('test')
|
const assertError = new Error('test')
|
||||||
const assertCtx = createCtx()
|
const assertCtx = createCtx()
|
||||||
|
|
|
@ -6,7 +6,7 @@ const indexMap = [
|
||||||
'thirdCall',
|
'thirdCall',
|
||||||
]
|
]
|
||||||
|
|
||||||
export function fakeHttp(inj1, inj2, inj3) {
|
export function fakeHttp(inj1, inj2) {
|
||||||
let intermediate = {
|
let intermediate = {
|
||||||
createServer: function(cb) {
|
createServer: function(cb) {
|
||||||
if (inj1) inj1(cb)
|
if (inj1) inj1(cb)
|
||||||
|
@ -15,9 +15,6 @@ export function fakeHttp(inj1, inj2, inj3) {
|
||||||
listen: function(port, ip, cb) {
|
listen: function(port, ip, cb) {
|
||||||
if (inj2) inj2(port, ip, cb)
|
if (inj2) inj2(port, ip, cb)
|
||||||
else if (cb) cb()
|
else if (cb) cb()
|
||||||
},
|
|
||||||
on: function(name, handler) {
|
|
||||||
if (inj3) inj3(name, handler)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,7 +24,6 @@ export function fakeHttp(inj1, inj2, inj3) {
|
||||||
|
|
||||||
export function createReq(def) {
|
export function createReq(def) {
|
||||||
return defaults(def, {
|
return defaults(def, {
|
||||||
headers: {},
|
|
||||||
on: spy(),
|
on: spy(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -44,7 +40,7 @@ export function createRes(def) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createCtx(def = {}, endHandler = null) {
|
export function createCtx(def, endHandler) {
|
||||||
return defaults(def, {
|
return defaults(def, {
|
||||||
req: createReq(),
|
req: createReq(),
|
||||||
res: createRes({ end: endHandler || spy() }),
|
res: createRes({ end: endHandler || spy() }),
|
||||||
|
@ -57,7 +53,6 @@ export function createCtx(def = {}, endHandler = null) {
|
||||||
body: null,
|
body: null,
|
||||||
type: null,
|
type: null,
|
||||||
length: null,
|
length: null,
|
||||||
headers: {},
|
|
||||||
log: {
|
log: {
|
||||||
error: spy(),
|
error: spy(),
|
||||||
info: spy(),
|
info: spy(),
|
||||||
|
|
|
@ -1,128 +0,0 @@
|
||||||
import { Eltro as t, assert, stub } from 'eltro'
|
|
||||||
import { setTimeout } from 'timers/promises'
|
|
||||||
import { Flaska, JsonHandler } from '../flaska.mjs'
|
|
||||||
import Client from './client.mjs'
|
|
||||||
|
|
||||||
const port = 51024
|
|
||||||
const log = {
|
|
||||||
fatal: stub(),
|
|
||||||
error: stub(),
|
|
||||||
warn: stub(),
|
|
||||||
info: stub(),
|
|
||||||
debug: stub(),
|
|
||||||
trace: stub(),
|
|
||||||
log: stub(),
|
|
||||||
}
|
|
||||||
const flaska = new Flaska({ log })
|
|
||||||
const client = new Client(port)
|
|
||||||
|
|
||||||
let reqBody = null
|
|
||||||
|
|
||||||
flaska.after(function(ctx) {
|
|
||||||
if (ctx.aborted) return
|
|
||||||
ctx.log.info(ctx.status)
|
|
||||||
})
|
|
||||||
|
|
||||||
flaska.post('/json', JsonHandler(), function(ctx) {
|
|
||||||
ctx.body = { success: true }
|
|
||||||
reqBody = ctx.req.body
|
|
||||||
})
|
|
||||||
flaska.post('/json/slow', JsonHandler(), async function(ctx) {
|
|
||||||
await setTimeout(300)
|
|
||||||
ctx.body = { success: true }
|
|
||||||
ctx.status = 201
|
|
||||||
reqBody = ctx.req.body
|
|
||||||
})
|
|
||||||
|
|
||||||
function reset() {
|
|
||||||
log.fatal.reset()
|
|
||||||
log.error.reset()
|
|
||||||
log.warn.reset()
|
|
||||||
log.info.reset()
|
|
||||||
log.debug.reset()
|
|
||||||
log.trace.reset()
|
|
||||||
log.log.reset()
|
|
||||||
}
|
|
||||||
|
|
||||||
t.before(function() {
|
|
||||||
return flaska.listenAsync(port)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.describe('/json', function() {
|
|
||||||
t.beforeEach(function() {
|
|
||||||
log.info.reset()
|
|
||||||
})
|
|
||||||
t.test('should return success and store body', async function() {
|
|
||||||
const assertBody = { a: '' }
|
|
||||||
for (let i = 0; i < 1010; i++) {
|
|
||||||
assertBody.a += 'aaaaaaaaaa'
|
|
||||||
}
|
|
||||||
reqBody = null
|
|
||||||
let body = await client.post('/json', assertBody)
|
|
||||||
assert.deepEqual(body, { success: true })
|
|
||||||
assert.deepStrictEqual(reqBody, assertBody)
|
|
||||||
assert.strictEqual(log.info.callCount, 1)
|
|
||||||
assert.strictEqual(log.info.firstCall[0], 200)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.test('should return and log correctly', async function() {
|
|
||||||
const assertBody = { a: '' }
|
|
||||||
for (let i = 0; i < 1010; i++) {
|
|
||||||
assertBody.a += 'aaaaaaaaaa'
|
|
||||||
}
|
|
||||||
reqBody = null
|
|
||||||
let body = await client.post('/json/slow', assertBody)
|
|
||||||
assert.deepEqual(body, { success: true })
|
|
||||||
assert.deepStrictEqual(reqBody, assertBody)
|
|
||||||
assert.strictEqual(log.info.callCount, 1)
|
|
||||||
assert.strictEqual(log.info.firstCall[0], 201)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.test('should fail if body is too big', async function() {
|
|
||||||
reset()
|
|
||||||
|
|
||||||
const assertBody = { a: '' }
|
|
||||||
for (let i = 0; i < 10300; i++) {
|
|
||||||
assertBody.a += 'aaaaaaaaaa'
|
|
||||||
}
|
|
||||||
|
|
||||||
let err = await assert.isRejected(client.post('/json', assertBody))
|
|
||||||
|
|
||||||
assert.strictEqual(err.body.status, 413)
|
|
||||||
assert.ok(log.error.called)
|
|
||||||
assert.match(log.error.firstCall[0].message, /10240/)
|
|
||||||
assert.strictEqual(log.error.firstCall[0].status, 413)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.test('should fail if not a valid json', async function() {
|
|
||||||
reset()
|
|
||||||
|
|
||||||
let err = await assert.isRejected(client.customRequest('POST', '/json', 'XXXXX'))
|
|
||||||
|
|
||||||
assert.strictEqual(err.body.status, 400)
|
|
||||||
assert.match(err.body.message, /invalid json/i)
|
|
||||||
assert.match(err.body.message, /token[^X]+X/i)
|
|
||||||
assert.strictEqual(err.body.request, 'XXXXX')
|
|
||||||
assert.strictEqual(log.error.callCount, 1)
|
|
||||||
assert.match(log.error.firstCall[0].message, /invalid json/i)
|
|
||||||
assert.match(log.error.firstCall[0].message, /token[^X]+X/i)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.test('should handle incomplete requests correctly', async function() {
|
|
||||||
reset()
|
|
||||||
|
|
||||||
let req = await client.customRequest('POST', '/json', 'aaaa', { returnRequest: true })
|
|
||||||
|
|
||||||
req.write('part1')
|
|
||||||
|
|
||||||
await setTimeout(20)
|
|
||||||
|
|
||||||
req.destroy()
|
|
||||||
|
|
||||||
await setTimeout(20)
|
|
||||||
|
|
||||||
assert.strictEqual(log.error.callCount, 0)
|
|
||||||
assert.strictEqual(log.info.callCount, 0)
|
|
||||||
// assert.strictEqual(log.info.firstCall[0].message, 'aborted')
|
|
||||||
})
|
|
||||||
})
|
|
|
@ -1,77 +1,20 @@
|
||||||
import fsSync from 'fs'
|
import { Eltro as t, assert} from 'eltro'
|
||||||
import http from 'http'
|
import { Flaska } from '../flaska.mjs'
|
||||||
import fs from 'fs/promises'
|
|
||||||
import formidable from 'formidable'
|
|
||||||
import { Eltro as t, assert, stub } from 'eltro'
|
|
||||||
import { setTimeout } from 'timers/promises'
|
|
||||||
import { Flaska, FormidableHandler, FileResponse } from '../flaska.mjs'
|
|
||||||
import Client from './client.mjs'
|
import Client from './client.mjs'
|
||||||
|
|
||||||
const port = 51025
|
const port = 51024
|
||||||
const log = {
|
const flaska = new Flaska({})
|
||||||
fatal: stub(),
|
|
||||||
error: stub(),
|
|
||||||
warn: stub(),
|
|
||||||
info: stub(),
|
|
||||||
debug: stub(),
|
|
||||||
trace: stub(),
|
|
||||||
log: stub(),
|
|
||||||
}
|
|
||||||
const flaska = new Flaska({ log })
|
|
||||||
const client = new Client(port)
|
const client = new Client(port)
|
||||||
|
|
||||||
let file = null
|
let reqBody = null
|
||||||
let uploaded = []
|
|
||||||
|
|
||||||
flaska.after(function(ctx) {
|
|
||||||
if (ctx.aborted) return
|
|
||||||
ctx.log.info(ctx.status)
|
|
||||||
})
|
|
||||||
|
|
||||||
flaska.get('/', function(ctx) {
|
flaska.get('/', function(ctx) {
|
||||||
ctx.body = { status: true }
|
ctx.body = { status: true }
|
||||||
})
|
})
|
||||||
flaska.get('/timeout', function(ctx) {
|
flaska.post('/test', function(ctx) {
|
||||||
return new Promise(function() {})
|
ctx.body = { success: true }
|
||||||
|
// console.log(ctx)
|
||||||
})
|
})
|
||||||
flaska.get('/file', function(ctx) {
|
|
||||||
file = fsSync.createReadStream('./test/test.png')
|
|
||||||
ctx.body = file
|
|
||||||
})
|
|
||||||
flaska.get('/filehandle', function(ctx) {
|
|
||||||
return fs.stat('./test/test.txt').then(function(stat) {
|
|
||||||
ctx.body = new FileResponse('./test/test.txt', stat)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
flaska.post('/file/upload', FormidableHandler(formidable, {
|
|
||||||
uploadDir: './test/upload'
|
|
||||||
}), function(ctx) {
|
|
||||||
uploaded.push(ctx.req.file)
|
|
||||||
ctx.body = ctx.req.file
|
|
||||||
})
|
|
||||||
flaska.post('/file/upload/many', FormidableHandler(formidable, {
|
|
||||||
uploadDir: './test/upload',
|
|
||||||
}), function(ctx) {
|
|
||||||
uploaded.push(ctx.req.files.herp)
|
|
||||||
uploaded.push(ctx.req.files.derp)
|
|
||||||
ctx.body = ctx.req.files
|
|
||||||
})
|
|
||||||
flaska.get('/file/leak', function(ctx) {
|
|
||||||
file = fsSync.createReadStream('./test/test.png')
|
|
||||||
ctx.body = file
|
|
||||||
|
|
||||||
return new Promise(function() {})
|
|
||||||
})
|
|
||||||
|
|
||||||
function reset() {
|
|
||||||
log.fatal.reset()
|
|
||||||
log.error.reset()
|
|
||||||
log.warn.reset()
|
|
||||||
log.info.reset()
|
|
||||||
log.debug.reset()
|
|
||||||
log.trace.reset()
|
|
||||||
log.log.reset()
|
|
||||||
}
|
|
||||||
|
|
||||||
t.before(function() {
|
t.before(function() {
|
||||||
return flaska.listenAsync(port)
|
return flaska.listenAsync(port)
|
||||||
|
@ -85,339 +28,14 @@ t.describe('/', function() {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
t.describe('/timeout', function() {
|
t.describe('/test', function() {
|
||||||
t.test('server should handle timeout', async function() {
|
t.test('should return success and store body', async function() {
|
||||||
reset()
|
reqBody = null
|
||||||
|
let body = await client.post('/test')
|
||||||
let err = await assert.isRejected(client.customRequest('GET', '/timeout', null, { timeout: 20 }))
|
assert.deepEqual(body, { success: true })
|
||||||
|
|
||||||
assert.match(err.message, /timed out/)
|
|
||||||
assert.notOk(log.error.called)
|
|
||||||
assert.notOk(log.info.called)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
t.describe('/file', function() {
|
|
||||||
t.test('server should pipe', async function() {
|
|
||||||
log.error.reset()
|
|
||||||
|
|
||||||
let target = fsSync.createWriteStream('./test_tmp.png')
|
|
||||||
|
|
||||||
await client.customRequest('GET', '/file', null, { toPipe: target })
|
|
||||||
|
|
||||||
while (!target.closed && !file.closed) {
|
|
||||||
await setTimeout(10)
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.ok(target.closed)
|
|
||||||
assert.ok(file.closed)
|
|
||||||
|
|
||||||
let [statSource, statTarget] = await Promise.all([
|
|
||||||
fs.stat('./test/test.png'),
|
|
||||||
fs.stat('./test_tmp.png'),
|
|
||||||
])
|
|
||||||
|
|
||||||
assert.strictEqual(statSource.size, statTarget.size)
|
|
||||||
})
|
|
||||||
t.test('server should autoclose body file handles on errors', async function() {
|
|
||||||
reset()
|
|
||||||
|
|
||||||
file = null
|
|
||||||
|
|
||||||
let req = await client.customRequest('GET', '/file/leak', null, { returnRequest: true })
|
|
||||||
|
|
||||||
req.end()
|
|
||||||
|
|
||||||
while (!file) {
|
|
||||||
await setTimeout(10)
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.ok(file)
|
|
||||||
assert.notOk(file.closed)
|
|
||||||
|
|
||||||
req.destroy()
|
|
||||||
|
|
||||||
while (!file.closed) {
|
|
||||||
await setTimeout(10)
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.strictEqual(log.error.callCount, 0)
|
|
||||||
assert.strictEqual(log.info.callCount, 0)
|
|
||||||
|
|
||||||
assert.ok(file.closed)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
t.describe('/filehandle', function() {
|
|
||||||
const agent = new http.Agent({
|
|
||||||
keepAlive: true,
|
|
||||||
maxSockets: 1,
|
|
||||||
keepAliveMsecs: 3000,
|
|
||||||
})
|
|
||||||
|
|
||||||
t.test('server should send correctly', async function() {
|
|
||||||
log.error.reset()
|
|
||||||
|
|
||||||
let res = await client.customRequest('GET', '/filehandle', null, { getRaw: true, agent: agent })
|
|
||||||
assert.strictEqual(res.status, 200)
|
|
||||||
assert.strictEqual(res.headers['content-length'], '11')
|
|
||||||
assert.strictEqual(res.headers['content-type'], 'text/plain')
|
|
||||||
assert.match(res.headers['etag'], /\"\d+-11-\d+"/)
|
|
||||||
assert.ok(res.headers['last-modified'])
|
|
||||||
let d = new Date(Date.parse(res.headers['last-modified']))
|
|
||||||
let etag = res.headers['etag']
|
|
||||||
assert.ok(d.getTime())
|
|
||||||
|
|
||||||
res = await client.customRequest('GET', '/filehandle', null, { getRaw: true, agent: agent,
|
|
||||||
headers: {
|
|
||||||
'If-Modified-Since': d.toUTCString()
|
|
||||||
},
|
|
||||||
})
|
|
||||||
assert.strictEqual(res.status, 304)
|
|
||||||
assert.strictEqual(res.data, '')
|
|
||||||
assert.strictEqual(res.headers['etag'], etag)
|
|
||||||
|
|
||||||
res = await client.customRequest('GET', '/filehandle', null, { getRaw: true, agent: agent,
|
|
||||||
headers: {
|
|
||||||
'If-None-Match': etag
|
|
||||||
},
|
|
||||||
})
|
|
||||||
assert.strictEqual(res.status, 304)
|
|
||||||
assert.strictEqual(res.data, '')
|
|
||||||
assert.strictEqual(res.headers['etag'], etag)
|
|
||||||
|
|
||||||
res = await client.customRequest('GET', '/filehandle', null, { getRaw: true, agent: agent,
|
|
||||||
headers: {
|
|
||||||
'Range': 'bytes=2-5'
|
|
||||||
},
|
|
||||||
})
|
|
||||||
assert.strictEqual(res.status, 206)
|
|
||||||
assert.strictEqual(res.data, 'llo ')
|
|
||||||
assert.strictEqual(res.headers['content-length'], '4')
|
|
||||||
|
|
||||||
|
|
||||||
res = await client.customRequest('GET', '/filehandle', null, { getRaw: true, agent: agent,
|
|
||||||
headers: {
|
|
||||||
'Range': 'bytes=0-0'
|
|
||||||
},
|
|
||||||
})
|
|
||||||
assert.strictEqual(res.status, 206)
|
|
||||||
assert.strictEqual(res.data, 'H')
|
|
||||||
assert.strictEqual(res.headers['content-length'], '1')
|
|
||||||
})
|
|
||||||
|
|
||||||
t.after(function() {
|
|
||||||
agent.destroy()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
t.describe('/file/upload', function() {
|
|
||||||
t.test('server should upload file', async function() {
|
|
||||||
let res = await client.upload('/file/upload', './test/test.png')
|
|
||||||
|
|
||||||
let [statSource, statTarget] = await Promise.all([
|
|
||||||
fs.stat('./test/test.png'),
|
|
||||||
fs.stat(res.path),
|
|
||||||
])
|
|
||||||
|
|
||||||
assert.strictEqual(statSource.size, statTarget.size)
|
|
||||||
assert.strictEqual(statSource.size, res.size)
|
|
||||||
assert.strictEqual(res.type, 'image/png')
|
|
||||||
})
|
|
||||||
t.test('server should have correct type', async function() {
|
|
||||||
let res = await client.upload('/file/upload', './test/test.jpg')
|
|
||||||
|
|
||||||
let [statSource, statTarget] = await Promise.all([
|
|
||||||
fs.stat('./test/test.jpg'),
|
|
||||||
fs.stat(res.path),
|
|
||||||
])
|
|
||||||
|
|
||||||
assert.strictEqual(statSource.size, statTarget.size)
|
|
||||||
assert.strictEqual(statSource.size, res.size)
|
|
||||||
assert.strictEqual(res.type, 'image/jpeg')
|
|
||||||
})
|
|
||||||
t.test('server should use type from user', async function() {
|
|
||||||
const assertType = 'some/test/here'
|
|
||||||
let res = await client.upload('/file/upload', './test/test.jpg', 'POST', {}, assertType)
|
|
||||||
|
|
||||||
let [statSource, statTarget] = await Promise.all([
|
|
||||||
fs.stat('./test/test.jpg'),
|
|
||||||
fs.stat(res.path),
|
|
||||||
])
|
|
||||||
|
|
||||||
assert.strictEqual(statSource.size, statTarget.size)
|
|
||||||
assert.strictEqual(statSource.size, res.size)
|
|
||||||
assert.strictEqual(res.type, assertType)
|
|
||||||
})
|
|
||||||
t.test('server should attempt to correct type if type is application/octet-stream', async function() {
|
|
||||||
const assertNotType = 'application/octet-stream'
|
|
||||||
let res = await client.upload('/file/upload', './test/test.jpg', 'POST', {}, assertNotType)
|
|
||||||
|
|
||||||
let [statSource, statTarget] = await Promise.all([
|
|
||||||
fs.stat('./test/test.jpg'),
|
|
||||||
fs.stat(res.path),
|
|
||||||
])
|
|
||||||
|
|
||||||
assert.strictEqual(statSource.size, statTarget.size)
|
|
||||||
assert.strictEqual(statSource.size, res.size)
|
|
||||||
assert.notStrictEqual(res.type, assertNotType)
|
|
||||||
assert.strictEqual(res.type, 'image/jpeg')
|
|
||||||
})
|
|
||||||
t.test('server fall back to type application/octet-stream if unknown', async function() {
|
|
||||||
let res = await client.upload('/file/upload', './test/test.gibberish')
|
|
||||||
|
|
||||||
let [statSource, statTarget] = await Promise.all([
|
|
||||||
fs.stat('./test/test.gibberish'),
|
|
||||||
fs.stat(res.path),
|
|
||||||
])
|
|
||||||
|
|
||||||
assert.strictEqual(statSource.size, statTarget.size)
|
|
||||||
assert.strictEqual(statSource.size, res.size)
|
|
||||||
assert.strictEqual(res.type, 'application/octet-stream')
|
|
||||||
|
|
||||||
res = await client.upload('/file/upload', './test/test.gibberish', 'POST', {}, 'application/octet-stream')
|
|
||||||
assert.strictEqual(res.type, 'application/octet-stream')
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
t.describe('/file/upload/many', function() {
|
|
||||||
t.test('server should upload file', async function() {
|
|
||||||
let res = await client.upload('/file/upload/many', {
|
|
||||||
herp: './test/test.jpg',
|
|
||||||
derp: './test/test.png',
|
|
||||||
})
|
|
||||||
|
|
||||||
let [statSourcePng, statSourceJpg, statTargetHerp, statTargetDerp] = await Promise.all([
|
|
||||||
fs.stat('./test/test.png'),
|
|
||||||
fs.stat('./test/test.jpg'),
|
|
||||||
fs.stat(res.herp.path),
|
|
||||||
fs.stat(res.derp.path),
|
|
||||||
])
|
|
||||||
|
|
||||||
assert.strictEqual(statSourceJpg.size, statTargetHerp.size)
|
|
||||||
assert.strictEqual(statSourceJpg.size, res.herp.size)
|
|
||||||
assert.strictEqual(statSourcePng.size, statTargetDerp.size)
|
|
||||||
assert.strictEqual(statSourcePng.size, res.derp.size)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
t.describe('HEAD', function() {
|
|
||||||
const agent = new http.Agent({
|
|
||||||
keepAlive: true,
|
|
||||||
maxSockets: 1,
|
|
||||||
keepAliveMsecs: 3000,
|
|
||||||
})
|
|
||||||
|
|
||||||
t.describe('/file', function() {
|
|
||||||
t.test('server return HEAD for pipes', async function() {
|
|
||||||
log.error.reset()
|
|
||||||
|
|
||||||
let res = await client.customRequest('HEAD', '/file', null, { getRaw: true, agent: agent })
|
|
||||||
|
|
||||||
while (!file.closed) {
|
|
||||||
await setTimeout(10)
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.ok(file.closed)
|
|
||||||
|
|
||||||
let statSource = await fs.stat('./test/test.png')
|
|
||||||
|
|
||||||
assert.strictEqual(res.data, '')
|
|
||||||
assert.strictEqual(res.headers['content-type'], 'image/png')
|
|
||||||
assert.notOk(res.headers['content-length'])
|
|
||||||
})
|
|
||||||
t.test('server should autoclose body file handles on errors', async function() {
|
|
||||||
reset()
|
|
||||||
|
|
||||||
file = null
|
|
||||||
|
|
||||||
let req = await client.customRequest('HEAD', '/file/leak', null, { returnRequest: true })
|
|
||||||
|
|
||||||
req.end()
|
|
||||||
|
|
||||||
while (!file) {
|
|
||||||
await setTimeout(10)
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.ok(file)
|
|
||||||
assert.notOk(file.closed)
|
|
||||||
|
|
||||||
req.destroy()
|
|
||||||
|
|
||||||
while (!file.closed) {
|
|
||||||
await setTimeout(10)
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.strictEqual(log.error.callCount, 0)
|
|
||||||
assert.strictEqual(log.info.callCount, 0)
|
|
||||||
|
|
||||||
assert.ok(file.closed)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
t.describe('/filehandle', function() {
|
|
||||||
t.test('server should send correctly', async function() {
|
|
||||||
log.error.reset()
|
|
||||||
|
|
||||||
let res = await client.customRequest('HEAD', '/filehandle', null, { getRaw: true, agent: agent })
|
|
||||||
assert.strictEqual(res.status, 200)
|
|
||||||
assert.strictEqual(res.headers['content-length'], '11')
|
|
||||||
assert.strictEqual(res.headers['content-type'], 'text/plain')
|
|
||||||
assert.match(res.headers['etag'], /\"\d+-11-\d+"/)
|
|
||||||
assert.ok(res.headers['last-modified'])
|
|
||||||
let d = new Date(Date.parse(res.headers['last-modified']))
|
|
||||||
let etag = res.headers['etag']
|
|
||||||
assert.ok(d.getTime())
|
|
||||||
assert.strictEqual(res.data, '')
|
|
||||||
|
|
||||||
res = await client.customRequest('HEAD', '/filehandle', null, { getRaw: true, agent: agent,
|
|
||||||
headers: {
|
|
||||||
'If-Modified-Since': d.toUTCString()
|
|
||||||
},
|
|
||||||
})
|
|
||||||
assert.strictEqual(res.status, 304)
|
|
||||||
assert.strictEqual(res.data, '')
|
|
||||||
assert.strictEqual(res.headers['etag'], etag)
|
|
||||||
|
|
||||||
res = await client.customRequest('HEAD', '/filehandle', null, { getRaw: true, agent: agent,
|
|
||||||
headers: {
|
|
||||||
'If-None-Match': etag
|
|
||||||
},
|
|
||||||
})
|
|
||||||
assert.strictEqual(res.status, 304)
|
|
||||||
assert.strictEqual(res.data, '')
|
|
||||||
assert.strictEqual(res.headers['etag'], etag)
|
|
||||||
|
|
||||||
res = await client.customRequest('HEAD', '/filehandle', null, { getRaw: true, agent: agent,
|
|
||||||
headers: {
|
|
||||||
'Range': 'bytes=2-5'
|
|
||||||
},
|
|
||||||
})
|
|
||||||
assert.strictEqual(res.status, 206)
|
|
||||||
assert.strictEqual(res.data, '')
|
|
||||||
assert.strictEqual(res.headers['content-length'], '4')
|
|
||||||
|
|
||||||
|
|
||||||
res = await client.customRequest('HEAD', '/filehandle', null, { getRaw: true, agent: agent,
|
|
||||||
headers: {
|
|
||||||
'Range': 'bytes=0-0'
|
|
||||||
},
|
|
||||||
})
|
|
||||||
assert.strictEqual(res.status, 206)
|
|
||||||
assert.strictEqual(res.data, '')
|
|
||||||
assert.strictEqual(res.headers['content-length'], '1')
|
|
||||||
})
|
|
||||||
|
|
||||||
t.after(function() {
|
|
||||||
agent.destroy()
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
t.after(function() {
|
t.after(function() {
|
||||||
return Promise.all([
|
return flaska.closeAsync()
|
||||||
fs.rm('./test_tmp.png', { force: true }),
|
|
||||||
Promise.all(uploaded.map(file => fs.rm(file.path, { force: true }))),
|
|
||||||
flaska.closeAsync(),
|
|
||||||
])
|
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,11 +1,5 @@
|
||||||
import os from 'os'
|
import { Eltro as t, assert} from 'eltro'
|
||||||
import path from 'path'
|
import { QueryHandler, JsonHandler } from '../flaska.mjs'
|
||||||
import { Buffer } from 'buffer'
|
|
||||||
import { Eltro as t, assert, stub} from 'eltro'
|
|
||||||
import { QueryHandler, JsonHandler, FormidableHandler, HttpError, CorsHandler } from '../flaska.mjs'
|
|
||||||
import { createCtx } from './helper.mjs'
|
|
||||||
import { finished } from 'stream'
|
|
||||||
import { setTimeout } from 'timers/promises'
|
|
||||||
|
|
||||||
t.describe('#QueryHandler()', function() {
|
t.describe('#QueryHandler()', function() {
|
||||||
let queryHandler = QueryHandler()
|
let queryHandler = QueryHandler()
|
||||||
|
@ -29,721 +23,26 @@ t.describe('#QueryHandler()', function() {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
t.describe('#CorsHandler()', function() {
|
|
||||||
let corsHandler
|
|
||||||
let ctx
|
|
||||||
|
|
||||||
t.test('should return a handler', function() {
|
|
||||||
corsHandler = CorsHandler()
|
|
||||||
assert.strictEqual(typeof(corsHandler), 'function')
|
|
||||||
})
|
|
||||||
|
|
||||||
t.describe('OPTIONS', function() {
|
|
||||||
t.beforeEach(function() {
|
|
||||||
ctx = createCtx()
|
|
||||||
ctx.method = 'OPTIONS'
|
|
||||||
})
|
|
||||||
|
|
||||||
t.test('should set status and headers', function() {
|
|
||||||
const assertOrigin = 'http://my.site.here'
|
|
||||||
const assertRequestHeaders = 'asdf,foobar'
|
|
||||||
|
|
||||||
corsHandler = CorsHandler({
|
|
||||||
allowedOrigins: [assertOrigin],
|
|
||||||
})
|
|
||||||
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['Vary'])
|
|
||||||
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['Vary'], 'Origin')
|
|
||||||
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.notOk(ctx.headers['Access-Control-Allow-Credentials'])
|
|
||||||
assert.notOk(ctx.headers['Access-Control-Max-Age'])
|
|
||||||
assert.strictEqual(ctx.status, 204)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.test('should set Allow-Credentials if credentials is specified', function() {
|
|
||||||
const assertOrigin = 'http://my.site.here'
|
|
||||||
const assertRequestHeaders = 'asdf,foobar'
|
|
||||||
|
|
||||||
corsHandler = CorsHandler({
|
|
||||||
allowedOrigins: [assertOrigin],
|
|
||||||
credentials: true,
|
|
||||||
})
|
|
||||||
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['Vary'])
|
|
||||||
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['Vary'], 'Origin')
|
|
||||||
assert.strictEqual(ctx.headers['Access-Control-Allow-Credentials'], 'true')
|
|
||||||
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.notOk(ctx.headers['Access-Control-Max-Age'])
|
|
||||||
assert.strictEqual(ctx.status, 204)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.test('should set Max-Age if maxAge is specified', function() {
|
|
||||||
const assertOrigin = 'http://my.site.here'
|
|
||||||
const assertRequestHeaders = 'asdf,foobar'
|
|
||||||
const assertMaxAge = '600'
|
|
||||||
|
|
||||||
corsHandler = CorsHandler({
|
|
||||||
allowedOrigins: [assertOrigin],
|
|
||||||
maxAge: assertMaxAge,
|
|
||||||
})
|
|
||||||
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['Vary'])
|
|
||||||
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['Vary'], 'Origin')
|
|
||||||
assert.strictEqual(ctx.headers['Access-Control-Allow-Origin'], assertOrigin)
|
|
||||||
assert.strictEqual(ctx.headers['Access-Control-Max-Age'], assertMaxAge)
|
|
||||||
assert.strictEqual(ctx.headers['Access-Control-Allow-Methods'], 'GET,HEAD,PUT,POST,DELETE,PATCH')
|
|
||||||
assert.strictEqual(ctx.headers['Access-Control-Allow-Headers'], assertRequestHeaders)
|
|
||||||
assert.notOk(ctx.headers['Access-Control-Allow-Credentials'])
|
|
||||||
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.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['Vary'], 'Origin')
|
|
||||||
assert.notOk(ctx.headers['Access-Control-Allow-Credentials'])
|
|
||||||
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.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['Vary'], 'Origin')
|
|
||||||
assert.notOk(ctx.headers['Access-Control-Allow-Credentials'])
|
|
||||||
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.test('should not add any headers if origin missing', function() {
|
|
||||||
corsHandler = CorsHandler({
|
|
||||||
allowedOrigins: ['https://test.com'],
|
|
||||||
})
|
|
||||||
ctx.req.headers['origin'] = null
|
|
||||||
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['Vary'], 'Origin')
|
|
||||||
assert.notOk(ctx.headers['Access-Control-Allow-Origin'])
|
|
||||||
assert.notOk(ctx.headers['Access-Control-Allow-Methods'])
|
|
||||||
assert.notOk(ctx.headers['Access-Control-Allow-Headers'])
|
|
||||||
assert.strictEqual(ctx.status, 204)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.test('should not add any headers if origin not found', function() {
|
|
||||||
const assertOrigin = 'http://my.site.here'
|
|
||||||
const assertAllowedMethods = 'GET,HEAD'
|
|
||||||
|
|
||||||
corsHandler = CorsHandler({
|
|
||||||
allowedOrigins: ['https://my.site.here'],
|
|
||||||
allowedMethods: assertAllowedMethods,
|
|
||||||
allowedHeaders: false,
|
|
||||||
})
|
|
||||||
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['Vary'], 'Origin')
|
|
||||||
assert.notOk(ctx.headers['Access-Control-Allow-Origin'])
|
|
||||||
assert.notOk(ctx.headers['Access-Control-Allow-Methods'])
|
|
||||||
assert.notOk(ctx.headers['Access-Control-Allow-Headers'])
|
|
||||||
assert.strictEqual(ctx.status, 204)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.test('should not add any headers if request-method is missing', function() {
|
|
||||||
const assertOrigin = 'http://my.site.here'
|
|
||||||
|
|
||||||
corsHandler = CorsHandler({
|
|
||||||
allowedOrigins: ['http://my.site.here'],
|
|
||||||
})
|
|
||||||
ctx.req.headers['origin'] = assertOrigin
|
|
||||||
delete ctx.req.headers['access-control-request-method']
|
|
||||||
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['Vary'], 'Origin')
|
|
||||||
assert.notOk(ctx.headers['Access-Control-Allow-Origin'])
|
|
||||||
assert.notOk(ctx.headers['Access-Control-Allow-Methods'])
|
|
||||||
assert.notOk(ctx.headers['Access-Control-Allow-Headers'])
|
|
||||||
assert.strictEqual(ctx.status, 204)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.test('should set headers if allowedOrigins has a *', function() {
|
|
||||||
const assertOrigin = 'http://my.site.here'
|
|
||||||
|
|
||||||
corsHandler = CorsHandler({
|
|
||||||
allowedOrigins: ['*'],
|
|
||||||
})
|
|
||||||
ctx.req.headers['origin'] = assertOrigin
|
|
||||||
ctx.req.headers['access-control-request-method'] = 'GET'
|
|
||||||
|
|
||||||
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['Vary'], 'Origin')
|
|
||||||
assert.strictEqual(ctx.headers['Access-Control-Allow-Origin'], assertOrigin)
|
|
||||||
assert.ok(ctx.headers['Access-Control-Allow-Methods'])
|
|
||||||
assert.strictEqual(ctx.status, 204)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
t.describe('GET/POST/DELETE/PATCH/PUT', function() {
|
|
||||||
let testMethods = ['GET', 'POST', 'DELETE', 'PATCH', 'PUT']
|
|
||||||
|
|
||||||
t.test('should set header but no status', function() {
|
|
||||||
testMethods.forEach(function(method) {
|
|
||||||
ctx = createCtx()
|
|
||||||
ctx.method = method
|
|
||||||
ctx.status = method
|
|
||||||
|
|
||||||
const assertOrigin = 'http://my.site.here'
|
|
||||||
|
|
||||||
corsHandler = CorsHandler({
|
|
||||||
allowedOrigins: [assertOrigin],
|
|
||||||
})
|
|
||||||
ctx.req.headers['origin'] = assertOrigin
|
|
||||||
ctx.req.headers['access-control-request-headers'] = 'asdfasdfasdfsfad'
|
|
||||||
|
|
||||||
assert.notOk(ctx.headers['Vary'])
|
|
||||||
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['Vary'], 'Origin')
|
|
||||||
assert.strictEqual(ctx.headers['Access-Control-Allow-Origin'], assertOrigin)
|
|
||||||
assert.notOk(ctx.headers['Access-Control-Allow-Credentials'])
|
|
||||||
assert.notOk(ctx.headers['Access-Control-Allow-Methods'])
|
|
||||||
assert.notOk(ctx.headers['Access-Control-Allow-Headers'])
|
|
||||||
assert.notOk(ctx.headers['Access-Control-Expose-Headers'])
|
|
||||||
assert.strictEqual(ctx.status, method)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
t.test('should set credential header if specifed', function() {
|
|
||||||
testMethods.forEach(function(method) {
|
|
||||||
ctx = createCtx()
|
|
||||||
ctx.method = method
|
|
||||||
ctx.status = method
|
|
||||||
|
|
||||||
const assertOrigin = 'http://my.site.here'
|
|
||||||
|
|
||||||
corsHandler = CorsHandler({
|
|
||||||
allowedOrigins: [assertOrigin],
|
|
||||||
credentials: true,
|
|
||||||
})
|
|
||||||
ctx.req.headers['origin'] = assertOrigin
|
|
||||||
|
|
||||||
assert.notOk(ctx.headers['Vary'])
|
|
||||||
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['Vary'], 'Origin')
|
|
||||||
assert.strictEqual(ctx.headers['Access-Control-Allow-Credentials'], 'true')
|
|
||||||
assert.strictEqual(ctx.headers['Access-Control-Allow-Origin'], assertOrigin)
|
|
||||||
assert.notOk(ctx.headers['Access-Control-Allow-Methods'])
|
|
||||||
assert.notOk(ctx.headers['Access-Control-Allow-Headers'])
|
|
||||||
assert.notOk(ctx.headers['Access-Control-Expose-Headers'])
|
|
||||||
assert.strictEqual(ctx.status, method)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
t.test('should set expose headers if specifed', function() {
|
|
||||||
testMethods.forEach(function(method) {
|
|
||||||
const assertExposeHeaders = 'Some, Test, Here'
|
|
||||||
ctx = createCtx()
|
|
||||||
ctx.method = method
|
|
||||||
ctx.status = method
|
|
||||||
|
|
||||||
const assertOrigin = 'http://my.site.here'
|
|
||||||
|
|
||||||
corsHandler = CorsHandler({
|
|
||||||
allowedOrigins: [assertOrigin],
|
|
||||||
exposeHeaders: assertExposeHeaders,
|
|
||||||
})
|
|
||||||
ctx.req.headers['origin'] = assertOrigin
|
|
||||||
|
|
||||||
assert.notOk(ctx.headers['Vary'])
|
|
||||||
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['Vary'], 'Origin')
|
|
||||||
assert.strictEqual(ctx.headers['Access-Control-Allow-Origin'], assertOrigin)
|
|
||||||
assert.notOk(ctx.headers['Access-Control-Allow-Credentials'])
|
|
||||||
assert.notOk(ctx.headers['Access-Control-Allow-Methods'])
|
|
||||||
assert.notOk(ctx.headers['Access-Control-Allow-Headers'])
|
|
||||||
assert.strictEqual(ctx.headers['Access-Control-Expose-Headers'], assertExposeHeaders)
|
|
||||||
assert.strictEqual(ctx.status, method)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
t.test('should not add any headers if origin missing', function() {
|
|
||||||
testMethods.forEach(function(method) {
|
|
||||||
ctx = createCtx()
|
|
||||||
ctx.method = method
|
|
||||||
ctx.status = method
|
|
||||||
|
|
||||||
corsHandler = CorsHandler({
|
|
||||||
allowedOrigins: ['https://test.com'],
|
|
||||||
})
|
|
||||||
ctx.req.headers['origin'] = null
|
|
||||||
|
|
||||||
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['Vary'], 'Origin')
|
|
||||||
assert.notOk(ctx.headers['Access-Control-Allow-Origin'])
|
|
||||||
assert.notOk(ctx.headers['Access-Control-Allow-Methods'])
|
|
||||||
assert.notOk(ctx.headers['Access-Control-Allow-Headers'])
|
|
||||||
assert.notOk(ctx.headers['Access-Control-Expose-Headers'])
|
|
||||||
assert.strictEqual(ctx.status, method)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
t.test('should not add any headers if origin not found', function() {
|
|
||||||
testMethods.forEach(function(method) {
|
|
||||||
ctx = createCtx()
|
|
||||||
ctx.method = method
|
|
||||||
ctx.status = method
|
|
||||||
|
|
||||||
const assertOrigin = 'http://my.site.here'
|
|
||||||
const assertAllowedMethods = 'GET,HEAD'
|
|
||||||
|
|
||||||
corsHandler = CorsHandler({
|
|
||||||
allowedOrigins: ['https://my.site.here'],
|
|
||||||
allowedMethods: assertAllowedMethods,
|
|
||||||
allowedHeaders: false,
|
|
||||||
})
|
|
||||||
ctx.req.headers['origin'] = assertOrigin
|
|
||||||
|
|
||||||
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['Vary'], 'Origin')
|
|
||||||
assert.notOk(ctx.headers['Access-Control-Allow-Origin'])
|
|
||||||
assert.notOk(ctx.headers['Access-Control-Allow-Methods'])
|
|
||||||
assert.notOk(ctx.headers['Access-Control-Allow-Headers'])
|
|
||||||
assert.notOk(ctx.headers['Access-Control-Expose-Headers'])
|
|
||||||
assert.strictEqual(ctx.status, method)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
t.describe('#JsonHandler()', function() {
|
t.describe('#JsonHandler()', function() {
|
||||||
let jsonHandler = JsonHandler()
|
let jsonHandler = JsonHandler()
|
||||||
let ctx
|
|
||||||
|
|
||||||
t.beforeEach(function() {
|
|
||||||
ctx = createCtx()
|
|
||||||
})
|
|
||||||
|
|
||||||
t.test('should return a handler', function() {
|
t.test('should return a handler', function() {
|
||||||
assert.strictEqual(typeof(jsonHandler), 'function')
|
assert.strictEqual(typeof(jsonHandler), 'function')
|
||||||
})
|
})
|
||||||
|
|
||||||
t.test('should support fetching body from request', async function() {
|
t.test('should support separating query from request url', async function() {
|
||||||
const assertBody = { a: 1, temp: 'test', hello: 'world'}
|
const assertBody = { a: 1, temp: 'test', hello: 'world'}
|
||||||
let parsed = JSON.stringify(assertBody)
|
let parsed = JSON.stringify(assertBody)
|
||||||
let finished = false
|
|
||||||
let err = null
|
|
||||||
|
|
||||||
jsonHandler(ctx).catch(function(error) {
|
const ctx = {
|
||||||
err = error
|
req: [
|
||||||
}).then(function() {
|
Promise.resolve(Buffer.from(parsed.slice(0, parsed.length / 2))),
|
||||||
finished = true
|
Promise.resolve(Buffer.from(parsed.slice(parsed.length / 2))),
|
||||||
})
|
]
|
||||||
|
}
|
||||||
assert.ok(ctx.req.on.called)
|
|
||||||
assert.strictEqual(ctx.req.on.firstCall[0], 'data')
|
|
||||||
assert.strictEqual(ctx.req.on.secondCall[0], 'end')
|
|
||||||
|
|
||||||
assert.strictEqual(finished, false)
|
|
||||||
|
|
||||||
ctx.req.on.firstCall[1](Buffer.from(parsed.slice(0, parsed.length / 2)))
|
|
||||||
|
|
||||||
assert.strictEqual(finished, false)
|
|
||||||
|
|
||||||
ctx.req.on.firstCall[1](Buffer.from(parsed.slice(parsed.length / 2)))
|
|
||||||
|
|
||||||
assert.strictEqual(finished, false)
|
|
||||||
|
|
||||||
ctx.req.on.secondCall[1]()
|
|
||||||
|
|
||||||
await setTimeout(10)
|
|
||||||
|
|
||||||
assert.strictEqual(finished, true)
|
|
||||||
assert.strictEqual(err, null)
|
|
||||||
|
|
||||||
|
await jsonHandler(ctx)
|
||||||
assert.notStrictEqual(ctx.req.body, assertBody)
|
assert.notStrictEqual(ctx.req.body, assertBody)
|
||||||
assert.deepStrictEqual(ctx.req.body, assertBody)
|
assert.deepStrictEqual(ctx.req.body, assertBody)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.test('should throw if buffer grows too large', async function() {
|
|
||||||
let defaultLimit = 10 * 1024
|
|
||||||
let segmentSize = 100
|
|
||||||
|
|
||||||
let finished = false
|
|
||||||
let err = null
|
|
||||||
|
|
||||||
jsonHandler(ctx).catch(function(error) {
|
|
||||||
err = error
|
|
||||||
}).then(function() {
|
|
||||||
finished = true
|
|
||||||
})
|
|
||||||
|
|
||||||
for (let i = 0; i < defaultLimit; i += segmentSize) {
|
|
||||||
ctx.req.on.firstCall[1](Buffer.alloc(segmentSize, 'a'))
|
|
||||||
}
|
|
||||||
|
|
||||||
await setTimeout(10)
|
|
||||||
|
|
||||||
assert.strictEqual(finished, true)
|
|
||||||
assert.notStrictEqual(err, null)
|
|
||||||
|
|
||||||
assert.ok(err instanceof HttpError)
|
|
||||||
assert.strictEqual(err.status, 413)
|
|
||||||
assert.match(err.message, new RegExp(100 * 103))
|
|
||||||
assert.match(err.message, new RegExp(defaultLimit))
|
|
||||||
})
|
|
||||||
|
|
||||||
t.test('should throw if buffer is not valid json', async function() {
|
|
||||||
let finished = false
|
|
||||||
let err = null
|
|
||||||
|
|
||||||
jsonHandler(ctx).catch(function(error) {
|
|
||||||
err = error
|
|
||||||
}).then(function() {
|
|
||||||
finished = true
|
|
||||||
})
|
|
||||||
|
|
||||||
ctx.req.on.firstCall[1](Buffer.alloc(10, 'X'))
|
|
||||||
ctx.req.on.firstCall[1](Buffer.alloc(10, 'X'))
|
|
||||||
ctx.req.on.secondCall[1]()
|
|
||||||
|
|
||||||
await setTimeout(10)
|
|
||||||
|
|
||||||
assert.strictEqual(finished, true)
|
|
||||||
assert.notStrictEqual(err, null)
|
|
||||||
|
|
||||||
assert.ok(err instanceof HttpError)
|
|
||||||
assert.strictEqual(err.status, 400)
|
|
||||||
assert.match(err.message, /JSON/)
|
|
||||||
assert.match(err.message, /Unexpected token[^X]+X/i)
|
|
||||||
assert.strictEqual(err.body.status, 400)
|
|
||||||
assert.match(err.body.message, /Invalid JSON/i)
|
|
||||||
assert.match(err.body.message, /Unexpected token[^X]+X/i)
|
|
||||||
assert.strictEqual(err.body.request, 'XXXXXXXXXXXXXXXXXXXX')
|
|
||||||
})
|
|
||||||
|
|
||||||
t.test('should not throw if body is empty', async function() {
|
|
||||||
let finished = false
|
|
||||||
let err = null
|
|
||||||
|
|
||||||
jsonHandler(ctx).catch(function(error) {
|
|
||||||
err = error
|
|
||||||
}).then(function() {
|
|
||||||
finished = true
|
|
||||||
})
|
|
||||||
|
|
||||||
assert.ok(ctx.req.on.called)
|
|
||||||
assert.strictEqual(ctx.req.on.firstCall[0], 'data')
|
|
||||||
assert.strictEqual(ctx.req.on.secondCall[0], 'end')
|
|
||||||
|
|
||||||
assert.strictEqual(finished, false)
|
|
||||||
|
|
||||||
ctx.req.on.secondCall[1]()
|
|
||||||
|
|
||||||
await setTimeout(10)
|
|
||||||
|
|
||||||
assert.strictEqual(finished, true)
|
|
||||||
assert.strictEqual(err, null)
|
|
||||||
|
|
||||||
assert.deepStrictEqual(ctx.req.body, {})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
t.describe('#FormidableHandler()', function() {
|
|
||||||
let formidable
|
|
||||||
let incomingForm
|
|
||||||
let ctx
|
|
||||||
|
|
||||||
t.beforeEach(function() {
|
|
||||||
ctx = createCtx()
|
|
||||||
formidable = {
|
|
||||||
IncomingForm: stub(),
|
|
||||||
fsRename: stub().resolves(),
|
|
||||||
}
|
|
||||||
|
|
||||||
incomingForm = {
|
|
||||||
parse: stub().returnWith(function(req, cb) {
|
|
||||||
cb(null, {}, { file: { name: 'asdf.png' } })
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
formidable.IncomingForm.returns(incomingForm)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.test('should call formidable with correct defaults', async function() {
|
|
||||||
let handler = FormidableHandler(formidable)
|
|
||||||
await handler(ctx)
|
|
||||||
|
|
||||||
assert.strictEqual(incomingForm.uploadDir, os.tmpdir())
|
|
||||||
assert.strictEqual(incomingForm.maxFileSize, 8 * 1024 * 1024)
|
|
||||||
assert.strictEqual(incomingForm.maxFieldsSize, 10 * 1024)
|
|
||||||
assert.strictEqual(incomingForm.maxFields, 50)
|
|
||||||
assert.strictEqual(incomingForm.parse.firstCall[0], ctx.req)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.test('should apply fields and rename file before returning', async function() {
|
|
||||||
const assertFilename = 'Lets love.png'
|
|
||||||
const assertOriginalPath = 'Hitoshi/Fujima/Yuigi.png'
|
|
||||||
const assertFile = { a: 1, name: assertFilename, path: assertOriginalPath }
|
|
||||||
const assertFields = { b: 2, c: 3 }
|
|
||||||
let handler = FormidableHandler(formidable)
|
|
||||||
|
|
||||||
incomingForm.parse.returnWith(function(req, cb) {
|
|
||||||
cb(null, assertFields, { file: assertFile })
|
|
||||||
})
|
|
||||||
|
|
||||||
assert.notOk(ctx.req.body)
|
|
||||||
assert.notOk(ctx.req.file)
|
|
||||||
let prefix = new Date().toISOString().replace(/-/g, '').replace('T', '_').replace(/:/g, '').split('.')[0]
|
|
||||||
await handler(ctx)
|
|
||||||
|
|
||||||
assert.strictEqual(ctx.req.body, assertFields)
|
|
||||||
assert.strictEqual(ctx.req.file, assertFile)
|
|
||||||
assert.strictEqual(ctx.req.file.path, path.join(os.tmpdir(), ctx.req.file.filename))
|
|
||||||
assert.match(ctx.req.file.filename, new RegExp(prefix))
|
|
||||||
assert.match(ctx.req.file.filename, new RegExp(assertFilename))
|
|
||||||
|
|
||||||
assert.ok(formidable.fsRename.called)
|
|
||||||
assert.strictEqual(formidable.fsRename.firstCall[0], assertOriginalPath)
|
|
||||||
assert.strictEqual(formidable.fsRename.firstCall[1], ctx.req.file.path)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.test('should throw parse error if parse fails', async function() {
|
|
||||||
const assertError = new Error('Aozora')
|
|
||||||
|
|
||||||
|
|
||||||
let handler = FormidableHandler(formidable)
|
|
||||||
|
|
||||||
incomingForm.parse.returnWith(function(req, cb) {
|
|
||||||
cb(assertError)
|
|
||||||
})
|
|
||||||
|
|
||||||
let err = await assert.isRejected(handler(ctx))
|
|
||||||
|
|
||||||
assert.notStrictEqual(err, assertError)
|
|
||||||
assert.ok(err instanceof HttpError)
|
|
||||||
assert.strictEqual(err.message, assertError.message)
|
|
||||||
assert.strictEqual(err.status, 400)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.test('should throw rename error if rename fails', async function() {
|
|
||||||
const assertError = new Error('Aozora')
|
|
||||||
formidable.fsRename.rejects(assertError)
|
|
||||||
|
|
||||||
let handler = FormidableHandler(formidable)
|
|
||||||
let err = await assert.isRejected(handler(ctx))
|
|
||||||
|
|
||||||
assert.strictEqual(err, assertError)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.test('should not call rename if no file is present', async function() {
|
|
||||||
const assertNotError = new Error('Aozora')
|
|
||||||
const assertFields = { a: 1 }
|
|
||||||
formidable.fsRename.rejects(assertNotError)
|
|
||||||
|
|
||||||
let handler = FormidableHandler(formidable)
|
|
||||||
|
|
||||||
incomingForm.parse.returnWith(function(req, cb) {
|
|
||||||
cb(null, assertFields, null)
|
|
||||||
})
|
|
||||||
|
|
||||||
await handler(ctx)
|
|
||||||
|
|
||||||
assert.strictEqual(ctx.req.body, assertFields)
|
|
||||||
assert.strictEqual(ctx.req.file, null)
|
|
||||||
|
|
||||||
incomingForm.parse.returnWith(function(req, cb) {
|
|
||||||
cb(null, assertFields, { file: null })
|
|
||||||
})
|
|
||||||
|
|
||||||
await handler(ctx)
|
|
||||||
|
|
||||||
assert.strictEqual(ctx.req.body, assertFields)
|
|
||||||
assert.strictEqual(ctx.req.file, null)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.test('should throw filename error if filename fails', async function() {
|
|
||||||
const assertError = new Error('Dallaglio Piano')
|
|
||||||
|
|
||||||
let handler = FormidableHandler(formidable, {
|
|
||||||
filename: function() {
|
|
||||||
throw assertError
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
let err = await assert.isRejected(handler(ctx))
|
|
||||||
|
|
||||||
assert.strictEqual(err, assertError)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.test('should default to original name of filename returns null', async function() {
|
|
||||||
const assertFilename = 'Herrscher.png'
|
|
||||||
|
|
||||||
let handler = FormidableHandler(formidable, {
|
|
||||||
filename: function() {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
incomingForm.parse.returnWith(function(req, cb) {
|
|
||||||
cb(null, { }, { file: { name: assertFilename } })
|
|
||||||
})
|
|
||||||
|
|
||||||
await handler(ctx)
|
|
||||||
|
|
||||||
assert.strictEqual(ctx.req.file.filename, assertFilename)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.test('should support multiple filename calls in same second', async function() {
|
|
||||||
const assertFilename = 'Herrscher.png'
|
|
||||||
|
|
||||||
let handler = FormidableHandler(formidable)
|
|
||||||
|
|
||||||
await handler(ctx)
|
|
||||||
|
|
||||||
let file1 = ctx.req.file
|
|
||||||
|
|
||||||
await handler(ctx)
|
|
||||||
|
|
||||||
let file2 = ctx.req.file
|
|
||||||
|
|
||||||
assert.notStrictEqual(file1, file2)
|
|
||||||
assert.notStrictEqual(file1.filename, file2.filename)
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
t.test('should support parsing fields as json', async function() {
|
|
||||||
const assertFields = { b: '2', c: '3', e: 'asdf', f: '{"a": 1}' }
|
|
||||||
let handler = FormidableHandler(formidable, {
|
|
||||||
parseFields: true,
|
|
||||||
})
|
|
||||||
|
|
||||||
incomingForm.parse.returnWith(function(req, cb) {
|
|
||||||
cb(null, assertFields, { file: { name: 'test.png' } })
|
|
||||||
})
|
|
||||||
|
|
||||||
await handler(ctx)
|
|
||||||
|
|
||||||
assert.strictEqual(ctx.req.body, assertFields)
|
|
||||||
assert.strictEqual(ctx.req.body.b, 2)
|
|
||||||
assert.strictEqual(ctx.req.body.c, 3)
|
|
||||||
assert.strictEqual(ctx.req.body.e, 'asdf')
|
|
||||||
assert.deepStrictEqual(ctx.req.body.f, {a: 1})
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
|
@ -45,21 +45,8 @@ t.describe('#addRoute()', function() {
|
||||||
router.addRoute('/:test/bla', function() {})
|
router.addRoute('/:test/bla', function() {})
|
||||||
router.addRoute('/bla/bla', function() {})
|
router.addRoute('/bla/bla', function() {})
|
||||||
router.addRoute('/bla/bla/bla', function() {})
|
router.addRoute('/bla/bla/bla', function() {})
|
||||||
assert.throws(function() { router.addRoute('/:asdf', function() {}) }, /existing/)
|
assert.throws(function() { router.addRoute('/:asdf/', function() {}) }, /param/)
|
||||||
assert.throws(function() { router.addRoute('/:test/asdf/:foobar', function() {}) }, /existing/)
|
assert.throws(function() { router.addRoute('/:test/asdf/:foobar', function() {}) }, /param/)
|
||||||
})
|
|
||||||
|
|
||||||
t.test('fail if adding anything after a fullparam', function() {
|
|
||||||
let router = new FlaskaRouter()
|
|
||||||
assert.throws(function() { router.addRoute('/::bla/:bla', function() {}) }, /after/)
|
|
||||||
assert.throws(function() { router.addRoute('/:test/::bla/test', function() {}) }, /after/)
|
|
||||||
assert.throws(function() { router.addRoute('/::test/bla', function() {}) }, /after/)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.test('should work with param and full param side by side', function() {
|
|
||||||
let router = new FlaskaRouter()
|
|
||||||
router.addRoute('/:bla', function() {})
|
|
||||||
router.addRoute('/::bla', function() {})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
t.test('fail if adding multiple fullparam', function() {
|
t.test('fail if adding multiple fullparam', function() {
|
||||||
|
@ -69,8 +56,10 @@ t.describe('#addRoute()', function() {
|
||||||
router.addRoute('/:test/bla', function() {})
|
router.addRoute('/:test/bla', function() {})
|
||||||
router.addRoute('/:test/::bla', function() {})
|
router.addRoute('/:test/::bla', function() {})
|
||||||
router.addRoute('/bla/bla/bla', function() {})
|
router.addRoute('/bla/bla/bla', function() {})
|
||||||
assert.throws(function() { router.addRoute('/:test/asdf/::bla', function() {}) }, /existing/)
|
assert.throws(function() { router.addRoute('/:test/asdf/::bla/fail', function() {}) }, /full.+param/)
|
||||||
assert.throws(function() { router.addRoute('/:test/::bla', function() {}) }, /existing/)
|
assert.throws(function() { router.addRoute('/:test/::bla/test', function() {}) }, /full.+param/)
|
||||||
|
assert.throws(function() { router.addRoute('/:test/:bla', function() {}) }, /full.+param/)
|
||||||
|
assert.throws(function() { router.addRoute('/::test', function() {}) }, /partial.+param/)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.test('add route correctly', function() {
|
t.test('add route correctly', function() {
|
||||||
|
@ -78,7 +67,39 @@ t.describe('#addRoute()', function() {
|
||||||
let router = new FlaskaRouter()
|
let router = new FlaskaRouter()
|
||||||
router.addRoute('/a/b/c', assertHandler)
|
router.addRoute('/a/b/c', assertHandler)
|
||||||
let result = router.match('/a/b/c')
|
let result = router.match('/a/b/c')
|
||||||
assert.strictEqual(result.path.handlers[0], assertHandler)
|
assert.strictEqual(result.handler, assertHandler)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.test('add param route correctly', function() {
|
||||||
|
let assertHandler = function() { return 1 }
|
||||||
|
let router = new FlaskaRouter()
|
||||||
|
router.addRoute('/a/:b/c', assertHandler)
|
||||||
|
assert.ok(router.root.children.get('a'))
|
||||||
|
assert.ok(router.root.children.get('a').children.get('__param'))
|
||||||
|
assert.strictEqual(router.root.children.get('a').children.get('__param').paramName, 'b')
|
||||||
|
assert.ok(router.root.children.get('a').children.get('__param').children.get('c'))
|
||||||
|
assert.strictEqual(router.root.children.get('a').children.get('__param').children.get('c').handler, assertHandler)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.test('add full param route correctly', function() {
|
||||||
|
let assertHandler = function() { return 1 }
|
||||||
|
let router = new FlaskaRouter()
|
||||||
|
router.addRoute('/a/::b', assertHandler)
|
||||||
|
assert.ok(router.root.children.get('a'))
|
||||||
|
assert.ok(router.root.children.get('a').children.get('__fullparam'))
|
||||||
|
assert.strictEqual(router.root.children.get('a').children.get('__fullparam').fullparamName, 'b')
|
||||||
|
assert.strictEqual(router.root.children.get('a').children.get('__fullparam').handler, assertHandler)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.test('add param route correctly', function() {
|
||||||
|
let assertHandler = function() { return 1 }
|
||||||
|
let router = new FlaskaRouter()
|
||||||
|
router.addRoute('/a/:b/c', assertHandler)
|
||||||
|
assert.ok(router.root.children.get('a'))
|
||||||
|
assert.ok(router.root.children.get('a').children.get('__param'))
|
||||||
|
assert.strictEqual(router.root.children.get('a').children.get('__param').paramName, 'b')
|
||||||
|
assert.ok(router.root.children.get('a').children.get('__param').children.get('c'))
|
||||||
|
assert.strictEqual(router.root.children.get('a').children.get('__param').children.get('c').handler, assertHandler)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.test('support single middlewares correctly', function() {
|
t.test('support single middlewares correctly', function() {
|
||||||
|
@ -86,10 +107,10 @@ t.describe('#addRoute()', function() {
|
||||||
let assertMiddleware = function() { return 1 }
|
let assertMiddleware = function() { return 1 }
|
||||||
let router = new FlaskaRouter()
|
let router = new FlaskaRouter()
|
||||||
router.addRoute('/a', assertMiddleware, assertHandler)
|
router.addRoute('/a', assertMiddleware, assertHandler)
|
||||||
let result = router.match('/a')
|
assert.ok(router.root.children.get('a'))
|
||||||
assert.strictEqual(result.path.handlers.length, 2)
|
assert.strictEqual(router.root.children.get('a').handler, assertHandler)
|
||||||
assert.strictEqual(result.path.handlers[0], assertMiddleware)
|
assert.strictEqual(router.root.children.get('a').middlewares.length, 1)
|
||||||
assert.strictEqual(result.path.handlers[1], assertHandler)
|
assert.strictEqual(router.root.children.get('a').middlewares[0], assertMiddleware)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.test('support multi middlewares correctly', function() {
|
t.test('support multi middlewares correctly', function() {
|
||||||
|
@ -98,15 +119,15 @@ t.describe('#addRoute()', function() {
|
||||||
let router = new FlaskaRouter()
|
let router = new FlaskaRouter()
|
||||||
router.addRoute('/a', [assertMiddleware], assertHandler)
|
router.addRoute('/a', [assertMiddleware], assertHandler)
|
||||||
router.addRoute('/b', [assertMiddleware, assertMiddleware], assertHandler)
|
router.addRoute('/b', [assertMiddleware, assertMiddleware], assertHandler)
|
||||||
let resultA = router.match('/a')
|
assert.ok(router.root.children.get('a'))
|
||||||
assert.strictEqual(resultA.path.handlers.length, 2)
|
assert.strictEqual(router.root.children.get('a').handler, assertHandler)
|
||||||
assert.strictEqual(resultA.path.handlers[0], assertMiddleware)
|
assert.strictEqual(router.root.children.get('a').middlewares.length, 1)
|
||||||
assert.strictEqual(resultA.path.handlers[1], assertHandler)
|
assert.strictEqual(router.root.children.get('a').middlewares[0], assertMiddleware)
|
||||||
let resultB = router.match('/b')
|
assert.ok(router.root.children.get('b'))
|
||||||
assert.strictEqual(resultB.path.handlers.length, 3)
|
assert.strictEqual(router.root.children.get('b').handler, assertHandler)
|
||||||
assert.strictEqual(resultB.path.handlers[0], assertMiddleware)
|
assert.strictEqual(router.root.children.get('b').middlewares.length, 2)
|
||||||
assert.strictEqual(resultB.path.handlers[1], assertMiddleware)
|
assert.strictEqual(router.root.children.get('b').middlewares[0], assertMiddleware)
|
||||||
assert.strictEqual(resultB.path.handlers[2], assertHandler)
|
assert.strictEqual(router.root.children.get('b').middlewares[1], assertMiddleware)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -115,20 +136,43 @@ t.describe('#match()', function() {
|
||||||
let assertMatched = false
|
let assertMatched = false
|
||||||
let router = new FlaskaRouter()
|
let router = new FlaskaRouter()
|
||||||
router.addRoute('/test', function() { assertMatched = true })
|
router.addRoute('/test', function() { assertMatched = true })
|
||||||
let result = router.match('/test').path
|
let result = router.match('/test')
|
||||||
assert.strictEqual(result.handlers.length, 1)
|
assert.ok(result.handler)
|
||||||
result.handlers[0]()
|
assert.ok(result.middlewares)
|
||||||
|
assert.strictEqual(result.middlewares.length, 0)
|
||||||
|
result.handler()
|
||||||
|
assert.strictEqual(assertMatched, true)
|
||||||
|
|
||||||
|
// Test with extra slash at the end
|
||||||
|
assertMatched = false
|
||||||
|
result = router.match('/test/')
|
||||||
|
assert.ok(result.handler)
|
||||||
|
assert.ok(result.middlewares)
|
||||||
|
assert.strictEqual(result.middlewares.length, 0)
|
||||||
|
result.handler()
|
||||||
assert.strictEqual(assertMatched, true)
|
assert.strictEqual(assertMatched, true)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.test('return middlewares in handlers', function() {
|
|
||||||
|
t.test('return middlewares', function() {
|
||||||
let assertMatched = false
|
let assertMatched = false
|
||||||
let assertMiddleware = function() { assertMatched = true }
|
let assertMiddleware = function() { assertMatched = true }
|
||||||
let router = new FlaskaRouter()
|
let router = new FlaskaRouter()
|
||||||
router.addRoute('/test', assertMiddleware, function() { })
|
router.addRoute('/test', assertMiddleware, function() { })
|
||||||
let result = router.match('/test').path
|
let result = router.match('/test')
|
||||||
assert.strictEqual(result.handlers.length, 2)
|
assert.ok(result.handler)
|
||||||
result.handlers[0]()
|
assert.ok(result.middlewares)
|
||||||
|
assert.strictEqual(result.middlewares.length, 1)
|
||||||
|
result.middlewares[0]()
|
||||||
|
assert.strictEqual(assertMatched, true)
|
||||||
|
|
||||||
|
// Test with extra slash at the end
|
||||||
|
assertMatched = false
|
||||||
|
result = router.match('/test/')
|
||||||
|
assert.ok(result.handler)
|
||||||
|
assert.ok(result.middlewares)
|
||||||
|
assert.strictEqual(result.middlewares.length, 1)
|
||||||
|
result.middlewares[0]()
|
||||||
assert.strictEqual(assertMatched, true)
|
assert.strictEqual(assertMatched, true)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -138,8 +182,20 @@ t.describe('#match()', function() {
|
||||||
let router = new FlaskaRouter()
|
let router = new FlaskaRouter()
|
||||||
router.addRoute('/test/:id', function() { assertMatched = true })
|
router.addRoute('/test/:id', function() { assertMatched = true })
|
||||||
let result = router.match('/test/' + assertParameter)
|
let result = router.match('/test/' + assertParameter)
|
||||||
assert.strictEqual(result.path.handlers.length, 1)
|
assert.ok(result.handler)
|
||||||
result.path.handlers[0]()
|
assert.ok(result.middlewares)
|
||||||
|
assert.strictEqual(result.middlewares.length, 0)
|
||||||
|
result.handler()
|
||||||
|
assert.strictEqual(assertMatched, true)
|
||||||
|
assert.strictEqual(result.params.id, assertParameter)
|
||||||
|
|
||||||
|
// Test with extra slash at the end
|
||||||
|
assertMatched = false
|
||||||
|
result = router.match('/test/' + assertParameter + '/')
|
||||||
|
assert.ok(result.handler)
|
||||||
|
assert.ok(result.middlewares)
|
||||||
|
assert.strictEqual(result.middlewares.length, 0)
|
||||||
|
result.handler()
|
||||||
assert.strictEqual(assertMatched, true)
|
assert.strictEqual(assertMatched, true)
|
||||||
assert.strictEqual(result.params.id, assertParameter)
|
assert.strictEqual(result.params.id, assertParameter)
|
||||||
})
|
})
|
||||||
|
@ -150,8 +206,20 @@ t.describe('#match()', function() {
|
||||||
let router = new FlaskaRouter()
|
let router = new FlaskaRouter()
|
||||||
router.addRoute('/test/::id', function() { assertMatched = true })
|
router.addRoute('/test/::id', function() { assertMatched = true })
|
||||||
let result = router.match('/test/' + assertParameter)
|
let result = router.match('/test/' + assertParameter)
|
||||||
assert.strictEqual(result.path.handlers.length, 1)
|
assert.ok(result.handler)
|
||||||
result.path.handlers[0]()
|
assert.ok(result.middlewares)
|
||||||
|
assert.strictEqual(result.middlewares.length, 0)
|
||||||
|
result.handler()
|
||||||
|
assert.strictEqual(assertMatched, true)
|
||||||
|
assert.strictEqual(result.params.id, assertParameter)
|
||||||
|
|
||||||
|
// Test with extra slash at the end
|
||||||
|
assertMatched = false
|
||||||
|
result = router.match('/test/' + assertParameter + '/')
|
||||||
|
assert.ok(result.handler)
|
||||||
|
assert.ok(result.middlewares)
|
||||||
|
assert.strictEqual(result.middlewares.length, 0)
|
||||||
|
result.handler()
|
||||||
assert.strictEqual(assertMatched, true)
|
assert.strictEqual(assertMatched, true)
|
||||||
assert.strictEqual(result.params.id, assertParameter)
|
assert.strictEqual(result.params.id, assertParameter)
|
||||||
})
|
})
|
||||||
|
@ -163,13 +231,15 @@ t.describe('#match()', function() {
|
||||||
router.addRoute('/test/:bla', assertParamFunc)
|
router.addRoute('/test/:bla', assertParamFunc)
|
||||||
router.addRoute('/::id', assertFullFunc)
|
router.addRoute('/::id', assertFullFunc)
|
||||||
let result = router.match('/test/123')
|
let result = router.match('/test/123')
|
||||||
assert.strictEqual(result.path.handlers.length, 1)
|
assert.strictEqual(result.handler, assertParamFunc)
|
||||||
assert.strictEqual(result.path.handlers[0], assertParamFunc)
|
assert.ok(result.middlewares)
|
||||||
|
assert.strictEqual(result.middlewares.length, 0)
|
||||||
assert.strictEqual(result.params.bla, '123')
|
assert.strictEqual(result.params.bla, '123')
|
||||||
|
|
||||||
result = router.match('/test/123/asdf')
|
result = router.match('/test/123/asdf')
|
||||||
assert.strictEqual(result.path.handlers.length, 1)
|
assert.strictEqual(result.handler, assertFullFunc)
|
||||||
assert.strictEqual(result.path.handlers[0], assertFullFunc)
|
assert.ok(result.middlewares)
|
||||||
|
assert.strictEqual(result.middlewares.length, 0)
|
||||||
assert.strictEqual(result.params.id, 'test/123/asdf')
|
assert.strictEqual(result.params.id, 'test/123/asdf')
|
||||||
assert.notOk(result.params.bla)
|
assert.notOk(result.params.bla)
|
||||||
})
|
})
|
||||||
|
@ -180,8 +250,19 @@ t.describe('#match()', function() {
|
||||||
router.addRoute('/test/:id', function() { assertMatched = false })
|
router.addRoute('/test/:id', function() { assertMatched = false })
|
||||||
router.addRoute('/test/:id/test1', function() { })
|
router.addRoute('/test/:id/test1', function() { })
|
||||||
let result = router.match('/test/asdf/test1')
|
let result = router.match('/test/asdf/test1')
|
||||||
assert.strictEqual(result.path.handlers.length, 1)
|
assert.ok(result.handler)
|
||||||
result.path.handlers[0]()
|
assert.ok(result.middlewares)
|
||||||
|
assert.strictEqual(result.middlewares.length, 0)
|
||||||
|
result.handler()
|
||||||
|
assert.strictEqual(assertMatched, true)
|
||||||
|
assert.strictEqual(result.params.id, 'asdf')
|
||||||
|
|
||||||
|
// Test with extra slash at the end
|
||||||
|
result = router.match('/test/asdf/test1/')
|
||||||
|
assert.ok(result.handler)
|
||||||
|
assert.ok(result.middlewares)
|
||||||
|
assert.strictEqual(result.middlewares.length, 0)
|
||||||
|
result.handler()
|
||||||
assert.strictEqual(assertMatched, true)
|
assert.strictEqual(assertMatched, true)
|
||||||
assert.strictEqual(result.params.id, 'asdf')
|
assert.strictEqual(result.params.id, 'asdf')
|
||||||
})
|
})
|
||||||
|
@ -196,25 +277,24 @@ t.describe('#match()', function() {
|
||||||
router.addRoute('/foo/::path', assertFunction)
|
router.addRoute('/foo/::path', assertFunction)
|
||||||
router.addRoute('/::path', assertFailFunction)
|
router.addRoute('/::path', assertFailFunction)
|
||||||
|
|
||||||
assert.strictEqual(router.match('/test/123').path.handlers[0], assertFunction)
|
assert.strictEqual(router.match('/test/123').handler, assertFunction)
|
||||||
assert.strictEqual(router.match('/test/asdfasdg').path.handlers[0], assertFunction)
|
assert.strictEqual(router.match('/test/asdfasdg').handler, assertFunction)
|
||||||
assert.strictEqual(router.match('/test/test/sdafsda').path.handlers[0], assertFunction)
|
assert.strictEqual(router.match('/test/test/sdafsda').handler, assertFunction)
|
||||||
assert.strictEqual(router.match('/test/test/sdafsda/gdfshe4/43y34/wtaw').path.handlers[0], assertFunction)
|
assert.strictEqual(router.match('/test/test/sdafsda/gdfshe4/43y34/wtaw').handler, assertFunction)
|
||||||
assert.strictEqual(router.match('/foo/123').path.handlers[0], assertFunction)
|
assert.strictEqual(router.match('/foo/123').handler, assertFunction)
|
||||||
assert.strictEqual(router.match('/foo/bar/baz/test').path.handlers[0], assertFunction)
|
assert.strictEqual(router.match('/foo/bar/baz/test').handler, assertFunction)
|
||||||
assert.ok(router.match('/test/123/yweherher/reher/h34h34/'))
|
assert.ok(router.match('/test/123/yweherher/reher/h34h34/'))
|
||||||
assert.strictEqual(router.match('/test/123/yweherher/reher/h34h34/').path.handlers[0], assertFailFunction)
|
assert.strictEqual(router.match('/test/123/yweherher/reher/h34h34/').handler, assertFailFunction)
|
||||||
assert.ok(router.match('/test/foo/bar'))
|
assert.ok(router.match('/test/foo/bar'))
|
||||||
assert.strictEqual(router.match('/test/foo/bar').path.handlers[0], assertFailFunction)
|
assert.strictEqual(router.match('/test/foo/bar').handler, assertFailFunction)
|
||||||
assert.ok(router.match('/'))
|
assert.ok(router.match('/'))
|
||||||
assert.strictEqual(router.match('/').path.handlers[0], assertFailFunction)
|
assert.strictEqual(router.match('/').handler, assertFailFunction)
|
||||||
assert.ok(router.match('/something/else/goes/here'))
|
assert.ok(router.match('/something/else/goes/here'))
|
||||||
assert.strictEqual(router.match('/something/else/goes/here').path.handlers[0], assertFailFunction)
|
assert.strictEqual(router.match('/something/else/goes/here').handler, assertFailFunction)
|
||||||
|
|
||||||
router.addRoute('/', assertRootFunction)
|
router.addRoute('/', assertRootFunction)
|
||||||
router.compile()
|
|
||||||
assert.ok(router.match('/'))
|
assert.ok(router.match('/'))
|
||||||
assert.strictEqual(router.match('/').path.handlers[0], assertRootFunction)
|
assert.strictEqual(router.match('/').handler, assertRootFunction)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.test('return null when no match is found', function() {
|
t.test('return null when no match is found', function() {
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 32 KiB |
BIN
test/test.jpg
BIN
test/test.jpg
Binary file not shown.
Before Width: | Height: | Size: 32 KiB |
|
@ -1 +0,0 @@
|
||||||
Hello World
|
|
Loading…
Reference in a new issue