Skip to content

Commit e4eb781

Browse files
authored
V2 (#7)
* making sequential default router * updating deps * updating documentation * supporting nested routers * updating demos * updating benchmarks
1 parent 510bcad commit e4eb781

15 files changed

+340
-77
lines changed

README.md

Lines changed: 79 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,18 @@
11
# 0http
22
Cero friction HTTP framework:
33
- Tweaked Node.js Server for high throughput.
4-
- The request router you like.
5-
6-
> If no router is provided, it uses the `find-my-way` router as default implementation.
4+
- Use the request router you like.
75

86
## Usage
97
```js
108
const cero = require('0http')
119
const { router, server } = cero()
1210

13-
router.on('GET', '/hello', (req, res) => {
11+
router.get('/hello', (req, res) => {
1412
res.end('Hello World!')
1513
})
1614

17-
router.on('POST', '/do', (req, res) => {
15+
router.post('/do', (req, res) => {
1816
// ...
1917
res.statusCode = 201
2018
res.end()
@@ -30,41 +28,49 @@ server.listen(3000)
3028
```js
3129
router.lookup = (req, res) // -> should trigger router search and handlers execution
3230
```
33-
### find-my-way router
34-
> https://github.com/delvedor/find-my-way
35-
36-
This is the default router in `0http` if no router is provided via configuration. Internally uses a [Radix Tree](https://en.wikipedia.org/wiki/Radix_tree)
37-
router that will bring better performance over iterative regular expressions matching.
3831

39-
### 0http - sequential
40-
This a `0http` extended implementation of the [trouter](https://www.npmjs.com/package/trouter) router. Includes support for middlewares and shortcuts for routes registration.
41-
As this is an iterative regular expression matching router, it tends to be slower than `find-my-way` when the number of registered routes increases. However, tiny micro-services should not see major performance degradation.
32+
### 0http - sequential (default router)
33+
This a `0http` extended implementation of the [trouter](https://www.npmjs.com/package/trouter) router. Includes support for middlewares, nested routers and shortcuts for routes registration.
34+
As this is an iterative regular expression matching router, it tends to be slower than `find-my-way` when the number of registered routes increases; to mitigate this issue, we use
35+
an internal LRU cache to store the matching results of the previous requests, resulting on a super-fast matching process.
4236

4337
Supported HTTP verbs: `GET, HEAD, PATCH, OPTIONS, CONNECT, DELETE, TRACE, POST, PUT`
4438

4539
```js
4640
const cero = require('0http')
47-
const { router, server } = cero({
48-
router: require('0http/lib/router/sequential')()
49-
})
41+
const { router, server } = cero({})
5042

43+
// global middleware example
5144
router.use('/', (req, res, next) => {
5245
res.write('Hello ')
5346
next()
5447
})
5548

49+
// route middleware example
5650
const routeMiddleware = (req, res, next) => {
5751
res.write('World')
5852
next()
5953
}
54+
55+
// GET /sayhi route with middleware and handler
6056
router.get('/sayhi', routeMiddleware, (req, res) => {
6157
res.end('!')
6258
})
6359

6460
server.listen(3000)
6561
```
62+
#### Configuration Options
63+
- **defaultRoute**: Route handler when there is no router matching. Default value:
64+
```js
65+
(req, res) => {
66+
res.statusCode = 404
67+
res.end()
68+
}
69+
```
70+
- **cacheSize**: Router matching LRU cache size. Default value: `1000`
71+
6672
#### Async middlewares
67-
You can user async middlewares to await the remaining chain execution:
73+
You can user async middlewares to await the remaining chain execution. Let's describe with a custom error handler middleware:
6874
```js
6975
router.use('/', async (req, res, next) => {
7076
try {
@@ -75,10 +81,45 @@ router.use('/', async (req, res, next) => {
7581
}
7682
})
7783

78-
router.get('/sayhi', () => { throw new Error('Uuuups!') }, (req, res) => {
79-
res.end('!')
84+
router.get('/sayhi', (req, res) => {
85+
throw new Error('Uuuups!')
8086
})
8187
```
88+
89+
#### Nested Routers
90+
You can simply use `sequential` router intances as nested routers:
91+
```js
92+
const cero = require('../index')
93+
const { router, server } = cero({})
94+
95+
const nested = require('0http/lib/router/sequential')()
96+
nested.get('/url', (req, res, next) => {
97+
res.end(req.url)
98+
})
99+
router.use('/v1', nested)
100+
101+
server.listen(3000)
102+
```
103+
104+
### find-my-way router
105+
> https://github.com/delvedor/find-my-way
106+
107+
Super-fast raw HTTP router with no goodies. Internally uses a [Radix Tree](https://en.wikipedia.org/wiki/Radix_tree)
108+
router that will bring better performance over iterative regular expressions matching.
109+
```js
110+
const cero = require('../index')
111+
const { router, server } = cero({
112+
router: require('find-my-way')()
113+
})
114+
115+
router.on('GET', '/hi', (req, res) => {
116+
res.end('Hello World!')
117+
})
118+
119+
server.listen(3000)
120+
```
121+
122+
82123
## Servers
83124
`0http` is just a wrapper for the servers and routers implementations you provide.
84125
```js
@@ -111,7 +152,7 @@ const { router, server } = cero({
111152
server: low()
112153
})
113154

114-
router.on('GET', '/hi', (req, res) => {
155+
router.get('/hi', (req, res) => {
115156
res.end('Hello World!')
116157
})
117158

@@ -126,33 +167,32 @@ server.listen(3000, (socket) => {
126167
server.close()
127168
```
128169

129-
130-
## Benchmarks (22/07/2019)
131-
**Node version**: v10.16.0
132-
**Laptop**: MacBook Pro 2016, 2,7 GHz Intel Core i7, 16 GB 2133 MHz LPDDR3
170+
## Benchmarks (30/12/2019)
171+
**Node version**: v12.14.0
172+
**Laptop**: MacBook Pro 2019, 2,4 GHz Intel Core i9, 32 GB 2400 MHz DDR4
133173
**Server**: Single instance
134174

135175
```bash
136-
wrk -t8 -c8 -d5s http://127.0.0.1:3000/hi
176+
wrk -t8 -c40 -d5s http://127.0.0.1:3000/hi
137177
```
138178

139179
### 1 route registered
140-
- **0http (find-my-way + low)**
141-
`Requests/sec: 121006.70`
142-
- 0http (find-my-way)
143-
`Requests/sec: 68101.15`
144-
- 0http (sequential)
145-
`Requests/sec: 67124.65`
146-
- restana v3.3.1
147-
`Requests/sec: 59519.98`
180+
- **0http (find-my-way + low)**
181+
`Requests/sec: 135436.99`
182+
- 0http (sequential + low)
183+
`Requests/sec: 134281.32`
184+
- 0http (sequential)
185+
`Requests/sec: 88438.69`
186+
- 0http (find-my-way)
187+
`Requests/sec: 87597.44`
188+
- restana v3.4.2
189+
`Requests/sec: 73455.97`
148190

149191
### 5 routes registered
150-
- 0http (find-my-way)
151-
`Requests/sec: 68067.34`
152-
- 0http (sequential)
153-
`Requests/sec: 64141.28`
154-
- restana v3.3.1
155-
`Requests/sec: 59501.34`
192+
- **0http (sequential)**
193+
`Requests/sec: 85839.17`
194+
- 0http (find-my-way)
195+
`Requests/sec: 82682.86`
156196

157197
> For more accurate benchmarks please see:
158198
> - https://github.com/the-benchmarker/web-frameworks
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
const findMyWay = require('find-my-way')
2+
const cero = require('../index')
3+
4+
const { router, server } = cero({
5+
router: findMyWay()
6+
})
7+
8+
router.on('GET', '/hi4', (req, res) => {
9+
res.end('Hello World!')
10+
})
11+
12+
router.on('GET', '/hi3', (req, res) => {
13+
res.end('Hello World!')
14+
})
15+
16+
router.on('GET', '/hi2', (req, res) => {
17+
res.end('Hello World!')
18+
})
19+
20+
router.on('GET', '/hi1', (req, res) => {
21+
res.end('Hello World!')
22+
})
23+
24+
router.on('GET', '/hi', (req, res) => {
25+
res.end('Hello World!')
26+
})
27+
28+
server.listen(3000)

demos/basic-low-find-my-way.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
const low = require('./../lib/server/low')
2+
const cero = require('../index')
3+
4+
const { router, server } = cero({
5+
server: low(),
6+
router: require('find-my-way')()
7+
})
8+
9+
router.on('GET', '/hi', (req, res) => {
10+
res.end('Hello World!')
11+
})
12+
13+
server.start(3000, () => {})

demos/basic-sequential-5-routes.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
const sequential = require('../lib/router/sequential')
2+
const cero = require('../index')
3+
4+
const { router, server } = cero({
5+
router: sequential()
6+
})
7+
8+
router.on('GET', '/hi4', (req, res) => {
9+
res.end('Hello World!')
10+
})
11+
12+
router.on('GET', '/hi3', (req, res) => {
13+
res.end('Hello World!')
14+
})
15+
16+
router.on('GET', '/hi2', (req, res) => {
17+
res.end('Hello World!')
18+
})
19+
20+
router.on('GET', '/hi1', (req, res) => {
21+
res.end('Hello World!')
22+
})
23+
24+
router.on('GET', '/hi', (req, res) => {
25+
res.end('Hello World!')
26+
})
27+
28+
server.listen(3000)

demos/basic-sequential.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ const { router, server } = cero({
55
router: sequential()
66
})
77

8-
router.on('GET', '/hi', (req, res) => {
8+
router.get('/hi', (req, res) => {
99
res.end('Hello World!')
1010
})
1111

demos/basic.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
const cero = require('./../index')
2-
32
const { router, server } = cero()
43

5-
router.on('GET', '/hi', (req, res) => {
4+
router.get('/hi', (req, res) => {
65
res.end('Hello World!')
76
})
87

demos/async-middleware.js renamed to demos/error-handling.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,12 @@ router.use('/', async (req, res, next) => {
1414
}
1515
})
1616

17-
router.get('/sayhi', () => { throw new Error('Uuuups!') }, (req, res) => {
18-
res.end('!')
17+
router.get('/err1', (req, res) => {
18+
throw new Error('Uuuups!')
19+
})
20+
21+
router.get('/err2', (req, res, next) => {
22+
next(new Error('Uuuups!'))
1923
})
2024

2125
server.listen(3000)

index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
const http = require('http')
22

33
module.exports = (config = {}) => {
4-
const router = config.router || require('find-my-way')()
4+
const router = config.router || require('./lib/router/sequential')()
55
const server = config.server || http.createServer()
66

77
server.on('request', (req, res) => {

lib/next.js

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,29 @@
1-
function next (middlewares, req, res, middlewareIndex = 0) {
2-
const middleware = middlewares[middlewareIndex]
1+
function next (middlewares, req, res, index, routerPatterns = {}, defaultRoute) {
2+
const middleware = middlewares[index]
33
if (!middleware) {
44
if (!res.finished) {
5-
return res.end()
5+
return defaultRoute(req, res)
66
}
77

88
return
99
}
1010

1111
function step (err) {
12-
if (err) return err
13-
return next(middlewares, req, res, ++middlewareIndex)
12+
if (err) throw err
13+
return next(middlewares, req, res, ++index, routerPatterns, defaultRoute)
1414
}
1515

16-
return middleware(req, res, step)
16+
if (middleware.id) {
17+
// nested routes support
18+
const pattern = routerPatterns[middleware.id]
19+
if (pattern) {
20+
req.preRouterUrl = req.url
21+
req.url = req.url.replace(pattern, '')
22+
}
23+
middleware.lookup(req, res, step)
24+
} else {
25+
return middleware(req, res, step)
26+
}
1727
}
1828

1929
module.exports = next

0 commit comments

Comments
 (0)