topbit 2.0.0 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.cn.md +132 -177
- package/README.md +801 -592
- package/bin/new-ctl.js +7 -3
- package/demo/allow.js +13 -13
- package/demo/controller/api.js +15 -0
- package/demo/extends.js +5 -0
- package/demo/http2.js +34 -0
- package/demo/http2_proxy_backend.js +45 -0
- package/demo/http2proxy.js +48 -0
- package/demo/http_proxy_backend.js +44 -0
- package/demo/httpproxy.js +47 -0
- package/demo/loader.js +27 -0
- package/demo/log.js +1 -1
- package/demo/memlimit.js +1 -1
- package/demo/min.js +1 -1
- package/demo/serv.js +1 -1
- package/images/topbit-middleware.webp +0 -0
- package/images/topbit.png +0 -0
- package/package.json +7 -6
- package/src/_loadExtends.js +21 -0
- package/src/bodyparser.js +1 -1
- package/src/context1.js +19 -19
- package/src/context2.js +11 -8
- package/src/ctxpool.js +1 -0
- package/src/extends/Http2Pool.js +365 -0
- package/src/extends/__randstring.js +24 -0
- package/src/extends/cookie.js +44 -0
- package/src/extends/cors.js +334 -0
- package/src/extends/errorlog.js +252 -0
- package/src/extends/http2limit.js +126 -0
- package/src/extends/http2proxy.js +691 -0
- package/src/extends/jwt.js +217 -0
- package/src/extends/mixlogger.js +63 -0
- package/src/extends/paramcheck.js +266 -0
- package/src/extends/proxy.js +662 -0
- package/src/extends/realip.js +34 -0
- package/src/extends/referer.js +68 -0
- package/src/extends/resource.js +398 -0
- package/src/extends/session.js +174 -0
- package/src/extends/setfinal.js +50 -0
- package/src/extends/sni.js +48 -0
- package/src/extends/sse.js +293 -0
- package/src/extends/timing.js +111 -0
- package/src/extends/tofile.js +123 -0
- package/src/http1.js +15 -16
- package/src/http2.js +5 -5
- package/src/httpc.js +3 -3
- package/src/lib/npargv.js +354 -0
- package/src/lib/zipdata.js +45 -0
- package/src/middleware1.js +15 -16
- package/src/middleware2.js +4 -9
- package/src/token/token.js +4 -5
- package/src/topbit.js +13 -11
- package/test/{test-helper.js → test-ext.js} +1 -1
- package/test/test-route.js +1 -1
- package/cache/allow.js +0 -130
- package/cache/errserv.js +0 -45
- package/cache/minserv.js +0 -167
- package/cache/router.js +0 -84
- package/cache/servsock.js +0 -286
- package/cache/sni.js +0 -66
- package/images/titbit-middleware.png +0 -0
- package/images/titbit.png +0 -0
- package/tmp/buff-code +0 -134
- package/tmp/devplan +0 -9
- package/tmp/evt-test.js +0 -34
- package/tmp/fastParseUrl.js +0 -302
- package/tmp/router-rule.js +0 -559
- package/tmp/test-cdps.js +0 -122
- package/tmp/titbit.js +0 -1286
- /package/{cache/rsa → demo/cert}/localhost-cert.pem +0 -0
- /package/{cache/rsa → demo/cert}/localhost-privkey.pem +0 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('node:fs')
|
|
4
|
+
const tls = require('node:tls')
|
|
5
|
+
|
|
6
|
+
class SNI {
|
|
7
|
+
constructor(certs = {}) {
|
|
8
|
+
this.certs = {}
|
|
9
|
+
|
|
10
|
+
if (typeof certs !== 'object') {
|
|
11
|
+
throw new Error('必须传递key-value形式的配置,key是域名,value是证书路径')
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
let t = '';
|
|
15
|
+
for (let h in certs) {
|
|
16
|
+
t = certs[h]
|
|
17
|
+
|
|
18
|
+
if (t.key === undefined || t.cert === undefined) {
|
|
19
|
+
console.error(`${h} 没有设置key和cert`)
|
|
20
|
+
continue
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
try {
|
|
24
|
+
this.certs[h] = {
|
|
25
|
+
key : fs.readFileSync(t.key),
|
|
26
|
+
cert : fs.readFileSync(t.cert)
|
|
27
|
+
}
|
|
28
|
+
} catch (err) {
|
|
29
|
+
console.error(h, err.message)
|
|
30
|
+
continue
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
callback() {
|
|
37
|
+
return (servername, cb) => {
|
|
38
|
+
return cb(null, tls.createSecureContext(this.certs[servername]))
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
init(app) {
|
|
43
|
+
app.config.server.SNICallback = this.callback()
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
module.exports = SNI
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
function fmtMessage(msg, withEnd=true) {
|
|
4
|
+
let text = ''
|
|
5
|
+
|
|
6
|
+
if (Array.isArray(msg)) {
|
|
7
|
+
let textarr = []
|
|
8
|
+
|
|
9
|
+
for (let m of msg) {
|
|
10
|
+
text = fmtMessage(m, false)
|
|
11
|
+
text && textarr.push(text)
|
|
12
|
+
}
|
|
13
|
+
if (textarr.length === 0) return ''
|
|
14
|
+
|
|
15
|
+
return textarr.join('\n\n') + '\n\n'
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (msg === null || msg === undefined || msg === '') {
|
|
19
|
+
return ''
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
let typ = typeof msg
|
|
23
|
+
|
|
24
|
+
if (typ === 'number') {
|
|
25
|
+
msg = `${msg}`
|
|
26
|
+
typ = 'string'
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (typ === 'object') {
|
|
30
|
+
if (!(msg.event || msg.data || msg.retry || msg.id)) return ''
|
|
31
|
+
|
|
32
|
+
if (msg.data === undefined) msg.data = ''
|
|
33
|
+
|
|
34
|
+
let datatype = typeof msg.data
|
|
35
|
+
|
|
36
|
+
switch (datatype) {
|
|
37
|
+
case 'number':
|
|
38
|
+
msg.data = msg.data.toString()
|
|
39
|
+
break
|
|
40
|
+
case 'object':
|
|
41
|
+
msg.data = JSON.stringify(msg.data).replaceAll('\n', '%0A')
|
|
42
|
+
break
|
|
43
|
+
case 'function':
|
|
44
|
+
msg.data = msg.data.toString().replaceAll('\n', '%0A')
|
|
45
|
+
break
|
|
46
|
+
case 'string':
|
|
47
|
+
msg.data = msg.data.replaceAll('\n', '%0A')
|
|
48
|
+
break
|
|
49
|
+
|
|
50
|
+
default:
|
|
51
|
+
msg.data = `${msg.data}`
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
text = `event: ${msg.event || 'message'}\ndata: ${msg.data}\n`
|
|
55
|
+
if (msg.id) {
|
|
56
|
+
text += `id: ${msg.id}\n`
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (msg.retry) {
|
|
60
|
+
text += `retry: ${msg.retry}\n`
|
|
61
|
+
}
|
|
62
|
+
} else if (typ === 'string') {
|
|
63
|
+
if (msg[0] !== ':') {
|
|
64
|
+
text = `data: ${msg.replaceAll('\n', '%0A')}\n`
|
|
65
|
+
} else {
|
|
66
|
+
text = msg.replaceAll('\n', '%0A') + '\n'
|
|
67
|
+
}
|
|
68
|
+
} else if (typ === 'function') {
|
|
69
|
+
text = `event: function\ndata: ${msg.toString().replaceAll('\n', '%0A')}\n`
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (withEnd) {
|
|
73
|
+
text += `\n\n`
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return text
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
class SSE {
|
|
80
|
+
|
|
81
|
+
constructor(options = {}) {
|
|
82
|
+
this.timer = null
|
|
83
|
+
this.handle = null
|
|
84
|
+
this.timeSlice = 1000
|
|
85
|
+
|
|
86
|
+
this.retry = 0
|
|
87
|
+
this.timeout = 15000
|
|
88
|
+
|
|
89
|
+
this.fmtMsg = fmtMessage
|
|
90
|
+
|
|
91
|
+
this.handleClose = null
|
|
92
|
+
this.handleError = null
|
|
93
|
+
|
|
94
|
+
this.mode = 'timer'
|
|
95
|
+
|
|
96
|
+
for (let k in options) {
|
|
97
|
+
switch (k) {
|
|
98
|
+
case 'timeSlice':
|
|
99
|
+
case 'timeout':
|
|
100
|
+
case 'retry':
|
|
101
|
+
if (typeof options[k] === 'number' && options[k] >= 0) {
|
|
102
|
+
this[k] = options[k]
|
|
103
|
+
}
|
|
104
|
+
break
|
|
105
|
+
|
|
106
|
+
case 'handle':
|
|
107
|
+
case 'handleClose':
|
|
108
|
+
case 'handleError':
|
|
109
|
+
if (typeof options[k] === 'function') this[k] = optionsp[k]
|
|
110
|
+
break
|
|
111
|
+
|
|
112
|
+
case 'mode':
|
|
113
|
+
if (['timer', 'generator', 'yield'].indexOf(options[k]) >= 0)
|
|
114
|
+
this[k] = options[k]
|
|
115
|
+
break
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
async interval(ctx) {
|
|
122
|
+
if (!this.handle || typeof this.handle !== 'function') {
|
|
123
|
+
throw new Error('请设置handle为要处理的函数,然后再次运行。')
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
let self = this
|
|
127
|
+
|
|
128
|
+
if (self.timer) {
|
|
129
|
+
clearInterval(this.timer)
|
|
130
|
+
this.timer = null
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return new Promise((rv, rj) => {
|
|
134
|
+
ctx.res.on('error', err => {
|
|
135
|
+
clearInterval(self.timer)
|
|
136
|
+
self.timer = null
|
|
137
|
+
rj(err)
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
ctx.res.on('close', () => {
|
|
141
|
+
clearInterval(self.timer)
|
|
142
|
+
self.timer = null
|
|
143
|
+
rv('sse closed')
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
self.timer = setInterval(async () => {
|
|
147
|
+
ctx.box.sseCount += 1
|
|
148
|
+
if (self.timeout > 0 && ctx.box.sseCount * self.timeSlice > self.timeout) {
|
|
149
|
+
if (self.retry > 0) {
|
|
150
|
+
ctx.sendmsg({data: 'timeout', retry: self.retry})
|
|
151
|
+
}
|
|
152
|
+
return ctx.res.end()
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
try {
|
|
156
|
+
await self.handle(ctx)
|
|
157
|
+
} catch (err) {
|
|
158
|
+
clearInterval(self.timer)
|
|
159
|
+
self.timer = null
|
|
160
|
+
rj(err)
|
|
161
|
+
}
|
|
162
|
+
}, self.timeSlice || 1000)
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
async moment(t) {
|
|
168
|
+
return new Promise((rv) => {
|
|
169
|
+
setTimeout(rv, t)
|
|
170
|
+
})
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
gn(ctx) {
|
|
174
|
+
if (!this.handle || typeof this.handle !== 'function') {
|
|
175
|
+
throw new Error('请设置handle为要处理的函数,然后再次运行。')
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
let self = this
|
|
179
|
+
|
|
180
|
+
ctx.box.sseNext = true
|
|
181
|
+
|
|
182
|
+
ctx.res.on('error', err => {
|
|
183
|
+
ctx.box.sseNext = false
|
|
184
|
+
ctx.box.sseError = err
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
ctx.res.on('close', () => {
|
|
188
|
+
ctx.box.sseNext = false
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
return async function * () {
|
|
192
|
+
while (true) {
|
|
193
|
+
let tm = Date.now()
|
|
194
|
+
|
|
195
|
+
if (self.timeout > 0 && (tm - ctx.box.sseTime) > self.timeout) {
|
|
196
|
+
if (self.retry > 0) {
|
|
197
|
+
ctx.sendmsg({data: 'timeout', retry: self.retry})
|
|
198
|
+
}
|
|
199
|
+
return
|
|
200
|
+
}
|
|
201
|
+
ctx.box.sseCount += 1
|
|
202
|
+
try {
|
|
203
|
+
await self.handle(ctx)
|
|
204
|
+
} catch (err) {
|
|
205
|
+
ctx.box.sseNext = false
|
|
206
|
+
ctx.box.sseError = err
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (ctx.box.sseNext) {
|
|
210
|
+
yield tm
|
|
211
|
+
} else {
|
|
212
|
+
break
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
async rungn(ctx) {
|
|
220
|
+
let yn = this.gn(ctx)
|
|
221
|
+
let r
|
|
222
|
+
let y = yn()
|
|
223
|
+
|
|
224
|
+
while (true) {
|
|
225
|
+
r = await y.next()
|
|
226
|
+
|
|
227
|
+
if (r.done) break
|
|
228
|
+
|
|
229
|
+
if (this.timeSlice > 0) await this.moment(this.timeSlice)
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
if (ctx.box.sseError) {
|
|
233
|
+
if (this.handleError && typeof this.handleError === 'function')
|
|
234
|
+
this.handleError(ctx.box.sseError, ctx)
|
|
235
|
+
else throw ctx.box.sseError
|
|
236
|
+
} else if (this.handleClose && typeof this.handleClose === 'function') {
|
|
237
|
+
this.handleClose(ctx)
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
autoRun(ctx) {
|
|
242
|
+
if (this.mode === 'timer') {
|
|
243
|
+
return this.interval(ctx)
|
|
244
|
+
.then(data => {
|
|
245
|
+
if (typeof this.handleClose === 'function') this.handleClose(ctx)
|
|
246
|
+
})
|
|
247
|
+
.catch(err => {
|
|
248
|
+
if (typeof this.handleError === 'function') {
|
|
249
|
+
this.handleError(err, ctx)
|
|
250
|
+
} else {
|
|
251
|
+
throw err
|
|
252
|
+
}
|
|
253
|
+
})
|
|
254
|
+
} else {
|
|
255
|
+
ctx.box.sseTime = Date.now()
|
|
256
|
+
return this.rungn(ctx)
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
mid() {
|
|
261
|
+
let self = this
|
|
262
|
+
|
|
263
|
+
return async (ctx, next) => {
|
|
264
|
+
ctx.setHeader('content-type', 'text/event-stream;charset=utf-8').sendHeader()
|
|
265
|
+
ctx.sse = self
|
|
266
|
+
//用于统计是否超时断开并发送retry
|
|
267
|
+
ctx.box.sseCount = 0
|
|
268
|
+
if (!ctx.sendmsg || typeof ctx.sendmsg !== 'function') {
|
|
269
|
+
ctx.sendmsg = (msg, cb=undefined) => {
|
|
270
|
+
let emsg = fmtMessage(msg)
|
|
271
|
+
if (emsg) return ctx.res.write(emsg, cb)
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
ctx.res.setTimeout(self.timeout, () => {
|
|
276
|
+
if (ctx.res.writable) ctx.res.end()
|
|
277
|
+
})
|
|
278
|
+
|
|
279
|
+
//http2协议需要设置session超时,否则如果默认的服务超时设置比self.timeout短,会导致无法收到消息。
|
|
280
|
+
if (ctx.major == 2 && ctx.res.session && ctx.res.session.listenerCount) {
|
|
281
|
+
//http2的session会保持连接,如果stream超时关闭后,session可能会维持连接,此时有可能会复用session。
|
|
282
|
+
if (ctx.res.session.listenerCount('timeout') < 2) {
|
|
283
|
+
ctx.res.session.setTimeout(self.timeout, () => {})
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
await self.autoRun(ctx)
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
module.exports = SSE
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const fs = require('node:fs')
|
|
4
|
+
|
|
5
|
+
class Timing {
|
|
6
|
+
|
|
7
|
+
constructor(options = {}) {
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* route保存所有路由的最近耗时记录。
|
|
11
|
+
* maxLimit是保存记录上限。
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
this.route = {
|
|
15
|
+
GET : new Map(),
|
|
16
|
+
POST : new Map(),
|
|
17
|
+
PUT : new Map(),
|
|
18
|
+
DELETE : new Map(),
|
|
19
|
+
OPTIONS : new Map(),
|
|
20
|
+
PATCH : new Map()
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
this.maxLimit = 100
|
|
24
|
+
|
|
25
|
+
this.test = false
|
|
26
|
+
|
|
27
|
+
this.logfile = ''
|
|
28
|
+
|
|
29
|
+
if (typeof options !== 'object') {
|
|
30
|
+
options = {}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
for (let k in options) {
|
|
34
|
+
switch (k) {
|
|
35
|
+
case 'test':
|
|
36
|
+
this.test = options[k]
|
|
37
|
+
break
|
|
38
|
+
|
|
39
|
+
case 'logfile':
|
|
40
|
+
this.logfile = options[k]
|
|
41
|
+
break
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
mid() {
|
|
48
|
+
let self = this
|
|
49
|
+
|
|
50
|
+
return async (c, next) => {
|
|
51
|
+
|
|
52
|
+
let start_time = Date.now()
|
|
53
|
+
|
|
54
|
+
await next()
|
|
55
|
+
|
|
56
|
+
let end_time = Date.now()
|
|
57
|
+
|
|
58
|
+
let time_consume = end_time - start_time
|
|
59
|
+
|
|
60
|
+
if (self.route[c.method] !== undefined) {
|
|
61
|
+
if (!self.route[c.method].has(c.routepath)) {
|
|
62
|
+
self.route[c.method].set(c.routepath, {
|
|
63
|
+
total : 1,
|
|
64
|
+
consume: time_consume
|
|
65
|
+
})
|
|
66
|
+
} else {
|
|
67
|
+
let t = self.route[c.method].get(c.routepath)
|
|
68
|
+
if (t.total > self.maxLimit) {
|
|
69
|
+
t.total = 0
|
|
70
|
+
t.consume = 0
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
t.total += 1
|
|
74
|
+
t.consume += time_consume
|
|
75
|
+
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
let last = self.route[c.method].get(c.routepath)
|
|
81
|
+
|
|
82
|
+
if (self.test) {
|
|
83
|
+
|
|
84
|
+
console.log(c.method, c.path, time_consume, 'ms')
|
|
85
|
+
|
|
86
|
+
console.log(c.method, c.path,'\n',
|
|
87
|
+
` Count: ${last.total} Total: ${last.consume} ms `,
|
|
88
|
+
`Average: ${(last.consume/last.total).toFixed(2)} ms`
|
|
89
|
+
)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (self.logfile) {
|
|
93
|
+
|
|
94
|
+
let log_text = `${c.method} - ${c.path} - count: ${last.total}, `
|
|
95
|
+
+ `total: ${last.consume}ms, `
|
|
96
|
+
+ `average: ${(last.consume/last.total).toFixed(2)}ms\n`
|
|
97
|
+
|
|
98
|
+
fs.writeFile(self.logfile, log_text, {flag: 'a+'}, err => {
|
|
99
|
+
if (self.test && err) {
|
|
100
|
+
console.error(err)
|
|
101
|
+
}
|
|
102
|
+
})
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
module.exports = Timing
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('node:fs')
|
|
4
|
+
const crypto = require('node:crypto')
|
|
5
|
+
|
|
6
|
+
function extName(fname) {
|
|
7
|
+
let ind = fname.length - 2
|
|
8
|
+
|
|
9
|
+
while (ind > 0 && fname[ind] !== '.') {
|
|
10
|
+
ind -= 1
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
if (ind <= 0) return ''
|
|
14
|
+
|
|
15
|
+
return fname.substring(ind)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
let fmtbits = (n) => {
|
|
19
|
+
return n < 10 ? `0${n}` : n
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function makeName(filename = '') {
|
|
23
|
+
let tm = new Date()
|
|
24
|
+
|
|
25
|
+
let orgname = `${tm.getFullYear()}-${fmtbits(tm.getMonth()+1)}-${fmtbits(tm.getDate())}_`
|
|
26
|
+
+ `${fmtbits(tm.getHours())}-${fmtbits(tm.getMinutes())}-${fmtbits(tm.getSeconds())}`
|
|
27
|
+
+ `_${tm.getMilliseconds()}${parseInt(Math.random() * 1000) + 1}${parseInt(Math.random() * 100000) + 10000}`
|
|
28
|
+
|
|
29
|
+
if (filename) return (orgname + extName(filename))
|
|
30
|
+
|
|
31
|
+
return orgname
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async function moveFile(target, filename = null) {
|
|
35
|
+
if (!this || !this.rawBody) return false
|
|
36
|
+
|
|
37
|
+
if (!filename) filename = makeName(this.filename || '')
|
|
38
|
+
|
|
39
|
+
let ds = ''
|
|
40
|
+
if (target[target.length-1] !== '/') ds = '/'
|
|
41
|
+
|
|
42
|
+
let pathfile = `${target}${ds}${filename}`
|
|
43
|
+
|
|
44
|
+
let fd = await new Promise((rv, rj) => {
|
|
45
|
+
fs.open(pathfile, 'w+', 0o644, (err, fd) => {
|
|
46
|
+
if (err) {
|
|
47
|
+
rj(err)
|
|
48
|
+
} else {
|
|
49
|
+
rv(fd)
|
|
50
|
+
}
|
|
51
|
+
})
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
return new Promise((rv, rj) => {
|
|
55
|
+
fs.write(fd, this.rawBody, this.start, this.length,
|
|
56
|
+
(err, bytesWritten, buffer) => {
|
|
57
|
+
if (err) {
|
|
58
|
+
rj(err)
|
|
59
|
+
} else {
|
|
60
|
+
rv(filename)
|
|
61
|
+
}
|
|
62
|
+
})
|
|
63
|
+
})
|
|
64
|
+
.finally(() => {
|
|
65
|
+
fs.close(fd, (err) => {})
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function getFile(name, ind=0) {
|
|
71
|
+
if (!this || !this.files) return null
|
|
72
|
+
|
|
73
|
+
if (this.files[name] === undefined) {
|
|
74
|
+
return ind < 0 ? [] : null
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (ind >= this.files[name].length) {
|
|
78
|
+
return null
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
let flist = this.files[name]
|
|
82
|
+
|
|
83
|
+
if (ind < 0) {
|
|
84
|
+
for (let i = 0; i < flist.length; i++) {
|
|
85
|
+
if (flist[i].toFile === undefined) {
|
|
86
|
+
flist[i].rawBody = this.rawBody
|
|
87
|
+
flist[i].toFile = moveFile
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return flist
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (flist[ind].toFile === undefined) {
|
|
94
|
+
flist[ind].rawBody = this.rawBody
|
|
95
|
+
flist[ind].toFile = moveFile
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return flist[ind]
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
class ToFile {
|
|
103
|
+
|
|
104
|
+
constructor() {
|
|
105
|
+
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
mid() {
|
|
109
|
+
let self = this
|
|
110
|
+
return async (c, next) => {
|
|
111
|
+
if (!c.isUpload) {
|
|
112
|
+
return await next()
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
c.getFile = getFile
|
|
116
|
+
await next()
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
module.exports = ToFile
|
package/src/http1.js
CHANGED
|
@@ -122,9 +122,8 @@ class Http1 {
|
|
|
122
122
|
ctx.ip = remote_ip;
|
|
123
123
|
|
|
124
124
|
ctx.port = req.socket.remotePort;
|
|
125
|
-
ctx.
|
|
126
|
-
ctx.
|
|
127
|
-
ctx.reply = ctx.response;
|
|
125
|
+
ctx.req = req;
|
|
126
|
+
ctx.res = res;
|
|
128
127
|
|
|
129
128
|
ctx.path = urlobj.path;
|
|
130
129
|
ctx.query = urlobj.query;
|
|
@@ -147,7 +146,7 @@ class Http1 {
|
|
|
147
146
|
mid() {
|
|
148
147
|
let self = this;
|
|
149
148
|
|
|
150
|
-
let noBodyMethods =
|
|
149
|
+
let noBodyMethods = Object.create(null);
|
|
151
150
|
|
|
152
151
|
['GET','OPTIONS','HEAD','TRACE'].forEach(a => {
|
|
153
152
|
noBodyMethods[a] = true;
|
|
@@ -162,24 +161,24 @@ class Http1 {
|
|
|
162
161
|
//客户端和服务端解析不会允许非法method
|
|
163
162
|
if ( noBodyMethods[ctx.method] ) {
|
|
164
163
|
//实际上这个回调函数不会执行,因为会立即触发end事件,此处可以保证非法的请求也可以提交数据。
|
|
165
|
-
ctx.
|
|
166
|
-
ctx.
|
|
167
|
-
ctx.
|
|
168
|
-
ctx.
|
|
164
|
+
ctx.req.on('data', data => {
|
|
165
|
+
ctx.res.statusCode = 400;
|
|
166
|
+
ctx.res.end(self.config.badRequest);
|
|
167
|
+
ctx.req.destroy();
|
|
169
168
|
});
|
|
170
169
|
} else {
|
|
171
170
|
let bigBodyEnd = false;
|
|
172
171
|
bodyBuffer = [];
|
|
173
|
-
ctx.
|
|
172
|
+
ctx.req.on('data', data => {
|
|
174
173
|
bodylength += data.length;
|
|
175
174
|
if (bodylength > ctx.maxBody) {
|
|
176
175
|
if (bigBodyEnd) return;
|
|
177
176
|
bigBodyEnd = true;
|
|
178
177
|
|
|
179
178
|
bodyBuffer = null;
|
|
180
|
-
ctx.
|
|
181
|
-
ctx.
|
|
182
|
-
ctx.
|
|
179
|
+
ctx.res.statusCode = 413;
|
|
180
|
+
ctx.res.end('', () => {
|
|
181
|
+
ctx.req.destroy();
|
|
183
182
|
});
|
|
184
183
|
return ;
|
|
185
184
|
}
|
|
@@ -189,18 +188,18 @@ class Http1 {
|
|
|
189
188
|
|
|
190
189
|
//若请求体太大,此时会进行destroy处理,触发close事件,但不会触发end。
|
|
191
190
|
//通过记录resolved状态避免重复调用rv。
|
|
192
|
-
ctx.
|
|
191
|
+
ctx.req.on('close', () => {
|
|
193
192
|
(!resolved) && rv();
|
|
194
193
|
});
|
|
195
194
|
|
|
196
|
-
ctx.
|
|
195
|
+
ctx.req.on('end',() => {
|
|
197
196
|
resolved = true;
|
|
198
197
|
rv();
|
|
199
198
|
});
|
|
200
199
|
|
|
201
200
|
});
|
|
202
201
|
|
|
203
|
-
if (!ctx.
|
|
202
|
+
if (!ctx.res.writable || ctx.res.writableEnded) {
|
|
204
203
|
return;
|
|
205
204
|
}
|
|
206
205
|
|
|
@@ -210,7 +209,7 @@ class Http1 {
|
|
|
210
209
|
bodyBuffer = null;
|
|
211
210
|
}
|
|
212
211
|
|
|
213
|
-
await next();
|
|
212
|
+
await next(ctx);
|
|
214
213
|
};
|
|
215
214
|
|
|
216
215
|
}
|
package/src/http2.js
CHANGED
|
@@ -126,8 +126,8 @@ class Httpt {
|
|
|
126
126
|
ctx.ip = remote_ip;
|
|
127
127
|
ctx.port = stream.session.socket.remotePort;
|
|
128
128
|
ctx.stream = stream;
|
|
129
|
-
ctx.
|
|
130
|
-
ctx.
|
|
129
|
+
ctx.res = ctx.stream;
|
|
130
|
+
ctx.req = ctx.stream;
|
|
131
131
|
|
|
132
132
|
ctx.dataHeaders = {};
|
|
133
133
|
ctx.headers = headers;
|
|
@@ -152,7 +152,7 @@ class Httpt {
|
|
|
152
152
|
|
|
153
153
|
mid() {
|
|
154
154
|
let self = this;
|
|
155
|
-
let noBodyMethods =
|
|
155
|
+
let noBodyMethods = Object.create(null);
|
|
156
156
|
|
|
157
157
|
['GET','OPTIONS','HEAD','TRACE'].forEach(a => {
|
|
158
158
|
noBodyMethods[a] = true;
|
|
@@ -164,7 +164,7 @@ class Httpt {
|
|
|
164
164
|
let bodyBuffer;
|
|
165
165
|
|
|
166
166
|
await new Promise((rv, rj) => {
|
|
167
|
-
if (
|
|
167
|
+
if (noBodyMethods[ctx.method]) {
|
|
168
168
|
ctx.stream.on('data', data => {
|
|
169
169
|
ctx.stream.respond({':status' : '400'});
|
|
170
170
|
ctx.stream.end(self.config.badRequest);
|
|
@@ -223,7 +223,7 @@ class Httpt {
|
|
|
223
223
|
bodyBuffer = null;
|
|
224
224
|
}
|
|
225
225
|
|
|
226
|
-
await next();
|
|
226
|
+
await next(ctx);
|
|
227
227
|
}
|
|
228
228
|
//end func
|
|
229
229
|
}
|
package/src/httpc.js
CHANGED
|
@@ -132,10 +132,10 @@ Httpc.prototype.onRequest = function () {
|
|
|
132
132
|
ctx.ip = remote_ip
|
|
133
133
|
|
|
134
134
|
ctx.port = req.socket.remotePort
|
|
135
|
-
ctx.
|
|
136
|
-
ctx.
|
|
135
|
+
ctx.req = req
|
|
136
|
+
ctx.res = res
|
|
137
137
|
ctx.stream = res
|
|
138
|
-
|
|
138
|
+
|
|
139
139
|
ctx.headers = req.headers
|
|
140
140
|
|
|
141
141
|
ctx.path = urlobj.path
|