topbit 2.0.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.cn.md +133 -176
- package/README.md +803 -592
- package/bin/new-ctl.js +7 -3
- package/demo/allow.js +13 -13
- package/demo/controller/api.js +15 -0
- package/demo/extends.js +5 -0
- package/demo/http2.js +34 -0
- package/demo/http2_proxy_backend.js +45 -0
- package/demo/http2proxy.js +48 -0
- package/demo/http_proxy_backend.js +44 -0
- package/demo/httpproxy.js +47 -0
- package/demo/loader.js +27 -0
- package/demo/log.js +1 -1
- package/demo/memlimit.js +1 -1
- package/demo/min.js +1 -1
- package/demo/serv.js +1 -1
- package/images/topbit-middleware.png +0 -0
- package/images/topbit.png +0 -0
- package/package.json +7 -6
- package/src/_loadExtends.js +21 -0
- package/src/bodyparser.js +1 -1
- package/src/context1.js +19 -19
- package/src/context2.js +11 -8
- package/src/ctxpool.js +1 -0
- package/src/extends/Http2Pool.js +365 -0
- package/src/extends/__randstring.js +24 -0
- package/src/extends/cookie.js +44 -0
- package/src/extends/cors.js +334 -0
- package/src/extends/errorlog.js +252 -0
- package/src/extends/http2limit.js +126 -0
- package/src/extends/http2proxy.js +691 -0
- package/src/extends/jwt.js +217 -0
- package/src/extends/mixlogger.js +63 -0
- package/src/extends/paramcheck.js +266 -0
- package/src/extends/proxy.js +662 -0
- package/src/extends/realip.js +34 -0
- package/src/extends/referer.js +68 -0
- package/src/extends/resource.js +398 -0
- package/src/extends/session.js +174 -0
- package/src/extends/setfinal.js +50 -0
- package/src/extends/sni.js +48 -0
- package/src/extends/sse.js +293 -0
- package/src/extends/timing.js +111 -0
- package/src/extends/tofile.js +123 -0
- package/src/http1.js +15 -16
- package/src/http2.js +5 -5
- package/src/httpc.js +3 -3
- package/src/lib/npargv.js +354 -0
- package/src/lib/zipdata.js +45 -0
- package/src/middleware1.js +15 -16
- package/src/middleware2.js +4 -9
- package/src/token/token.js +4 -5
- package/src/topbit.js +13 -11
- package/test/{test-helper.js → test-ext.js} +1 -1
- package/test/test-route.js +1 -1
- package/cache/allow.js +0 -130
- package/cache/errserv.js +0 -45
- package/cache/minserv.js +0 -167
- package/cache/router.js +0 -84
- package/cache/servsock.js +0 -286
- package/cache/sni.js +0 -66
- package/images/titbit-middleware.png +0 -0
- package/images/titbit.png +0 -0
- package/tmp/buff-code +0 -134
- package/tmp/devplan +0 -9
- package/tmp/evt-test.js +0 -34
- package/tmp/fastParseUrl.js +0 -302
- package/tmp/router-rule.js +0 -559
- package/tmp/test-cdps.js +0 -122
- package/tmp/titbit.js +0 -1286
- /package/{cache/rsa → demo/cert}/localhost-cert.pem +0 -0
- /package/{cache/rsa → demo/cert}/localhost-privkey.pem +0 -0
package/README.md
CHANGED
|
@@ -1,305 +1,294 @@
|
|
|
1
|
-

