From 0c2030c2cd1c2c4bff5038c2444709de14d20b18 Mon Sep 17 00:00:00 2001 From: Jonatan Nilsson Date: Wed, 11 Mar 2020 12:34:34 +0000 Subject: [PATCH] added basic bottle router and benchmarks for it --- benchmark/const.js | 87 ++++++ benchmark/index.js | 125 ++++++++ benchmark/package-lock.json | 597 ++++++++++++++++++++++++++++++++++++ benchmark/package.json | 18 ++ benchmark/router_bottle.js | 18 ++ benchmark/router_express.js | 18 ++ benchmark/router_koa.js | 18 ++ bottle.js | 124 ++++++++ package.json | 20 ++ 9 files changed, 1025 insertions(+) create mode 100644 benchmark/const.js create mode 100644 benchmark/index.js create mode 100644 benchmark/package-lock.json create mode 100644 benchmark/package.json create mode 100644 benchmark/router_bottle.js create mode 100644 benchmark/router_express.js create mode 100644 benchmark/router_koa.js create mode 100644 bottle.js create mode 100644 package.json diff --git a/benchmark/const.js b/benchmark/const.js new file mode 100644 index 0000000..cae5d6b --- /dev/null +++ b/benchmark/const.js @@ -0,0 +1,87 @@ +let callItem = function() {} + +export const overrideDummy = function(override) { + callItem = override +} + +export const dummy = function() { callItem() } + +export const allRoutes = [ + '/', + '/api/articles', + '/api/articles/:articleId/file', + '/api/articles/:id', + '/api/articles/public', + '/api/articles/public/:id', + '/api/file', + '/api/file/:id', + '/api/media', + '/api/media/:id', + '/api/pages', + '/api/pages/:id', + '/api/pages/:pageId/articles', + '/api/pages/:pageId/articles/public', + '/api/staff', + '/api/staff/:id', +] + +export const allManyRoutes = [ + '/', + '/api/articles', + '/api/articles/:articleId/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/:id', + '/api/categories/:id/products/:productId', + '/api/categories/:id/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/:id', + '/api/orders/:orderId/sell', + '/api/pages', + '/api/pages/:id', + '/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', +] diff --git a/benchmark/index.js b/benchmark/index.js new file mode 100644 index 0000000..3b25b26 --- /dev/null +++ b/benchmark/index.js @@ -0,0 +1,125 @@ +import assert from 'assert' +import Benchmark from 'benchmarkjs-pretty' +import { koaRouter1, koaRouter2 } from './router_koa.js' +import { bottleRouter1, bottleRouter2 } from './router_bottle.js' +import { expressRouter1, expressRouter2 } from './router_express.js' +import * as consts from './const.js' + +let assertOk = true +let testData = null + +consts.overrideDummy(function() { testData = assertOk }) + +function TestSmallStaticRoute() { + return new Benchmark.default('Small router static route benchmark: /api/staff (16 routes registered)') + .add('expressjs', function() { + testData = null + expressRouter1.handle({ + url: '/api/staff', + method: 'GET', + }, {}, function() { }) + assert.ok(testData) + }) + .add('koa-router', function() { + testData = koaRouter1.match('/api/staff', 'GET') + assert.ok(testData.route) + }) + .add('bottle-router', function() { + testData = bottleRouter1.match('/api/staff') + assert.ok(testData.handler) + }) + .run() + .then(function() {}, function(e) { + console.error('error:', e) + process.exit(1) + }) +} + +function TestSmallParamRoute() { + return new Benchmark.default('Small router param route benchmark: /api/staff/:id (16 routes registered)') + .add('expressjs', function() { + testData = null + expressRouter1.handle({ + url: '/api/staff/justatest', + method: 'GET', + }, {}, function() { }) + assert.ok(testData) + }) + .add('koa-router', function() { + testData = koaRouter1.match('/api/staff/justatest', 'GET') + assert.ok(testData.route) + }) + .add('bottle-router', function() { + testData = bottleRouter1.match('/api/staff/justatest') + assert.ok(testData.handler) + }) + .run() + .then(function() {}, function(e) { + console.error('error:', e) + process.exit(1) + }) +} + +function TestLargeStaticRoute() { + return new Benchmark.default('Large router static route benchmark: /api/staff (58 routes registered)') + .add('expressjs', function() { + testData = null + expressRouter2.handle({ + url: '/api/staff', + method: 'GET', + }, {}, function() { }) + assert.ok(testData) + }) + .add('koa-router', function() { + testData = koaRouter2.match('/api/staff', 'GET') + assert.ok(testData.route) + }) + .add('bottle-router', function() { + testData = bottleRouter2.match('/api/staff') + assert.ok(testData.handler) + }) + .run() + .then(function() {}, function(e) { + console.error('error:', e) + process.exit(1) + }) +} + +function TestLargeParamRoute() { + return new Benchmark.default('Large router param route benchmark: /api/staff/:id (58 routes registered)') + .add('expressjs', function() { + testData = null + expressRouter2.handle({ + url: '/api/staff/justatest', + method: 'GET', + }, {}, function() { }) + assert.ok(testData) + }) + .add('koa-router', function() { + testData = koaRouter2.match('/api/staff/justatest', 'GET') + assert.ok(testData.route) + }) + .add('bottle-router', function() { + testData = bottleRouter2.match('/api/staff/justatest') + assert.ok(testData.handler) + }) + .run() + .then(function() {}, function(e) { + console.error('error:', e) + process.exit(1) + }) +} + +TestSmallStaticRoute() + .then(function() { + return TestSmallParamRoute() + }) + .then(function() { + return TestLargeStaticRoute() + }) + .then(function() { + return TestLargeParamRoute() + }) + .then(function() { + process.exit(0) + }) diff --git a/benchmark/package-lock.json b/benchmark/package-lock.json new file mode 100644 index 0000000..61b522f --- /dev/null +++ b/benchmark/package-lock.json @@ -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=" + } + } +} diff --git a/benchmark/package.json b/benchmark/package.json new file mode 100644 index 0000000..558f166 --- /dev/null +++ b/benchmark/package.json @@ -0,0 +1,18 @@ +{ + "name": "benchmark", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "start": "node ", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "type": "module", + "author": "", + "license": "WTFPL", + "dependencies": { + "benchmarkjs-pretty": "^2.0.0", + "express": "^4.17.1", + "koa-router": "^8.0.8" + } +} diff --git a/benchmark/router_bottle.js b/benchmark/router_bottle.js new file mode 100644 index 0000000..fb94ecf --- /dev/null +++ b/benchmark/router_bottle.js @@ -0,0 +1,18 @@ +import { BottleRouter } from '../bottle.js' +import * as consts from './const.js' + +const router1 = new BottleRouter() +const router2 = new BottleRouter() + +for (let key in consts.allRoutes) { + router1.addRoute(consts.allRoutes[key], consts.dummy) +} + +for (let key in consts.allManyRoutes) { + router2.addRoute(consts.allManyRoutes[key], consts.dummy) +} + +export { + router1 as bottleRouter1, + router2 as bottleRouter2, +} diff --git a/benchmark/router_express.js b/benchmark/router_express.js new file mode 100644 index 0000000..d4805ab --- /dev/null +++ b/benchmark/router_express.js @@ -0,0 +1,18 @@ +import express from 'express' +import * as consts from './const.js' + +const router1 = new express.Router() +const router2 = new express.Router() + +for (let key in consts.allRoutes) { + router1.get(consts.allRoutes[key], consts.dummy) +} + +for (let key in consts.allManyRoutes) { + router2.get(consts.allManyRoutes[key], consts.dummy) +} + +export { + router1 as expressRouter1, + router2 as expressRouter2, +} diff --git a/benchmark/router_koa.js b/benchmark/router_koa.js new file mode 100644 index 0000000..2f83504 --- /dev/null +++ b/benchmark/router_koa.js @@ -0,0 +1,18 @@ +import koaRouter from 'koa-router' +import * as consts from './const.js' + +const router1 = new koaRouter() +const router2 = new koaRouter() + +for (let key in consts.allRoutes) { + router1.get(consts.allRoutes[key], consts.dummy) +} + +for (let key in consts.allManyRoutes) { + router2.get(consts.allManyRoutes[key], consts.dummy) +} + +export { + router1 as koaRouter1, + router2 as koaRouter2, +} diff --git a/bottle.js b/bottle.js new file mode 100644 index 0000000..5f9aa6c --- /dev/null +++ b/bottle.js @@ -0,0 +1,124 @@ +/** + * Router + */ + +function Branch() { + this._map = new Map() + this._paramName = null + this._paramPrototype = null + this._handler = null +} + +const __paramMapName = '__param' + +function BottleRouter() { + this._root = new Branch() +} + +BottleRouter.prototype.addRoute = function(route, handler) { + if (route[0] !== '/') + throw new Error(`route "${route}" must start with forward slash`); + + let start = 1; + let end = 1; + let name = ''; + let param = ''; + let objectPrototype = {}; + let paramDefined = false; + let isParam = false; + let branch = this._root; + let hashConflict = new Map(); + for (let i = 1; i <= route.length; i++) { + if ((i === route.length || route[i] === '/') && end > start) { + let child; + let number = 0; + name = route.substring(start, end); + if (isParam) { + param = name; + name = __paramMapName; + } + if (branch._map.has(name)) { + child = branch._map.get(name); + } + else { + child = new Branch(); + branch._map.set(name, child); + } + branch = child; + end = i; + start = i; + if (isParam) { + branch._paramName = param; + Object.defineProperty(objectPrototype, param, { + enumerable: true, + writable: true, + value: '', + }); + paramDefined = true; + isParam = false; + } + } + if (i === route.length) { + branch._handler = handler; + branch._paramPrototype = objectPrototype; + continue; + } + if (route[i] === ':') { + isParam = true; + end = start = i + 1; + } + else if (route[i] === '/') { + end = start = i + 1; + } + else { + end++; + } + } +} + +BottleRouter.prototype.match = function(url) { + let branch = this._root; + let start = 1; + let end = 1; + let output; + let map; + let name; + let char; + let paramMap = new Map(); + for (let i = 1; i <= url.length; i++) { + char = url[i]; + if ((i === url.length || char === '/') && end > start) { + name = url.slice(start, end); + map = branch._map; + if (output = map.get(name)) { + branch = output; + } + else if (output = map.get(__paramMapName)) { + branch = output; + paramMap.set(branch._paramName, name); + } else { + return null + } + i++; + end = start = i; + char = url[i]; + } + if (i >= url.length) { + return { + handler: branch._handler, + params: paramMap, + }; + } + if (char === '/') { + end = start = i + 1; + } + else { + end++; + } + } + return null; +} + +export { + BottleRouter, +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..09368b1 --- /dev/null +++ b/package.json @@ -0,0 +1,20 @@ +{ + "name": "bottle-node", + "version": "1.0.0", + "description": "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.", + "main": "bottle.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "type": "module", + "repository": { + "type": "git", + "url": "git+https://github.com/nfp-projects/bottle-node.git" + }, + "author": "", + "license": "WTFPL", + "bugs": { + "url": "https://github.com/nfp-projects/bottle-node/issues" + }, + "homepage": "https://github.com/nfp-projects/bottle-node#readme" +}