topbit 1.0.0 → 2.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.
Files changed (71) hide show
  1. package/LICENSE +128 -0
  2. package/README.cn.md +1562 -0
  3. package/README.md +1272 -0
  4. package/bin/app.js +17 -0
  5. package/bin/loadinfo.sh +18 -0
  6. package/bin/new-ctl.js +230 -0
  7. package/bin/newapp.js +22 -0
  8. package/cache/allow.js +130 -0
  9. package/cache/errserv.js +45 -0
  10. package/cache/minserv.js +167 -0
  11. package/cache/router.js +84 -0
  12. package/cache/rsa/localhost-cert.pem +19 -0
  13. package/cache/rsa/localhost-privkey.pem +28 -0
  14. package/cache/servsock.js +286 -0
  15. package/cache/sni.js +66 -0
  16. package/demo/allow.js +98 -0
  17. package/demo/group-api.js +161 -0
  18. package/demo/group-api2.js +109 -0
  19. package/demo/log.js +118 -0
  20. package/demo/memlimit.js +31 -0
  21. package/demo/min.js +7 -0
  22. package/demo/serv.js +15 -0
  23. package/images/middleware.jpg +0 -0
  24. package/images/titbit-middleware.png +0 -0
  25. package/images/titbit.png +0 -0
  26. package/package.json +38 -8
  27. package/src/bodyparser.js +420 -0
  28. package/src/connfilter.js +125 -0
  29. package/src/context1.js +166 -0
  30. package/src/context2.js +179 -0
  31. package/src/ctxpool.js +38 -0
  32. package/src/ext.js +318 -0
  33. package/src/fastParseUrl.js +426 -0
  34. package/src/headerLimit.js +18 -0
  35. package/src/http1.js +337 -0
  36. package/src/http2.js +337 -0
  37. package/src/httpc.js +251 -0
  38. package/src/loader/loader.js +999 -0
  39. package/src/logger.js +32 -0
  40. package/src/loggermsg.js +349 -0
  41. package/src/makeId.js +200 -0
  42. package/src/midcore.js +213 -0
  43. package/src/middleware1.js +104 -0
  44. package/src/middleware2.js +121 -0
  45. package/src/monitor.js +380 -0
  46. package/src/movefile.js +30 -0
  47. package/src/optionsCheck.js +54 -0
  48. package/src/randstring.js +23 -0
  49. package/src/router.js +682 -0
  50. package/src/sendmsg.js +27 -0
  51. package/src/strong.js +72 -0
  52. package/src/token/token.js +462 -0
  53. package/src/topbit.js +1291 -0
  54. package/src/versionCheck.js +31 -0
  55. package/test/test-bigctx.js +29 -0
  56. package/test/test-daemon-args.js +7 -0
  57. package/test/test-find.js +69 -0
  58. package/test/test-helper.js +81 -0
  59. package/test/test-route-sort.js +71 -0
  60. package/test/test-route.js +49 -0
  61. package/test/test-route2.js +51 -0
  62. package/test/test-run-args.js +7 -0
  63. package/test/test-url.js +52 -0
  64. package/tmp/buff-code +134 -0
  65. package/tmp/devplan +9 -0
  66. package/tmp/evt-test.js +34 -0
  67. package/tmp/fastParseUrl.js +302 -0
  68. package/tmp/router-rule.js +559 -0
  69. package/tmp/test-cdps.js +122 -0
  70. package/tmp/titbit.js +1286 -0
  71. package/main.js +0 -0
