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.
Files changed (72) hide show
  1. package/README.cn.md +133 -176
  2. package/README.md +803 -592
  3. package/bin/new-ctl.js +7 -3
  4. package/demo/allow.js +13 -13
  5. package/demo/controller/api.js +15 -0
  6. package/demo/extends.js +5 -0
  7. package/demo/http2.js +34 -0
  8. package/demo/http2_proxy_backend.js +45 -0
  9. package/demo/http2proxy.js +48 -0
  10. package/demo/http_proxy_backend.js +44 -0
  11. package/demo/httpproxy.js +47 -0
  12. package/demo/loader.js +27 -0
  13. package/demo/log.js +1 -1
  14. package/demo/memlimit.js +1 -1
  15. package/demo/min.js +1 -1
  16. package/demo/serv.js +1 -1
  17. package/images/topbit-middleware.png +0 -0
  18. package/images/topbit.png +0 -0
  19. package/package.json +7 -6
  20. package/src/_loadExtends.js +21 -0
  21. package/src/bodyparser.js +1 -1
  22. package/src/context1.js +19 -19
  23. package/src/context2.js +11 -8
  24. package/src/ctxpool.js +1 -0
  25. package/src/extends/Http2Pool.js +365 -0
  26. package/src/extends/__randstring.js +24 -0
  27. package/src/extends/cookie.js +44 -0
  28. package/src/extends/cors.js +334 -0
  29. package/src/extends/errorlog.js +252 -0
  30. package/src/extends/http2limit.js +126 -0
  31. package/src/extends/http2proxy.js +691 -0
  32. package/src/extends/jwt.js +217 -0
  33. package/src/extends/mixlogger.js +63 -0
  34. package/src/extends/paramcheck.js +266 -0
  35. package/src/extends/proxy.js +662 -0
  36. package/src/extends/realip.js +34 -0
  37. package/src/extends/referer.js +68 -0
  38. package/src/extends/resource.js +398 -0
  39. package/src/extends/session.js +174 -0
  40. package/src/extends/setfinal.js +50 -0
  41. package/src/extends/sni.js +48 -0
  42. package/src/extends/sse.js +293 -0
  43. package/src/extends/timing.js +111 -0
  44. package/src/extends/tofile.js +123 -0
  45. package/src/http1.js +15 -16
  46. package/src/http2.js +5 -5
  47. package/src/httpc.js +3 -3
  48. package/src/lib/npargv.js +354 -0
  49. package/src/lib/zipdata.js +45 -0
  50. package/src/middleware1.js +15 -16
  51. package/src/middleware2.js +4 -9
  52. package/src/token/token.js +4 -5
  53. package/src/topbit.js +13 -11
  54. package/test/{test-helper.js → test-ext.js} +1 -1
  55. package/test/test-route.js +1 -1
  56. package/cache/allow.js +0 -130
  57. package/cache/errserv.js +0 -45
  58. package/cache/minserv.js +0 -167
  59. package/cache/router.js +0 -84
  60. package/cache/servsock.js +0 -286
  61. package/cache/sni.js +0 -66
  62. package/images/titbit-middleware.png +0 -0
  63. package/images/titbit.png +0 -0
  64. package/tmp/buff-code +0 -134
  65. package/tmp/devplan +0 -9
  66. package/tmp/evt-test.js +0 -34
  67. package/tmp/fastParseUrl.js +0 -302
  68. package/tmp/router-rule.js +0 -559
  69. package/tmp/test-cdps.js +0 -122
  70. package/tmp/titbit.js +0 -1286
  71. /package/{cache/rsa → demo/cert}/localhost-cert.pem +0 -0
  72. /package/{cache/rsa → demo/cert}/localhost-privkey.pem +0 -0
