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,420 @@
1
+ /**
2
+ module bodyparser
3
+ Copyright (C) 2019.08 BraveWang
4
+ */
5
+
6
+ /**
7
+ * 有可能存在content-type,不存在filename,这种情况,其实是子multipart,需要再次解析。
8
+ * 但是此出不做处理,只做单层解析。
9
+ Content-type: multipart/form-data, boundary=AaB03x
10
+ --AaB03x
11
+ content-disposition: form-data; name="field1"
12
+ Joe Blow
13
+ --AaB03x
14
+ content-disposition: form-data; name="pics"
15
+ Content-type: multipart/mixed, boundary=BbC04y
16
+ --BbC04y
17
+ Content-disposition: attachment; filename="file1.txt"
18
+ Content-Type: text/plain
19
+ ... contents of file1.txt ...
20
+ --BbC04y
21
+ Content-disposition: attachment; filename="file2.gif"
22
+ Content-type: image/gif
23
+ Content-Transfer-Encoding: binary
24
+ ...contents of file2.gif...
25
+ --BbC04y--
26
+ --AaB03x--
27
+ */
28
+
29
+ //content-disposition,此格式定义了三个参数:inline、attachment、form-data
30
+ //attachment用于下载,form-data用于上传,多个参数用;分割,name和filename必须要有引号包含。
31
+ //一个非常操蛋的问题是,name和filename属性值都是可能包含;的,而且浏览器也不会对;进行转义。
32
+ //这种看起来不是很复杂的格式其实带来了很多问题,因为属性的值是未知的,在长期的实践中遇到过各种情况。
33
+
34
+ 'use strict';
35
+
36
+ const {fpqs} = require('./fastParseUrl.js')
37
+
38
+ class Bodyparser {
39
+
40
+ constructor(options = {}) {
41
+
42
+ this.maxFiles = 15;
43
+
44
+ this.maxMultipartHeaders = 9;
45
+
46
+ //multipart 最大消息头绝对不可能超过此值。
47
+ //考虑到一些附属的消息头比如content-length、content-encoding等加上文件名最大长度。
48
+ //一般极端情况长度不会超过1000,超过此值,则几乎可以肯定是错误的数据或恶意请求。
49
+ this.maxHeaderSize = 1024;
50
+
51
+ this.maxFormLength = 0;
52
+
53
+ this.maxFormKey = 100;
54
+
55
+ if (typeof options === 'object') {
56
+ for (let k in options) {
57
+ switch (k) {
58
+
59
+ case 'maxFiles':
60
+ case 'maxFormLength':
61
+ case 'maxFormKey':
62
+ if (typeof options[k] === 'number' && options[k] > 0) {
63
+ this[k] = options[k];
64
+ }
65
+ break;
66
+
67
+ }
68
+ }
69
+
70
+ }
71
+
72
+ this.pregUpload = /multipart.* boundary.*=/i;
73
+ this.formType = 'application/x-www-form-urlencoded';
74
+
75
+ this.methods = ['POST', 'PUT', 'PATCH', 'DELETE'];
76
+
77
+ this.multiLength = 'multipart/form-data'.length;
78
+
79
+ this.formdataLength = 'form-data'.length;
80
+ this.formdataBorder = [' ', ';', '"', "'"];
81
+ }
82
+
83
+ /*
84
+ 解析上传文件数据的函数,此函数解析的是整体的文件,
85
+ 解析过程参照HTTP/1.1协议。
86
+ */
87
+ parseUploadData(ctx) {
88
+ //let bdy = ctx.headers['content-type'].split('=')[1];
89
+ let ctype = ctx.headers['content-type'];
90
+
91
+ //multipart/form-data;boundary length is 28
92
+ let bdy = ctype.substring(ctype.indexOf('=', 28)+1);
93
+
94
+ if (!bdy) return false;
95
+
96
+ bdy = bdy.trim();
97
+
98
+ bdy = `--${bdy}`;
99
+
100
+ let bdy_crlf = `${bdy}\r\n`;
101
+ let crlf_bdy = `\r\n${bdy}`;
102
+
103
+ let file_end = 0;
104
+ let file_start = 0;
105
+
106
+ file_start = ctx.rawBody.indexOf(bdy_crlf);
107
+ if (file_start < 0) {
108
+ return ;
109
+ }
110
+ let bdycrlf_length = bdy_crlf.length;
111
+ file_start += bdycrlf_length;
112
+
113
+ let i=0; //保证不出现死循环或恶意数据产生大量无意义循环
114
+
115
+ while (i < this.maxFiles) {
116
+ file_end = ctx.rawBody.indexOf(crlf_bdy, file_start);
117
+
118
+ if (file_end <= 0) break;
119
+
120
+ this.parseSingleFile(ctx, file_start, file_end);
121
+
122
+ //\r\n--boundary\r\n
123
+ file_start = file_end + bdycrlf_length + 2;
124
+
125
+ i++;
126
+ }
127
+
128
+ }
129
+
130
+ /**
131
+ * Content-Disposition: form-data; name="NAME"; filename="FILENAME"\r\n
132
+ * Content-Type: TYPE
133
+ *
134
+ * @param {object} ctx
135
+ * @param {number} start_ind
136
+ * @param {number} end_ind
137
+ */
138
+ parseSingleFile(ctx, start_ind, end_ind) {
139
+ let header_end_ind = ctx.rawBody.indexOf('\r\n\r\n',start_ind);
140
+ let headerlength = header_end_ind - start_ind;
141
+ if (headerlength > this.maxHeaderSize) {
142
+ return false;
143
+ }
144
+ let header_data = ctx.rawBody.toString('utf8', start_ind, header_end_ind);
145
+
146
+ let data_start = header_end_ind+4;
147
+ //let data_end = end_ind;
148
+ let data_length = end_ind - 4 - header_end_ind;
149
+
150
+ //file data
151
+ //let headerlist = header_data.split('\r\n');
152
+ let headers = {};
153
+ let colon_index;
154
+ let crlf_indstart = 0;
155
+ //绝对不可能一两个字符就开始换行,第一行必须是content-disposition: xxx
156
+ let crlf_ind = header_data.indexOf('\r\n', 1);
157
+ let hcount = 0;
158
+ let hstr = '';
159
+ if (crlf_ind < 0) {
160
+ colon_index = header_data.indexOf(':');
161
+ colon_index > 0 && (
162
+ headers[ header_data.substring(0, colon_index).trim().toLowerCase() ] = header_data.substring(colon_index+1).trim()
163
+ );
164
+ } else {
165
+ while (crlf_ind > crlf_indstart && hcount < this.maxMultipartHeaders) {
166
+ hstr = header_data.substring(crlf_indstart, crlf_ind);
167
+ colon_index = hstr.indexOf(':');
168
+ hcount++;
169
+
170
+ colon_index > 0 && (
171
+ headers[ hstr.substring(0, colon_index).trim().toLowerCase() ] = hstr.substring(colon_index+1).trim()
172
+ );
173
+
174
+ crlf_indstart = crlf_ind;
175
+ if (crlf_ind < headerlength) {
176
+ crlf_ind = header_data.indexOf('\r\n', crlf_ind+3);
177
+ crlf_ind < 0 && (crlf_ind = headerlength);
178
+ }
179
+ }
180
+ }
181
+ /*
182
+ let colon_index = 0;
183
+ let hline = '';
184
+ for (let i = 0; i < headerlist.length && i < this.maxMultipartHeaders; i++) {
185
+ hline = headerlist[i];
186
+ colon_index = hline.indexOf(':');
187
+ if (colon_index < 0) continue;
188
+ headers[hline.substring(0, colon_index).trim().toLowerCase()] = hline.substring(colon_index+1).trim();
189
+ }*/
190
+
191
+ let cdps = headers['content-disposition'];
192
+ if (!cdps) return false;
193
+
194
+ let cdpobj = this.parseFormData(cdps);
195
+ if (!cdpobj) return false;
196
+
197
+ if (cdpobj.filename !== undefined) {
198
+ let file_post = {
199
+ filename: cdpobj.filename,
200
+ 'content-type': headers['content-type'] || 'application/octet-stream',
201
+ start: data_start,
202
+ end: end_ind,
203
+ length: data_length,
204
+ headers: headers,
205
+ rawHeader: header_data
206
+ };
207
+
208
+ let slash_index = file_post.filename.lastIndexOf('/');
209
+ if (slash_index >= 0) {
210
+ file_post.filename = file_post.filename.substring(slash_index+1);
211
+ }
212
+
213
+ //content-type
214
+ file_post.type = file_post['content-type'];
215
+ let upload_name = cdpobj.name || 'file';
216
+
217
+ if (ctx.files[upload_name] === undefined) {
218
+ ctx.files[upload_name] = [ file_post ];
219
+ } else {
220
+ ctx.files[upload_name].push(file_post);
221
+ }
222
+
223
+ } else {
224
+ //不支持子multipart格式
225
+ if (headers['content-type'] && headers['content-type'].indexOf('multipart/mixed') === 0) {
226
+ return false;
227
+ }
228
+
229
+ if (this.maxFormLength > 0 && data_length > this.maxFormLength) {
230
+ return false;
231
+ }
232
+
233
+ let name = cdpobj.name;
234
+
235
+ if (name) {
236
+ let name_value = ctx.rawBody.toString('utf8', data_start, end_ind);
237
+ if (ctx.body[name] === undefined) {
238
+ ctx.body[name] = name_value;
239
+
240
+ } else if (Array.isArray(ctx.body[name])) {
241
+ ctx.body[name].push(name_value);
242
+ } else {
243
+ ctx.body[name] = [ctx.body[name], name_value];
244
+ }
245
+ }
246
+ }
247
+
248
+ }
249
+
250
+ parseFormData(cdps) {
251
+ let rk = this.formdataLength
252
+ if (cdps.substring(0, rk) !== 'form-data') return false;
253
+ while (cdps[rk] === ';' || cdps[rk] === ' ') rk++
254
+
255
+ let cdpobj = {}
256
+ let cindex = 0
257
+ let statestr = cdps.substring(rk)
258
+ let cstart=0, i, k, q=''
259
+ let kname = ''
260
+ let eq_break = false
261
+ let real_index = 0
262
+
263
+ let state_length = statestr.length
264
+
265
+ while (cindex < state_length) {
266
+ //cindex = statestr.indexOf('=', cindex)
267
+ //if (cindex < 0) break
268
+ eq_break = false
269
+ real_index = 0
270
+ while (cindex < state_length) {
271
+ switch (statestr[cindex]) {
272
+ case ';':
273
+ cindex++
274
+ while(statestr[cindex] === ' ' && cindex < state_length) cindex++
275
+ cstart = cindex
276
+ break
277
+
278
+ //cstart是从一个非空字符开始的
279
+ case ' ':
280
+ ;(real_index <= 0) && (real_index = cindex)
281
+ cindex++
282
+ break
283
+
284
+ case '=':
285
+ eq_break = true
286
+ break
287
+
288
+ default:
289
+ real_index > 0 && (real_index = 0)
290
+ cindex++
291
+ break
292
+ }
293
+
294
+ if (eq_break) break
295
+ }
296
+
297
+ if (cindex >= state_length) break
298
+
299
+ i = cindex + 1
300
+ while (statestr[i] === ' ' && i < state_length) i++
301
+
302
+ kname = statestr.substring(cstart, real_index || cindex)
303
+ //kname = statestr.substring(cstart, cindex).trimEnd()
304
+ if (!kname) {cindex=i;cstart=cindex;continue}
305
+
306
+ if (i >= state_length) {
307
+ //说明还是有=,但是后续没有赋值
308
+ cdpobj[kname] = ''
309
+ break
310
+ }
311
+
312
+ q = statestr[i]
313
+
314
+ if (q === ';') {
315
+ k = i
316
+ //name= 说明没有数据的值
317
+ cdpobj[kname] = ''
318
+ k++
319
+ } else if (q) {
320
+ if (q === '"' || q === "'") {
321
+ i++
322
+ } else {q = ''}
323
+ k = i
324
+ while (k < state_length) {
325
+ //有可能就是携带\\,引号应该被转义。
326
+ //if (statestr[k] === '\\') k+=2
327
+
328
+ if ( (q && statestr[k] === q)
329
+ || (!q && (statestr[k] === ';' || statestr[k] === ' ')) )
330
+ {
331
+ cdpobj[kname] = statestr.substring(i, k)
332
+ k++
333
+ break
334
+ }
335
+ k++
336
+ }
337
+ //如果到了字符串末尾但是还没有设置值
338
+ if (!cdpobj[kname] && k === state_length) {
339
+ cdpobj[kname] = statestr.substring(i, k)
340
+ break
341
+ }
342
+ }/* else if (!q) {
343
+ break
344
+ } */
345
+
346
+ cindex = k
347
+ while (cindex < state_length
348
+ && (statestr[cindex] === ' '
349
+ || statestr[cindex] === ';'
350
+ || statestr[cindex] === '"'
351
+ || statestr[cindex] === "'"
352
+ )
353
+ ) { cindex++ }
354
+
355
+ cstart = cindex
356
+ }
357
+
358
+ return cdpobj
359
+ }
360
+
361
+ checkUploadHeader(typestr) {
362
+ if (typestr.indexOf('multipart/form-data') === 0
363
+ && (typestr.indexOf('boundary=', this.multiLength) > 0
364
+ || typestr.indexOf('boundary =', this.multiLength) > 0))
365
+ {
366
+ return true;
367
+ }
368
+
369
+ return false;
370
+ }
371
+
372
+ mid() {
373
+ let self = this;
374
+ let json_length = ('application/json').length;
375
+ let json_next = [' ', ';'];
376
+
377
+ return async (ctx, next) => {
378
+ let m1 = ctx.method[0]
379
+
380
+ if ((m1 === 'P' || m1 === 'D') && ctx.rawBody && (ctx.rawBody instanceof Buffer || typeof ctx.rawBody === 'string'))
381
+ {
382
+ if (ctx.headers['content-type'] === undefined) {
383
+ ctx.headers['content-type'] = '';
384
+ }
385
+
386
+ let ctype = ctx.headers['content-type'];
387
+
388
+ if ( self.checkUploadHeader(ctype) ) {
389
+
390
+ ctx.isUpload = true;
391
+ self.parseUploadData(ctx, self.maxFiles);
392
+
393
+ } else if (ctype && ctype.indexOf(self.formType) >= 0) {
394
+
395
+ //autoDecode = true
396
+ fpqs(ctx.rawBody.toString('utf8'), ctx.body, true, self.maxFormKey);
397
+
398
+ } else if (ctype.indexOf('text/') === 0) {
399
+ ctx.body = ctx.rawBody.toString('utf8');
400
+
401
+ } else if (ctype === 'application/json'
402
+ || (ctype.indexOf('application/json') === 0 && json_next.indexOf(ctype[json_length])>=0 ) )
403
+ {
404
+ //有可能会传递application/jsonb等其他json*的格式。
405
+ try {
406
+ ctx.body = JSON.parse( ctx.rawBody.toString('utf8') );
407
+ } catch (err) {
408
+ return ctx.status(400).send('bad json data');
409
+ }
410
+ } else {
411
+ ctx.body = ctx.rawBody;
412
+ }
413
+ }
414
+
415
+ await next(ctx);
416
+ };
417
+ }
418
+ }
419
+
420
+ module.exports = Bodyparser;
@@ -0,0 +1,125 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ module connfilter
5
+ Copyright (C) 2019.08 BraveWang
6
+ */
7
+ /**
8
+ * 请求过滤模块,此模块要挂载到connection事件上。
9
+ * @param {object} options 选项值参考:
10
+ * - unitTime {number}
11
+ * - maxConn {number}
12
+ * - deny {array}
13
+ * - allow {array}
14
+ * rundata是运行时数据,这个数据需要实时更新到负载监控,所以可以通过传递一个对象指向全局应用。
15
+ *
16
+ */
17
+
18
+ let connfilter = function (limit, rundata) {
19
+
20
+ if (! (this instanceof connfilter)) {
21
+ return new connfilter(limit, rundata);
22
+ }
23
+
24
+ let the = this;
25
+
26
+ this.iptable = new Map();
27
+
28
+ /**
29
+ * 请求过滤函数。
30
+ * @param {object} sock 当前请求的socket实例。
31
+ */
32
+ this.callback = (sock) => {
33
+ rundata.conn++;
34
+ sock.on('close', (e) => {
35
+ rundata.conn--;
36
+ });
37
+
38
+ let remote_ip = sock.remoteAddress;
39
+ /**
40
+ * 注意,这需要你指明所运行的模式是IPv4,也就是要指明host为'0.0.0.0'或是其他,
41
+ * 否则会默认使用IPv6的地址,这时候,remoteAddress显示::ffff:127.0.0.1这样的字符串。
42
+ * */
43
+ if (limit.deny) {
44
+ if ( (limit.deny_type === 's' && limit.deny.has(remote_ip))
45
+ || (limit.deny_type === 'f' && limit.deny(remote_ip)) )
46
+ {
47
+ sock.destroy();
48
+ return false;
49
+ }
50
+ }
51
+
52
+ //检测是否超过最大连接数限制。
53
+ if (limit.maxConn > 0 && rundata.conn > limit.maxConn) {
54
+ sock.destroy();
55
+ return false;
56
+ }
57
+
58
+ //如果开启了单元时间内单个IP最大访问次数限制则检测是否合法。
59
+ let ipcount;
60
+
61
+ if (limit.maxIPRequest > 0 &&
62
+ !( (limit.allow_type === 's' && limit.allow && limit.allow.has(remote_ip))
63
+ || (limit.allow_type === 'f' && limit.allow(remote_ip)) ) )
64
+ {
65
+ let tm = Date.now();
66
+
67
+ if (the.iptable.has(remote_ip)) {
68
+
69
+ ipcount = this.iptable.get(remote_ip);
70
+
71
+ if (tm - ipcount.time > limit.unitTime) {
72
+ ipcount.count = 1;
73
+ ipcount.time = tm;
74
+ this.iptable.delete(remote_ip);
75
+ this.iptable.set(remote_ip, ipcount);
76
+ } else {
77
+ if (ipcount.count >= limit.maxIPRequest) {
78
+ sock.destroy();
79
+ return false;
80
+ } else {
81
+ ipcount.count++;
82
+ }
83
+ }
84
+
85
+ } else if (the.iptable.size >= limit.maxIPCache) {
86
+ /**
87
+ * 如果已经超过IP最大缓存数量限制则关闭连接,这种情况在极端情况下会出现。
88
+ * 不过最大缓存数量不能低于最大连接数。否则并发支持会受限制。
89
+ * */
90
+ sock.destroy();
91
+ return false;
92
+
93
+ } else {
94
+ the.iptable.set(remote_ip, {count: 1, time: tm});
95
+ }
96
+ }
97
+
98
+ return true;
99
+ };
100
+
101
+ this.intervalId = null;
102
+
103
+ /**
104
+ * 限制IP请求次数的定时器。
105
+ * 这意味着会定期进行一次大清空。
106
+ */
107
+ if (limit.maxIPRequest > 0) {
108
+ this.intervalId = setInterval(() => {
109
+ if (the.iptable.size >= limit.maxIPCache) {
110
+ the.iptable.clear();
111
+ } else {
112
+ let tm = Date.now();
113
+
114
+ for (let [k, v] of the.iptable) {
115
+ if ( (tm - v.time - 5000) < limit.unitTime) break;
116
+ the.iptable.delete(k);
117
+ }
118
+ }
119
+
120
+ }, limit.unitTime + 5000);
121
+ }
122
+
123
+ }
124
+
125
+ module.exports = connfilter;
@@ -0,0 +1,166 @@
1
+ 'use strict'
2
+
3
+ const ext = require('./ext.js')
4
+ const moveFile = require('./movefile.js')
5
+
6
+ class Context {
7
+
8
+ constructor () {
9
+
10
+ this.version = '1.1'
11
+
12
+ //主版本号
13
+ this.major = 1
14
+
15
+ this.maxBody = 0
16
+
17
+ this.method = ''
18
+
19
+ this.host = ''
20
+
21
+ this.protocol = ''
22
+
23
+ this.ip = ''
24
+
25
+ //实际的访问路径
26
+ this.path = ''
27
+
28
+ this.name = ''
29
+
30
+ this.headers = {}
31
+
32
+ //实际执行请求的路径
33
+ this.routepath = ''
34
+
35
+ this.param = {}
36
+
37
+ this.query = {}
38
+
39
+ this.body = {}
40
+
41
+ this.isUpload = false
42
+
43
+ this.group = ''
44
+
45
+ this.rawBody = ''
46
+
47
+ this.bodyLength = 0
48
+
49
+ this.files = {}
50
+
51
+ this.requestCall = null
52
+
53
+ this.ext = ext
54
+
55
+ this.port = 0
56
+
57
+ this.data = ''
58
+ this.dataEncoding = 'utf8'
59
+
60
+ this.req = null
61
+ this.res = null
62
+
63
+ this.box = {}
64
+
65
+ this.service = null
66
+
67
+ this.user = null
68
+ }
69
+
70
+ json(data) {
71
+ return this.setHeader('content-type', 'application/json').to(data)
72
+ }
73
+
74
+ text(data, encoding='utf-8') {
75
+ return this.setHeader('content-type', `text/plain;charset=${encoding}`).to(data)
76
+ }
77
+
78
+ html(data, encoding='utf-8') {
79
+ return this.setHeader('content-type', `text/html;charset=${encoding}`).to(data)
80
+ }
81
+
82
+ to(d) {
83
+ this.data = d
84
+ }
85
+
86
+ getFile(name, ind = 0) {
87
+ if (ind < 0) {
88
+ return this.files[name] || []
89
+ }
90
+
91
+ if (this.files[name] === undefined) {
92
+ return null
93
+ }
94
+
95
+ if (ind >= this.files[name].length) {
96
+ return null
97
+ }
98
+
99
+ return this.files[name][ind]
100
+ }
101
+
102
+ setHeader(name, val) {
103
+ this.res.setHeader(name, val)
104
+ return this
105
+ }
106
+
107
+ sendHeader() {
108
+ !this.res
109
+ && !this.res.headersSent
110
+ && this.res.writeHead(this.res.statusCode)
111
+
112
+ return this
113
+ }
114
+
115
+ status(stcode = null) {
116
+ if (stcode === null) {
117
+ return this.res.statusCode
118
+ }
119
+
120
+ if (this.res) {
121
+ this.res.statusCode = stcode
122
+ }
123
+
124
+ return this
125
+ }
126
+
127
+ moveFile(upf, target) {
128
+ return moveFile(this, upf, target)
129
+ }
130
+
131
+ /**
132
+ * @param {(fs.ReadStream|string)} filename
133
+ * @param {object} options
134
+ * */
135
+ pipe(filename, options={}) {
136
+ return ext.pipe(filename, this.res, options)
137
+ }
138
+
139
+ pipeJson(filename) {
140
+ return this.setHeader('content-type', 'application/json').pipe(filename)
141
+ }
142
+
143
+ pipeText(filename, encoding='utf-8') {
144
+ return this.setHeader('content-type', `text/plain;charset=${encoding}`).pipe(filename)
145
+ }
146
+
147
+ pipeHtml(filename, encoding='utf-8') {
148
+ return this.setHeader('content-type', `text/html;charset=${encoding}`).pipe(filename)
149
+ }
150
+
151
+ removeHeader(name) {
152
+ this.res.removeHeader(name)
153
+ return this
154
+ }
155
+
156
+ write(data) {
157
+ this.res.write(data)
158
+ return this
159
+ }
160
+
161
+ }
162
+
163
+ Context.prototype.oo = Context.prototype.to
164
+ Context.prototype.ok = Context.prototype.to
165
+
166
+ module.exports = Context