topbit 1.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 (89) hide show
  1. package/LICENSE +128 -0
  2. package/README.cn.md +1519 -0
  3. package/README.md +1483 -0
  4. package/bin/app.js +17 -0
  5. package/bin/loadinfo.sh +18 -0
  6. package/bin/new-ctl.js +234 -0
  7. package/bin/newapp.js +22 -0
  8. package/demo/allow.js +98 -0
  9. package/demo/cert/localhost-cert.pem +19 -0
  10. package/demo/cert/localhost-privkey.pem +28 -0
  11. package/demo/controller/api.js +15 -0
  12. package/demo/extends.js +5 -0
  13. package/demo/group-api.js +161 -0
  14. package/demo/group-api2.js +109 -0
  15. package/demo/http2.js +34 -0
  16. package/demo/http2_proxy_backend.js +45 -0
  17. package/demo/http2proxy.js +48 -0
  18. package/demo/http_proxy_backend.js +44 -0
  19. package/demo/httpproxy.js +47 -0
  20. package/demo/loader.js +27 -0
  21. package/demo/log.js +118 -0
  22. package/demo/memlimit.js +31 -0
  23. package/demo/min.js +7 -0
  24. package/demo/serv.js +15 -0
  25. package/images/middleware.jpg +0 -0
  26. package/images/topbit-middleware.png +0 -0
  27. package/images/topbit.png +0 -0
  28. package/package.json +42 -11
  29. package/src/_loadExtends.js +21 -0
  30. package/src/bodyparser.js +420 -0
  31. package/src/connfilter.js +125 -0
  32. package/src/context1.js +166 -0
  33. package/src/context2.js +182 -0
  34. package/src/ctxpool.js +39 -0
  35. package/src/ext.js +318 -0
  36. package/src/extends/Http2Pool.js +365 -0
  37. package/src/extends/__randstring.js +24 -0
  38. package/src/extends/cookie.js +44 -0
  39. package/src/extends/cors.js +334 -0
  40. package/src/extends/errorlog.js +252 -0
  41. package/src/extends/http2limit.js +126 -0
  42. package/src/extends/http2proxy.js +691 -0
  43. package/src/extends/jwt.js +217 -0
  44. package/src/extends/mixlogger.js +63 -0
  45. package/src/extends/paramcheck.js +266 -0
  46. package/src/extends/proxy.js +662 -0
  47. package/src/extends/realip.js +34 -0
  48. package/src/extends/referer.js +68 -0
  49. package/src/extends/resource.js +398 -0
  50. package/src/extends/session.js +174 -0
  51. package/src/extends/setfinal.js +50 -0
  52. package/src/extends/sni.js +48 -0
  53. package/src/extends/sse.js +293 -0
  54. package/src/extends/timing.js +111 -0
  55. package/src/extends/tofile.js +123 -0
  56. package/src/fastParseUrl.js +426 -0
  57. package/src/headerLimit.js +18 -0
  58. package/src/http1.js +336 -0
  59. package/src/http2.js +337 -0
  60. package/src/httpc.js +251 -0
  61. package/src/lib/npargv.js +354 -0
  62. package/src/lib/zipdata.js +45 -0
  63. package/src/loader/loader.js +999 -0
  64. package/src/logger.js +32 -0
  65. package/src/loggermsg.js +349 -0
  66. package/src/makeId.js +200 -0
  67. package/src/midcore.js +213 -0
  68. package/src/middleware1.js +103 -0
  69. package/src/middleware2.js +116 -0
  70. package/src/monitor.js +380 -0
  71. package/src/movefile.js +30 -0
  72. package/src/optionsCheck.js +54 -0
  73. package/src/randstring.js +23 -0
  74. package/src/router.js +682 -0
  75. package/src/sendmsg.js +27 -0
  76. package/src/strong.js +72 -0
  77. package/src/token/token.js +461 -0
  78. package/src/topbit.js +1293 -0
  79. package/src/versionCheck.js +31 -0
  80. package/test/test-bigctx.js +29 -0
  81. package/test/test-daemon-args.js +7 -0
  82. package/test/test-ext.js +81 -0
  83. package/test/test-find.js +69 -0
  84. package/test/test-route-sort.js +71 -0
  85. package/test/test-route.js +49 -0
  86. package/test/test-route2.js +51 -0
  87. package/test/test-run-args.js +7 -0
  88. package/test/test-url.js +52 -0
  89. package/main.js +0 -0