@@ -0,0 +1,365 @@
1
+ 'use strict';
2
+
3
+ const http2 = require('node:http2')
4
+ const crypto = require('node:crypto')
5
+
6
+ class Http2Pool {
7
+
8
+ constructor(options = {}) {
9
+ // 存储session的Map
10
+ if (!options || typeof options !== 'object') options = {}
11
+ if (!options.connectOptions) options.connectOptions = {}
12
+
13
+ this.pool = new Map()
14
+
15
+ this.innerConnectDelay = 0
16
+ this.failedCount = 0
17
+ this.reconnecting = false
18
+ // 配置项
19
+ this.maxStreamId = !isNaN(options.maxStreamId) && options.maxStreamId > 1
20
+ ? options.maxStreamId
21
+ : 90000
22
+
23
+ this.timeout = options.timeout || 30000
24
+ this.connectTimeout = options.connectTimeout || 15000
25
+
26
+ this.max = (options.max && !isNaN(options.max) && options.max > 0) ? options.max : 50
27
+ this.poolMax = parseInt(this.max * 1.5 + 0.5)
28
+ this.maxConnect = (options.maxConnect && !isNaN(options.maxConnect) && options.maxConnect > 0)
29
+ ? options.maxConnect
30
+ : this.poolMax + 500
31
+
32
+ this.url = options.url || ''
33
+ this.debug = options.debug || false
34
+ // 连接选项
35
+ this.connectOptions = {
36
+ rejectUnauthorized: false,
37
+ requestCert: false,
38
+ peerMaxConcurrentStreams: 100,
39
+ timeout: this.timeout,
40
+ ...options.connectOptions
41
+ }
42
+
43
+ this.reconnDelay = 100
44
+ if (options.reconnDelay !== undefined && !isNaN(options.reconnDelay)) {
45
+ this.reconnDelay = options.reconnDelay
46
+ }
47
+
48
+ this.parent = null
49
+
50
+ if (options.parent && typeof options.parent === 'object') {
51
+ this.parent = options.parent
52
+ }
53
+
54
+ this.maxAliveStreams = options.maxAliveStreams || 100
55
+
56
+ this.quiet = false
57
+ if (options.quiet)
58
+ this.quiet = !!options.quiet
59
+ }
60
+
61
+ /**
62
+ * 创建新的session连接
63
+ */
64
+ async connect() {
65
+ if (this.pool.size > this.maxConnect) {
66
+ return {
67
+ deny: true,
68
+ error: `超出最大连接限制:${this.maxConnect}`
69
+ }
70
+ }
71
+
72
+ const session = http2.connect(this.url, this.connectOptions)
73
+
74
+ // 生成唯一session id
75
+ const sessionId = crypto.randomBytes(16).toString('hex')
76
+
77
+ // 初始化session相关计数器和状态
78
+ const sessionState = {
79
+ using: false,
80
+ id: sessionId,
81
+ session,
82
+ streamCount: 0,
83
+ url: this.url,
84
+ connected: false,
85
+ error: null,
86
+ aliveStreams: 0
87
+ }
88
+
89
+ // 处理session事件
90
+ this._handleSessionEvents(sessionState)
91
+
92
+ // 等待连接建立
93
+ try {
94
+ let timeout_timer = null
95
+ let resolved = false
96
+ let rejected = false
97
+
98
+ await new Promise((resolve, reject) => {
99
+ session.once('connect', () => {
100
+ if (timeout_timer) {
101
+ clearTimeout(timeout_timer)
102
+ timeout_timer = null
103
+ }
104
+
105
+ if (this.failedCount > 0) {
106
+ this.failedCount--
107
+ }
108
+
109
+ this.innerConnectDelay = 0
110
+
111
+ sessionState.connected = true
112
+ this.parent && !this.parent.alive && (this.parent.alive = true)
113
+
114
+ resolve()
115
+ })
116
+
117
+ session.once('error', err => {
118
+ if (timeout_timer) {
119
+ clearTimeout(timeout_timer)
120
+ timeout_timer = null
121
+ }
122
+
123
+ if (this.pool.size < 1) {
124
+ this.parent && (this.parent.alive = false)
125
+ }
126
+
127
+ this.failedCount++
128
+
129
+ if (this.failedCount < 10) {
130
+ this.innerConnectDelay = this.failedCount
131
+ } if (this.failedCount < 60000) {
132
+ this.innerConnectDelay = parseInt(this.failedCount / 10)
133
+ } else { this.innerConnectDelay = 6000 }
134
+
135
+ !rejected && (rejected = true) && reject(err)
136
+ })
137
+
138
+ session.once('goaway', err => {
139
+ if (timeout_timer) {
140
+ clearTimeout(timeout_timer)
141
+ timeout_timer = null
142
+ }
143
+
144
+ !rejected && (rejected = true) && reject(err||new Error('goaway'))
145
+ })
146
+
147
+ session.once('frameError', err => {
148
+ if (timeout_timer) {
149
+ clearTimeout(timeout_timer)
150
+ timeout_timer = null
151
+ }
152
+ !rejected && (rejected = true) && reject(err)
153
+ })
154
+
155
+ if (!timeout_timer) {
156
+ timeout_timer = setTimeout(() => {
157
+ timeout_timer = null
158
+ !session.destroyed && session.destroy()
159
+ !rejected && (rejected = true) && reject(new Error('connect timeout'))
160
+ }, this.connectTimeout)
161
+ }
162
+ })
163
+ } catch (err) {
164
+ sessionState.error = err
165
+ sessionState.session = null
166
+ this.debug && console.error(err)
167
+ } finally {
168
+ this.reconnecting = false
169
+ }
170
+
171
+ if (this.pool.size < this.poolMax && sessionState.connected) {
172
+ this.pool.set(sessionId, sessionState)
173
+ }
174
+
175
+ return sessionState
176
+ }
177
+
178
+ createPool(max=0) {
179
+ if (max <= 0) max = this.max
180
+
181
+ for (let i = 0; i < max; i++) {
182
+ this.connect()
183
+ }
184
+ }
185
+
186
+ delayConnect() {
187
+ if (this.reconnecting) return false
188
+
189
+ let delay_time = this.reconnDelay + this.innerConnectDelay
190
+
191
+ if (delay_time > 0) {
192
+ if (!this.delayTimer) {
193
+ this.reconnecting = true
194
+ this.delayTimer = setTimeout(() => {
195
+ this.delayTimer = null
196
+ this.connect()
197
+ }, delay_time)
198
+ }
199
+ } else {
200
+ this.reconnecting = true
201
+ this.connect()
202
+ }
203
+ }
204
+
205
+ /**
206
+ * 处理session的各种事件
207
+ */
208
+ _handleSessionEvents(sessionState) {
209
+ const { session, id } = sessionState
210
+
211
+ session.on('close', () => {
212
+ // session关闭时从pool中移除
213
+ this.pool.delete(id)
214
+
215
+ if (this.pool.size < 1) {
216
+ this.delayConnect()
217
+ }
218
+ })
219
+
220
+ session.on('error', err => {
221
+ this.debug && console.error(err)
222
+ !session.destroyed && session.destroy()
223
+ this.pool.delete(id)
224
+ })
225
+
226
+ session.on('frameError', err => {
227
+ !session.destroyed && session.destroy()
228
+ this.pool.delete(id)
229
+ })
230
+
231
+ session.on('goaway', err => {
232
+ this.debug && err && console.error('..........goaway........', err)
233
+ !session.destroyed && session.close()
234
+ this.pool.delete(id)
235
+ })
236
+
237
+ session.setTimeout(this.timeout, () => {
238
+ this.debug && console.error('session.....time.....out......')
239
+ if (!session.destroyed) {
240
+ session.destroy()
241
+ }
242
+
243
+ this.pool.delete(id)
244
+ })
245
+ }
246
+
247
+ isSessionHealthy(session) {
248
+ return session
249
+ && !session.destroyed
250
+ && !session.closed
251
+ && session.socket
252
+ && !session.socket.destroyed
253
+ }
254
+
255
+ /**
256
+ * 获取可用的session,如果没有则创建新的
257
+ */
258
+ async getSession() {
259
+ if (this.pool.size > 0) {
260
+ let items = this.pool.entries()
261
+ for (const [id, state] of items) {
262
+ if (state.connected
263
+ && state.streamCount < this.maxStreamId
264
+ && this.isSessionHealthy(state.session))
265
+ {
266
+ if (state.aliveStreams < this.maxAliveStreams) {
267
+ return state
268
+ }
269
+ } else {
270
+ state.connected = false
271
+ if (!state.session.destroyed) {
272
+ state.session.close()
273
+
274
+ if (state.aliveStreams < 1) {
275
+ state.session.destroy()
276
+ }
277
+ /* else {
278
+ let sess = state.session
279
+ setTimeout(() => {
280
+ !sess.destroyed && sess.destroy()
281
+ sess = null
282
+ }, this.timeout + 5000)
283
+ } */
284
+ }
285
+
286
+ this.pool.delete(state.id)
287
+ }
288
+ }
289
+ }
290
+
291
+ return this.connect()
292
+ }
293
+
294
+ /**
295
+ * 创建新的请求stream
296
+ */
297
+ async request(headers, sessionState=null) {
298
+ !sessionState && (sessionState = await this.getSession())
299
+
300
+ sessionState.streamCount++
301
+
302
+ if (!sessionState.connected) {
303
+ if (this.quiet) return null
304
+ throw new Error('There is no connected')
305
+ }
306
+
307
+ //创建请求stream
308
+ return sessionState.session.request(headers)
309
+ }
310
+
311
+ /**
312
+ * 关闭所有session
313
+ */
314
+ closeAll() {
315
+ for (const [id, state] of this.pool.entries()) {
316
+ if (!state.session.destroyed) {
317
+ state.session.close()
318
+ }
319
+ }
320
+
321
+ this.pool.clear()
322
+ }
323
+
324
+ async aok() {
325
+ if (this.pool.size <= 0) {
326
+ await this.connect()
327
+ }
328
+
329
+ return this.ok()
330
+ }
331
+
332
+ ok() {
333
+ let items = this.pool.entries()
334
+
335
+ for (const [id, state] of items) {
336
+ if (state.connected) return true
337
+ }
338
+
339
+ return false
340
+ }
341
+
342
+ /**
343
+ * 获取当前pool状态
344
+ */
345
+ status() {
346
+ const status = {
347
+ total: this.pool.size,
348
+ sessions: []
349
+ }
350
+
351
+ let items = this.pool.entries()
352
+
353
+ for (const [id, state] of items) {
354
+ status.sessions.push({
355
+ id: state.id,
356
+ streamCount: state.streamCount,
357
+ connected: state.connected
358
+ })
359
+ }
360
+
361
+ return status
362
+ }
363
+ }
364
+
365
+ module.exports = Http2Pool
@@ -0,0 +1,24 @@
1
+ 'use strict'
2
+
3
+ let saltArr = [
4
+ 'a','b','c','d','e','f','g',
5
+ 'h','i','j','k','l','m','n',
6
+ 'o','p','q','r','s','t','u',
7
+ 'v','w','x','y','z','1','2',
8
+ '3','4','5','6','7','8','9'
9
+ ]
10
+
11
+ function randstring (length = 8) {
12
+
13
+ let saltstr = ''
14
+ let ind = 0
15
+
16
+ for(let i = 0; i < length; i++) {
17
+ ind = parseInt( Math.random() * saltArr.length)
18
+ saltstr += saltArr[ ind ]
19
+ }
20
+
21
+ return saltstr
22
+ }
23
+
24
+ module.exports = randstring
@@ -0,0 +1,44 @@
1
+ 'use strict';
2
+
3
+ class Cookie {
4
+ constructor() {
5
+
6
+ }
7
+
8
+ mid() {
9
+
10
+ return async (rr, next) => {
11
+ rr.cookie = {}
12
+
13
+ if (rr.headers['cookie']) {
14
+ let cookies = rr.headers['cookie'].split(';').filter(c => c.length > 0)
15
+
16
+ let tmpList = []
17
+ let name = ''
18
+
19
+ for (let i = 0; i < cookies.length; i++) {
20
+ tmpList = cookies[i].split('=').filter(p => p.length > 0)
21
+ name = tmpList[0].trim()
22
+
23
+ if (name.length == 0) {
24
+ continue
25
+ }
26
+
27
+ if (tmpList.length < 2) {
28
+ rr.cookie[name] = ''
29
+ } else {
30
+ rr.cookie[name] = tmpList[1]
31
+ }
32
+ }
33
+ }
34
+
35
+ await next(rr)
36
+
37
+ rr.cookie = null
38
+ }
39
+
40
+ }
41
+
42
+ }
43
+
44
+ module.exports = Cookie