|
|
2
2
|
|
|
3
|
-
#
|
|
3
|
+
# Topbit
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
[中文文档](README.cn.md)
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
> If the ECMAScript proposal for a type system is approved, JavaScript will natively support types in the future, eliminating the need to consider TypeScript support.
|
|
9
|
-
> If this proposal is not adopted, TypeScript support will be considered later.
|
|
10
|
-
|
|
11
|
-
> Reference link: <a href="https://github.com/tc39/proposal-type-annotations" target="_blank">JavaScript Type Annotations Proposal</a>
|
|
12
|
-
|
|
13
|
-
> For bugs or questions, please submit an issue or send a private message.
|
|
14
|
-
|
|
15
|
-
> It is extremely fast, both in route lookup and middleware execution.
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
A Node.js web development framework that supports both HTTP/1.1 and HTTP/2 protocols, providing a robust middleware mechanism.
|
|
7
|
+
Topbit is a server-side Web framework based on Node.js. It has no third-party dependencies and is optimized for extreme performance with a unique routing and middleware grouping execution mechanism.
|
|
19
8
|
|
|
20
9
|
**Core Features:**
|
|
21
10
|
|
|
22
|
-
*
|
|
23
|
-
* Middleware
|
|
24
|
-
*
|
|
25
|
-
* Middleware
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
* Support
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
* Control over maximum memory usage for subprocesses, with automatic restarts when limits are exceeded or when memory usage surpasses a threshold and there are no active connections.
|
|
37
|
-
* Default configurations for network security to mitigate DDoS attacks and other security issues at the software service layer.
|
|
38
|
-
|
|
39
|
-
## !Note
|
|
40
|
-
|
|
41
|
-
Always use the latest version whenever possible. **titbit performs route lookup before creating the request context object. If no route is found, the request context object is not created.** This avoids unnecessary operations and includes detection for some erroneous or malicious requests, with error status codes 404 and 400. To customize error messages during this process, use the `notFound` and `badRequest` initialization options, which by default return simple text messages. (For routes you define, handle 404 errors internally as needed.)
|
|
42
|
-
|
|
43
|
-
## **v25.x Version Changes**
|
|
44
|
-
|
|
45
|
-
Starting with v25.0.0, the request context and related details have been updated, flattening data attributes. The `res` object in the request context has been removed, and `ctx.res.body` is no longer used to collect response data; instead, `ctx.data` is used directly.
|
|
46
|
-
|
|
47
|
-
Use the `ctx.send()` function to set the final response data. The code remains compatible, so no changes are required, and you can upgrade directly.
|
|
11
|
+
* **Request Context Design:** Shields interface differences.
|
|
12
|
+
* **Global Middleware Pattern.**
|
|
13
|
+
* **Route Grouping and Naming.**
|
|
14
|
+
* **Group-Based Middleware Execution:** Middleware is matched and executed based on request methods and route groups.
|
|
15
|
+
* **Daemon Mode:** Supports multi-process clustering and automated worker process load adjustment.
|
|
16
|
+
* **Load Monitoring:** Displays child process load status.
|
|
17
|
+
* **Body Parsing:** Parses body data by default.
|
|
18
|
+
* **HTTP/1.1 & HTTP/2:** Supports enabling HTTP/1.1 or HTTP/2 via configuration. Allows simultaneous support for HTTP/2 and HTTP/1.1.
|
|
19
|
+
* **HTTPS Support:** Supports HTTPS configuration (HTTP/2 service requires HTTPS).
|
|
20
|
+
* **Request Limiting:** Limits the maximum number of visits per single IP within a specific time period.
|
|
21
|
+
* **IP Blacklist and Whitelist.**
|
|
22
|
+
* **Memory Management:** In cluster mode, monitors child processes and restarts them if they exceed the maximum memory limit.
|
|
23
|
+
* **Auto-Scaling:** Optional automatic load mode: creates new child processes to handle requests based on load and restores the initial state when idle.
|
|
24
|
+
* **Security:** Default settings related to network security to avoid DDoS attacks and other network security issues at the software service level.
|
|
48
25
|
|
|
49
26
|
## Installation
|
|
50
27
|
|
|
51
28
|
```javascript
|
|
52
|
-
npm i
|
|
29
|
+
npm i topbit
|
|
53
30
|
```
|
|
54
31
|
|
|
55
|
-
You can also install
|
|
32
|
+
You can also install via yarn:
|
|
56
33
|
|
|
57
34
|
```javascript
|
|
58
|
-
yarn add
|
|
35
|
+
yarn add topbit
|
|
59
36
|
```
|
|
60
37
|
|
|
61
|
-
## Compatibility
|
|
62
|
-
|
|
63
|
-
Since v21.8.1, updates have been kept compatible, allowing seamless version upgrades without compatibility concerns. If future technological changes require breaking updates, detailed explanations will be provided. Please review the documentation and Wiki.
|
|
64
|
-
|
|
65
|
-
Before v21.8.1, major version numbers ensured compatibility.
|
|
66
|
-
|
|
67
|
-
<a href="https://gitee.com/daoio/titbit/wikis/%E7%89%88%E6%9C%AC%E6%94%B9%E8%BF%9B%E8%AF%B4%E6%98%8E?sort_id=3220595" target="_blank">· Important Version Improvements</a>
|
|
68
|
-
|
|
69
38
|
## Minimal Example
|
|
70
39
|
|
|
71
40
|
```javascript
|
|
72
41
|
'use strict'
|
|
73
42
|
|
|
74
|
-
const
|
|
43
|
+
const Topbit = require('topbit')
|
|
75
44
|
|
|
76
|
-
const app = new
|
|
45
|
+
const app = new Topbit({
|
|
77
46
|
debug: true
|
|
78
47
|
})
|
|
79
48
|
|
|
80
49
|
app.run(1234)
|
|
81
50
|
```
|
|
82
51
|
|
|
83
|
-
When no routes are added,
|
|
52
|
+
When no routes are added, Topbit adds a default route:
|
|
84
53
|
|
|
85
54
|
`/*`
|
|
86
55
|
|
|
87
|
-
Visiting
|
|
56
|
+
Visiting via a browser will show a very simple page. This is merely for initial understanding and documentation access; it will not affect actual development.
|
|
88
57
|
|
|
89
|
-
##
|
|
58
|
+
## Add a Route
|
|
90
59
|
|
|
91
|
-
```
|
|
60
|
+
``` JavaScript
|
|
92
61
|
'use strict'
|
|
93
62
|
|
|
94
|
-
const
|
|
63
|
+
const Topbit = require('topbit')
|
|
95
64
|
|
|
96
|
-
const app = new
|
|
65
|
+
const app = new Topbit({
|
|
97
66
|
debug: true
|
|
98
67
|
})
|
|
99
68
|
|
|
69
|
+
|
|
100
70
|
app.get('/', async ctx => {
|
|
101
|
-
ctx.
|
|
71
|
+
ctx.to('success')
|
|
102
72
|
})
|
|
103
73
|
|
|
104
|
-
//
|
|
74
|
+
// Defaults to listening on 0.0.0.0, parameters are consistent with the native listen interface.
|
|
105
75
|
app.run(1234)
|
|
76
|
+
|
|
106
77
|
```
|
|
107
78
|
|
|
108
|
-
`ctx.data`
|
|
109
|
-
>
|
|
110
|
-
**It’s recommended to use `ctx.send()` to set response data, as versions prior to v25.0.0 used `ctx.res.body` for responses, and `send` ensures compatibility.**
|
|
79
|
+
`ctx.data` is the response data to be returned. You can also use `ctx.to(data)`.
|
|
80
|
+
> Actually, `ctx.to()` internally sets the value of `ctx.data`. **Using `ctx.to()` to set return data is recommended.**
|
|
111
81
|
|
|
112
|
-
## Using
|
|
82
|
+
## Using Import
|
|
113
83
|
|
|
114
|
-
In `.mjs` files, you can use ES6
|
|
84
|
+
In `.mjs` files, you can use ES6 import:
|
|
115
85
|
|
|
116
86
|
```javascript
|
|
117
|
-
import
|
|
87
|
+
import Topbit from 'topbit'
|
|
118
88
|
|
|
119
|
-
const app = new
|
|
89
|
+
const app = new Topbit({
|
|
120
90
|
debug: true
|
|
121
91
|
})
|
|
122
92
|
|
|
123
93
|
app.get('/', async ctx => {
|
|
124
|
-
|
|
94
|
+
ctx.to('success')
|
|
125
95
|
})
|
|
126
96
|
|
|
127
97
|
app.run(1234)
|
|
98
|
+
|
|
128
99
|
```
|
|
129
100
|
|
|
130
101
|
## Routes and Request Types
|
|
131
102
|
|
|
132
|
-
HTTP
|
|
133
|
-
|
|
103
|
+
The HTTP start line defines the request type, also known as the Request Method. Current request methods:
|
|
134
104
|
```
|
|
135
|
-
GET POST PUT PATCH DELETE OPTIONS
|
|
105
|
+
GET POST PUT PATCH DELETE OPTIONS TRACE HEAD
|
|
136
106
|
```
|
|
137
107
|
|
|
138
|
-
The
|
|
108
|
+
The first 6 are the most commonly used. For each request type, the router has a corresponding lowercase function for mounting routes. For convenience, after initializing the app, you can use the shortcut calls with the same names on the `app` instance. (The framework supports these types.)
|
|
139
109
|
|
|
140
110
|
**Example:**
|
|
141
111
|
|
|
142
|
-
```
|
|
112
|
+
``` JavaScript
|
|
113
|
+
|
|
143
114
|
'use strict'
|
|
144
115
|
|
|
145
|
-
const
|
|
116
|
+
const Topbit = require('titibit')
|
|
146
117
|
|
|
147
|
-
const app = new
|
|
118
|
+
const app = new Topbit({
|
|
148
119
|
debug: true
|
|
149
120
|
})
|
|
150
121
|
|
|
151
122
|
app.get('/', async c => {
|
|
152
|
-
c.
|
|
123
|
+
c.to('success')
|
|
153
124
|
})
|
|
154
125
|
|
|
155
126
|
app.get('/p', async c => {
|
|
156
|
-
c.
|
|
127
|
+
c.to(`${c.method} ${c.routepath}`)
|
|
157
128
|
})
|
|
158
129
|
|
|
159
130
|
app.post('/', async c => {
|
|
160
|
-
//
|
|
161
|
-
c.
|
|
131
|
+
// Return uploaded data
|
|
132
|
+
c.to(c.body)
|
|
162
133
|
})
|
|
163
134
|
|
|
164
135
|
app.put('/p', async c => {
|
|
165
|
-
c.
|
|
166
|
-
method: c.method,
|
|
167
|
-
body: c.body,
|
|
168
|
-
query: c.query
|
|
136
|
+
c.to({
|
|
137
|
+
method : c.method,
|
|
138
|
+
body : c.body,
|
|
139
|
+
query : c.query
|
|
169
140
|
})
|
|
170
141
|
})
|
|
171
142
|
|
|
172
|
-
//
|
|
143
|
+
// Defaults to listening on 0.0.0.0
|
|
173
144
|
app.run(8080)
|
|
145
|
+
|
|
174
146
|
```
|
|
175
147
|
|
|
176
|
-
##
|
|
148
|
+
## Get URL Parameters
|
|
177
149
|
|
|
178
|
-
- Query
|
|
179
|
-
- Form
|
|
150
|
+
- Query strings in the URL (parameters like `?a=1&b=2`) are parsed into `c.query`.
|
|
151
|
+
- Form submitted data is parsed into `c.body`.
|
|
180
152
|
|
|
181
|
-
>
|
|
153
|
+
> The content-type for forms is `application/x-www-form-urlencoded`.
|
|
182
154
|
|
|
183
|
-
```
|
|
184
|
-
'use strict'
|
|
155
|
+
``` JavaScript
|
|
156
|
+
'use strict';
|
|
185
157
|
|
|
186
|
-
const
|
|
158
|
+
const Topbit = require('topbit');
|
|
187
159
|
|
|
188
|
-
let app = new
|
|
189
|
-
|
|
160
|
+
let app = new Topbit({
|
|
161
|
+
debug: true
|
|
190
162
|
})
|
|
191
163
|
|
|
192
164
|
app.get('/q', async c => {
|
|
193
|
-
// Query
|
|
194
|
-
// Returns JSON text
|
|
195
|
-
c.
|
|
165
|
+
// Query strings after ? in URL are parsed into query.
|
|
166
|
+
// Returns JSON text, main difference is content-type in header is text/json
|
|
167
|
+
c.to(c.query)
|
|
196
168
|
})
|
|
197
169
|
|
|
198
170
|
app.post('/p', async c => {
|
|
199
|
-
// Data
|
|
171
|
+
// Data submitted via POST/PUT is saved to body.
|
|
172
|
+
// If it's a form, it is automatically parsed; otherwise, raw text value is saved.
|
|
200
173
|
// Middleware can be used to handle various data types.
|
|
201
|
-
c.
|
|
174
|
+
c.to(c.body)
|
|
202
175
|
})
|
|
203
176
|
|
|
204
177
|
app.run(2019)
|
|
178
|
+
|
|
205
179
|
```
|
|
206
180
|
|
|
207
|
-
##
|
|
181
|
+
## Get POST Data
|
|
208
182
|
|
|
209
|
-
|
|
183
|
+
Requests that submit body data are POST and PUT. In front-end pages, this is usually form submission or asynchronous requests.
|
|
210
184
|
|
|
211
|
-
- Form
|
|
185
|
+
- Form submitted data is parsed into `c.body`.
|
|
212
186
|
|
|
213
|
-
>
|
|
214
|
-
> Asynchronous requests often use `content-type: application/json`.
|
|
187
|
+
> Form content-type is `application/x-www-form-urlencoded`.
|
|
215
188
|
|
|
216
|
-
|
|
189
|
+
> Asynchronous request data often has a content-type of `application/json`.
|
|
217
190
|
|
|
218
|
-
|
|
191
|
+
For both types above, `c.body` will be an object.
|
|
192
|
+
|
|
193
|
+
``` JavaScript
|
|
219
194
|
'use strict'
|
|
220
195
|
|
|
221
|
-
const
|
|
196
|
+
const Topbit = require('topbit')
|
|
222
197
|
|
|
223
|
-
let app = new
|
|
198
|
+
let app = new Topbit({debug: true})
|
|
224
199
|
|
|
225
200
|
app.post('/p', async c => {
|
|
226
|
-
//
|
|
227
|
-
// Middleware can be used to
|
|
228
|
-
c.
|
|
229
|
-
})
|
|
201
|
+
// POST/PUT data saved to body. Forms are auto-parsed to object.
|
|
202
|
+
// Middleware can be used to process various data.
|
|
203
|
+
c.to(c.body)
|
|
204
|
+
});
|
|
230
205
|
|
|
231
206
|
app.run(2019)
|
|
207
|
+
|
|
232
208
|
```
|
|
233
209
|
|
|
234
|
-
## About
|
|
210
|
+
## About content-type
|
|
235
211
|
|
|
236
|
-
|
|
237
|
-
Basic form data is parsed into `c.body` as a JavaScript object.
|
|
212
|
+
**application/x-www-form-urlencoded**
|
|
238
213
|
|
|
239
|
-
|
|
240
|
-
For `content-type` starting with `text/`, such as `text/json`, the framework does not parse the data. It converts the uploaded data to a UTF-8 encoded string and assigns it to `c.body`. Further processing is left to the developer.
|
|
214
|
+
Basic form types are parsed into `c.body` as a JS object.
|
|
241
215
|
|
|
242
|
-
|
|
243
|
-
For file uploads, the framework parses the data by default, and the parsed file objects are stored in `c.files`, accessible via `c.getFile`.
|
|
216
|
+
**text/\***
|
|
244
217
|
|
|
245
|
-
|
|
246
|
-
This type is parsed using `JSON.parse`.
|
|
218
|
+
If content-type is `text/*` (starts with text/), such as `text/json`, the framework does not parse it. It simply converts the uploaded data to a string in utf8 format and assigns it to `c.body`. Subsequent processing is decided by the developer.
|
|
247
219
|
|
|
248
|
-
**
|
|
249
|
-
For other `content-type` values, `c.body` points to `c.rawBody`, which is the raw `Buffer` data.
|
|
220
|
+
**multipart/form-data;boundary=xxx**
|
|
250
221
|
|
|
251
|
-
|
|
222
|
+
If content-type is a file upload type, it is parsed by default. The parsed file objects are placed in `c.files`, accessible via `c.getFile`.
|
|
252
223
|
|
|
253
|
-
|
|
224
|
+
**application/json**
|
|
254
225
|
|
|
255
|
-
|
|
226
|
+
This type will be parsed using `JSON.parse`.
|
|
256
227
|
|
|
257
|
-
|
|
228
|
+
**Other types**
|
|
229
|
+
|
|
230
|
+
If content-type is any other type, `c.body` defaults to point to `c.rawBody`, which is the rawest Buffer data.
|
|
231
|
+
|
|
232
|
+
The framework provides basic core support. Other types need to be handled by the developer or via extensions.
|
|
233
|
+
|
|
234
|
+
To be easy to use while leaving enough space for developers, you can completely discard the default body parsing by setting the initialization option `parseBody` to `false`. You can also extend upon this.
|
|
235
|
+
|
|
236
|
+
The body parsing module is essentially a middleware designed to facilitate extension and replacement.
|
|
237
|
+
|
|
238
|
+
## to Function Returning Data
|
|
239
|
+
|
|
240
|
+
The `to` function is a wrapper for `c.data`; it sets the value of `c.data`. There are two aliases: `ok` and `oo`. You can choose freely based on the scenario.
|
|
241
|
+
|
|
242
|
+
``` JavaScript
|
|
258
243
|
|
|
259
|
-
```javascript
|
|
260
244
|
app.get('/', async c => {
|
|
261
|
-
c.
|
|
245
|
+
c.to('success')
|
|
262
246
|
})
|
|
263
247
|
|
|
264
248
|
app.get('/randerr', async c => {
|
|
265
249
|
let n = parseInt(Math.random() * 10)
|
|
266
250
|
if (n >= 5) {
|
|
267
|
-
c.
|
|
251
|
+
c.ok('success')
|
|
268
252
|
} else {
|
|
269
|
-
//
|
|
253
|
+
// Return 404 status code
|
|
270
254
|
/*
|
|
271
255
|
Equivalent to:
|
|
272
256
|
c.status(404).data = 'not found'
|
|
273
257
|
*/
|
|
274
|
-
|
|
275
|
-
c.status(404).
|
|
258
|
+
// You can use chain calls in versions above v22.4.6.
|
|
259
|
+
c.status(404).oo('not found')
|
|
276
260
|
}
|
|
277
261
|
})
|
|
278
262
|
|
|
279
263
|
app.run(1234)
|
|
264
|
+
|
|
280
265
|
```
|
|
281
266
|
|
|
282
|
-
##
|
|
267
|
+
## Chain Calls
|
|
283
268
|
|
|
284
|
-
|
|
269
|
+
You can use chain calls for `setHeader`, `status`, and `sendHeader`.
|
|
285
270
|
|
|
286
271
|
```javascript
|
|
272
|
+
|
|
287
273
|
app.get('/', async c => {
|
|
274
|
+
|
|
288
275
|
c.setHeader('content-type', 'text/plain; charset=utf-8')
|
|
289
276
|
.setHeader('x-server', 'nodejs server')
|
|
290
277
|
.status(200)
|
|
291
|
-
.
|
|
278
|
+
.to(`${Date.now()} Math.random()}`)
|
|
279
|
+
|
|
292
280
|
})
|
|
281
|
+
|
|
293
282
|
```
|
|
294
283
|
|
|
295
284
|
## Route Parameters
|
|
296
285
|
|
|
297
|
-
```
|
|
286
|
+
``` JavaScript
|
|
298
287
|
app.get('/:name/:id', async c => {
|
|
299
|
-
//
|
|
300
|
-
let username = c.param.name
|
|
301
|
-
let uid = c.param.id
|
|
302
|
-
c.
|
|
288
|
+
// Use : to indicate route parameters, request params are parsed into c.param
|
|
289
|
+
let username = c.param.name;
|
|
290
|
+
let uid = c.param.id;
|
|
291
|
+
c.to(`${username} ${id}`)
|
|
303
292
|
})
|
|
304
293
|
|
|
305
294
|
app.run(8000)
|
|
@@ -307,273 +296,313 @@ app.run(8000)
|
|
|
307
296
|
|
|
308
297
|
## Wildcard Path Parameters
|
|
309
298
|
|
|
310
|
-
|
|
299
|
+
`*` represents any path, but it must appear at the end of the route.
|
|
300
|
+
|
|
301
|
+
``` JavaScript
|
|
311
302
|
|
|
312
|
-
```javascript
|
|
313
303
|
app.get('/static/*', async c => {
|
|
314
|
-
//
|
|
304
|
+
// Any path represented by * is parsed into c.param.starPath
|
|
315
305
|
let spath = c.param.starPath
|
|
316
|
-
|
|
306
|
+
|
|
307
|
+
c.to(spath)
|
|
317
308
|
})
|
|
309
|
+
|
|
318
310
|
```
|
|
319
311
|
|
|
312
|
+
----
|
|
313
|
+
|
|
320
314
|
## Route Lookup Rules
|
|
321
315
|
|
|
322
|
-
|
|
316
|
+
----
|
|
323
317
|
|
|
324
|
-
|
|
318
|
+
The route lookup process strictly controls the order of parameterized routes and routes with `*`, rather than matching based on addition order.
|
|
325
319
|
|
|
326
|
-
|
|
320
|
+
Applications developed with previous versions are unaffected; there are no compatibility issues. Stricter ordering reduces the likelihood of conflicts.
|
|
327
321
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
322
|
+
Route Lookup Strategy:
|
|
323
|
+
|
|
324
|
+
1. Ordinary string paths.
|
|
325
|
+
2. Parameterized routes (routes with fewer parameters match first).
|
|
326
|
+
3. Routes with `*` (matched in longest-to-shortest pattern).
|
|
331
327
|
|
|
332
328
|
```
|
|
333
329
|
Example:
|
|
334
|
-
|
|
330
|
+
Existing routes: /x/y/:id /x/y/* /x/* /x/:key/:id
|
|
331
|
+
|
|
332
|
+
/x/y/123 matches /x/y/:id first, stops matching.
|
|
333
|
+
|
|
334
|
+
/x/y/123/345 matches /x/y/* first, stops matching.
|
|
335
335
|
|
|
336
|
-
/x/
|
|
337
|
-
|
|
338
|
-
/x/
|
|
339
|
-
|
|
340
|
-
/x/static/images/a.jpg
|
|
336
|
+
/x/q/123 will match /x/:key/:id.
|
|
337
|
+
|
|
338
|
+
/x/a.jpg will match /x/*, other routes cannot match.
|
|
339
|
+
|
|
340
|
+
/x/static/images/a.jpg will match /x/*, other routes cannot match.
|
|
341
341
|
```
|
|
342
342
|
|
|
343
|
-
|
|
343
|
+
----
|
|
344
|
+
|
|
345
|
+
## Grouping Routes
|
|
344
346
|
|
|
345
|
-
You can use `app.middleware` to specify middleware and use the returned `group` method to add grouped routes, or
|
|
347
|
+
You can use `app.middleware` to specify middleware and use the returned `group` method to add grouped routes, or use `app.group` directly.
|
|
346
348
|
|
|
347
|
-
|
|
349
|
+
**topbit.prototype.middleware(mids, options=null)**
|
|
348
350
|
|
|
349
|
-
- `mids
|
|
350
|
-
- `options
|
|
351
|
+
- `mids` is an array. Each element is a middleware function or an array where the first element is the middleware and the second is the options for adding that middleware.
|
|
352
|
+
- `options` defaults to `null`. Pass an object for options applying to all `mids`, e.g., `{pre: true}`.
|
|
351
353
|
|
|
352
|
-
|
|
354
|
+
**topbit.prototype.group(group_name, callback, prefix=true)**
|
|
353
355
|
|
|
354
|
-
- `group_name
|
|
355
|
-
- `callback
|
|
356
|
-
- `prefix
|
|
356
|
+
- `group_name` is a string representing the route group name. If it is a valid path, it also serves as the route prefix.
|
|
357
|
+
- `callback` is a callback function. The parameters received by the callback can still call `middleware` and `group`, as well as `get`, `post`, etc., to add routes.
|
|
358
|
+
- `prefix` is a boolean, defaults to `true`. It controls whether `group_name` is added as a route prefix. However, it only acts as a prefix if `group_name` is a valid route string.
|
|
357
359
|
|
|
358
360
|
```javascript
|
|
359
361
|
'use strict'
|
|
360
362
|
|
|
361
|
-
const
|
|
363
|
+
const Topbit = require('topbit')
|
|
362
364
|
|
|
363
|
-
const app = new
|
|
365
|
+
const app = new Topbit({
|
|
364
366
|
debug: true
|
|
365
367
|
})
|
|
366
368
|
|
|
367
369
|
// Middleware function
|
|
368
370
|
let mid_timing = async (c, next) => {
|
|
369
371
|
console.time('request')
|
|
370
|
-
await next()
|
|
372
|
+
await next(c)
|
|
371
373
|
console.timeEnd('request')
|
|
372
374
|
}
|
|
373
375
|
|
|
374
|
-
// The
|
|
375
|
-
//
|
|
376
|
+
// The return value of group can use 'use' and 'pre' to add middleware.
|
|
377
|
+
// /api is also added to the route prefix.
|
|
376
378
|
app.group('/api', route => {
|
|
377
379
|
route.get('/test', async c => {
|
|
378
|
-
c.
|
|
380
|
+
c.to('api test')
|
|
379
381
|
})
|
|
380
382
|
|
|
381
383
|
route.get('/:name', async c => {
|
|
382
|
-
c.
|
|
384
|
+
c.to(c.param)
|
|
383
385
|
})
|
|
384
386
|
})
|
|
385
387
|
|
|
386
|
-
// Add middleware to
|
|
388
|
+
// Add middleware to the corresponding group
|
|
387
389
|
app.use(
|
|
388
390
|
async (c, next) => {
|
|
389
391
|
console.log(c.method, c.headers)
|
|
390
|
-
await next()
|
|
391
|
-
}, {
|
|
392
|
+
await next(c)
|
|
393
|
+
}, {group: '/sub'}
|
|
392
394
|
).group('/sub', route => {
|
|
393
395
|
route.get('/:id', async c => {
|
|
394
|
-
c.
|
|
396
|
+
c.to(c.param.id)
|
|
395
397
|
})
|
|
396
398
|
})
|
|
397
399
|
|
|
398
|
-
// Test
|
|
399
|
-
app.group('
|
|
400
|
+
// Test: Does not conform to route rules, so it won't be a path prefix.
|
|
401
|
+
app.group('Test', route => {
|
|
400
402
|
route.get('/test', async c => {
|
|
401
403
|
console.log(c.group, c.name)
|
|
402
|
-
c.
|
|
404
|
+
c.to('test ok')
|
|
403
405
|
}, 'test')
|
|
404
406
|
})
|
|
405
407
|
|
|
406
408
|
app.run(1234)
|
|
409
|
+
|
|
407
410
|
```
|
|
408
411
|
|
|
409
|
-
|
|
412
|
+
Specifying multiple middlewares in this way can be somewhat complex; you can use the `middleware` method. See the example below.
|
|
410
413
|
|
|
411
414
|
### Assigning Middleware to Groups and Subgroups
|
|
412
415
|
|
|
413
416
|
```javascript
|
|
414
417
|
'use strict'
|
|
415
418
|
|
|
416
|
-
const
|
|
417
|
-
|
|
419
|
+
const Topbit = require('topbit')
|
|
420
|
+
// Import ToFile extension
|
|
421
|
+
const {ToFile} = require('topbit-toolkit')
|
|
418
422
|
|
|
419
|
-
const app = new
|
|
423
|
+
const app = new Topbit({
|
|
420
424
|
debug: true
|
|
421
425
|
})
|
|
422
426
|
|
|
423
427
|
// Middleware function
|
|
424
428
|
let mid_timing = async (c, next) => {
|
|
425
429
|
console.time('request')
|
|
426
|
-
await next()
|
|
430
|
+
await next(c)
|
|
427
431
|
console.timeEnd('request')
|
|
428
432
|
}
|
|
429
433
|
|
|
430
434
|
let sub_mid_test = async (c, next) => {
|
|
431
435
|
console.log('mid test start')
|
|
432
|
-
await next()
|
|
436
|
+
await next(c)
|
|
433
437
|
console.log('mid test end')
|
|
434
438
|
}
|
|
435
439
|
|
|
436
|
-
//
|
|
437
|
-
//
|
|
438
|
-
app.middleware([
|
|
439
|
-
// Timing middleware runs before receiving request body data, so `pre` is set to `true`.
|
|
440
|
-
[mid_timing, { pre: true }],
|
|
441
|
-
// ToFile extension runs after receiving request body data, only for POST and PUT.
|
|
442
|
-
[new ToFile(), { method: ['POST', 'PUT'] }]
|
|
443
|
-
]).group('/api', route => {
|
|
444
|
-
route.get('/test', async c => {
|
|
445
|
-
c.send('api test')
|
|
446
|
-
})
|
|
447
|
-
|
|
448
|
-
route.get('/:name', async c => {
|
|
449
|
-
c.send(c.param)
|
|
450
|
-
})
|
|
440
|
+
// group return value can use use, pre, middleware to add middleware.
|
|
441
|
+
// /api is also added to the route prefix.
|
|
451
442
|
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
443
|
+
app.middleware([
|
|
444
|
+
// Time recording middleware, runs before receiving body data, so set pre: true
|
|
445
|
+
[ mid_timing, {pre: true} ],
|
|
446
|
+
|
|
447
|
+
// ToFile extension runs after receiving body data, only for POST and PUT requests
|
|
448
|
+
[ new ToFile(), {method: ['POST', 'PUT']} ]
|
|
449
|
+
])
|
|
450
|
+
.group('/api', route => {
|
|
451
|
+
route.get('/test', async c => {
|
|
452
|
+
c.to('api test')
|
|
453
|
+
})
|
|
454
|
+
|
|
455
|
+
route.get('/:name', async c => {
|
|
456
|
+
c.to(c.param)
|
|
457
|
+
})
|
|
458
|
+
|
|
459
|
+
// Subgroup /sub enables middleware sub_mid_test.
|
|
460
|
+
// Simultaneously, subgroups inherit all middleware from the upper layer.
|
|
461
|
+
route.middleware([sub_mid_test])
|
|
462
|
+
.group('/sub', sub => {
|
|
463
|
+
sub.get('/:key', async c => {
|
|
464
|
+
c.to(c.param)
|
|
465
|
+
})
|
|
466
|
+
})
|
|
457
467
|
})
|
|
458
|
-
})
|
|
459
468
|
|
|
460
469
|
app.run(1234)
|
|
470
|
+
|
|
461
471
|
```
|
|
462
472
|
|
|
463
|
-
|
|
473
|
+
Groups support nested calls, but the hierarchy cannot exceed 9 levels. Usually, nesting more than 3 levels indicates a design issue and should be reconsidered.
|
|
474
|
+
|
|
475
|
+
**This feature is not as convenient and easy to use as the automatic loading mechanism of the `TopbitLoader` extension. However, in practice, requirements vary. Sometimes you have to use a single file for the service while still taking advantage of the framework's routing and middleware grouping, and also conveniently writing logic-clear, structured code. Therefore, `middleware` and `group` interfaces are convenient for handling this. Also, if you are not used to TopbitLoader's MCM pattern (Middleware - Controller - Model, similar to MVC), this method combines well with other module code.**
|
|
464
476
|
|
|
465
|
-
|
|
477
|
+
The function of assigning routing groups above is non-intrusive; it will not affect existing code, nor will it conflict with TopbitLoader.
|
|
466
478
|
|
|
467
|
-
|
|
479
|
+
**!! Complex route handling functions should be placed in separate modules and completed using a unified automated loading function.**
|
|
468
480
|
|
|
469
|
-
|
|
481
|
+
It supports adding via return values, so passing a callback function is not mandatory:
|
|
470
482
|
|
|
471
483
|
```javascript
|
|
472
484
|
'use strict'
|
|
473
485
|
|
|
474
|
-
const
|
|
475
|
-
|
|
486
|
+
const Topbit = require('topbit')
|
|
487
|
+
// Import ToFile extension
|
|
488
|
+
const {ToFile} = require('topbit-toolkit')
|
|
476
489
|
|
|
477
|
-
const app = new
|
|
490
|
+
const app = new Topbit({
|
|
478
491
|
debug: true
|
|
479
492
|
})
|
|
480
493
|
|
|
481
494
|
// Middleware function
|
|
482
495
|
let mid_timing = async (c, next) => {
|
|
483
496
|
console.time('request')
|
|
484
|
-
await next()
|
|
497
|
+
await next(c)
|
|
485
498
|
console.timeEnd('request')
|
|
486
499
|
}
|
|
487
500
|
|
|
488
501
|
let sub_mid_test = async (c, next) => {
|
|
489
502
|
console.log('mid test start')
|
|
490
|
-
await next()
|
|
503
|
+
await next(c)
|
|
491
504
|
console.log('mid test end')
|
|
492
505
|
}
|
|
493
506
|
|
|
494
507
|
let route = app.middleware([
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
508
|
+
// Time recording middleware, pre set to true
|
|
509
|
+
[ mid_timing, {pre: true} ],
|
|
510
|
+
|
|
511
|
+
// ToFile extension runs after receiving body data, only for POST and PUT
|
|
512
|
+
[ new ToFile(), {method: ['POST', 'PUT']} ]
|
|
513
|
+
])
|
|
514
|
+
.group('/api')
|
|
500
515
|
|
|
501
516
|
route.get('/test', async c => {
|
|
502
|
-
c.
|
|
517
|
+
c.to('api test')
|
|
503
518
|
})
|
|
504
519
|
|
|
505
520
|
route.get('/:name', async c => {
|
|
506
|
-
c.
|
|
521
|
+
c.to(c.param)
|
|
507
522
|
})
|
|
508
523
|
|
|
509
|
-
// Subgroup
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
524
|
+
// Subgroup /sub enables middleware sub_mid_test.
|
|
525
|
+
// Subgroup inherits upper layer middleware.
|
|
526
|
+
route.middleware([sub_mid_test])
|
|
527
|
+
.group('/sub', sub => {
|
|
528
|
+
sub.get('/:key', async c => {
|
|
529
|
+
c.to(c.param)
|
|
530
|
+
})
|
|
513
531
|
})
|
|
514
|
-
})
|
|
515
532
|
|
|
516
533
|
app.run(1234)
|
|
534
|
+
|
|
517
535
|
```
|
|
518
536
|
|
|
519
|
-
|
|
537
|
+
----
|
|
520
538
|
|
|
521
|
-
|
|
539
|
+
## Uploading Files
|
|
522
540
|
|
|
523
|
-
|
|
541
|
+
Uploaded files are parsed by default. You can turn this off by passing the `parseBody` option when initializing the service (detailed in options below).
|
|
542
|
+
Parsed file data is stored in `c.files`. The specific structure is shown later.
|
|
543
|
+
|
|
544
|
+
``` JavaScript
|
|
524
545
|
'use strict'
|
|
525
546
|
|
|
526
|
-
const
|
|
547
|
+
const Topbit = require('topbit')
|
|
527
548
|
|
|
528
|
-
const app = new
|
|
549
|
+
const app = new Topbit()
|
|
529
550
|
|
|
530
551
|
app.post('/upload', async c => {
|
|
552
|
+
|
|
531
553
|
let f = c.getFile('image')
|
|
532
554
|
|
|
533
|
-
// Helper
|
|
555
|
+
// Helper functions: makeName generates a name based on timestamp by default,
|
|
556
|
+
// extName parses the file extension.
|
|
557
|
+
// let fname = `${c.ext.makeName()}${c.ext.extName(f.filename)}`
|
|
558
|
+
|
|
559
|
+
// Generate a unique filename based on original extension + timestamp + random number.
|
|
534
560
|
let fname = c.ext.makeName(f.filename)
|
|
535
561
|
|
|
536
562
|
try {
|
|
537
|
-
c.
|
|
563
|
+
c.to(await c.moveFile(f, fname))
|
|
538
564
|
} catch (err) {
|
|
539
|
-
c.status(500).
|
|
565
|
+
c.status(500).to(err.message)
|
|
540
566
|
}
|
|
541
|
-
|
|
567
|
+
|
|
568
|
+
}, 'upload-image'); // Name the route 'upload-image', accessible in c.name.
|
|
542
569
|
|
|
543
570
|
app.run(1234)
|
|
571
|
+
|
|
544
572
|
```
|
|
545
573
|
|
|
546
|
-
##
|
|
574
|
+
## c.files Data Structure
|
|
547
575
|
|
|
548
|
-
|
|
576
|
+
This structure is designed based on the data construction of HTTP protocol file uploads. HTTP protocol allows multiple files for the same upload name, so it parses into an array. Using `getFile` returns the first file by default, as usually, one upload name corresponds to one file.
|
|
549
577
|
|
|
550
|
-
> For front-end
|
|
551
|
-
>
|
|
578
|
+
> For the front-end, the upload name is the name attribute in the HTML form: `<input type="file" name="image">`.
|
|
579
|
+
> `image` is the upload name; do not confuse upload name with filename.
|
|
552
580
|
|
|
553
581
|
```javascript
|
|
554
582
|
{
|
|
555
|
-
image: [
|
|
583
|
+
image : [
|
|
556
584
|
{
|
|
557
585
|
'content-type': CONTENT_TYPE,
|
|
558
|
-
// Available since
|
|
586
|
+
// Available since 23.2.6, alias for content-type for easier access
|
|
559
587
|
type: CONTENT_TYPE,
|
|
560
588
|
filename: ORIGIN_FILENAME,
|
|
561
|
-
start: START,
|
|
562
|
-
end: END,
|
|
589
|
+
start : START,
|
|
590
|
+
end : END,
|
|
563
591
|
length: LENGTH,
|
|
564
592
|
rawHeader: HEADER_DATA,
|
|
565
593
|
headers: {...}
|
|
566
594
|
},
|
|
567
595
|
...
|
|
568
596
|
],
|
|
569
|
-
|
|
597
|
+
|
|
598
|
+
video : [
|
|
570
599
|
{
|
|
571
600
|
'content-type': CONTENT_TYPE,
|
|
572
|
-
// Available since
|
|
601
|
+
// Available since 23.2.6, alias for content-type
|
|
573
602
|
type: CONTENT_TYPE,
|
|
574
603
|
filename: ORIGIN_FILENAME,
|
|
575
|
-
start: START,
|
|
576
|
-
end: END,
|
|
604
|
+
start : START,
|
|
605
|
+
end : END,
|
|
577
606
|
length: LENGTH,
|
|
578
607
|
rawHeader: HEADER_DATA,
|
|
579
608
|
headers: {...}
|
|
@@ -583,690 +612,872 @@ The structure is designed based on the HTTP protocol’s file upload data format
|
|
|
583
612
|
}
|
|
584
613
|
```
|
|
585
614
|
|
|
586
|
-
`c.getFile
|
|
615
|
+
`c.getFile` indexes by name. The default index value is 0. If a number less than 0 is passed, it gets the entire file array; returns null if not found.
|
|
587
616
|
|
|
588
|
-
## Body
|
|
617
|
+
## Max Body Data Limit
|
|
589
618
|
|
|
590
619
|
```javascript
|
|
591
620
|
'use strict'
|
|
592
621
|
|
|
593
|
-
const
|
|
622
|
+
const Topbit = require('topbit')
|
|
594
623
|
|
|
595
|
-
const app = new
|
|
596
|
-
//
|
|
624
|
+
const app = new Topbit({
|
|
625
|
+
// Allows POST or PUT request data max value to be approx 20MB.
|
|
626
|
+
// Unit is bytes.
|
|
597
627
|
maxBody: 20000000
|
|
598
628
|
})
|
|
599
629
|
|
|
630
|
+
//...
|
|
631
|
+
|
|
600
632
|
app.run(1234)
|
|
633
|
+
|
|
601
634
|
```
|
|
602
635
|
|
|
603
636
|
## Middleware
|
|
604
637
|
|
|
605
|
-
Middleware is a
|
|
638
|
+
Middleware is a very useful pattern. Implementation varies slightly between languages, but the essence is the same. The middleware mechanism allows developers to organize code better and implement complex logic easily. In fact, the entire framework runs on a middleware pattern.
|
|
606
639
|
|
|
607
|
-
|
|
640
|
+
Middleware Diagram:
|
|
608
641
|
|
|
609
642
|

|
|
610
643
|
|
|
611
|
-
|
|
644
|
+
This framework's middleware is designed to distinguish by route groups and recognize different request types to determine whether to execute or skip to the next layer. This makes it extremely fast. Multiple routes and groups have their own middleware, do not conflict, and avoid meaningless calls. Reference format:
|
|
645
|
+
|
|
646
|
+
``` JavaScript
|
|
612
647
|
|
|
613
|
-
```javascript
|
|
614
648
|
/*
|
|
615
|
-
The second parameter is optional
|
|
616
|
-
Here,
|
|
617
|
-
This design ensures
|
|
649
|
+
The second parameter is optional; omitting it enables the middleware globally.
|
|
650
|
+
Here, the second parameter indicates: execute only for POST requests AND the route group must be /api.
|
|
651
|
+
This design ensures execution on demand, avoiding unnecessary operations.
|
|
618
652
|
*/
|
|
619
653
|
app.add(async (c, next) => {
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
}, {
|
|
654
|
+
console.log('before');
|
|
655
|
+
await next(c);
|
|
656
|
+
console.log('after');
|
|
657
|
+
}, {method: 'POST', group: '/api'});
|
|
658
|
+
|
|
624
659
|
```
|
|
625
660
|
|
|
626
|
-
Middleware added with `add` executes in reverse order of addition (standard onion model
|
|
661
|
+
Middleware added with `add` executes in reverse order of addition (LIFO), which is the standard onion model. To provide logic that is easier to understand, the `use` interface is provided. Middleware added with `use` executes in the order of addition (FIFO). Different frameworks have different logic for order implementation, but sequential execution suits developer habits better.
|
|
627
662
|
|
|
628
|
-
**
|
|
663
|
+
**It is recommended to use `use` to add middleware:**
|
|
629
664
|
|
|
630
|
-
```
|
|
665
|
+
``` JavaScript
|
|
631
666
|
// Executes first
|
|
632
667
|
app.use(async (c, next) => {
|
|
633
668
|
let start_time = Date.now()
|
|
634
|
-
await next()
|
|
669
|
+
await next(c)
|
|
635
670
|
let end_time = Date.now()
|
|
636
671
|
console.log(end_time - start_time)
|
|
637
672
|
})
|
|
638
673
|
|
|
639
|
-
// Executes
|
|
674
|
+
// Executes later
|
|
640
675
|
app.use(async (c, next) => {
|
|
641
676
|
console.log(c.method, c.path)
|
|
642
|
-
await next()
|
|
677
|
+
await next(c)
|
|
643
678
|
})
|
|
644
679
|
|
|
645
|
-
//
|
|
646
|
-
// Available
|
|
680
|
+
// use can be cascaded: app.use(m1).use(m2)
|
|
681
|
+
// Available after v21.5.4, but this feature is not critical
|
|
682
|
+
// because the topbit-loader extension offers much more powerful functionality.
|
|
647
683
|
```
|
|
648
684
|
|
|
649
|
-
##
|
|
685
|
+
## Topbit Complete Flow Chart
|
|
650
686
|
|
|
651
|
-

|
|
652
688
|
|
|
653
|
-
> **
|
|
689
|
+
> **It is important to know that internally, body data reception and parsing are also middleware. The order is deliberately arranged, separating `pre` and `use` interfaces.**
|
|
654
690
|
|
|
655
691
|
## Middleware Parameters
|
|
656
692
|
|
|
657
|
-
|
|
693
|
+
Using `use` or `pre` interfaces to add middleware supports a second parameter for precise control via option properties:
|
|
658
694
|
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
695
|
+
* `group`: Route group. Indicates which group to execute for.
|
|
696
|
+
* `method`: Request method. String or Array, must be uppercase.
|
|
697
|
+
* `name`: Request name. Indicates execution only for this request name.
|
|
662
698
|
|
|
663
699
|
Example:
|
|
664
700
|
|
|
665
701
|
```javascript
|
|
702
|
+
|
|
666
703
|
app.get('/xyz', async c => {
|
|
667
|
-
|
|
668
|
-
|
|
704
|
+
//...
|
|
705
|
+
// Route group named proxy
|
|
706
|
+
}, {group: 'proxy'})
|
|
669
707
|
|
|
670
708
|
app.use(proxy, {
|
|
671
|
-
method: ['PUT', 'POST', 'GET', 'DELETE', 'OPTIONS'],
|
|
672
|
-
group
|
|
709
|
+
method : ['PUT', 'POST', 'GET', 'DELETE', 'OPTIONS'],
|
|
710
|
+
// Execute for requests in route group 'proxy'.
|
|
711
|
+
group : 'proxy'
|
|
673
712
|
})
|
|
674
713
|
```
|
|
675
714
|
|
|
676
|
-
##
|
|
715
|
+
## pre (Before Receiving Body Data)
|
|
677
716
|
|
|
678
|
-
The main difference between `pre` and `use` is that `pre`
|
|
717
|
+
The main difference between middleware added via the `pre` interface and `use` is that `pre` executes before receiving body data. It can be used for permission filtering operations before receiving data. Its parameters are consistent with `use`.
|
|
679
718
|
|
|
680
|
-
For a consistent experience, you can use `use`
|
|
719
|
+
For a consistent development experience, you can use the `use` interface and simply specify `pre` in the options:
|
|
681
720
|
|
|
682
721
|
```javascript
|
|
683
722
|
let setbodysize = async (c, next) => {
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
}
|
|
723
|
+
// Set max body receive data to ~10k.
|
|
724
|
+
c.maxBody = 10000;
|
|
725
|
+
await next(c);
|
|
726
|
+
};
|
|
727
|
+
|
|
728
|
+
// Equivalent to app.pre(setbodysize);
|
|
729
|
+
app.use(setbodysize, {pre: true});
|
|
688
730
|
|
|
689
|
-
// Equivalent to app.pre(setbodysize)
|
|
690
|
-
app.use(setbodysize, { pre: true })
|
|
691
731
|
```
|
|
692
732
|
|
|
693
|
-
`pre`
|
|
733
|
+
Using `pre` allows for more complex processing, including intercepting and not executing the next layer. For example, the `proxy` module in the `topbit-toolkit` extension uses this feature to implement a high-performance proxy service directly as a framework middleware. Its main operation is to set the request's `data` event to receive data at this layer, handle other logic, and return directly.
|
|
694
734
|
|
|
695
|
-
**
|
|
735
|
+
**Dynamically limiting request body size based on different request types**
|
|
696
736
|
|
|
697
|
-
This can be
|
|
737
|
+
This requirement can be solved by adding middleware via `pre`:
|
|
698
738
|
|
|
699
739
|
```javascript
|
|
700
|
-
|
|
701
|
-
|
|
740
|
+
|
|
741
|
+
const app = new Topbit({
|
|
742
|
+
// Default max body limit ~10M.
|
|
702
743
|
maxBody: 10000000
|
|
703
744
|
})
|
|
704
745
|
|
|
705
746
|
app.pre(async (c, next) => {
|
|
747
|
+
|
|
706
748
|
let ctype = c.headers['content-type'] || ''
|
|
707
749
|
|
|
708
750
|
if (ctype.indexOf('text/') === 0) {
|
|
709
|
-
//
|
|
751
|
+
// 50K
|
|
710
752
|
c.maxBody = 50000
|
|
711
753
|
} else if (ctype.indexOf('application/') === 0) {
|
|
712
|
-
//
|
|
754
|
+
// 100K
|
|
713
755
|
c.maxBody = 100000
|
|
714
756
|
} else if (ctype.indexOf('multipart/form-data') < 0) {
|
|
715
|
-
//
|
|
757
|
+
// 10K
|
|
716
758
|
c.maxBody = 10000
|
|
717
759
|
}
|
|
718
760
|
|
|
719
|
-
await next()
|
|
720
|
-
|
|
761
|
+
await next(c)
|
|
762
|
+
|
|
763
|
+
}, {method: ['POST', 'PUT']})
|
|
764
|
+
|
|
765
|
+
|
|
721
766
|
```
|
|
722
767
|
|
|
723
|
-
|
|
768
|
+
If these parameters appear in the file simultaneously, it can look complex and be hard to maintain, but the functionality is powerful. Therefore, leaving it to automated program completion can greatly simplify coding.
|
|
769
|
+
|
|
770
|
+
**For complete project structure setup, please use `topbit-loader`. This extension completes automatic loading of routes/models and automatic orchestration of middleware. <a target=_blank href="https://gitee.com/daoio/topbit-loader">topbit-loader</a>**
|
|
724
771
|
|
|
725
772
|
## HTTPS
|
|
726
773
|
|
|
727
774
|
```javascript
|
|
728
775
|
'use strict'
|
|
729
776
|
|
|
730
|
-
const
|
|
777
|
+
const Topbit = require('topbit')
|
|
731
778
|
|
|
732
|
-
//
|
|
733
|
-
const app = new
|
|
734
|
-
|
|
735
|
-
|
|
779
|
+
// Just pass the path to the certificate and key files
|
|
780
|
+
const app = new Topbit({
|
|
781
|
+
// './xxx.pem' file also works
|
|
782
|
+
cert: './xxx.cert',
|
|
783
|
+
key: './xxx.key'
|
|
736
784
|
})
|
|
737
785
|
|
|
738
786
|
app.run(1234)
|
|
787
|
+
|
|
739
788
|
```
|
|
740
789
|
|
|
741
|
-
##
|
|
790
|
+
## Simultaneous Support for HTTP/2 and HTTP/1.1 (Compatibility Mode)
|
|
742
791
|
|
|
743
|
-
Compatibility mode
|
|
792
|
+
Compatibility mode utilizes the ALPN protocol and requires HTTPS, so certificates and keys must be configured.
|
|
744
793
|
|
|
745
794
|
```javascript
|
|
746
795
|
'use strict'
|
|
747
796
|
|
|
748
|
-
const
|
|
797
|
+
const Topbit = require('topbit')
|
|
749
798
|
|
|
750
|
-
//
|
|
751
|
-
const app = new
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
799
|
+
// Just pass the path to the certificate and key files
|
|
800
|
+
const app = new Topbit({
|
|
801
|
+
cert: './xxx.cert',
|
|
802
|
+
key: './xxx.key',
|
|
803
|
+
// Enable http2 and allow http1; compatibility mode is enabled automatically
|
|
804
|
+
http2: true,
|
|
805
|
+
allowHTTP1: true
|
|
757
806
|
})
|
|
758
807
|
|
|
759
808
|
app.run(1234)
|
|
809
|
+
|
|
760
810
|
```
|
|
761
811
|
|
|
762
812
|
## Configuration Options
|
|
763
813
|
|
|
764
|
-
|
|
814
|
+
Full configuration options for app initialization are as follows. Please read the comments carefully.
|
|
765
815
|
|
|
766
|
-
```
|
|
767
|
-
{
|
|
768
|
-
|
|
769
|
-
|
|
816
|
+
``` JavaScript
|
|
817
|
+
{
|
|
818
|
+
// This config represents max bytes for POST/PUT form submission and file uploads.
|
|
819
|
+
maxBody : 8000000,
|
|
770
820
|
|
|
771
|
-
|
|
772
|
-
|
|
821
|
+
// Max number of files to parse
|
|
822
|
+
maxFiles : 12,
|
|
773
823
|
|
|
774
|
-
|
|
824
|
+
daemon : false, // Enable daemon mode
|
|
775
825
|
|
|
776
|
-
|
|
777
|
-
|
|
826
|
+
/*
|
|
827
|
+
After enabling daemon mode, if path is not empty string, pid is written to this file. Used for service management.
|
|
828
|
+
*/
|
|
829
|
+
pidFile : '',
|
|
778
830
|
|
|
779
|
-
|
|
780
|
-
|
|
831
|
+
// Whether to enable global logging. true enables it, outputting request info or writing to file.
|
|
832
|
+
globalLog: false,
|
|
781
833
|
|
|
782
|
-
|
|
783
|
-
|
|
834
|
+
// Log output type: 'stdio' for terminal, 'file' for file output.
|
|
835
|
+
logType : 'stdio',
|
|
784
836
|
|
|
785
|
-
|
|
786
|
-
|
|
837
|
+
// File path for successful request logs
|
|
838
|
+
logFile : '',
|
|
787
839
|
|
|
788
|
-
|
|
789
|
-
|
|
840
|
+
// File path for error request logs
|
|
841
|
+
errorLogFile : '',
|
|
790
842
|
|
|
791
|
-
|
|
792
|
-
|
|
843
|
+
// Max lines per log file
|
|
844
|
+
logMaxLines: 50000,
|
|
793
845
|
|
|
794
|
-
|
|
795
|
-
|
|
846
|
+
// Max number of historical log files
|
|
847
|
+
logHistory: 50,
|
|
796
848
|
|
|
797
|
-
|
|
798
|
-
|
|
849
|
+
// Custom log handler function
|
|
850
|
+
logHandle: null,
|
|
799
851
|
|
|
800
|
-
|
|
801
|
-
|
|
852
|
+
// Enable HTTPS
|
|
853
|
+
https : false,
|
|
802
854
|
|
|
803
|
-
|
|
855
|
+
http2 : false,
|
|
804
856
|
|
|
805
|
-
|
|
857
|
+
allowHTTP1: false,
|
|
806
858
|
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
859
|
+
// HTTPS key and cert file paths. If paths are set, https is automatically set to true.
|
|
860
|
+
key : '',
|
|
861
|
+
cert : '',
|
|
810
862
|
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
863
|
+
// Server options written in 'server', passed during http service initialization. Reference http2.createSecureServer, tls.createServer
|
|
864
|
+
server : {
|
|
865
|
+
handshakeTimeout: 8192, // TLS handshake timeout
|
|
866
|
+
//sessionTimeout: 350,
|
|
867
|
+
},
|
|
816
868
|
|
|
817
|
-
|
|
818
|
-
|
|
869
|
+
// Set server timeout in ms. Specific request timeouts can be set in specific requests.
|
|
870
|
+
timeout : 15000,
|
|
819
871
|
|
|
820
|
-
|
|
872
|
+
debug : false,
|
|
821
873
|
|
|
822
|
-
|
|
823
|
-
|
|
874
|
+
// Ignore trailing / in paths
|
|
875
|
+
ignoreSlash: true,
|
|
824
876
|
|
|
825
|
-
|
|
826
|
-
|
|
877
|
+
// Enable request limiting
|
|
878
|
+
useLimit: false,
|
|
827
879
|
|
|
828
|
-
|
|
829
|
-
|
|
880
|
+
// Max connections, 0 means no limit
|
|
881
|
+
maxConn : 1024,
|
|
830
882
|
|
|
831
|
-
|
|
832
|
-
|
|
883
|
+
// Max connections per single IP within unit time, 0 means no limit
|
|
884
|
+
maxIPRequest: 0,
|
|
833
885
|
|
|
834
|
-
|
|
835
|
-
|
|
886
|
+
// Rate limit unit time for IP, 1 means 1 second. Default 60 seconds. Range 0.1 ~ 86400.
|
|
887
|
+
unitTime : 60,
|
|
888
|
+
|
|
889
|
+
// Display load info, requires enabling cluster mode via daemon interface
|
|
890
|
+
loadMonitor : true,
|
|
836
891
|
|
|
837
|
-
|
|
838
|
-
|
|
892
|
+
// Load info type: text, json, --null
|
|
893
|
+
// json type is for program communication, convenient for interface development
|
|
894
|
+
loadInfoType : 'text',
|
|
839
895
|
|
|
840
|
-
|
|
841
|
-
|
|
896
|
+
// Load info file path. If not set, outputs to terminal; otherwise saves to file.
|
|
897
|
+
loadInfoFile : '',
|
|
842
898
|
|
|
843
|
-
|
|
844
|
-
|
|
899
|
+
// Data to return for 404
|
|
900
|
+
notFound: 'Not Found',
|
|
901
|
+
|
|
902
|
+
// Data to return for 400
|
|
903
|
+
badRequest : 'Bad Request',
|
|
845
904
|
|
|
846
|
-
|
|
847
|
-
|
|
905
|
+
// Percentage parameter controlling max memory usage for child processes. Range -0.42 ~ 0.36. Base is 0.52, so default is 80%.
|
|
906
|
+
memFactor: 0.28,
|
|
848
907
|
|
|
849
|
-
|
|
850
|
-
|
|
908
|
+
// Max URL length
|
|
909
|
+
maxUrlLength: 2048,
|
|
851
910
|
|
|
852
|
-
|
|
853
|
-
|
|
911
|
+
// Max number of request context cache pool.
|
|
912
|
+
maxpool: 4096,
|
|
854
913
|
|
|
855
|
-
|
|
856
|
-
|
|
914
|
+
// Timer milliseconds for child process resource reporting.
|
|
915
|
+
monitorTimeSlice: 640,
|
|
857
916
|
|
|
858
|
-
|
|
859
|
-
|
|
917
|
+
// When globalLog is true, record real IP address? Mainly used in reverse proxy mode.
|
|
918
|
+
realIP: false,
|
|
860
919
|
|
|
861
|
-
|
|
862
|
-
|
|
920
|
+
// Max allowed querystring parameters.
|
|
921
|
+
maxQuery: 25,
|
|
863
922
|
|
|
864
|
-
|
|
865
|
-
|
|
923
|
+
// Enable strong mode? If enabled, processes rejectionHandled and uncaughtException events,
|
|
924
|
+
// and captures errors: TypeError,ReferenceError,RangeError,AssertionError,URIError,Error.
|
|
925
|
+
strong: false,
|
|
866
926
|
|
|
867
|
-
|
|
868
|
-
|
|
927
|
+
// Fast querystring parsing. Multiple values with same name only set the first one, not parsed as array.
|
|
928
|
+
fastParseQuery: false,
|
|
929
|
+
|
|
930
|
+
// Whether to auto decode Query parameters using decodeURIComponent.
|
|
931
|
+
autoDecodeQuery: true,
|
|
869
932
|
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
strong: false,
|
|
933
|
+
// In multipart format, limit max length of a single form item.
|
|
934
|
+
maxFormLength: 1000000,
|
|
873
935
|
|
|
874
|
-
|
|
875
|
-
|
|
936
|
+
/* Error handler function. Unifies collection of runtime errors:
|
|
937
|
+
tlsClientError, server error, secureConnection error, clientError, thrown runtime errors.
|
|
938
|
+
errname is a string marking error info and location, format --ERR-CONNECTION--, --ERR-CLIENT--, etc.
|
|
876
939
|
|
|
877
|
-
|
|
878
|
-
|
|
940
|
+
Usually Node.js thrown errors have code and message.
|
|
941
|
+
errname is optional but passed.
|
|
942
|
+
Pass custom function via config option to implement custom error collection/handling.
|
|
943
|
+
*/
|
|
944
|
+
errorHandle: (err, errname) => {
|
|
945
|
+
this.config.debug && console.error(errname, err)
|
|
946
|
+
},
|
|
879
947
|
|
|
880
|
-
|
|
881
|
-
|
|
948
|
+
// Max load rate percentage. Default 75 means if CPU usage > 75%, automatically create child process.
|
|
949
|
+
// Must enable auto load mode via autoWorker.
|
|
950
|
+
maxLoadRate: 75,
|
|
882
951
|
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
errorHandle: (err, errname) => {
|
|
886
|
-
this.config.debug && console.error(errname, err)
|
|
887
|
-
},
|
|
952
|
+
// http2 protocol http2Stream timeout. If not set, -1 means consistent with timeout.
|
|
953
|
+
streamTimeout: -1,
|
|
888
954
|
|
|
889
|
-
|
|
890
|
-
|
|
955
|
+
// Request timeout. This is total request time, mainly to counter malicious requests.
|
|
956
|
+
// e.g. Slowloris attacks where 1 byte is sent per second.
|
|
957
|
+
// Idle timeout won't work, allowing long-term resource occupation.
|
|
958
|
+
// This is a DDoS attack.
|
|
959
|
+
requestTimeout: 100000,
|
|
891
960
|
|
|
892
|
-
|
|
893
|
-
|
|
961
|
+
};
|
|
962
|
+
// For HTTP status codes, only these two are needed here. Others don't strictly need full support,
|
|
963
|
+
// and you can handle them in your application implementation.
|
|
964
|
+
// Because once execution starts, corresponding status codes can be returned via runtime state.
|
|
965
|
+
// Before that, the framework prepares for the onion model execution, which is very fast.
|
|
894
966
|
|
|
895
|
-
// Total request timeout to counter malicious requests (e.g., DDoS attacks).
|
|
896
|
-
requestTimeout: 100000
|
|
897
|
-
}
|
|
898
967
|
```
|
|
899
968
|
|
|
900
969
|
## Request Context
|
|
901
970
|
|
|
902
|
-
|
|
971
|
+
Request context is an object encapsulating various request data. This design handles differences between HTTP/1.1 and HTTP/2 protocols and incompatibilities from Node.js evolution. For design and performance, the HTTP2 module encapsulates the request object as a stream, not the `http` module's `IncomingMessage` and `ServerResponse`.
|
|
903
972
|
|
|
904
|
-
**Request Context Properties and
|
|
973
|
+
**Request Context Properties and Description**
|
|
905
974
|
|
|
906
975
|
| Property | Description |
|
|
907
|
-
|
|
908
|
-
| version | Protocol version
|
|
909
|
-
| major | Major protocol version
|
|
910
|
-
| maxBody |
|
|
911
|
-
| method | Request
|
|
912
|
-
| host |
|
|
913
|
-
| protocol | Protocol string
|
|
914
|
-
| path |
|
|
915
|
-
| routepath | Actual route string
|
|
916
|
-
| query | URL
|
|
976
|
+
| ---- | ---- |
|
|
977
|
+
| version | Protocol version, string type, '1.1' or '2'. |
|
|
978
|
+
| major | Major protocol version number, 1, 2, 3 (HTTP/1.1, HTTP/2, HTTP/3). |
|
|
979
|
+
| maxBody | Max supported request body bytes. Number. Defaults to `maxBody` init option. Can be set automatically in middleware based on request. |
|
|
980
|
+
| method | Request type (GET, POST, etc.). Uppercase string. |
|
|
981
|
+
| host | Service hostname, value of `request.headers.host`. |
|
|
982
|
+
| protocol | Protocol string without colon, 'https', 'http'. |
|
|
983
|
+
| path | Specific request path. |
|
|
984
|
+
| routepath | Actual executed route string. |
|
|
985
|
+
| query | URL passed parameters. |
|
|
917
986
|
| param | Route parameters. |
|
|
918
987
|
| files | Uploaded file information. |
|
|
919
|
-
| body |
|
|
920
|
-
| port | Client request port. |
|
|
921
|
-
| ip | Client IP address (socket address
|
|
922
|
-
| headers |
|
|
923
|
-
| isUpload() |
|
|
924
|
-
| name | Route name
|
|
925
|
-
| group | Route group
|
|
926
|
-
|
|
|
927
|
-
|
|
|
928
|
-
| box |
|
|
929
|
-
| service |
|
|
930
|
-
| data |
|
|
931
|
-
| ext |
|
|
932
|
-
|
|
|
933
|
-
| write(data) |
|
|
934
|
-
| moveFile(file, target_filepath) |
|
|
935
|
-
| status() |
|
|
936
|
-
| setHeader(k, v) |
|
|
937
|
-
| removeHeader(k) |
|
|
938
|
-
| getFile(name) |
|
|
939
|
-
| sendHeader() |
|
|
940
|
-
| user | Standard property for user login
|
|
941
|
-
| json(data) |
|
|
942
|
-
| text(data) |
|
|
943
|
-
| html(data) |
|
|
944
|
-
| pipe(filepath) |
|
|
945
|
-
| pipeJson(filepath) |
|
|
946
|
-
| pipeText(filepath) |
|
|
947
|
-
| pipeHtml(filepath) |
|
|
948
|
-
|
|
949
|
-
|
|
988
|
+
| body | Body request data. Format depends on content-type (string, object, buffer). |
|
|
989
|
+
| port | Client request port number. |
|
|
990
|
+
| ip | Client request IP address (socket address). Check x-real-ip or x-forwarded-for if using proxy. |
|
|
991
|
+
| headers | Points to `request.headers`. |
|
|
992
|
+
| isUpload() | Is it a file upload request? Checks if content-type header is multipart/form-data. |
|
|
993
|
+
| name | Route name, defaults to empty string. |
|
|
994
|
+
| group | Route group, defaults to empty string. |
|
|
995
|
+
| res | HTTP/1.1 points to response, HTTP/2 points to stream. |
|
|
996
|
+
| req | HTTP/1.1 is `IncomingMessage` object, HTTP/2 points to stream object. |
|
|
997
|
+
| box | Defaults to empty object. Can add any property value to dynamically pass info to next layer components. |
|
|
998
|
+
| service | Object for dependency injection, points to `app.service`. |
|
|
999
|
+
| data | Saves final data to return to client. Assign to data or use `ctx.to`. Pre-v24.x was `ctx.res.body`. |
|
|
1000
|
+
| ext | Provides helper functions. See wiki. |
|
|
1001
|
+
| to(data) | Function to set `ctx.data`. |
|
|
1002
|
+
| write(data) | Directly write data to client. |
|
|
1003
|
+
| moveFile(file:object, target_filepath:string) | Function to move uploaded file to specified path. |
|
|
1004
|
+
| status() | Function to set status code. |
|
|
1005
|
+
| setHeader(k, v) | Function to set header. |
|
|
1006
|
+
| removeHeader(k) | Function to remove header waiting to be sent. |
|
|
1007
|
+
| getFile(name) | Function to get uploaded file info (reads files property). |
|
|
1008
|
+
| sendHeader() | Function to send headers for http2. `setHeader` only caches headers. For http/1.1, this is an empty function for code consistency. |
|
|
1009
|
+
| user | Standard property for user login, defaults to null. |
|
|
1010
|
+
| json(data) | Function, sets return data and marks type as json. |
|
|
1011
|
+
| text(data) | Function, sets return data and marks type as text. |
|
|
1012
|
+
| html(data) | Function, sets return data and marks type as html. |
|
|
1013
|
+
| pipe(filepath) | Function, stream response data. Example: `await ctx.setHeader('content-type', 'text/html').pipe('./index.html')` |
|
|
1014
|
+
| pipeJson(filepath) | Stream file data as json type. |
|
|
1015
|
+
| pipeText(filepath) | Stream file data as text type. |
|
|
1016
|
+
| pipeHtml(filepath) | Stream file data as html type. |
|
|
1017
|
+
|
|
1018
|
+
Note: `to` function only sets `ctx.data` value; data is returned at the end. It's same as direct assignment, but function calls reveal errors faster (wrong assignment adds a property without error but returns incorrect data).
|
|
950
1019
|
|
|
951
1020
|
## Dependency Injection
|
|
952
1021
|
|
|
953
|
-
The request context
|
|
1022
|
+
The request context has a `service` item pointing to `app.service`. After initializing the app, all data/instances needed can be mounted to `app.service`.
|
|
954
1023
|
|
|
955
|
-
```
|
|
956
|
-
|
|
1024
|
+
``` JavaScript
|
|
1025
|
+
|
|
1026
|
+
'use strict';
|
|
957
1027
|
|
|
958
|
-
const
|
|
1028
|
+
const Topbit = require('topbit');
|
|
959
1029
|
|
|
960
|
-
|
|
1030
|
+
let app = new Topbit({
|
|
961
1031
|
debug: true
|
|
962
|
-
})
|
|
1032
|
+
});
|
|
963
1033
|
|
|
964
1034
|
// Overwrites if exists, adds if not.
|
|
965
|
-
app.addService('name', 'first')
|
|
1035
|
+
app.addService('name', 'first');
|
|
966
1036
|
app.addService('data', {
|
|
967
|
-
id: 123,
|
|
968
|
-
ip: '127.0.0.1'
|
|
969
|
-
})
|
|
1037
|
+
id : 123,
|
|
1038
|
+
ip : '127.0.0.1'
|
|
1039
|
+
});
|
|
970
1040
|
|
|
1041
|
+
/*
|
|
1042
|
+
This might seem useless in a single file where variables are accessible.
|
|
1043
|
+
However, it becomes very important for module separation.
|
|
1044
|
+
*/
|
|
971
1045
|
app.get('/info', async c => {
|
|
972
|
-
c.
|
|
973
|
-
name: c.service.name,
|
|
974
|
-
data: c.service.data
|
|
1046
|
+
c.to({
|
|
1047
|
+
name : c.service.name,
|
|
1048
|
+
data : c.service.data
|
|
975
1049
|
})
|
|
976
1050
|
})
|
|
977
1051
|
|
|
978
1052
|
app.run(1234)
|
|
1053
|
+
|
|
979
1054
|
```
|
|
980
1055
|
|
|
981
|
-
## Extending
|
|
1056
|
+
## Extending Request Context
|
|
982
1057
|
|
|
983
|
-
To
|
|
1058
|
+
To add extension support to the request context object, use `app.httpServ.context`. This property is the constructor for the request context.
|
|
984
1059
|
|
|
985
1060
|
**Example:**
|
|
986
1061
|
|
|
987
1062
|
```javascript
|
|
988
1063
|
'use strict'
|
|
989
|
-
|
|
990
|
-
const
|
|
991
|
-
|
|
992
|
-
const app = new titbit({
|
|
993
|
-
debug: true
|
|
1064
|
+
const Topbit = require('topbit')
|
|
1065
|
+
const app = new Topbit({
|
|
1066
|
+
debug: true
|
|
994
1067
|
})
|
|
995
1068
|
|
|
996
|
-
//
|
|
1069
|
+
// 'this' represents the request context
|
|
997
1070
|
app.httpServ.context.prototype.testCtx = function () {
|
|
998
|
-
|
|
1071
|
+
console.log(this.method, this.path)
|
|
999
1072
|
}
|
|
1000
1073
|
|
|
1001
1074
|
app.get('/test', async ctx => {
|
|
1002
|
-
|
|
1075
|
+
ctx.testCtx()
|
|
1003
1076
|
})
|
|
1004
1077
|
|
|
1005
1078
|
app.run(1234)
|
|
1079
|
+
|
|
1006
1080
|
```
|
|
1007
1081
|
|
|
1008
|
-
##
|
|
1082
|
+
## app.isMaster and app.isWorker
|
|
1009
1083
|
|
|
1010
|
-
|
|
1084
|
+
Node.js v16.x recommends `isPrimary` over `isMaster`. However, `isMaster` is still available. After Topbit initialization, `app` has two getters: `isMaster` and `isWorker`. They function identically to `cluster` properties. Purposes:
|
|
1011
1085
|
|
|
1012
|
-
- Avoid
|
|
1013
|
-
- Shield
|
|
1086
|
+
- Avoid writing `const cluster = require('cluster')` again.
|
|
1087
|
+
- Shield future incompatible changes in `cluster`, enhancing code compatibility.
|
|
1014
1088
|
|
|
1015
|
-
##
|
|
1089
|
+
## daemon and run
|
|
1016
1090
|
|
|
1017
|
-
|
|
1091
|
+
`run` parameters: `port`, `host`. `host` defaults to `0.0.0.0`. Can also be `sockPath` (path to .sock file), supported by http listen interface. Using .sock ignores host.
|
|
1018
1092
|
|
|
1019
|
-
|
|
1093
|
+
`daemon` first two parameters match `run`. Third parameter is a number indicating how many child processes to use. Defaults to 0, which automatically creates child processes based on CPU core count. It maintains stable child process count, creating new ones if any terminate unexpectedly.
|
|
1020
1094
|
|
|
1021
|
-
**In cluster mode,
|
|
1095
|
+
**In cluster mode, max child processes will not exceed 2x CPU cores.**
|
|
1022
1096
|
|
|
1023
|
-
|
|
1097
|
+
Example:
|
|
1024
1098
|
|
|
1025
1099
|
```javascript
|
|
1026
|
-
|
|
1100
|
+
|
|
1101
|
+
// Host defaults to 0.0.0.0, port 1234
|
|
1027
1102
|
app.run(1234)
|
|
1028
1103
|
|
|
1029
|
-
// Listen on localhost
|
|
1104
|
+
// Listen on localhost, only accessible locally
|
|
1030
1105
|
app.run(1234, 'localhost')
|
|
1031
1106
|
|
|
1032
|
-
// Use 2
|
|
1107
|
+
// Use 2 child processes, host defaults to 0.0.0.0
|
|
1033
1108
|
app.daemon(1234, 2)
|
|
1034
1109
|
|
|
1035
|
-
// Use 3
|
|
1110
|
+
// Use 3 child processes
|
|
1036
1111
|
app.daemon(1234, 'localhost', 3)
|
|
1112
|
+
|
|
1037
1113
|
```
|
|
1038
1114
|
|
|
1039
1115
|
## Logging
|
|
1040
1116
|
|
|
1041
|
-
The framework provides global logging
|
|
1117
|
+
The framework provides global logging. When using cluster mode (`daemon` interface), use initialization option `globalLog` to enable global logs and specify a log file. In single process mode, logs output to terminal (can use output/error redirection to save to file).
|
|
1042
1118
|
|
|
1043
|
-
**Note: Only
|
|
1119
|
+
**Note: Only daemon execution (cluster mode) allows saving logs to file natively. `run` (single process) outputs to screen; use IO redirection to save.**
|
|
1044
1120
|
|
|
1045
|
-
|
|
1121
|
+
Besides file saving and terminal output, `logHandle` option allows custom log processing functions.
|
|
1046
1122
|
|
|
1047
|
-
**
|
|
1123
|
+
**Setting `logHandle` invalidates `logFile` and `errorLogFile`. See code for details.**
|
|
1048
1124
|
|
|
1049
|
-
|
|
1050
|
-
|
|
1125
|
+
Example:
|
|
1126
|
+
|
|
1127
|
+
``` JavaScript
|
|
1051
1128
|
|
|
1052
|
-
const
|
|
1129
|
+
const Topbit = require('topbit')
|
|
1130
|
+
|
|
1131
|
+
const app = new Topbit({
|
|
1053
1132
|
debug: true,
|
|
1133
|
+
// Enable global log
|
|
1054
1134
|
globalLog: true,
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1135
|
+
|
|
1136
|
+
// Output to file. Default is 'stdio' (terminal).
|
|
1137
|
+
logType: 'file'
|
|
1138
|
+
|
|
1139
|
+
// Log file for status codes 2xx or 3xx
|
|
1140
|
+
logFile : '/tmp/topbit.log',
|
|
1141
|
+
|
|
1142
|
+
// Log file for errors (4xx or 5xx)
|
|
1143
|
+
errorLogFile: '/tmp/topbit-error.log',
|
|
1144
|
+
|
|
1145
|
+
// Custom handler function. logFile/errorLogFile become invalid.
|
|
1146
|
+
// Parameters: (worker, message)
|
|
1147
|
+
// worker: see cluster worker docs
|
|
1148
|
+
/*
|
|
1149
|
+
msg is log object, properties:
|
|
1150
|
+
{
|
|
1151
|
+
type : '_log',
|
|
1152
|
+
success : true,
|
|
1153
|
+
log : '@ GET | https://localhost:2021/randst | 200 | 2020-10-31 20:27:7 | 127.0.0.1 | User-Agent'
|
|
1154
|
+
}
|
|
1155
|
+
*/
|
|
1156
|
+
logHandle : (w, msg) => {
|
|
1061
1157
|
console.log(w.id, msg)
|
|
1062
1158
|
}
|
|
1159
|
+
|
|
1063
1160
|
})
|
|
1064
1161
|
|
|
1065
1162
|
app.daemon(1234, 3)
|
|
1163
|
+
|
|
1066
1164
|
```
|
|
1067
1165
|
|
|
1068
|
-
|
|
1166
|
+
Using middleware for logging does not conflict with global logging. However, middleware logging cannot capture 404s returned by the framework when no route matches (as context is not created to avoid overhead).
|
|
1167
|
+
|
|
1168
|
+
Furthermore, this method integrates better with cluster mode as it uses master-worker communication internally.
|
|
1069
1169
|
|
|
1070
1170
|
## Message Event Handling
|
|
1071
1171
|
|
|
1072
|
-
|
|
1172
|
+
Based on the `message` event, in daemon mode (based on cluster module), a `setMsgEvent` function is provided to handle events sent by child processes.
|
|
1073
1173
|
|
|
1074
|
-
|
|
1174
|
+
This requires the message sent by worker processes to be an object, with a mandatory `type` property indicating the event name. Other fields can be custom.
|
|
1075
1175
|
|
|
1076
|
-
|
|
1077
|
-
|
|
1176
|
+
Usage:
|
|
1177
|
+
|
|
1178
|
+
``` JavaScript
|
|
1179
|
+
|
|
1180
|
+
const Topbit = require('topbit')
|
|
1078
1181
|
const cluster = require('cluster')
|
|
1079
1182
|
|
|
1080
|
-
const app = new
|
|
1183
|
+
const app = new Topbit({
|
|
1081
1184
|
debug: true,
|
|
1082
1185
|
loadInfoFile: '/tmp/loadinfo.log'
|
|
1083
1186
|
})
|
|
1084
1187
|
|
|
1085
1188
|
if (cluster.isMaster) {
|
|
1086
1189
|
app.setMsgEvent('test-msg', (worker, msg, handle) => {
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1190
|
+
// Child process receives message via message event
|
|
1191
|
+
worker.to({
|
|
1192
|
+
id : worker.id,
|
|
1193
|
+
data : 'ok'
|
|
1090
1194
|
})
|
|
1195
|
+
|
|
1091
1196
|
console.log(msg)
|
|
1092
1197
|
})
|
|
1093
1198
|
} else {
|
|
1199
|
+
// Receive message sent by worker.to
|
|
1094
1200
|
process.on('message', msg => {
|
|
1095
1201
|
console.log(msg)
|
|
1096
1202
|
})
|
|
1097
1203
|
|
|
1098
|
-
|
|
1099
|
-
process.
|
|
1100
|
-
type: 'test-msg',
|
|
1101
|
-
pid: process.pid,
|
|
1102
|
-
time: new Date().toLocaleString()
|
|
1204
|
+
setIneterval(() => {
|
|
1205
|
+
process.to({
|
|
1206
|
+
type : 'test-msg',
|
|
1207
|
+
pid : process.pid,
|
|
1208
|
+
time : (new Date()).toLocaleString()
|
|
1103
1209
|
})
|
|
1104
1210
|
}, 1000)
|
|
1211
|
+
|
|
1105
1212
|
}
|
|
1213
|
+
|
|
1106
1214
|
```
|
|
1107
1215
|
|
|
1108
|
-
Since
|
|
1216
|
+
Sending messages from worker processes is complex. Since version 22.4.0, a `send` method is provided for quick message sending. It only sends to master from a worker process, so no extra worker check is needed.
|
|
1109
1217
|
|
|
1110
|
-
##
|
|
1218
|
+
## app.to and app.workerMsg
|
|
1111
1219
|
|
|
1112
|
-
|
|
1220
|
+
Let's rewrite the worker message sending part of the code above:
|
|
1113
1221
|
|
|
1114
1222
|
```javascript
|
|
1115
|
-
const titbit = require('titbit')
|
|
1116
1223
|
|
|
1117
|
-
const
|
|
1224
|
+
const Topbit = require('topbit')
|
|
1225
|
+
|
|
1226
|
+
const app = new Topbit({
|
|
1118
1227
|
debug: true,
|
|
1119
1228
|
loadInfoFile: '/tmp/loadinfo.log'
|
|
1120
1229
|
})
|
|
1121
1230
|
|
|
1231
|
+
// Master process registers message event type. Worker process ignores this.
|
|
1122
1232
|
app.setMsgEvent('test-msg', (worker, msg, handle) => {
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1233
|
+
// Child process receives message via message event
|
|
1234
|
+
worker.to({
|
|
1235
|
+
id : worker.id,
|
|
1236
|
+
data : 'ok'
|
|
1126
1237
|
})
|
|
1238
|
+
|
|
1127
1239
|
console.log(msg)
|
|
1128
1240
|
})
|
|
1129
1241
|
|
|
1242
|
+
// Only worker process listens.
|
|
1130
1243
|
app.workerMsg(msg => {
|
|
1131
1244
|
console.log(msg)
|
|
1132
1245
|
})
|
|
1133
1246
|
|
|
1134
|
-
cluster.isWorker
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1247
|
+
cluster.isWorker
|
|
1248
|
+
&&
|
|
1249
|
+
setInterval(() => {
|
|
1250
|
+
// Only worker executes.
|
|
1251
|
+
app.to('test-msg', {
|
|
1252
|
+
pid: process.pid,
|
|
1253
|
+
time: (new Date).toLocaleString()
|
|
1254
|
+
})
|
|
1255
|
+
|
|
1256
|
+
}, 1000)
|
|
1141
1257
|
|
|
1142
1258
|
app.daemon(1234, 2)
|
|
1259
|
+
|
|
1143
1260
|
```
|
|
1144
1261
|
|
|
1145
|
-
##
|
|
1262
|
+
## Automatically Adjusting Child Process Quantity
|
|
1146
1263
|
|
|
1147
|
-
The `daemon`
|
|
1264
|
+
The parameter passed to `daemon` sets the base number of child processes, e.g.:
|
|
1148
1265
|
|
|
1149
|
-
```
|
|
1150
|
-
|
|
1266
|
+
``` JavaScript
|
|
1267
|
+
|
|
1268
|
+
// Use 2 child processes to handle requests.
|
|
1151
1269
|
app.daemon(1234, 2)
|
|
1270
|
+
|
|
1152
1271
|
```
|
|
1153
1272
|
|
|
1154
|
-
To automatically
|
|
1273
|
+
To automatically create child processes based on load and terminate them when idle (maintaining base quantity), use `autoWorker` to set a maximum value. This value must be larger than the base quantity to take effect.
|
|
1155
1274
|
|
|
1156
1275
|
```javascript
|
|
1157
|
-
|
|
1276
|
+
|
|
1277
|
+
// Max 9 child processes.
|
|
1158
1278
|
app.autoWorker(9)
|
|
1159
1279
|
|
|
1280
|
+
//...
|
|
1281
|
+
|
|
1160
1282
|
app.daemon(1234, 2)
|
|
1283
|
+
|
|
1161
1284
|
```
|
|
1162
1285
|
|
|
1163
|
-
When load is high,
|
|
1286
|
+
When load is high, child processes are created. After a period of idleness, processes with 0 connections are terminated, restoring the base number.
|
|
1287
|
+
|
|
1288
|
+
**Available in v21.9.6+. Please use the latest version as this feature has been improved for stability and performance in subsequent versions.**
|
|
1164
1289
|
|
|
1165
|
-
|
|
1290
|
+
----
|
|
1166
1291
|
|
|
1167
1292
|
## Strong Mode
|
|
1168
1293
|
|
|
1169
|
-
Enable `strong`
|
|
1294
|
+
Enable strong mode via the `strong` option. This monitors `uncaughtException` and `unhandledRejection` events to ensure stable operation. Simplest usage: set `strong` to `true`.
|
|
1170
1295
|
|
|
1171
|
-
**All
|
|
1296
|
+
**All strong mode features can be implemented via the `process` module; this just simplifies it.**
|
|
1172
1297
|
|
|
1173
1298
|
```javascript
|
|
1174
|
-
'use strict'
|
|
1299
|
+
'use strict';
|
|
1175
1300
|
|
|
1176
|
-
const
|
|
1301
|
+
const Topbit = require('topbit');
|
|
1177
1302
|
|
|
1178
1303
|
setTimeout(() => {
|
|
1179
|
-
|
|
1180
|
-
|
|
1304
|
+
// Throw exception inside timer loop
|
|
1305
|
+
throw new Error(`test error`)
|
|
1306
|
+
}, 2000);
|
|
1181
1307
|
|
|
1182
|
-
const app = new
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1308
|
+
const app = new Topbit({
|
|
1309
|
+
// Debug mode, output errors.
|
|
1310
|
+
debug: true,
|
|
1311
|
+
// Enable strong mode
|
|
1312
|
+
strong: true
|
|
1313
|
+
});
|
|
1314
|
+
|
|
1315
|
+
app.run(1234);
|
|
1186
1316
|
|
|
1187
|
-
app.run(1234)
|
|
1188
1317
|
```
|
|
1189
1318
|
|
|
1190
|
-
By default,
|
|
1319
|
+
By default, strong mode captures:
|
|
1191
1320
|
|
|
1192
1321
|
```
|
|
1193
|
-
TypeError, ReferenceError, RangeError, AssertionError, URIError, Error
|
|
1322
|
+
'TypeError', 'ReferenceError', 'RangeError', 'AssertionError', 'URIError', 'Error'
|
|
1194
1323
|
```
|
|
1195
1324
|
|
|
1196
|
-
|
|
1325
|
+
You can customize handling by passing an object to `strong`.
|
|
1197
1326
|
|
|
1198
1327
|
```javascript
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1328
|
+
|
|
1329
|
+
// Core code example
|
|
1330
|
+
const app = new Topbit({
|
|
1331
|
+
// Debug mode, output errors.
|
|
1332
|
+
debug: true,
|
|
1333
|
+
// Enable strong mode
|
|
1334
|
+
strong: {
|
|
1335
|
+
// Silent behavior, no error output.
|
|
1336
|
+
quiet: true,
|
|
1337
|
+
// Custom error handler
|
|
1338
|
+
errorHandle: (err, errname) => {
|
|
1339
|
+
//....
|
|
1340
|
+
},
|
|
1341
|
+
|
|
1342
|
+
// Which errors to catch
|
|
1343
|
+
catchErrors: [
|
|
1344
|
+
'TypeError', 'URIError', 'Error', 'RangeError'
|
|
1345
|
+
]
|
|
1346
|
+
|
|
1347
|
+
}
|
|
1348
|
+
});
|
|
1349
|
+
|
|
1209
1350
|
```
|
|
1210
1351
|
|
|
1211
|
-
##
|
|
1352
|
+
## Simultaneous HTTP and HTTPS?
|
|
1212
1353
|
|
|
1213
|
-
|
|
1354
|
+
Note the question mark. You shouldn't do this in production. If HTTPS is enabled, HTTP isn't needed, and some frontend features won't work without HTTPS.
|
|
1214
1355
|
|
|
1215
|
-
|
|
1356
|
+
If needed (perhaps for testing), do this:
|
|
1216
1357
|
|
|
1217
1358
|
```javascript
|
|
1218
1359
|
'use strict'
|
|
1219
1360
|
|
|
1220
|
-
const
|
|
1361
|
+
const Topbit = require('topbit')
|
|
1221
1362
|
const http = require('node:http')
|
|
1222
|
-
const https = require('
|
|
1363
|
+
const https = require('https')
|
|
1223
1364
|
|
|
1224
|
-
const app = new
|
|
1225
|
-
|
|
1365
|
+
const app = new Topbit({
|
|
1366
|
+
// Enable debug
|
|
1367
|
+
debug: true,
|
|
1226
1368
|
})
|
|
1227
1369
|
|
|
1370
|
+
// Below are http/1.1 services. For http2, enable http2 and compatibility mode.
|
|
1371
|
+
// Or use topbit-httpc extension.
|
|
1372
|
+
|
|
1373
|
+
// In this case, you must set event listeners manually.
|
|
1374
|
+
|
|
1228
1375
|
let http_server = http.createServer(app.httpServ.onRequest())
|
|
1229
1376
|
let https_server = https.createServer(app.httpServ.onRequest())
|
|
1230
1377
|
|
|
1231
1378
|
http_server.listen(2025)
|
|
1232
1379
|
https_server.listen(2026)
|
|
1380
|
+
|
|
1381
|
+
```
|
|
1382
|
+
|
|
1383
|
+
**Note: You cannot support http2 in this scenario, but you can use http2 to be compatible with http1.**
|
|
1384
|
+
|
|
1385
|
+
## Request Limiting
|
|
1386
|
+
|
|
1387
|
+
The framework provides IP-based rate limiting to prevent dense requests from the same IP. For HTTP/2, use `http2limit` module from `topbit-toolkit`.
|
|
1388
|
+
|
|
1389
|
+
```javascript
|
|
1390
|
+
'use strict';
|
|
1391
|
+
|
|
1392
|
+
const Topbit = require('topbit')
|
|
1393
|
+
|
|
1394
|
+
const app = new Topbit({
|
|
1395
|
+
debug : true,
|
|
1396
|
+
// Enable request limiting
|
|
1397
|
+
useLimit: true,
|
|
1398
|
+
|
|
1399
|
+
// IPs allowed without frequency limit
|
|
1400
|
+
allow: new Set(['127.0.0.1']),
|
|
1401
|
+
|
|
1402
|
+
// IPs to deny
|
|
1403
|
+
deny: (ip) => {
|
|
1404
|
+
// Example only, supports function and Set
|
|
1405
|
+
if (ip.indexOf('1.') === 0) return false
|
|
1406
|
+
return true
|
|
1407
|
+
},
|
|
1408
|
+
|
|
1409
|
+
// Max requests per IP within unit time
|
|
1410
|
+
maxIPRequest: 6,
|
|
1411
|
+
|
|
1412
|
+
// Unit time, 15 seconds
|
|
1413
|
+
unitTime: 15,
|
|
1414
|
+
|
|
1415
|
+
// Max concurrent connections per worker process
|
|
1416
|
+
maxConn: 2000,
|
|
1417
|
+
|
|
1418
|
+
loadMonitor: true,
|
|
1419
|
+
loadInfoType : 'text',
|
|
1420
|
+
globalLog : true,
|
|
1421
|
+
logType: 'stdio',
|
|
1422
|
+
// Load info in memory
|
|
1423
|
+
loadInfoFile : '--mem'
|
|
1424
|
+
})
|
|
1425
|
+
|
|
1426
|
+
|
|
1427
|
+
app.get('/', async ctx => {
|
|
1428
|
+
ctx.to('ok')
|
|
1429
|
+
})
|
|
1430
|
+
|
|
1431
|
+
// Use 3 worker processes. Each allows 6 requests/unit time.
|
|
1432
|
+
// Total approx 6 requests? Set maxIPRequest to 2.
|
|
1433
|
+
app.daemon(1234, '0.0.0.0', 3)
|
|
1434
|
+
|
|
1233
1435
|
```
|
|
1234
1436
|
|
|
1235
|
-
|
|
1437
|
+
## Other
|
|
1438
|
+
|
|
1439
|
+
- After running, Topbit has a final wrapper middleware. Setting `c.data` returns data. It detects simple text types and sets content-type automatically (text/plain, text/html, application/json) if not set.
|
|
1236
1440
|
|
|
1237
|
-
|
|
1441
|
+
- Defaults to limiting max URL length and sets a max memory usage rate based on hardware.
|
|
1238
1442
|
|
|
1239
|
-
-
|
|
1240
|
-
- Default limits on URL length and memory usage are based on hardware.
|
|
1241
|
-
- Configurations and middleware allow for extension and overrides.
|
|
1242
|
-
- The framework is optimized for speed. For performance comparisons, test with multiple middleware and hundreds of routes.
|
|
1243
|
-
- The `sched` function sets cluster scheduling policy (`'rr'` or `'none'`), equivalent to `cluster.schedulingPolicy`.
|
|
1443
|
+
- All of this can be extended/overridden via config options or middleware.
|
|
1244
1444
|
|
|
1245
|
-
|
|
1445
|
+
- It is fast, and we focus on optimization. If comparing, add multiple middleware and hundreds of routes to test.
|
|
1446
|
+
|
|
1447
|
+
- Provides a `sched` function to quickly set cluster scheduling policy ('rr' or 'none'). Sets `cluster.schedulingPolicy`.
|
|
1448
|
+
|
|
1449
|
+
The framework automatically detects memory size and sets limits on initialization. You can change limits via properties in `secure` after init, but this requires using `daemon` (master managing workers).
|
|
1246
1450
|
|
|
1247
1451
|
```javascript
|
|
1248
1452
|
'use strict'
|
|
1249
1453
|
|
|
1250
|
-
const
|
|
1454
|
+
const Topbit = require('topbit');
|
|
1251
1455
|
|
|
1252
|
-
let app = new
|
|
1456
|
+
let app = new Topbit();
|
|
1253
1457
|
|
|
1254
|
-
|
|
1255
|
-
|
|
1458
|
+
/*
|
|
1459
|
+
Operations below can be controlled via memFactor option. See config options above.
|
|
1460
|
+
*/
|
|
1256
1461
|
|
|
1257
|
-
//
|
|
1258
|
-
app.secure.
|
|
1462
|
+
// Max memory 600M, auto restart only when connection count is 0.
|
|
1463
|
+
app.secure.maxmem = 600_000_000;
|
|
1259
1464
|
|
|
1260
|
-
//
|
|
1261
|
-
|
|
1465
|
+
// Mandatory restart max memory limit 900M. Total memory usage including Buffers.
|
|
1466
|
+
// Must be larger than maxmem. If memory > maxmem AND connections != 0,
|
|
1467
|
+
// AND continues to exceed diemem, process restarts immediately.
|
|
1468
|
+
app.secure.diemem = 900_000_000;
|
|
1469
|
+
|
|
1470
|
+
// Max RSS memory 800M. Program memory usage, excluding Buffer allocations.
|
|
1471
|
+
app.secure.maxrss = 800_000_000;
|
|
1262
1472
|
|
|
1263
1473
|
app.get('/', async c => {
|
|
1264
|
-
c.
|
|
1474
|
+
c.to('ok');
|
|
1265
1475
|
})
|
|
1266
1476
|
|
|
1267
|
-
app.daemon(8008, 2)
|
|
1477
|
+
app.daemon(8008, 2);
|
|
1478
|
+
|
|
1268
1479
|
```
|
|
1269
1480
|
|
|
1270
|
-
**Requires `loadMonitor
|
|
1481
|
+
**Note: Requires `loadMonitor` option (enabled by default unless set to false).**
|
|
1271
1482
|
|
|
1272
|
-
|
|
1483
|
+
Service initialization automatically sets configuration based on available system memory. Unless necessary, stick to default configurations.
|