@@ -0,0 +1,662 @@
1
+ 'use strict';
2
+
3
+ const urlparse = require('node:url');
4
+ const http = require('node:http');
5
+ const https = require('node:https');
6
+
7
+ /**
8
+ * {
9
+ * host : {}
10
+ * }
11
+ * {
12
+ * host : ''
13
+ * }
14
+ *
15
+ * {
16
+ * host : [
17
+ * {}
18
+ * ]
19
+ * }
20
+ *
21
+ */
22
+
23
+ class Proxy {
24
+
25
+ constructor(options = {}) {
26
+
27
+ this.methods = ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS', 'HEAD', 'PATCH', 'TRACE']
28
+
29
+ this.hostProxy = {}
30
+
31
+ this.proxyBalance = {}
32
+
33
+ this.pathTable = {}
34
+
35
+ this.config = {}
36
+
37
+ this.urlpreg = /(unix|http|https):\/\/[a-zA-Z0-9\-\_]+/
38
+
39
+ this.maxBody = 50000000
40
+
41
+ //是否启用全代理模式。
42
+ this.full = false
43
+
44
+ this.timeout = 15000
45
+
46
+ this.addIP = false
47
+
48
+ this.debug = false
49
+
50
+ this.autoClearListeners = false
51
+
52
+ //记录定时器
53
+ this.proxyIntervals = {}
54
+
55
+ this.connectOptions = {
56
+ family: 4
57
+ }
58
+
59
+ this.error = {
60
+ '502' : `<!DOCTYPE html><html>
61
+ <head>
62
+ <meta charset="UTF-8">
63
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
64
+ <title>Error 502</title>
65
+ </head>
66
+ <body>
67
+ <div style="width:100%;font-size:105%;color:#737373;padding:0.8rem;">
68
+ <h2>502 Bad Gateway</h2><br>
69
+ <p>代理请求不可达。</p>
70
+ </div>
71
+ </body>
72
+ </html>`,
73
+
74
+ '503' :`<!DOCTYPE html><html>
75
+ <head>
76
+ <meta charset="UTF-8">
77
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
78
+ <title>Error 503</title>
79
+ </head>
80
+ <body>
81
+ <div style="width:100%;font-size:105%;color:#737373;padding:0.8rem;">
82
+ <h2>503 Service Unavailable</h2><br>
83
+ <p>此服务暂时不可用。</p>
84
+ </div>
85
+ </body>
86
+ </html>`
87
+ }
88
+
89
+ if (typeof options !== 'object') {
90
+ options = {}
91
+ }
92
+
93
+ for (let k in options) {
94
+ switch (k) {
95
+ case 'host':
96
+ case 'config':
97
+ this.config = options[k]
98
+ break
99
+
100
+ case 'methods':
101
+ Array.isArray(options[k]) && (this.methods = options[k]);
102
+ break
103
+
104
+ case 'maxBody':
105
+ if (typeof options[k] == 'number' && parseInt(options[k]) >= 0) {
106
+ this.maxBody = parseInt(options[k])
107
+ }
108
+ break
109
+
110
+ case 'full':
111
+ case 'debug':
112
+ case 'autoClearListeners':
113
+ this[k] = !!options[k]
114
+ break
115
+
116
+ case 'timeout':
117
+ if (typeof options[k] === 'number' && options[k] >= 0) {
118
+ this.timeout = options[k]
119
+ }
120
+ break
121
+
122
+ case 'addIP':
123
+ this.addIP = options[k]
124
+ break
125
+
126
+ case 'connectOptions':
127
+ if (options[k] && typeof options[k] === 'object') {
128
+ for (let o in options[k]) this.connectOptions[o] = options[k][o]
129
+ }
130
+ break
131
+
132
+ default:;
133
+ }
134
+ }
135
+
136
+ this.setHostProxy(this.config)
137
+ }
138
+
139
+ fmtpath(path) {
140
+ path = path.trim()
141
+
142
+ if (path.length == 0) {
143
+ return '/*'
144
+ }
145
+
146
+ if (path[0] !== '/') {
147
+ path = `/${path}`
148
+ }
149
+
150
+ if (path.length > 1 && path[path.length - 1] !== '/') {
151
+ path = `${path}/`
152
+ }
153
+
154
+ if (path.indexOf('/:') >= 0) {
155
+ return path.substring(0, path.length-1)
156
+ }
157
+
158
+ return `${path}*`
159
+ }
160
+
161
+ setHostProxy(cfg) {
162
+ if (typeof cfg !== 'object') {
163
+ return
164
+ }
165
+
166
+ let pt = ''
167
+ let tmp = ''
168
+ let backend_obj = null
169
+
170
+ for (let k in cfg) {
171
+
172
+ if (typeof cfg[k] === 'string') {
173
+ cfg[k] = [ { path : '/', url : cfg[k] } ]
174
+
175
+ } else if (!(cfg[k] instanceof Array) && typeof cfg[k] === 'object') {
176
+ cfg[k] = [ cfg[k] ]
177
+
178
+ } else if ( !(cfg[k] instanceof Array) ) {
179
+ continue
180
+ }
181
+ /**
182
+ * {
183
+ * path : '',
184
+ * url : '',
185
+ * aliveCheckPath : '',
186
+ * headers : {}
187
+ * }
188
+ */
189
+ for (let i = 0; i < cfg[k].length; i++) {
190
+ tmp = cfg[k][i]
191
+
192
+ if (typeof tmp !== 'object' || (tmp instanceof Array) ) {
193
+ console.error(`${k} ${JSON.stringify(tmp)} 错误的配置格式`)
194
+ continue
195
+ }
196
+
197
+ if (tmp.path === undefined) {
198
+ tmp.path = '/'
199
+ }
200
+
201
+ if (tmp.url === undefined) {
202
+ console.error(`${k} ${tmp.path}:没有指定要代理转发的url。`)
203
+ continue
204
+ }
205
+
206
+ if (this.urlpreg.test(tmp.url) === false) {
207
+ console.error(`${tmp.url} : 错误的url,请检查。`)
208
+ continue
209
+ }
210
+
211
+ pt = this.fmtpath(tmp.path)
212
+
213
+ if (tmp.url[ tmp.url.length - 1 ] == '/') {
214
+ tmp.url = tmp.url.substring(0, tmp.url.length - 1)
215
+ }
216
+
217
+ if (tmp.headers !== undefined) {
218
+ if (typeof tmp.headers !== 'object') {
219
+ console.error(
220
+ `${k} ${tmp.url} ${tmp.path}:headers属性要求是object类型,使用key-value形式提供。`
221
+ );
222
+ continue
223
+ }
224
+ }
225
+
226
+ if (this.hostProxy[k] === undefined) {
227
+ this.hostProxy[k] = {}
228
+ this.proxyBalance[k] = {}
229
+ }
230
+
231
+ tmp.urlobj = this.parseUrl(tmp.url)
232
+
233
+ tmp.urlobj.timeout = tmp.timeout || this.timeout
234
+
235
+ backend_obj = {
236
+ url : tmp.url,
237
+ urlobj : tmp.urlobj,
238
+ headers : {},
239
+ path : tmp.path,
240
+ weight: 1,
241
+ weightCount : 0,
242
+ alive : true,
243
+ aliveCheckInterval : 5,
244
+ aliveCheckPath : '/',
245
+ intervalCount : 0,
246
+ rewrite: (tmp.rewrite && typeof tmp.rewrite === 'function') ? tmp.rewrite : null,
247
+ connectOptions: {...this.connectOptions}
248
+ }
249
+
250
+ if (tmp.connectOptions && typeof tmp.connectOptions) {
251
+ for (let o in tmp.connectOptions) {
252
+ backend_obj.connectOptions[o] = tmp.connectOptions[o]
253
+ }
254
+ }
255
+
256
+ if (tmp.headers !== undefined) {
257
+ for (let h in tmp.headers) {
258
+ backend_obj.headers[h] = tmp.headers[h]
259
+ }
260
+ }
261
+
262
+ if (typeof tmp.aliveCheckPath === 'string' && tmp.aliveCheckPath.length > 0) {
263
+ if (tmp.aliveCheckPath[0] !== '/') {
264
+ tmp.aliveCheckPath = `/${tmp.aliveCheckPath}`
265
+ }
266
+
267
+ backend_obj.aliveCheckPath = tmp.aliveCheckPath
268
+ }
269
+
270
+ if (tmp.weight && typeof tmp.weight === 'number' && tmp.weight > 1) {
271
+ backend_obj.weight = parseInt(tmp.weight)
272
+ }
273
+
274
+ if (tmp.aliveCheckInterval !== undefined && typeof tmp.aliveCheckInterval === 'number') {
275
+ if (tmp.aliveCheckInterval >= 0 && tmp.aliveCheckInterval <= 7200) {
276
+ backend_obj.aliveCheckInterval = tmp.aliveCheckInterval
277
+ }
278
+ }
279
+
280
+ if (this.hostProxy[k][pt] === undefined) {
281
+
282
+ this.hostProxy[k][pt] = [ backend_obj ]
283
+ this.proxyBalance[k][pt] = {
284
+ stepIndex : 0,
285
+ useWeight : false
286
+ }
287
+
288
+ } else if (this.hostProxy[k][pt] instanceof Array) {
289
+ this.hostProxy[k][pt].push(backend_obj)
290
+ }
291
+
292
+ if (backend_obj.weight > 1) {
293
+ this.proxyBalance[k][pt].useWeight = true
294
+ }
295
+
296
+ this.pathTable[pt] = 1
297
+ } //end sub for
298
+ } // end for
299
+ }
300
+
301
+ parseUrl(url) {
302
+ let u = new urlparse.URL(url)
303
+
304
+ let urlobj = {
305
+ hash : u.hash,
306
+ hostname: u.hostname,
307
+ protocol: u.protocol,
308
+ path : u.pathname,
309
+ method : 'GET',
310
+ headers : {},
311
+ }
312
+
313
+ if (u.search.length > 0) {
314
+ urlobj.path += u.search
315
+ }
316
+
317
+ if (u.protocol === 'unix:') {
318
+ urlobj.protocol = 'http:'
319
+ let sockarr = u.pathname.split('.sock')
320
+ urlobj.socketPath = `${sockarr[0]}.sock`
321
+ urlobj.path = sockarr[1]
322
+ } else {
323
+ urlobj.host = u.host
324
+ urlobj.port = u.port
325
+ }
326
+
327
+ if (u.protocol === 'https:') {
328
+ urlobj.requestCert = false
329
+ urlobj.rejectUnauthorized = false
330
+ }
331
+
332
+ return urlobj
333
+ }
334
+
335
+ copyUrlobj(uobj) {
336
+ let u = {
337
+ hash: uobj.hash,
338
+ hostname: uobj.hostname,
339
+ protocol: uobj.protocol,
340
+ path: uobj.path,
341
+ method: 'GET',
342
+ headers: {},
343
+ timeout: uobj.timeout
344
+ }
345
+
346
+ if (uobj.host) {
347
+ u.host = uobj.host
348
+ u.port = uobj.port
349
+ } else {
350
+ u.socketPath = uobj.socketPath
351
+ }
352
+
353
+ if (uobj.protocol === 'https:') {
354
+ u.requestCert = false
355
+ u.rejectUnauthorized = false
356
+ }
357
+
358
+ return u
359
+ }
360
+
361
+ getBackend(c, host) {
362
+ let prlist = this.hostProxy[host][c.routepath]
363
+ let pb = this.proxyBalance[host][c.routepath]
364
+ let pr
365
+
366
+ if (prlist.length === 1) {
367
+ pr = prlist[0]
368
+ } else {
369
+ if (pb.stepIndex >= prlist.length) {
370
+ pb.stepIndex = 0
371
+ }
372
+
373
+ pr = prlist[pb.stepIndex]
374
+
375
+ if (pb.useWeight) {
376
+ if (pr.weightCount >= pr.weight) {
377
+ pr.weightCount = 0
378
+ pb.stepIndex++
379
+ } else {
380
+ pr.weightCount++
381
+ }
382
+ } else {
383
+ pb.stepIndex++
384
+ }
385
+ }
386
+
387
+ if (pr.alive === false) {
388
+ for (let i = 0; i < prlist.length; i++) {
389
+
390
+ pr = prlist[i]
391
+
392
+ if (pr.alive === true) {
393
+ return pr
394
+ }
395
+ }
396
+ return null
397
+ }
398
+
399
+ return pr
400
+ }
401
+
402
+ mid() {
403
+ let self = this
404
+ let timeoutError = new Error('request timeout')
405
+ timeoutError.code = 'ETIMEOUT'
406
+
407
+ return async (c, next) => {
408
+
409
+ let host = c.host
410
+
411
+ let hind = c.host.length - 1
412
+
413
+ if (hind > 4) {
414
+ let eind = hind - 5
415
+
416
+ while (hind >= eind) {
417
+ if (c.host[hind] === ':') {
418
+ host = c.host.substring(0, hind)
419
+ break
420
+ }
421
+
422
+ hind--
423
+ }
424
+ }
425
+
426
+ if (self.hostProxy[host]===undefined || self.hostProxy[host][c.routepath]===undefined) {
427
+ if (self.full) {
428
+ return c.status(502).to(self.error['502'])
429
+ }
430
+ return await next(c)
431
+ }
432
+
433
+ let pr = self.getBackend(c, host)
434
+
435
+ if (pr === null) {
436
+ for (let i = 0; i < 200; i++) {
437
+ await new Promise((rv, rj) => {setTimeout(rv, 10)})
438
+ pr = self.getBackend(c, host)
439
+ if (pr) break
440
+ }
441
+
442
+ if (!pr)
443
+ return c.status(503).to(self.error['503'])
444
+ }
445
+
446
+ let urlobj = self.copyUrlobj(pr.urlobj)
447
+
448
+ urlobj.path = c.req.url
449
+ urlobj.headers = c.headers
450
+ urlobj.method = c.method
451
+
452
+ if (self.addIP && urlobj.headers['x-real-ip']) {
453
+ urlobj.headers['x-real-ip'] += `,${c.ip}`
454
+ } else {
455
+ urlobj.headers['x-real-ip'] = c.ip
456
+ }
457
+
458
+ let hci = urlobj.protocol == 'https:' ? https : http
459
+
460
+ for (let k in pr.connectOptions) {
461
+ urlobj[k] = pr.connectOptions[k]
462
+ }
463
+
464
+ if (pr.rewrite) {
465
+ let rw = pr.rewrite(c, c.req.url)
466
+
467
+ if (rw) {
468
+ let path_typ = typeof rw
469
+ if (path_typ === 'string') {
470
+ urlobj.path = rw
471
+ } else if (path_typ === 'object' && rw.redirect) {
472
+ return c.setHeader('location', rw.redirect)
473
+ }
474
+ }
475
+ }
476
+
477
+ let h = hci.request(urlobj)
478
+
479
+ return await new Promise((rv, rj) => {
480
+ let resolved = false
481
+ let rejected = false
482
+
483
+ c.req.on('timeout', () => {
484
+ !h.destroyed && h.destroy(timeoutError)
485
+ })
486
+
487
+ c.res.on('timeout', () => {
488
+ !h.destroyed && h.destroy(timeoutError)
489
+ })
490
+
491
+ h.on('timeout', () => {
492
+ !h.destroyed && h.destroy(timeoutError)
493
+ })
494
+
495
+ h.on('close', () => {
496
+ if (!resolved && !rejected) {
497
+ resolved = true
498
+ rv()
499
+ }
500
+ })
501
+
502
+ h.on('response', res => {
503
+ c.status(res.statusCode)
504
+
505
+ for (let k in res.headers) {
506
+ c.setHeader(k, res.headers[k])
507
+ }
508
+
509
+ res.on('data', chunk => {
510
+ c.res.write(chunk)
511
+ })
512
+
513
+ res.on('end', () => {
514
+ c.res.end()
515
+
516
+ if (!resolved && !rejected) {
517
+ resolved = true
518
+ rv()
519
+ }
520
+ })
521
+
522
+ res.on('error', err => {
523
+ if (!resolved && !rejected){
524
+ rejected = true
525
+ rj(err)
526
+ }
527
+ })
528
+ })
529
+
530
+ h.on('error', (err) => {
531
+ if (!resolved && !rejected) {
532
+ rejected = true
533
+ rj(err)
534
+ }
535
+ })
536
+
537
+ c.req.on('data', chunk => {
538
+ h.write(chunk)
539
+ })
540
+
541
+ c.req.on('end', () => {
542
+ h.end()
543
+ })
544
+
545
+ }).catch(err => {
546
+ self.debug && console.error(err);
547
+ c.status(503).to(self.error['503']);
548
+ })
549
+ .finally(() => {
550
+ this.autoClearListeners && h.removeAllListeners && h.removeAllListeners();
551
+ !h.destroyed && h.destroy();
552
+ })
553
+
554
+ }
555
+
556
+ }
557
+
558
+ timerRequest(pxy, timeout=false) {
559
+ let h = http
560
+
561
+ let opts = {
562
+ timeout : this.timeout + 30_000,
563
+ method: 'TRACE',
564
+ headers: {
565
+ 'user-agent': 'Node.js/Topbit,Topbit-Toolkit: Proxy,AliveCheck'
566
+ }
567
+ }
568
+
569
+ if (pxy.urlobj.protocol === 'https:') {
570
+ h = https
571
+ opts.rejectUnauthorized = false
572
+ opts.requestCert = false
573
+ }
574
+
575
+ for (let o in pxy.connectOptions) {
576
+ opts[o] = pxy.connectOptions[o]
577
+ }
578
+
579
+ let aliveUrl = `${pxy.urlobj.protocol}//${pxy.urlobj.host}${pxy.aliveCheckPath}`
580
+
581
+ let req = h.request(aliveUrl, opts)
582
+
583
+ req.on('error', err => {
584
+ pxy.alive = false
585
+ //当出现连接错误,立即发起一个请求,测试是否是某些特殊情况导致的异常,比如服务重启导致瞬间请求失败。
586
+ if (!timeout) {
587
+ setTimeout(() => {
588
+ this.timerRequest(pxy, true)
589
+ }, 500)
590
+ }
591
+ })
592
+
593
+ req.on('response', res => {
594
+ pxy.alive = true
595
+
596
+ res.on('error', err => {
597
+
598
+ })
599
+
600
+ res.on('data', chunk => {
601
+ pxy.alive = true
602
+ })
603
+
604
+ res.on('end', () => {
605
+ pxy.alive = true
606
+ })
607
+ })
608
+
609
+ req.end()
610
+ }
611
+
612
+ setTimer(pxys) {
613
+ let count = 0
614
+
615
+ for (let p of pxys) {
616
+ if (p.aliveCheckInterval > 0) count++
617
+ }
618
+
619
+ if (count === 0) return null
620
+
621
+ let self = this
622
+
623
+ return setInterval(() => {
624
+ for (let i = 0; i < pxys.length; i++) {
625
+ if (pxys[i].aliveCheckInterval <= 0) continue
626
+
627
+ pxys[i].intervalCount++
628
+
629
+ if (pxys[i].intervalCount >= pxys[i].aliveCheckInterval) {
630
+ pxys[i].intervalCount = 0
631
+ self.timerRequest(pxys[i])
632
+ }
633
+ }
634
+
635
+ }, 1000)
636
+
637
+ }
638
+
639
+ init(app) {
640
+ app.config.timeout = this.timeout
641
+
642
+ for (let p in this.pathTable) {
643
+ app.router.map(this.methods, p, async c => {}, '@titbit_proxy')
644
+ }
645
+
646
+ app.use(this.mid(), {pre: true, group: `titbit_proxy`})
647
+
648
+ for (let k in this.hostProxy) {
649
+
650
+ this.proxyIntervals[k] = {}
651
+
652
+ for (let p in this.hostProxy[k]) {
653
+ this.proxyIntervals[k][p] = this.setTimer(this.hostProxy[k][p])
654
+ }
655
+
656
+ }
657
+
658
+ }
659
+
660
+ }
661
+
662
+ module.exports = Proxy
@@ -0,0 +1,34 @@
1
+ 'use strict'
2
+
3
+ class RealIP {
4
+
5
+ constructor() {
6
+
7
+ }
8
+
9
+ mid() {
10
+ return async (c, next) => {
11
+ let realipstr = c.headers['x-real-ip'] || c.headers['x-forwarded-for'] || ''
12
+
13
+ if (realipstr !== '') {
14
+ if (realipstr.indexOf(',') > 0) {
15
+
16
+ c.box.realip = realipstr.split(',').filter(p => p.length > 0)
17
+
18
+ if (c.box.realip.length > 0) {
19
+ c.ip = c.box.realip[0].trim()
20
+ }
21
+ } else {
22
+ c.ip = realipstr
23
+ }
24
+ }
25
+
26
+ await next(c)
27
+
28
+ }
29
+
30
+ }
31
+
32
+ }
33
+
34
+ module.exports = RealIP