package/README.md ADDED
@@ -0,0 +1,1272 @@
1
+ ![](images/titbit.png)
2
+
3
+ # titbit
4
+
5
+ **titbit** is a server-side web framework initially designed to simplify development in educational settings, but it has also been used in some production systems. It is not a heavyweight framework, but it is far from overly simplistic.
6
+
7
+ > **About Type Systems and TypeScript Support**
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.
19
+
20
+ **Core Features:**
21
+
22
+ * Request context design that abstracts interface differences.
23
+ * Middleware pattern.
24
+ * Route grouping and naming.
25
+ * Middleware execution based on route groups, matching request methods and routes.
26
+ * Support for running as a daemon using the `cluster` module.
27
+ * Display of subprocess load information.
28
+ * Automatic parsing of request body data.
29
+ * Configurable support for HTTP/1.1 or HTTP/2 services, with a compatibility mode allowing simultaneous support for both.
30
+ * Support for enabling HTTPS (required for HTTP/2 services).
31
+ * Request rate limiting.
32
+ * Limiting the maximum number of requests from a single IP within a time period.
33
+ * IP blacklists and whitelists.
34
+ * In cluster mode, automatic restart of subprocesses that exceed maximum memory limits.
35
+ * Optional automatic load balancing: creates new subprocesses based on load and reverts to the initial state when idle.
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.
48
+
49
+ ## Installation
50
+
51
+ ```javascript
52
+ npm i titbit
53
+ ```
54
+
55
+ You can also install it using Yarn:
56
+
57
+ ```javascript
58
+ yarn add titbit
59
+ ```
60
+
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
+ ## Minimal Example
70
+
71
+ ```javascript
72
+ 'use strict'
73
+
74
+ const Titbit = require('titbit')
75
+
76
+ const app = new Titbit({
77
+ debug: true
78
+ })
79
+
80
+ app.run(1234)
81
+ ```
82
+
83
+ When no routes are added, titbit automatically adds a default route:
84
+
85
+ `/*`
86
+
87
+ Visiting this in a browser displays a simple page, intended for initial exploration and documentation access. It has no impact on actual development.
88
+
89
+ ## Adding a Route
90
+
91
+ ```javascript
92
+ 'use strict'
93
+
94
+ const Titbit = require('titbit')
95
+
96
+ const app = new Titbit({
97
+ debug: true
98
+ })
99
+
100
+ app.get('/', async ctx => {
101
+ ctx.send('success')
102
+ })
103
+
104
+ // Listens on 0.0.0.0 by default; parameters are consistent with the native `listen` interface.
105
+ app.run(1234)
106
+ ```
107
+
108
+ `ctx.data` holds the response data, or you can use `ctx.send(data)`.
109
+ > Internally, `ctx.send()` sets the value of `ctx.data`.
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.**
111
+
112
+ ## Using ES6 Imports
113
+
114
+ In `.mjs` files, you can use ES6 `import` syntax:
115
+
116
+ ```javascript
117
+ import Titbit from 'titbit'
118
+
119
+ const app = new Titbit({
120
+ debug: true
121
+ })
122
+
123
+ app.get('/', async ctx => {
124
+ ctx.send('success')
125
+ })
126
+
127
+ app.run(1234)
128
+ ```
129
+
130
+ ## Routes and Request Types
131
+
132
+ HTTP request methods (also called request types) are specified in the HTTP start line. Supported request methods:
133
+
134
+ ```
135
+ GET POST PUT PATCH DELETE OPTIONS TRACE HEAD
136
+ ```
137
+
138
+ The most commonly used are the first six. For each request type, the router provides a corresponding lowercase function for mounting routes. For convenience, these methods are also available directly on the `app` instance after initialization. (The framework only supports these methods.)
139
+
140
+ **Example:**
141
+
142
+ ```javascript
143
+ 'use strict'
144
+
145
+ const Titbit = require('titbit')
146
+
147
+ const app = new Titbit({
148
+ debug: true
149
+ })
150
+
151
+ app.get('/', async c => {
152
+ c.send('success')
153
+ })
154
+
155
+ app.get('/p', async c => {
156
+ c.send(`${c.method} ${c.routepath}`)
157
+ })
158
+
159
+ app.post('/', async c => {
160
+ // Returns the submitted data
161
+ c.send(c.body)
162
+ })
163
+
164
+ app.put('/p', async c => {
165
+ c.send({
166
+ method: c.method,
167
+ body: c.body,
168
+ query: c.query
169
+ })
170
+ })
171
+
172
+ // Listens on 0.0.0.0 by default; parameters are consistent with the native `listen` interface.
173
+ app.run(8080)
174
+ ```
175
+
176
+ ## Retrieving URL Parameters
177
+
178
+ - Query string parameters (e.g., `?a=1&b=2`) are parsed into `c.query`.
179
+ - Form-submitted data is parsed into `c.body`.
180
+
181
+ > Form submissions typically have a `content-type` of `application/x-www-form-urlencoded`.
182
+
183
+ ```javascript
184
+ 'use strict'
185
+
186
+ const titbit = require('titbit')
187
+
188
+ let app = new titbit({
189
+ debug: true
190
+ })
191
+
192
+ app.get('/q', async c => {
193
+ // Query string parameters from the URL are parsed into `query`.
194
+ // Returns JSON text with `content-type` set to `text/json`.
195
+ c.send(c.query)
196
+ })
197
+
198
+ app.post('/p', async c => {
199
+ // Data from POST or PUT requests is stored in `body`. For forms, it is automatically parsed; otherwise, it’s raw text.
200
+ // Middleware can be used to handle various data types.
201
+ c.send(c.body)
202
+ })
203
+
204
+ app.run(2019)
205
+ ```
206
+
207
+ ## Retrieving POST Data
208
+
209
+ POST and PUT requests submit data in the request body, typically from form submissions or asynchronous requests.
210
+
211
+ - Form-submitted data is parsed into `c.body`.
212
+
213
+ > Forms have a `content-type` of `application/x-www-form-urlencoded`.
214
+ > Asynchronous requests often use `content-type: application/json`.
215
+
216
+ For both types, `c.body` is an object.
217
+
218
+ ```javascript
219
+ 'use strict'
220
+
221
+ const titbit = require('titbit')
222
+
223
+ let app = new titbit({ debug: true })
224
+
225
+ app.post('/p', async c => {
226
+ // Data from POST or PUT requests is stored in `body`. For forms, it is automatically parsed into an object.
227
+ // Middleware can be used to handle various data types.
228
+ c.send(c.body)
229
+ })
230
+
231
+ app.run(2019)
232
+ ```
233
+
234
+ ## About `content-type`
235
+
236
+ **`application/x-www-form-urlencoded`**
237
+ Basic form data is parsed into `c.body` as a JavaScript object.
238
+
239
+ **`text/*`**
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.
241
+
242
+ **`multipart/form-data;boundary=xxx`**
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`.
244
+
245
+ **`application/json`**
246
+ This type is parsed using `JSON.parse`.
247
+
248
+ **Other Types**
249
+ For other `content-type` values, `c.body` points to `c.rawBody`, which is the raw `Buffer` data.
250
+
251
+ The framework provides core support for basic types, leaving other types for developers to handle or extend. To disable default body parsing, set the `parseBody` initialization option to `false`. You can also extend body parsing as needed.
252
+
253
+ The body parsing module is essentially a middleware, designed to facilitate extensions and replacements.
254
+
255
+ ## The `send` Function
256
+
257
+ The `send` function is a wrapper for setting `c.data`. It supports an optional second parameter for the status code (default is 0, which uses the module’s default status code, 200 in Node.js HTTP and HTTP/2).
258
+
259
+ ```javascript
260
+ app.get('/', async c => {
261
+ c.send('success')
262
+ })
263
+
264
+ app.get('/randerr', async c => {
265
+ let n = parseInt(Math.random() * 10)
266
+ if (n >= 5) {
267
+ c.send('success')
268
+ } else {
269
+ // Returns 404 status code
270
+ /*
271
+ Equivalent to:
272
+ c.status(404).data = 'not found'
273
+ */
274
+ // Chainable calls are supported in v22.4.6 and above.
275
+ c.status(404).send('not found')
276
+ }
277
+ })
278
+
279
+ app.run(1234)
280
+ ```
281
+
282
+ ## Chainable Calls
283
+
284
+ Starting with v22.4.6, `setHeader`, `status`, and `sendHeader` support chainable calls.
285
+
286
+ ```javascript
287
+ app.get('/', async c => {
288
+ c.setHeader('content-type', 'text/plain; charset=utf-8')
289
+ .setHeader('x-server', 'nodejs server')
290
+ .status(200)
291
+ .send(`${Date.now()} Math.random()`)
292
+ })
293
+ ```
294
+
295
+ ## Route Parameters
296
+
297
+ ```javascript
298
+ app.get('/:name/:id', async c => {
299
+ // Route parameters (denoted by `:`) are parsed into `c.param`.
300
+ let username = c.param.name
301
+ let uid = c.param.id
302
+ c.send(`${username} ${uid}`)
303
+ })
304
+
305
+ app.run(8000)
306
+ ```
307
+
308
+ ## Wildcard Path Parameters
309
+
310
+ The `*` wildcard indicates any path but must appear at the end of the route.
311
+
312
+ ```javascript
313
+ app.get('/static/*', async c => {
314
+ // The wildcard path is parsed into `c.param.starPath`.
315
+ let spath = c.param.starPath
316
+ c.send(spath)
317
+ })
318
+ ```
319
+
320
+ ## Route Lookup Rules
321
+
322
+ Since v23.5.9, the route lookup process has been optimized, particularly for routes with parameters and wildcards, enforcing stricter order control instead of matching based on the order routes were added.
323
+
324
+ This change does not affect applications developed with earlier versions, ensuring compatibility. The stricter order reduces the likelihood of conflicts.
325
+
326
+ **Route Lookup Strategy:**
327
+
328
+ 1. Exact string paths.
329
+ 2. Routes with parameters (fewer parameters match first).
330
+ 3. Wildcard (`*`) routes, matched from longest to shortest.
331
+
332
+ ```
333
+ Example:
334
+ Routes: /x/y/:id /x/y/* /x/* /x/:key/:id
335
+
336
+ /x/y/123 matches /x/y/:id and stops.
337
+ /x/y/123/345 matches /x/y/* and stops.
338
+ /x/q/123 matches /x/:key/:id.
339
+ /x/a.jpg matches /x/*, as no other routes match.
340
+ /x/static/images/a.jpg matches /x/*, as no other routes match.
341
+ ```
342
+
343
+ ## Route Grouping
344
+
345
+ You can use `app.middleware` to specify middleware and use the returned `group` method to add grouped routes, or directly use `app.group` to add grouped routes.
346
+
347
+ **`Titbit.prototype.middleware(mids, options=null)`**
348
+
349
+ - `mids`: An array where each element is a middleware function or an array containing a middleware function and its options.
350
+ - `options`: Defaults to `null`. Pass an object to apply options to all middleware, e.g., `{pre: true}`.
351
+
352
+ **`Titbit.prototype.group(group_name, callback, prefix=true)`**
353
+
354
+ - `group_name`: A string representing the group name. If it’s a valid path, it also serves as a route prefix.
355
+ - `callback`: A callback function that receives a parameter for adding middleware or routes using `get`, `post`, etc.
356
+ - `prefix`: A boolean (default `true`) controlling whether `group_name` is used as a route prefix (only if it’s a valid route string).
357
+
358
+ ```javascript
359
+ 'use strict'
360
+
361
+ const Titbit = require('titbit')
362
+
363
+ const app = new Titbit({
364
+ debug: true
365
+ })
366
+
367
+ // Middleware function
368
+ let mid_timing = async (c, next) => {
369
+ console.time('request')
370
+ await next()
371
+ console.timeEnd('request')
372
+ }
373
+
374
+ // The group return value supports `use` and `pre` for adding middleware.
375
+ // `/api` is also added as a route prefix.
376
+ app.group('/api', route => {
377
+ route.get('/test', async c => {
378
+ c.send('api test')
379
+ })
380
+
381
+ route.get('/:name', async c => {
382
+ c.send(c.param)
383
+ })
384
+ })
385
+
386
+ // Add middleware to a specific group
387
+ app.use(
388
+ async (c, next) => {
389
+ console.log(c.method, c.headers)
390
+ await next()
391
+ }, { group: '/sub' }
392
+ ).group('/sub', route => {
393
+ route.get('/:id', async c => {
394
+ c.send(c.param.id)
395
+ })
396
+ })
397
+
398
+ // Test group name (not a valid route, so it won’t be a prefix)
399
+ app.group('test', route => {
400
+ route.get('/test', async c => {
401
+ console.log(c.group, c.name)
402
+ c.send('test ok')
403
+ }, 'test')
404
+ })
405
+
406
+ app.run(1234)
407
+ ```
408
+
409
+ This approach can be complex when specifying multiple middleware. The `middleware` method simplifies this, as shown below.
410
+
411
+ ### Assigning Middleware to Groups and Subgroups
412
+
413
+ ```javascript
414
+ 'use strict'
415
+
416
+ const Titbit = require('titbit')
417
+ const { ToFile } = require('titbit-toolkit')
418
+
419
+ const app = new Titbit({
420
+ debug: true
421
+ })
422
+
423
+ // Middleware function
424
+ let mid_timing = async (c, next) => {
425
+ console.time('request')
426
+ await next()
427
+ console.timeEnd('request')
428
+ }
429
+
430
+ let sub_mid_test = async (c, next) => {
431
+ console.log('mid test start')
432
+ await next()
433
+ console.log('mid test end')
434
+ }
435
+
436
+ // The group return value supports `use`, `pre`, and `middleware` for adding middleware.
437
+ // `/api` is added as a route prefix.
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
+ })
451
+
452
+ // Subgroup `/sub` enables `sub_mid_test` middleware and inherits parent middleware.
453
+ route.middleware([sub_mid_test]).group('/sub', sub => {
454
+ sub.get('/:key', async c => {
455
+ c.send(c.param)
456
+ })
457
+ })
458
+ })
459
+
460
+ app.run(1234)
461
+ ```
462
+
463
+ Group nesting is supported but should not exceed 9 levels. Nesting beyond 3 levels is often a sign of poor design and should be reconsidered.
464
+
465
+ **This feature is non-intrusive and does not affect existing code or conflict with `titbit-loader`.**
466
+
467
+ **Complex route handlers should be placed in separate modules and loaded using a unified automation function.**
468
+
469
+ Starting with v24.0.9, you can add routes using return values without passing a callback function:
470
+
471
+ ```javascript
472
+ 'use strict'
473
+
474
+ const Titbit = require('titbit')
475
+ const { ToFile } = require('titbit-toolkit')
476
+
477
+ const app = new Titbit({
478
+ debug: true
479
+ })
480
+
481
+ // Middleware function
482
+ let mid_timing = async (c, next) => {
483
+ console.time('request')
484
+ await next()
485
+ console.timeEnd('request')
486
+ }
487
+
488
+ let sub_mid_test = async (c, next) => {
489
+ console.log('mid test start')
490
+ await next()
491
+ console.log('mid test end')
492
+ }
493
+
494
+ let route = app.middleware([
495
+ // Timing middleware runs before receiving request body data, so `pre` is set to `true`.
496
+ [mid_timing, { pre: true }],
497
+ // ToFile extension runs after receiving request body data, only for POST and PUT.
498
+ [new ToFile(), { method: ['POST', 'PUT'] }]
499
+ ]).group('/api')
500
+
501
+ route.get('/test', async c => {
502
+ c.send('api test')
503
+ })
504
+
505
+ route.get('/:name', async c => {
506
+ c.send(c.param)
507
+ })
508
+
509
+ // Subgroup `/sub` enables `sub_mid_test` middleware and inherits parent middleware.
510
+ route.middleware([sub_mid_test]).group('/sub', sub => {
511
+ sub.get('/:key', async c => {
512
+ c.send(c.param)
513
+ })
514
+ })
515
+
516
+ app.run(1234)
517
+ ```
518
+
519
+ ## File Uploads
520
+
521
+ File uploads are parsed by default. You can disable this by setting the `parseBody` option during initialization. Parsed file data is stored in `c.files`, with the structure detailed below.
522
+
523
+ ```javascript
524
+ 'use strict'
525
+
526
+ const titbit = require('titbit')
527
+
528
+ const app = new titbit()
529
+
530
+ app.post('/upload', async c => {
531
+ let f = c.getFile('image')
532
+
533
+ // Helper function to generate a unique filename based on timestamp and file extension.
534
+ let fname = c.ext.makeName(f.filename)
535
+
536
+ try {
537
+ c.send(await c.moveFile(f, fname))
538
+ } catch (err) {
539
+ c.status(500).send(err.message)
540
+ }
541
+ }, 'upload-image') // Names the route `upload-image`, accessible via `c.name`.
542
+
543
+ app.run(1234)
544
+ ```
545
+
546
+ ## `c.files` Data Structure
547
+
548
+ The structure is designed based on the HTTP protocol’s file upload data format. Since HTTP allows multiple files under the same upload name, files are parsed into an array. `c.getFile` returns the first file by default, as most cases involve a single file per upload name.
549
+
550
+ > For front-end developers, the upload name is the `name` attribute in the HTML form: `<input type="file" name="image">`.
551
+ > Do not confuse the upload name with the filename.
552
+
553
+ ```javascript
554
+ {
555
+ image: [
556
+ {
557
+ 'content-type': CONTENT_TYPE,
558
+ // Available since v23.2.6, alias for content-type
559
+ type: CONTENT_TYPE,
560
+ filename: ORIGIN_FILENAME,
561
+ start: START,
562
+ end: END,
563
+ length: LENGTH,
564
+ rawHeader: HEADER_DATA,
565
+ headers: {...}
566
+ },
567
+ ...
568
+ ],
569
+ video: [
570
+ {
571
+ 'content-type': CONTENT_TYPE,
572
+ // Available since v23.2.6, alias for content-type
573
+ type: CONTENT_TYPE,
574
+ filename: ORIGIN_FILENAME,
575
+ start: START,
576
+ end: END,
577
+ length: LENGTH,
578
+ rawHeader: HEADER_DATA,
579
+ headers: {...}
580
+ },
581
+ ...
582
+ ]
583
+ }
584
+ ```
585
+
586
+ `c.getFile(name)` retrieves file information by name, defaulting to index 0. If a negative index is provided, it returns the entire file array; if no files are found, it returns `null`.
587
+
588
+ ## Body Size Limit
589
+
590
+ ```javascript
591
+ 'use strict'
592
+
593
+ const titbit = require('titbit')
594
+
595
+ const app = new titbit({
596
+ // Sets the maximum data size for POST or PUT requests to ~20MB (in bytes).
597
+ maxBody: 20000000
598
+ })
599
+
600
+ app.run(1234)
601
+ ```
602
+
603
+ ## Middleware
604
+
605
+ Middleware is a powerful pattern, with implementations varying slightly across languages but sharing the same essence. Middleware allows developers to organize code effectively and handle complex logic. The framework’s entire operation is based on the middleware pattern.
606
+
607
+ **Middleware Diagram:**
608
+
609
+ ![](images/middleware.jpg)
610
+
611
+ The framework’s middleware is designed to execute based on route groups and request types, ensuring fast performance. Middleware is executed only when needed, avoiding unnecessary operations. Example:
612
+
613
+ ```javascript
614
+ /*
615
+ The second parameter is optional, indicating global middleware.
616
+ Here, it specifies that the middleware only applies to POST requests in the `/api` group.
617
+ This design ensures efficient execution without unnecessary operations.
618
+ */
619
+ app.add(async (c, next) => {
620
+ console.log('before')
621
+ await next()
622
+ console.log('after')
623
+ }, { method: 'POST', group: '/api' })
624
+ ```
625
+
626
+ Middleware added with `add` executes in reverse order of addition (standard onion model). To align with developer intuition, the `use` interface adds middleware that executes in the order it was added. Different frameworks handle execution order differently, but sequential execution is more intuitive.
627
+
628
+ **Recommendation: Use `use` to add middleware.**
629
+
630
+ ```javascript
631
+ // Executes first
632
+ app.use(async (c, next) => {
633
+ let start_time = Date.now()
634
+ await next()
635
+ let end_time = Date.now()
636
+ console.log(end_time - start_time)
637
+ })
638
+
639
+ // Executes second
640
+ app.use(async (c, next) => {
641
+ console.log(c.method, c.path)
642
+ await next()
643
+ })
644
+
645
+ // `use` supports chaining: app.use(m1).use(m2)
646
+ // Available since v21.5.4, though this is less critical with `titbit-loader`.
647
+ ```
648
+
649
+ ## titbit Complete Flow Diagram
650
+
651
+ ![](images/titbit-middleware.png)
652
+
653
+ > **Note: Internally, body data reception and parsing are also middleware, deliberately ordered and separated into `pre` and `use` interfaces.**
654
+
655
+ ## Middleware Parameters
656
+
657
+ The `use` and `pre` interfaces support a second parameter for precise control:
658
+
659
+ - `group`: Specifies the route group for the middleware.
660
+ - `method`: Request method(s) as a string or array (must be uppercase).
661
+ - `name`: Route name, restricting middleware to specific routes.
662
+
663
+ Example:
664
+
665
+ ```javascript
666
+ app.get('/xyz', async c => {
667
+ // Route grouped under 'proxy'
668
+ }, { group: 'proxy' })
669
+
670
+ app.use(proxy, {
671
+ method: ['PUT', 'POST', 'GET', 'DELETE', 'OPTIONS'],
672
+ group: 'proxy'
673
+ })
674
+ ```
675
+
676
+ ## `pre` Middleware (Before Body Parsing)
677
+
678
+ The main difference between `pre` and `use` is that `pre` middleware executes before receiving body data, useful for permission filtering. Its parameters are the same as `use`.
679
+
680
+ For a consistent experience, you can use `use` with the `pre` option:
681
+
682
+ ```javascript
683
+ let setbodysize = async (c, next) => {
684
+ // Sets max body size to ~10KB.
685
+ c.maxBody = 10000
686
+ await next()
687
+ }
688
+
689
+ // Equivalent to app.pre(setbodysize)
690
+ app.use(setbodysize, { pre: true })
691
+ ```
692
+
693
+ `pre` middleware can handle complex logic and intercept requests without proceeding to the next layer. For example, the `titbit-toolkit` proxy module uses this to implement a high-performance proxy as a middleware.
694
+
695
+ **Dynamic Body Size Limits by Request Type**
696
+
697
+ This can be achieved using `pre` middleware:
698
+
699
+ ```javascript
700
+ const app = new titbit({
701
+ // Default max body size ~10MB
702
+ maxBody: 10000000
703
+ })
704
+
705
+ app.pre(async (c, next) => {
706
+ let ctype = c.headers['content-type'] || ''
707
+
708
+ if (ctype.indexOf('text/') === 0) {
709
+ // 50KB
710
+ c.maxBody = 50000
711
+ } else if (ctype.indexOf('application/') === 0) {
712
+ // 100KB
713
+ c.maxBody = 100000
714
+ } else if (ctype.indexOf('multipart/form-data') < 0) {
715
+ // 10KB
716
+ c.maxBody = 10000
717
+ }
718
+
719
+ await next()
720
+ }, { method: ['POST', 'PUT'] })
721
+ ```
722
+
723
+ These parameters can make the code complex and hard to maintain, but they are powerful. For automation, use `titbit-loader`, which simplifies routing, model loading, and middleware orchestration: <a target="_blank" href="https://gitee.com/daoio/titbit-loader">titbit-loader</a>.
724
+
725
+ ## HTTPS
726
+
727
+ ```javascript
728
+ 'use strict'
729
+
730
+ const Titbit = require('titbit')
731
+
732
+ // Specify paths to the certificate and key files
733
+ const app = new Titbit({
734
+ cert: './xxx.cert',
735
+ key: './xxx.key'
736
+ })
737
+
738
+ app.run(1234)
739
+ ```
740
+
741
+ ## Supporting HTTP/2 and HTTP/1.1 (Compatibility Mode)
742
+
743
+ Compatibility mode uses the ALPN protocol and requires HTTPS, so certificate and key files must be configured.
744
+
745
+ ```javascript
746
+ 'use strict'
747
+
748
+ const Titbit = require('titbit')
749
+
750
+ // Specify paths to the certificate and key files
751
+ const app = new Titbit({
752
+ cert: './xxx.cert',
753
+ key: './xxx.key',
754
+ // Enable HTTP/2 and allow HTTP/1.1 compatibility
755
+ http2: true,
756
+ allowHTTP1: true
757
+ })
758
+
759
+ app.run(1234)
760
+ ```
761
+
762
+ ## Configuration Options
763
+
764
+ Complete configuration options for application initialization, with detailed comments:
765
+
766
+ ```javascript
767
+ {
768
+ // Maximum byte size for POST/PUT form submissions and file uploads.
769
+ maxBody: 8000000,
770
+
771
+ // Maximum number of files to parse.
772
+ maxFiles: 12,
773
+
774
+ daemon: false, // Enable daemon mode.
775
+
776
+ // If set to a non-empty string in daemon mode, writes the PID to this file for service management.
777
+ pidFile: '',
778
+
779
+ // Enable global logging to output or save request information.
780
+ globalLog: false,
781
+
782
+ // Log output method: 'stdio' for terminal, 'file' for file.
783
+ logType: 'stdio',
784
+
785
+ // File path for successful request logs (2xx or 3xx status codes).
786
+ logFile: '',
787
+
788
+ // File path for error request logs (4xx or 5xx status codes).
789
+ errorLogFile: '',
790
+
791
+ // Maximum number of log entries per file.
792
+ logMaxLines: 50000,
793
+
794
+ // Maximum number of historical log files.
795
+ logHistory: 50,
796
+
797
+ // Custom log handling function.
798
+ logHandle: null,
799
+
800
+ // Enable HTTPS.
801
+ https: false,
802
+
803
+ http2: false,
804
+
805
+ allowHTTP1: false,
806
+
807
+ // File paths for HTTPS key and certificate. Setting these enables `https: true`.
808
+ key: '',
809
+ cert: '',
810
+
811
+ // Server options passed to `http2.createSecureServer` or `tls.createServer`.
812
+ server: {
813
+ handshakeTimeout: 8192, // TLS handshake timeout
814
+ // sessionTimeout: 350,
815
+ },
816
+
817
+ // Server timeout (milliseconds). Can be overridden per request.
818
+ timeout: 15000,
819
+
820
+ debug: false,
821
+
822
+ // Ignore trailing slashes in paths.
823
+ ignoreSlash: true,
824
+
825
+ // Enable request limiting.
826
+ useLimit: false,
827
+
828
+ // Maximum connections (0 for unlimited).
829
+ maxConn: 1024,
830
+
831
+ // Maximum requests per IP within a time period (0 for unlimited).
832
+ maxIPRequest: 0,
833
+
834
+ // Time period for request limiting (default: 60 second).
835
+ unitTime: 60,
836
+
837
+ // Display load information (requires `daemon` mode).
838
+ loadMonitor: true,
839
+
840
+ // Load information type: 'text', 'json', or '--null'. JSON is for programmatic use.
841
+ loadInfoType: 'text',
842
+
843
+ // File path for load information (if unset, outputs to terminal).
844
+ loadInfoFile: '',
845
+
846
+ // Data for 404 responses.
847
+ notFound: 'Not Found',
848
+
849
+ // Data for 400 responses.
850
+ badRequest: 'Bad Request',
851
+
852
+ // Memory usage percentage factor for subprocesses (-0.42 to 0.36; base is 0.52, default is 80%).
853
+ memFactor: 0.28,
854
+
855
+ // Maximum URL length.
856
+ maxUrlLength: 2048,
857
+
858
+ // Maximum request context cache pool size.
859
+ maxpool: 4096,
860
+
861
+ // Interval (milliseconds) for subprocess resource reporting.
862
+ monitorTimeSlice: 640,
863
+
864
+ // Log real IP addresses in global logs (useful in reverse proxy mode).
865
+ realIP: false,
866
+
867
+ // Maximum number of query string parameters.
868
+ maxQuery: 25,
869
+
870
+ // Enable strong mode to handle `rejectionHandled` and `uncaughtException` events,
871
+ // capturing errors like TypeError, ReferenceError, etc.
872
+ strong: false,
873
+
874
+ // Fast query string parsing (uses only the first value for duplicate keys, not an array).
875
+ fastParseQuery: false,
876
+
877
+ // Automatically decode query parameters using `decodeURIComponent`.
878
+ autoDecodeQuery: true,
879
+
880
+ // Maximum length for a single form item in multipart format.
881
+ maxFormLength: 1000000,
882
+
883
+ // Error handling function for runtime errors (e.g., tlsClientError, server errors).
884
+ // `errname` is a string like `--ERR-CONNECTION--` or `--ERR-CLIENT--`.
885
+ errorHandle: (err, errname) => {
886
+ this.config.debug && console.error(errname, err)
887
+ },
888
+
889
+ // Maximum CPU load percentage (default: 75%). Effective only with `autoWorker`.
890
+ maxLoadRate: 75,
891
+
892
+ // HTTP/2 stream timeout (-1 to match `timeout`).
893
+ streamTimeout: -1,
894
+
895
+ // Total request timeout to counter malicious requests (e.g., DDoS attacks).
896
+ requestTimeout: 100000
897
+ }
898
+ ```
899
+
900
+ ## Request Context
901
+
902
+ The request context is an object encapsulating various request data, abstracting differences between HTTP/1.1 and HTTP/2 and handling Node.js version incompatibilities. For HTTP/2, the request object is a `stream`, not `IncomingMessage` and `ServerResponse` as in HTTP/1.1.
903
+
904
+ **Request Context Properties and Descriptions**
905
+
906
+ | Property | Description |
907
+ |----------|-------------|
908
+ | version | Protocol version (`'1.1'` or `'2'`). |
909
+ | major | Major protocol version (1, 2, or 3 for HTTP/1.1, HTTP/2, HTTP/3; 3 not yet supported). |
910
+ | maxBody | Maximum request body size (bytes), defaults to `maxBody` from initialization, adjustable in middleware. |
911
+ | method | Request method (e.g., `GET`, `POST`), uppercase string. |
912
+ | host | Hostname from `request.headers.host`. |
913
+ | protocol | Protocol string (`http` or `https`, no colon). |
914
+ | path | Requested path. |
915
+ | routepath | Actual route string executed. |
916
+ | query | URL query parameters. |
917
+ | param | Route parameters. |
918
+ | files | Uploaded file information. |
919
+ | body | Request body data (string, object, or Buffer, depending on `content-type`). |
920
+ | port | Client request port. |
921
+ | ip | Client IP address (socket address; check `x-real-ip` or `x-forwarded-for` for proxies). |
922
+ | headers | Reference to `request.headers`. |
923
+ | isUpload() | Checks if the request is a file upload (`multipart/form-data`). |
924
+ | name | Route name (default: empty string). |
925
+ | group | Route group (default: empty string). |
926
+ | reply | HTTP/1.1: `response`; HTTP/2: `stream`. |
927
+ | request | HTTP/1.1: `IncomingMessage`; HTTP/2: `stream`. |
928
+ | box | Empty object for dynamically passing data to subsequent layers. |
929
+ | service | Dependency injection object, points to `app.service`. |
930
+ | data | Final response data (set directly or via `ctx.send`). Before v24.x, was `ctx.res.body`. |
931
+ | ext | Helper functions (see Wiki). |
932
+ | send(data) | Sets `ctx.data`. |
933
+ | write(data) | Writes data directly to the client. |
934
+ | moveFile(file, target_filepath) | Moves an uploaded file to the specified path. |
935
+ | status() | Sets the status code. |
936
+ | setHeader(k, v) | Sets a response header. |
937
+ | removeHeader(k) | Removes a pending response header. |
938
+ | getFile(name) | Retrieves uploaded file information from `files`. |
939
+ | sendHeader() | Sends headers (HTTP/2 only; no-op for HTTP/1.1). |
940
+ | user | Standard property for user login (default: `null`). |
941
+ | json(data) | Sets response data with `content-type: application/json`. |
942
+ | text(data) | Sets response data with `content-type: text/plain`. |
943
+ | html(data) | Sets response data with `content-type: text/html`. |
944
+ | pipe(filepath) | Streams file data (e.g., `await ctx.setHeader('content-type', 'text/html').pipe('./index.html')`). |
945
+ | pipeJson(filepath) | Streams file data as JSON. |
946
+ | pipeText(filepath) | Streams file data as text. |
947
+ | pipeHtml(filepath) | Streams file data as HTML. |
948
+
949
+ **Note:** The `send` function only sets `ctx.data`. It’s equivalent to direct assignment but helps catch errors faster, as incorrect property assignments create new properties without errors, leading to incorrect responses.
950
+
951
+ ## Dependency Injection
952
+
953
+ The request context includes a `service` property pointing to `app.service`. After initializing the `app`, you can attach pre-initialized data or instances to `app.service`.
954
+
955
+ ```javascript
956
+ 'use strict'
957
+
958
+ const titbit = require('titbit')
959
+
960
+ const app = new titbit({
961
+ debug: true
962
+ })
963
+
964
+ // Overwrites if exists, adds if not.
965
+ app.addService('name', 'first')
966
+ app.addService('data', {
967
+ id: 123,
968
+ ip: '127.0.0.1'
969
+ })
970
+
971
+ app.get('/info', async c => {
972
+ c.send({
973
+ name: c.service.name,
974
+ data: c.service.data
975
+ })
976
+ })
977
+
978
+ app.run(1234)
979
+ ```
980
+
981
+ ## Extending the Request Context
982
+
983
+ To extend the request context, use `app.httpServ.context`. This is the constructor for the request context.
984
+
985
+ **Example:**
986
+
987
+ ```javascript
988
+ 'use strict'
989
+
990
+ const titbit = require('titbit')
991
+
992
+ const app = new titbit({
993
+ debug: true
994
+ })
995
+
996
+ // `this` refers to the request context
997
+ app.httpServ.context.prototype.testCtx = function () {
998
+ console.log(this.method, this.path)
999
+ }
1000
+
1001
+ app.get('/test', async ctx => {
1002
+ ctx.testCtx()
1003
+ })
1004
+
1005
+ app.run(1234)
1006
+ ```
1007
+
1008
+ ## `app.isMaster` and `app.isWorker`
1009
+
1010
+ Since Node.js v16.x, the `cluster` module recommends `isPrimary` over `isMaster`, though `isMaster` remains available. After initializing the `app`, `app.isMaster` and `app.isWorker` getter properties are provided, mirroring `cluster` properties to:
1011
+
1012
+ - Avoid requiring `const cluster = require('cluster')`.
1013
+ - Shield against future `cluster` incompatibilities.
1014
+
1015
+ ## `daemon` and `run`
1016
+
1017
+ The `run` interface accepts `port` and `host` (default: `0.0.0.0`). It also supports a `sockPath` (e.g., `.sock` file), consistent with the HTTP `listen` interface, in which case `host` is ignored.
1018
+
1019
+ The `daemon` interface shares the same first two parameters but supports a third parameter specifying the number of subprocesses. If set to 0, it defaults to the number of CPU cores. It maintains subprocess stability by creating new ones if any terminate unexpectedly.
1020
+
1021
+ **In cluster mode, the maximum number of subprocesses is twice the CPU core count.**
1022
+
1023
+ **Examples:**
1024
+
1025
+ ```javascript
1026
+ // Default host: 0.0.0.0, port: 1234
1027
+ app.run(1234)
1028
+
1029
+ // Listen on localhost (local access only)
1030
+ app.run(1234, 'localhost')
1031
+
1032
+ // Use 2 subprocesses, default host: 0.0.0.0
1033
+ app.daemon(1234, 2)
1034
+
1035
+ // Use 3 subprocesses
1036
+ app.daemon(1234, 'localhost', 3)
1037
+ ```
1038
+
1039
+ ## Logging
1040
+
1041
+ The framework provides global logging when using `daemon` mode (cluster). Enable it with the `globalLog` option, which supports file output or terminal output (in single-process `run` mode, logs go to the terminal but can be redirected to files).
1042
+
1043
+ **Note: Only `daemon` mode supports saving logs to files. In `run` mode, logs are output to the terminal but can be redirected.**
1044
+
1045
+ You can use the `logHandle` option to define a custom logging function, which overrides `logFile` and `errorLogFile`.
1046
+
1047
+ **Example:**
1048
+
1049
+ ```javascript
1050
+ const titbit = require('titbit')
1051
+
1052
+ const app = new titbit({
1053
+ debug: true,
1054
+ globalLog: true,
1055
+ logType: 'file', // 'file' for file output, 'stdio' for terminal
1056
+ logFile: '/tmp/titbit.log', // Successful requests (2xx, 3xx)
1057
+ errorLogFile: '/tmp/titbit-error.log', // Error requests (4xx, 5xx)
1058
+ logHandle: (w, msg) => {
1059
+ // Custom log handler; overrides logFile and errorLogFile
1060
+ // `msg` format: { type: '_log', success: true, log: '@ GET | https://localhost:2021/randst | 200 | 2020-10-31 20:27:7 | 127.0.0.1 | User-Agent' }
1061
+ console.log(w.id, msg)
1062
+ }
1063
+ })
1064
+
1065
+ app.daemon(1234, 3)
1066
+ ```
1067
+
1068
+ Middleware-based logging does not conflict with global logging but cannot capture 404 errors (no route found), as the framework returns early without creating a request context.
1069
+
1070
+ ## Message Event Handling
1071
+
1072
+ In `daemon` mode (using `cluster`), the `setMsgEvent` function handles messages sent by subprocesses. Messages must be objects with a required `type` property indicating the event name.
1073
+
1074
+ **Example:**
1075
+
1076
+ ```javascript
1077
+ const titbit = require('titbit')
1078
+ const cluster = require('cluster')
1079
+
1080
+ const app = new titbit({
1081
+ debug: true,
1082
+ loadInfoFile: '/tmp/loadinfo.log'
1083
+ })
1084
+
1085
+ if (cluster.isMaster) {
1086
+ app.setMsgEvent('test-msg', (worker, msg, handle) => {
1087
+ worker.send({
1088
+ id: worker.id,
1089
+ data: 'ok'
1090
+ })
1091
+ console.log(msg)
1092
+ })
1093
+ } else {
1094
+ process.on('message', msg => {
1095
+ console.log(msg)
1096
+ })
1097
+
1098
+ setInterval(() => {
1099
+ process.send({
1100
+ type: 'test-msg',
1101
+ pid: process.pid,
1102
+ time: new Date().toLocaleString()
1103
+ })
1104
+ }, 1000)
1105
+ }
1106
+ ```
1107
+
1108
+ Since v22.4.0, the `app.send` method simplifies sending messages from workers to the master process.
1109
+
1110
+ ## `app.send` and `app.workerMsg`
1111
+
1112
+ Rewriting the above example using `app.send` and `app.workerMsg`:
1113
+
1114
+ ```javascript
1115
+ const titbit = require('titbit')
1116
+
1117
+ const app = new titbit({
1118
+ debug: true,
1119
+ loadInfoFile: '/tmp/loadinfo.log'
1120
+ })
1121
+
1122
+ app.setMsgEvent('test-msg', (worker, msg, handle) => {
1123
+ worker.send({
1124
+ id: worker.id,
1125
+ data: 'ok'
1126
+ })
1127
+ console.log(msg)
1128
+ })
1129
+
1130
+ app.workerMsg(msg => {
1131
+ console.log(msg)
1132
+ })
1133
+
1134
+ cluster.isWorker &&
1135
+ setInterval(() => {
1136
+ app.send('test-msg', {
1137
+ pid: process.pid,
1138
+ time: new Date().toLocaleString()
1139
+ })
1140
+ }, 1000)
1141
+
1142
+ app.daemon(1234, 2)
1143
+ ```
1144
+
1145
+ ## Automatic Subprocess Adjustment
1146
+
1147
+ The `daemon` interface sets the base number of subprocesses:
1148
+
1149
+ ```javascript
1150
+ // Use 2 subprocesses
1151
+ app.daemon(1234, 2)
1152
+ ```
1153
+
1154
+ To automatically adjust subprocesses based on load, use `autoWorker` to set a maximum number of subprocesses (must be greater than the base number):
1155
+
1156
+ ```javascript
1157
+ // Maximum 9 subprocesses
1158
+ app.autoWorker(9)
1159
+
1160
+ app.daemon(1234, 2)
1161
+ ```
1162
+
1163
+ When load is high, new subprocesses are created. When idle, subprocesses with zero connections are terminated to revert to the base number.
1164
+
1165
+ **Available since v21.9.6. Use the latest version for improved stability and performance.**
1166
+
1167
+ ## Strong Mode
1168
+
1169
+ Enable `strong` mode to handle `uncaughtException` and `unhandledRejection` events, ensuring program stability. Simply set `strong: true`.
1170
+
1171
+ **All `strong` mode features can be implemented manually using the `process` module; this just simplifies the process.**
1172
+
1173
+ ```javascript
1174
+ 'use strict'
1175
+
1176
+ const titbit = require('titbit')
1177
+
1178
+ setTimeout(() => {
1179
+ throw new Error('test error')
1180
+ }, 2000)
1181
+
1182
+ const app = new titbit({
1183
+ debug: true,
1184
+ strong: true
1185
+ })
1186
+
1187
+ app.run(1234)
1188
+ ```
1189
+
1190
+ By default, `strong` mode catches:
1191
+
1192
+ ```
1193
+ TypeError, ReferenceError, RangeError, AssertionError, URIError, Error
1194
+ ```
1195
+
1196
+ Customize handling with an object:
1197
+
1198
+ ```javascript
1199
+ const app = new titbit({
1200
+ debug: true,
1201
+ strong: {
1202
+ quiet: true, // Suppress error output
1203
+ errorHandle: (err, errname) => {
1204
+ // Custom error handling
1205
+ },
1206
+ catchErrors: ['TypeError', 'URIError', 'Error', 'RangeError']
1207
+ }
1208
+ })
1209
+ ```
1210
+
1211
+ ## Running HTTP and HTTPS Simultaneously?
1212
+
1213
+ **This is not recommended in production.** If HTTPS is enabled, HTTP is unnecessary, and some front-end features require HTTPS.
1214
+
1215
+ For testing, you can do:
1216
+
1217
+ ```javascript
1218
+ 'use strict'
1219
+
1220
+ const Titbit = require('titbit')
1221
+ const http = require('node:http')
1222
+ const https = require('node:https')
1223
+
1224
+ const app = new Titbit({
1225
+ debug: true
1226
+ })
1227
+
1228
+ let http_server = http.createServer(app.httpServ.onRequest())
1229
+ let https_server = https.createServer(app.httpServ.onRequest())
1230
+
1231
+ http_server.listen(2025)
1232
+ https_server.listen(2026)
1233
+ ```
1234
+
1235
+ **Note: This setup does not support HTTP/2. Use HTTP/2 with `allowHTTP1` for compatibility.**
1236
+
1237
+ ## Miscellaneous
1238
+
1239
+ - A final middleware handles responses, automatically setting `content-type` (e.g., `text/plain`, `text/html`, `application/json`) if not set.
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`.
1244
+
1245
+ The framework auto-detects memory size and sets limits, adjustable via the `secure` object in `daemon` mode:
1246
+
1247
+ ```javascript
1248
+ 'use strict'
1249
+
1250
+ const Titbit = require('titbit')
1251
+
1252
+ let app = new Titbit()
1253
+
1254
+ // Max memory 600MB, restarts only when connections are 0.
1255
+ app.secure.maxmem = 600_000_000
1256
+
1257
+ // Hard limit 900MB; restarts if exceeded, even with active connections.
1258
+ app.secure.diemem = 900_000_000
1259
+
1260
+ // Max RSS memory 800MB (excludes Buffer allocations).
1261
+ app.secure.maxrss = 800_000_000
1262
+
1263
+ app.get('/', async c => {
1264
+ c.send('ok')
1265
+ })
1266
+
1267
+ app.daemon(8008, 2)
1268
+ ```
1269
+
1270
+ **Requires `loadMonitor: true` (default unless set to `false`).**
1271
+
1272
+ Use default configurations unless specific control is needed.