Skip to content

Commit e122174

Browse files
authored
feat: enhance error handler security, optimize static nested routing, and update documentation (#50)
1 parent c7c576c commit e122174

File tree

5 files changed

+262
-148
lines changed

5 files changed

+262
-148
lines changed

docs/README.md

Lines changed: 148 additions & 135 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,38 @@
55
[![TypeScript support](https://badgen.net/npm/types/0http)](https://www.npmjs.com/package/0http)
66
[![Github stars](https://badgen.net/github/stars/jkyberneees/0http?icon=github)](https://github.com/jkyberneees/0http)
77

8-
<img src="0http-logo.svg" width="400">
8+
<br/>
9+
<div align="center">
10+
<img src="0http-logo.svg" width="400" alt="0http Logo">
11+
<br/>
12+
<br/>
13+
<b>Zero friction HTTP framework for Node.js</b>
14+
<br/>
15+
<i>Tweaked for high throughput, low overhead, and maximum flexibility.</i>
16+
</div>
17+
<br/>
18+
19+
## Why 0http?
20+
21+
- 🚀 **Blazing Fast**: One of the fastest Node.js web frameworks. Optimized for speed with smart caching and efficient routing.
22+
- 🛠 **Highly Configurable**: Swap routers, servers, and customize behavior to fit your needs.
23+
- 🔌 **Middleware Support**: Express-like middleware chain with full `async/await` support.
24+
- ʦ **TypeScript Ready**: First-class TypeScript support for type-safe development.
25+
- 🧩 **Nested Routing**: Powerful nested router support for modular architectures, optimized for static paths.
26+
- 🛡️ **Production Ready**: Secure defaults with environment-aware error handling.
27+
28+
---
29+
30+
## Installation
931

10-
Zero friction HTTP framework:
11-
- Tweaked Node.js HTTP server for high throughput.
12-
- High-performance and customizable request routers.
32+
```bash
33+
npm install 0http
34+
```
1335

14-
![Performance Benchmarks](Benchmarks.png)
15-
> Check it yourself: https://web-frameworks-benchmark.netlify.app/result?f=feathersjs,0http,koa,fastify,nestjs-express,express,sails,nestjs-fastify,restana
36+
## Quick Start
37+
38+
### JavaScript
1639

17-
# Usage
18-
JavaScript:
1940
```js
2041
const zero = require('0http')
2142
const { router, server } = zero()
@@ -25,103 +46,96 @@ router.get('/hello', (req, res) => {
2546
})
2647

2748
router.post('/do', (req, res) => {
28-
// ...
2949
res.statusCode = 201
30-
res.end()
50+
res.end('Done!')
3151
})
3252

33-
//...
34-
35-
server.listen(3000)
53+
server.listen(3000, () => {
54+
console.log('Server listening on port 3000')
55+
})
3656
```
3757

38-
TypeScript:
58+
### TypeScript
59+
3960
```ts
4061
import zero from '0http'
4162
import { Protocol } from '0http/common'
4263

4364
const { router, server } = zero<Protocol.HTTP>()
4465

4566
router.use((req, res, next) => {
67+
console.log('Request received')
4668
return next()
4769
})
4870

4971
router.get('/hi', (req, res) => {
50-
res.end(`Hello World from TS!`)
72+
res.end('Hello World from TS!')
5173
})
5274

5375
server.listen(3000)
5476
```
5577

56-
# Routers
57-
`0http` allows you to define the router implementation you prefer as soon as it support the following interface:
78+
---
79+
80+
## Core Capabilities
81+
82+
### 1. Pluggable Routers
83+
`0http` allows you to define the router implementation you prefer.
84+
85+
#### Sequential Router (Default)
86+
An extended implementation of [trouter](https://www.npmjs.com/package/trouter).
87+
- **Features**: Middleware support, nested routers, regex matching.
88+
- **Performance**: Uses an internal LRU cache (optional) to store matching results, making it extremely fast even with many routes.
89+
- **Supported Verbs**: `GET, HEAD, PATCH, OPTIONS, CONNECT, DELETE, TRACE, POST, PUT`
90+
5891
```js
59-
router.lookup = (req, res) // -> should trigger router search and handlers execution
92+
const sequential = require('0http/lib/router/sequential')
93+
const { router } = zero({
94+
router: sequential({
95+
cacheSize: 2000 // Configurable cache size
96+
})
97+
})
6098
```
6199

62-
## 0http - sequential (default router)
63-
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.
64-
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
65-
an internal(optional) LRU cache to store the matching results of the previous requests, resulting on a super-fast matching process.
66-
67-
Supported HTTP verbs: `GET, HEAD, PATCH, OPTIONS, CONNECT, DELETE, TRACE, POST, PUT`
100+
#### Find-My-Way Router
101+
Integration with [find-my-way](https://github.com/delvedor/find-my-way), a super-fast Radix Tree router.
102+
- **Best for**: Static paths and high performance without regex overhead.
103+
- **Note**: Does not support all the middleware goodies of the sequential router.
68104

69105
```js
70-
const zero = require('0http')
71-
const { router, server } = zero({})
106+
const { router } = zero({
107+
router: require('find-my-way')()
108+
})
109+
```
110+
111+
### 2. Middleware Engine
112+
The middleware engine is optimized for performance and flexibility.
72113

73-
// global middleware example
114+
#### Global & Route Middleware
115+
```js
116+
// Global middleware
74117
router.use('/', (req, res, next) => {
75-
res.write('Hello ')
118+
res.setHeader('X-Powered-By', '0http')
76119
next()
77120
})
78121

79-
// route middleware example
80-
const routeMiddleware = (req, res, next) => {
81-
res.write('World')
122+
// Route-specific middleware
123+
const auth = (req, res, next) => {
124+
if (!req.headers.authorization) {
125+
res.statusCode = 401
126+
return res.end('Unauthorized')
127+
}
82128
next()
83129
}
84130

85-
// GET /sayhi route with middleware and handler
86-
router.get('/sayhi', routeMiddleware, (req, res) => {
87-
res.end('!')
131+
router.get('/protected', auth, (req, res) => {
132+
res.end('Secret Data')
88133
})
89-
90-
server.listen(3000)
91134
```
92-
### Configuration Options
93-
- **defaultRoute**: Route handler when there is no router matching. Default value:
94-
```js
95-
(req, res) => {
96-
res.statusCode = 404
97-
res.end()
98-
}
99-
```
100-
- **cacheSize**: The size of the LRU cache for router matching. If the value is `0`, the cache will be disabled. If the value is `<0`, the cache will have an unlimited size. If the value is `>0`, an LRU Cache will be used. Default value: `-1`, for extreme performance.
101-
- **errorHandler**: Global error handler function. Default value:
102-
103-
```js
104-
(err, req, res) => {
105-
res.statusCode = 500
106-
res.end(err.message)
107-
}
108-
```
109-
110-
* **prioRequestsProcessing**: `true` to use SetImmediate to prioritize router lookup, `false` to disable. By default `true`, if used with native Node.js `http` and `https` servers. Set to `false`, if using Node.js Native Addon server, such as uWebSockets.js, as this will cause a huge performance penalty
111135

112-
Example passing configuration options:
136+
#### Async/Await Support
137+
Fully supports async middlewares for clean code.
113138

114-
```js
115-
const sequential = require('0http/lib/router/sequential')
116-
const { router, server } = zero({
117-
router: sequential({
118-
cacheSize: 2000
119-
})
120-
})
121-
```
122-
123-
### Async middlewares
124-
You can use async middlewares to await the remaining chain execution. Let's describe with a custom error handler middleware:
125139
```js
126140
router.use('/', async (req, res, next) => {
127141
try {
@@ -131,91 +145,90 @@ router.use('/', async (req, res, next) => {
131145
res.end(err.message)
132146
}
133147
})
134-
135-
router.get('/sayhi', (req, res) => {
136-
throw new Error('Uuuups!')
137-
})
138148
```
139149

140-
### Nested Routers
141-
You can simply use `sequential` router instances as nested routers:
142-
```js
143-
const zero = require('../index')
144-
const { router, server } = zero({})
145-
146-
const nested = require('0http/lib/router/sequential')()
147-
nested.get('/url', (req, res, next) => {
148-
res.end(req.url)
149-
})
150-
router.use('/v1', nested)
150+
### 3. Nested Routers
151+
Organize your application with modular nested routers. `0http` optimizes static nested routes for better performance.
151152

152-
server.listen(3000)
153-
```
153+
```js
154+
const zero = require('0http')
155+
const { router, server } = zero()
154156

155-
## find-my-way router
156-
> https://github.com/delvedor/find-my-way
157+
const v1 = require('0http/lib/router/sequential')()
157158

158-
Super-fast raw HTTP router with no goodies. Internally uses a [Radix Tree](https://en.wikipedia.org/wiki/Radix_tree)
159-
router that will bring better performance over iterative regular expressions matching.
160-
```js
161-
const zero = require('../index')
162-
const { router, server } = zero({
163-
router: require('find-my-way')()
164-
})
159+
v1.get('/users', (req, res) => res.end('User List'))
160+
v1.get('/posts', (req, res) => res.end('Post List'))
165161

166-
router.on('GET', '/hi', (req, res) => {
167-
res.end('Hello World!')
168-
})
162+
// Mount the nested router
163+
router.use('/api/v1', v1)
169164

170165
server.listen(3000)
171166
```
172167

173-
# Servers
174-
`0http` is just a wrapper for the servers and routers implementations you provide.
168+
### 4. Custom Servers
169+
`0http` is server-agnostic. You can use the standard Node.js `http.Server`, `https.Server`, or even custom implementations.
170+
175171
```js
172+
const https = require('https')
173+
const fs = require('fs')
176174
const zero = require('0http')
177175

176+
const options = {
177+
key: fs.readFileSync('key.pem'),
178+
cert: fs.readFileSync('cert.pem')
179+
}
180+
178181
const { router, server } = zero({
179-
server: yourCustomServerInstance
182+
server: https.createServer(options)
180183
})
184+
185+
server.listen(443)
181186
```
182187

183-
## Node.js http.Server
184-
If no server is provided by configuration, the standard Node.js [http.Server](https://nodejs.org/api/http.html#http_class_http_server) implementation is used.
185-
Because this server offers the best balance between Node.js ecosystem compatibility and performance, we highly recommend it for most use cases.
188+
---
186189

187-
# Benchmarks (30/12/2019)
188-
**Node version**: v12.14.0
189-
**Laptop**: MacBook Pro 2019, 2,4 GHz Intel Core i9, 32 GB 2400 MHz DDR4
190-
**Server**: Single instance
190+
## Configuration Options
191191

192-
```bash
193-
wrk -t8 -c40 -d5s http://127.0.0.1:3000/hi
194-
```
192+
Pass a configuration object to `zero(config)`:
193+
194+
| Option | Description | Default |
195+
|--------|-------------|---------|
196+
| `router` | Custom router instance. | `sequential()` |
197+
| `server` | Custom server instance. | `http.createServer()` |
198+
| `defaultRoute` | Handler for 404 Not Found. | `(req, res) => { res.statusCode = 404; res.end() }` |
199+
| `errorHandler` | Global error handler. | Production-safe error handler (hides stack traces in prod). |
200+
| `prioRequestsProcessing` | Use `setImmediate` to prioritize request processing. | `true` (for Node.js http/https) |
201+
202+
### Sequential Router Options
203+
| Option | Description | Default |
204+
|--------|-------------|---------|
205+
| `cacheSize` | LRU cache size. `0` to disable, `<0` for unlimited. | `-1` (Unlimited) |
206+
207+
---
208+
209+
## Benchmarks
210+
211+
![Performance Benchmarks](Benchmarks.png)
212+
213+
> **Note**: Benchmarks are subject to hardware and environment.
214+
> Check the latest independent results: [Web Frameworks Benchmark](https://web-frameworks-benchmark.netlify.app/result?f=feathersjs,0http,koa,fastify,nestjs-express,express,sails,nestjs-fastify,restana)
215+
216+
**Snapshot (MacBook Pro i9, Node v12):**
217+
- **0http (sequential)**: ~88k req/sec
218+
- **0http (find-my-way)**: ~87k req/sec
219+
- **restana**: ~73k req/sec
220+
221+
---
222+
223+
## Ecosystem
224+
225+
- **[low-http-server](https://github.com/jkyberneees/low-http-server)**: A low-level HTTP server implementation for extreme performance, originally part of 0http.
226+
227+
## Support
228+
229+
If you love this project, consider supporting its maintenance:
230+
- **PayPal**: [Donate](https://www.paypal.me/kyberneees)
231+
232+
## License
195233

196-
## 1 route registered
197-
- 0http (sequential)
198-
`Requests/sec: 88438.69`
199-
- 0http (find-my-way)
200-
`Requests/sec: 87597.44`
201-
- restana v3.4.2
202-
`Requests/sec: 73455.97`
203-
204-
## 5 routes registered
205-
- **0http (sequential)**
206-
`Requests/sec: 85839.17`
207-
- 0http (find-my-way)
208-
`Requests/sec: 82682.86`
209-
210-
> For more accurate benchmarks please see:
211-
>
212-
> - https://github.com/the-benchmarker/web-frameworks
213-
214-
# Support / Donate 💚
215-
You can support the maintenance of this project:
216-
- PayPal: https://www.paypal.me/kyberneees
217-
- [TRON](https://www.binance.com/en/buy-TRON) Wallet: `TJ5Bbf9v4kpptnRsePXYDvnYcYrS5Tyxus`
218-
219-
# Breaking Changes:
220-
## 3.x
221-
- Low HTTP server implementation was moved to: https://github.com/jkyberneees/low-http-server
234+
MIT

lib/next.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,11 @@ function next (middlewares, req, res, index, routers, defaultRoute, errorHandler
3939
req.preRouterPath = req.path
4040

4141
// Replace pattern in URL - this is a hot path, optimize it
42-
req.url = req.url.replace(pattern, '')
42+
if (typeof pattern === 'number') {
43+
req.url = req.url.slice(pattern)
44+
} else {
45+
req.url = req.url.replace(pattern, '')
46+
}
4347

4448
// Ensure URL starts with a slash - use charCodeAt for performance
4549
if (req.url.length === 0 || req.url.charCodeAt(0) !== 47) { // 47 is '/'

0 commit comments

Comments
 (0)