You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(deserve): request timeout, session HMAC signing, and docs alignment 🔐
- Add requestTimeoutMs to Handler and Router, return 503 on timeout
- Body limit: remove redundant return so body stream wrapping runs when no early reject
- Docs (EN+ID): body-limit stream behavior, global middleware signature and hang note
- Docs (EN+ID): request timeout in routes-config and server-config
- Docs (EN+ID): session cookieSecret required, setSession async, HMAC
- Session: require cookieSecret, sign cookie with HMAC-SHA256, async setSession
- Types: HandlerOptions and RouterOptions requestTimeoutMs, SessionOptions cookieSecret required
- Update session tests for cookieSecret and async setSession
Copy file name to clipboardExpand all lines: docs/en/getting-started/routes-configuration.md
+15-3Lines changed: 15 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -4,15 +4,16 @@ Configure Deserve routes directory to match your project structure.
4
4
5
5
## Router Options
6
6
7
-
The `Router` constructor accepts configuration options. The main one is `routesDir` (directory for your route files):
7
+
The `Router` constructor accepts configuration options. Main options: `routesDir` (directory for route files), `requestTimeoutMs` (request timeout), and optional `errorResponseBuilder` / `staticHandler`.
8
8
9
9
```typescript
10
10
// 1. Import Router
11
11
import { Router } from'@neabyte/deserve'
12
12
13
-
// 2. Set custom routesDir (default: ./routes)
13
+
// 2. Set custom routesDir and optional request timeout (default: ./routes, no timeout)
14
14
const router =newRouter({
15
-
routesDir: 'src/routes'
15
+
routesDir: 'src/routes',
16
+
requestTimeoutMs: 30_000
16
17
})
17
18
```
18
19
@@ -32,6 +33,17 @@ const router = new Router({
32
33
})
33
34
```
34
35
36
+
### `requestTimeoutMs`
37
+
38
+
Optional timeout in milliseconds for the full request (middleware + route handler). If exceeded, the server responds with **503 Service Unavailable**. Omit or leave undefined for no timeout.
39
+
40
+
```typescript
41
+
const router =newRouter({
42
+
routesDir: 'routes',
43
+
requestTimeoutMs: 30_000
44
+
})
45
+
```
46
+
35
47
## Supported File Extensions
36
48
37
49
Deserve automatically detects and supports these file extensions:
You can set a request timeout when creating the router. If middleware and route handler do not finish within that time, the server responds with **503 Service Unavailable**:
Copy file name to clipboardExpand all lines: docs/en/middleware/body-limit.md
+8-12Lines changed: 8 additions & 12 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -2,7 +2,7 @@
2
2
3
3
> **Reference**: [RFC 7230 HTTP/1.1 Message Syntax and Routing](https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.1)
4
4
5
-
Body Limit middleware enforces maximum request body size by checking the `Content-Length` header. Prevents large payloads from overwhelming your server.
5
+
Body Limit middleware enforces maximum request body size. When a body is present, the body stream is always wrapped with a limiter so size is enforced regardless of headers. Prevents large payloads from overwhelming your server.
6
6
7
7
## Basic Usage
8
8
@@ -60,20 +60,16 @@ limit: 10 * 1024 * 1024
60
60
61
61
## How It Works
62
62
63
-
The middleware checks the `Content-Length` header before the body is read:
63
+
When a request has a body, the middleware wraps the body stream with a byte limiter so the size is enforced as the body is read (not only via headers):
64
64
65
-
1.**GET/HEAD requests** - Automatically skipped (no body)
66
-
2.**Content-Length present** - Validates against limit
67
-
3.**Transfer-Encoding present** - Passes through (chunked encoding)
68
-
4.**No headers** - Passes through (size unknown)
65
+
1.**GET/HEAD or no body** - No wrapping; request passes through.
66
+
2.**Body present** - Body stream is always wrapped with the limiter. If the client sends more bytes than `limit`, reading stops and the middleware responds with **413 Request Entity Too Large**.
67
+
3.**Content-Length** - When present and above `limit`, the middleware may reject the request before reading the body (early reject).
69
68
70
-
### RFC 7230 Compliance
69
+
### RFC 7230
71
70
72
-
The middleware follows RFC 7230:
73
-
74
-
- If both `Transfer-Encoding` and `Content-Length` are present, `Transfer-Encoding` takes precedence and body size is not validated
75
-
- Only validates `Content-Length` when `Transfer-Encoding` is absent
76
-
- Handles chunked encoding by passing through (can't check size upfront)
71
+
- If both `Transfer-Encoding` and `Content-Length` are present, `Transfer-Encoding` takes precedence.
72
+
- Chunked or unknown-length bodies are still limited by the wrapped stream; only the bytes read count toward the limit.
- **Return `awaitnext()`** - Always called to continue to next middleware or route handler, allows response modification and inspection
33
-
- **Return `Response`** - Stop processing and return response immediately
34
-
- **Return `undefined`** - Pass through middleware (automatically calls `next()`)
35
+
- **Return `awaitnext()`** - Continue to next middleware or route handler; allows response modification and inspection.
36
+
- **Return `Response`** - Stop processing and return that response immediately.
37
+
- **Return `undefined`** - Treated as pass-through (chain continues as if `next()` were called).
38
+
39
+
Middleware must either call `next()` and use its result or return a `Response`. If it does neither (e.g. never calls `next()` and returns nothing), the request can hang; use `requestTimeoutMs` in `Router` to cap request duration and get a 503 instead.
Copy file name to clipboardExpand all lines: docs/en/middleware/session.md
+25-18Lines changed: 25 additions & 18 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,10 +1,10 @@
1
1
# Session Middleware
2
2
3
-
Session middleware stores session data in a cookie and exposes it via `ctx.state`, suitable for login, preferences, or per-user state without a session database.
3
+
Session middleware stores session data in a signed cookie and exposes it via `ctx.state`, suitable for login, preferences, or per-user state without a session database. The cookie payload is signed with HMAC-SHA256; **`cookieSecret` is required**.
4
4
5
5
## Basic Usage
6
6
7
-
Use `Mware.session()` to add cookie-based session:
7
+
Use `Mware.session({ cookieSecret })` to add cookie-based session:
|`httpOnly`|`true`| Cookie not accessible from JavaScript |
90
97
91
98
## Limitations
92
99
93
-
- Session data is stored in the cookie (base64 + JSON). Do not store large or sensitive data; use only for identifiers or small data.
100
+
- Session data is stored in the cookie and signed with HMAC-SHA256. Do not store large or highly sensitive data; use only for identifiers or small data.
94
101
- For server-side or token-based session, use another mechanism (JWT, Redis, etc.) outside this middleware.
Copy file name to clipboardExpand all lines: docs/id/getting-started/routes-configuration.md
+15-3Lines changed: 15 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -4,15 +4,16 @@ Konfigurasi direktori routes Deserve agar sesuai dengan struktur proyek Anda.
4
4
5
5
## Opsi Router
6
6
7
-
Konstruktor `Router` menerima opsi konfigurasi. Opsi utama yang sering dipakai adalah `routesDir` (direktori tempat file route Anda):
7
+
Konstruktor `Router` menerima opsi konfigurasi. Opsi utama: `routesDir` (direktori file route), `requestTimeoutMs` (timeout request), serta opsional `errorResponseBuilder` / `staticHandler`.
8
8
9
9
```typescript
10
10
// 1. Import Router
11
11
import { Router } from'@neabyte/deserve'
12
12
13
-
// 2. Beri routesDir custom (default: ./routes)
13
+
// 2. routesDir custom dan opsional request timeout (default: ./routes, tanpa timeout)
14
14
const router =newRouter({
15
-
routesDir: 'src/routes'
15
+
routesDir: 'src/routes',
16
+
requestTimeoutMs: 30_000
16
17
})
17
18
```
18
19
@@ -32,6 +33,17 @@ const router = new Router({
32
33
})
33
34
```
34
35
36
+
### `requestTimeoutMs`
37
+
38
+
Opsi timeout dalam milidetik untuk seluruh request (middleware + route handler). Jika terlampaui, server merespons **503 Service Unavailable**. Omit atau biarkan undefined untuk tanpa timeout.
39
+
40
+
```typescript
41
+
const router =newRouter({
42
+
routesDir: 'routes',
43
+
requestTimeoutMs: 30_000
44
+
})
45
+
```
46
+
35
47
## Ekstensi File Yang Didukung
36
48
37
49
Deserve secara otomatis mendeteksi dan mendukung ekstensi file ini:
Anda bisa mengatur timeout request saat membuat router. Jika middleware dan route handler tidak selesai dalam waktu tersebut, server merespons **503 Service Unavailable**:
63
+
64
+
```typescript
65
+
const router =newRouter({
66
+
requestTimeoutMs: 30_000
67
+
})
68
+
awaitrouter.serve(8000)
69
+
```
70
+
71
+
Omit `requestTimeoutMs` untuk tanpa timeout (default).
72
+
60
73
## Graceful Shutdown
61
74
62
75
Gunakan `AbortSignal` untuk graceful server shutdown:
Copy file name to clipboardExpand all lines: docs/id/middleware/body-limit.md
+8-12Lines changed: 8 additions & 12 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -2,7 +2,7 @@
2
2
3
3
> **Referensi**: [RFC 7230 HTTP/1.1 Message Syntax and Routing](https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.1)
4
4
5
-
Middleware Body Limit menegakkan ukuran body request maksimumdengan memeriksa header `Content-Length`. Mencegah payload besar yang dapat membebani server Anda.
5
+
Middleware Body Limit menegakkan ukuran body request maksimum. Jika body ada, stream body selalu dibungkus dengan limiter sehingga ukuran ditegakkan terlepas dari header. Mencegah payload besar yang dapat membebani server Anda.
6
6
7
7
## Penggunaan Dasar
8
8
@@ -60,20 +60,16 @@ limit: 10 * 1024 * 1024
60
60
61
61
## Cara Kerja Body Limit
62
62
63
-
Middleware memeriksa header `Content-Length` sebelum body dibaca:
63
+
Jika request punya body, middleware membungkus stream body dengan byte limiter sehingga ukuran ditegakkan saat body dibaca (bukan hanya lewat header):
64
64
65
-
1.**Request GET/HEAD** - Secara otomatis dilewati (tidak ada body)
66
-
2.**Content-Length ada** - Memvalidasi terhadap limit
4.**Tidak ada header** - Melewati (ukuran tidak diketahui)
65
+
1.**GET/HEAD atau tanpa body** - Tidak dibungkus; request dilewati.
66
+
2.**Body ada** - Stream body selalu dibungkus dengan limiter. Jika klien mengirim byte lebih dari `limit`, pembacaan dihentikan dan middleware merespons **413 Request Entity Too Large**.
67
+
3.**Content-Length** - Jika ada dan di atas `limit`, middleware bisa menolak request sebelum membaca body (early reject).
69
68
70
-
### Kepatuhan RFC 7230
69
+
### RFC 7230
71
70
72
-
Middleware mengikuti RFC 7230:
73
-
74
-
- Jika `Transfer-Encoding` dan `Content-Length` keduanya ada, `Transfer-Encoding` memiliki prioritas dan ukuran body tidak divalidasi
75
-
- Hanya memvalidasi `Content-Length` ketika `Transfer-Encoding` tidak ada
76
-
- Menangani chunked encoding dengan melewati (tidak dapat memeriksa ukuran sebelumnya)
71
+
- Jika `Transfer-Encoding` dan `Content-Length` keduanya ada, `Transfer-Encoding` diutamakan.
72
+
- Body chunked atau ukuran tidak diketahui tetap dibatasi oleh stream yang dibungkus; hanya byte yang dibaca yang dihitung ke limit.
- **Return `awaitnext()`** - Selalu dipanggil untuk melanjutkan ke middleware atau route handler berikutnya, memungkinkan modifikasi dan inspeksi response
33
-
- **Return `Response`** - Hentikan pemrosesan dan kembalikan response segera
- **Return `awaitnext()`** - Lanjut ke middleware atau route handler berikutnya; memungkinkan modifikasi dan inspeksi response.
36
+
- **Return `Response`** - Hentikan pemrosesan dan kembalikan response tersebut.
37
+
- **Return `undefined`** - Dianggap pass-through (rantai berlanjut seperti `next()` dipanggil).
38
+
39
+
Middleware harus memanggil `next()` dan memakai hasilnya atau mengembalikan `Response`. Jika tidak (mis. tidak pernah memanggil `next()` dan tidak return apa-apa), request bisa hang; gunakan `requestTimeoutMs` di `Router` untuk membatasi durasi request dan mendapat 503.
0 commit comments