birdpack 1.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.
- package/lib/core.js +411 -0
- package/lib/fileType.json +35 -0
- package/lib/server.js +426 -0
- package/lib/tools.js +330 -0
- package/lib/websocket.js +312 -0
- package/package.json +17 -0
package/lib/tools.js
ADDED
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
const crypto = require('crypto');
|
|
2
|
+
|
|
3
|
+
const tools = {
|
|
4
|
+
wwwform:(byteData)=>{
|
|
5
|
+
if(typeof byteData == 'object'){
|
|
6
|
+
byteData = new TextDecoder('utf8').decode(byteData);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
let output = {};
|
|
10
|
+
if(typeof byteData == 'string'){
|
|
11
|
+
for(let x of byteData.split('&')){
|
|
12
|
+
var data = x.search('=');
|
|
13
|
+
let name = '';
|
|
14
|
+
let value = '';
|
|
15
|
+
if(data == -1){
|
|
16
|
+
name = decodeURIComponent(x.trim());
|
|
17
|
+
value = '';
|
|
18
|
+
}else{
|
|
19
|
+
try{
|
|
20
|
+
name = decodeURIComponent(x.substring(0, data).trim());
|
|
21
|
+
value = decodeURIComponent(x.substr(data + 1));
|
|
22
|
+
}catch(e){
|
|
23
|
+
this.server.log('error',`${util.format(e)}`);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
if(typeof output[name] == 'undefined'){
|
|
27
|
+
output[name] = [value];
|
|
28
|
+
}else{
|
|
29
|
+
output[name].push(value);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return output;
|
|
34
|
+
},
|
|
35
|
+
formdata:(byteData, key)=>{
|
|
36
|
+
byteData = Buffer.concat([Buffer.from('\r\n'),byteData]);
|
|
37
|
+
key = `\r\n--${key}`;
|
|
38
|
+
|
|
39
|
+
let endIndex = 0;
|
|
40
|
+
let startIndex = 0;
|
|
41
|
+
let check = true;
|
|
42
|
+
let output = {}
|
|
43
|
+
let keyLength = key.length + 2;
|
|
44
|
+
while(check){
|
|
45
|
+
startIndex = byteData.indexOf(key, endIndex) + keyLength;
|
|
46
|
+
endIndex = byteData.indexOf(key, startIndex);
|
|
47
|
+
if(endIndex > 0){
|
|
48
|
+
let data;
|
|
49
|
+
let content = byteData.slice(startIndex,endIndex);
|
|
50
|
+
|
|
51
|
+
let rawData = content.slice(content.indexOf('\r\n\r\n') + 4);
|
|
52
|
+
|
|
53
|
+
let fileName = content.indexOf(' filename="');
|
|
54
|
+
if(fileName != -1){
|
|
55
|
+
fileName += 11;
|
|
56
|
+
fileName = content.slice(fileName,content.indexOf('"',fileName)).toString();
|
|
57
|
+
|
|
58
|
+
let fileType = content.indexOf('Content-Type: ') + 14;
|
|
59
|
+
fileType = content.slice(fileType,content.indexOf('\r\n',fileType)).toString();
|
|
60
|
+
|
|
61
|
+
data = {
|
|
62
|
+
name:fileName,
|
|
63
|
+
type:fileType,
|
|
64
|
+
data:rawData
|
|
65
|
+
};
|
|
66
|
+
}else{
|
|
67
|
+
data = new TextDecoder('utf8').decode(rawData);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
let name=content.indexOf(' name="') + 7;
|
|
71
|
+
name = content.slice(name,content.indexOf('"',name)).toString();
|
|
72
|
+
|
|
73
|
+
if(typeof output[name] == 'undefined'){
|
|
74
|
+
output[name] = [data];
|
|
75
|
+
}else{
|
|
76
|
+
output[name].push(data);
|
|
77
|
+
}
|
|
78
|
+
}else{
|
|
79
|
+
check = false;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return output;
|
|
83
|
+
},
|
|
84
|
+
generateKey:(key)=>{
|
|
85
|
+
return crypto.createHash('sha1')
|
|
86
|
+
.update(key + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')
|
|
87
|
+
.digest('base64');
|
|
88
|
+
},
|
|
89
|
+
encodeFrame:(payload, opcode = 1)=>{
|
|
90
|
+
let payloadBuffer;
|
|
91
|
+
|
|
92
|
+
if(Buffer.isBuffer(payload)){
|
|
93
|
+
payloadBuffer = payload;
|
|
94
|
+
}else if(typeof payload == 'string'){
|
|
95
|
+
payloadBuffer = Buffer.from(payload);
|
|
96
|
+
}else{
|
|
97
|
+
payloadBuffer = Buffer.alloc(0);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const payloadLength = payloadBuffer.length;
|
|
101
|
+
let frame;
|
|
102
|
+
|
|
103
|
+
if(payloadLength <= 125){
|
|
104
|
+
frame = Buffer.alloc(2 + payloadLength);
|
|
105
|
+
frame[1] = payloadLength;
|
|
106
|
+
}else if (payloadLength <= 65535){
|
|
107
|
+
frame = Buffer.alloc(4 + payloadLength);
|
|
108
|
+
frame[1] = 126;
|
|
109
|
+
frame.writeUInt16BE(payloadLength, 2);
|
|
110
|
+
}else{
|
|
111
|
+
frame = Buffer.alloc(10 + payloadLength);
|
|
112
|
+
frame[1] = 127;
|
|
113
|
+
frame.writeBigUInt64BE(BigInt(payloadLength), 2);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
frame[0] = 0x80 | opcode;
|
|
117
|
+
payloadBuffer.copy(frame, frame.length - payloadLength);
|
|
118
|
+
return frame;
|
|
119
|
+
},
|
|
120
|
+
decodeFrame: class{
|
|
121
|
+
constructor(callback){
|
|
122
|
+
this.status = 0;
|
|
123
|
+
this.section = [];
|
|
124
|
+
this.callback = callback;
|
|
125
|
+
}
|
|
126
|
+
decode(buffer){
|
|
127
|
+
let raw;
|
|
128
|
+
while(true){
|
|
129
|
+
if(this.status === 0){
|
|
130
|
+
let head = this.header(buffer);
|
|
131
|
+
if (typeof head.type !== 'string' || head.type === false){
|
|
132
|
+
delete this.section;
|
|
133
|
+
this.section = [];
|
|
134
|
+
|
|
135
|
+
break;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
this.status = 1;
|
|
139
|
+
|
|
140
|
+
this.fin = head.fin;
|
|
141
|
+
this.opcode = head.opcode;
|
|
142
|
+
this.isMasked = head.isMasked;
|
|
143
|
+
this.length = head.length;
|
|
144
|
+
this.key = head.key;
|
|
145
|
+
if(head.type !== true){
|
|
146
|
+
this.type = head.type;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
raw = head.raw;
|
|
150
|
+
this.payloadReset();
|
|
151
|
+
if(raw.length == 0){
|
|
152
|
+
if(this.fin === 1){
|
|
153
|
+
this.status = 0;
|
|
154
|
+
delete this.section;
|
|
155
|
+
this.section = [];
|
|
156
|
+
this.callback({
|
|
157
|
+
type: this.type,
|
|
158
|
+
payload: this.type === 'utf8' ? raw.toString('utf8') : raw
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
break;
|
|
162
|
+
}
|
|
163
|
+
}else{
|
|
164
|
+
raw = buffer;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
let size = this.payloadLength + raw.length;
|
|
168
|
+
if(this.length === size){
|
|
169
|
+
if(this.isMasked){
|
|
170
|
+
let data = this.mask(this.key, raw, this.payloadLength);
|
|
171
|
+
this.payloadAdd(data);
|
|
172
|
+
}else{
|
|
173
|
+
this.payloadAdd(raw);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
this.section.push(this.payloadGet());
|
|
177
|
+
this.status = 0;
|
|
178
|
+
}else if(this.length < size){
|
|
179
|
+
let count = this.length - this.payloadLength;
|
|
180
|
+
let next = raw.slice(count);
|
|
181
|
+
raw = raw.slice(0, count);
|
|
182
|
+
|
|
183
|
+
if(this.isMasked){
|
|
184
|
+
let data = this.mask(this.key, raw, this.payloadLength);
|
|
185
|
+
this.payloadAdd(data);
|
|
186
|
+
}else{
|
|
187
|
+
this.payloadAdd(raw);
|
|
188
|
+
}
|
|
189
|
+
this.section.push(this.payloadGet());
|
|
190
|
+
this.status = 0;
|
|
191
|
+
if(this.fin === 1 && this.length === this.payloadLength){
|
|
192
|
+
let data = Buffer.concat(this.section);
|
|
193
|
+
delete this.section;
|
|
194
|
+
this.section = [];
|
|
195
|
+
this.callback({
|
|
196
|
+
type: this.type,
|
|
197
|
+
payload: this.type === 'utf8' ? data.toString('utf8') : data
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
buffer = next;
|
|
202
|
+
if(next.length > 0){
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
205
|
+
}else{
|
|
206
|
+
if(this.isMasked){
|
|
207
|
+
let data = this.mask(this.key, raw, this.payloadLength);
|
|
208
|
+
this.payloadAdd(data);
|
|
209
|
+
}else{
|
|
210
|
+
this.payloadAdd(raw);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (this.length === this.payloadLength) {
|
|
214
|
+
this.section.push(this.payloadGet());
|
|
215
|
+
this.status = 0;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
if(this.fin === 1 && this.length === this.payloadLength){
|
|
220
|
+
let data = Buffer.concat(this.section);
|
|
221
|
+
delete this.section;
|
|
222
|
+
this.section = [];
|
|
223
|
+
this.callback({
|
|
224
|
+
type: this.type,
|
|
225
|
+
payload: this.type === 'utf8' ? data.toString('utf8') : data
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
break;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
payloadReset(){
|
|
232
|
+
delete this.payload;
|
|
233
|
+
this.payload = [];
|
|
234
|
+
this.payloadLength = 0;
|
|
235
|
+
}
|
|
236
|
+
payloadAdd(buffer){
|
|
237
|
+
this.payload.push(buffer);
|
|
238
|
+
this.payloadLength += buffer.length;
|
|
239
|
+
}
|
|
240
|
+
payloadGet(){
|
|
241
|
+
return Buffer.concat(this.payload);
|
|
242
|
+
}
|
|
243
|
+
header(buffer){
|
|
244
|
+
if (buffer.length < 2) return false;
|
|
245
|
+
|
|
246
|
+
const firstByte = buffer[0];
|
|
247
|
+
const secondByte = buffer[1];
|
|
248
|
+
let offset = 2;
|
|
249
|
+
|
|
250
|
+
let fin = firstByte >> 7;
|
|
251
|
+
let opcode = firstByte & 0x0F;
|
|
252
|
+
let isMasked = secondByte >> 7;
|
|
253
|
+
let length = secondByte & 0x7F;
|
|
254
|
+
|
|
255
|
+
if (length === 126) {
|
|
256
|
+
if (buffer.length < 4) return false;
|
|
257
|
+
length = buffer.readUInt16BE(offset);
|
|
258
|
+
offset += 2;
|
|
259
|
+
} else if (length === 127) {
|
|
260
|
+
if (buffer.length < 10) return false;
|
|
261
|
+
length = Number(buffer.readBigUInt64BE(offset));
|
|
262
|
+
offset += 8;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
let key = isMasked ? buffer.slice(offset, offset + 4) : Buffer.alloc(0);
|
|
266
|
+
offset += isMasked ? 4 : 0;
|
|
267
|
+
|
|
268
|
+
let raw = buffer.slice(offset);
|
|
269
|
+
let type = this.getOpcodeType(opcode);
|
|
270
|
+
|
|
271
|
+
return {
|
|
272
|
+
fin,
|
|
273
|
+
opcode,
|
|
274
|
+
isMasked,
|
|
275
|
+
length,
|
|
276
|
+
key,
|
|
277
|
+
raw,
|
|
278
|
+
type
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
getOpcodeType(opcode){
|
|
282
|
+
switch (opcode) {
|
|
283
|
+
case 1: return 'utf8';
|
|
284
|
+
case 2: return 'binary';
|
|
285
|
+
case 8: return 'close';
|
|
286
|
+
case 9: return 'ping';
|
|
287
|
+
case 10: return 'pong';
|
|
288
|
+
case 0: return true;
|
|
289
|
+
default: return null;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
mask(key, raw, start){
|
|
293
|
+
let unmasked = Buffer.allocUnsafe(raw.length);
|
|
294
|
+
for (let i = 0; i < raw.length; i++) {
|
|
295
|
+
unmasked[i] = raw[i] ^ key[(start+i) % 4];
|
|
296
|
+
}
|
|
297
|
+
return unmasked;
|
|
298
|
+
}
|
|
299
|
+
},
|
|
300
|
+
statusText:(code)=>{
|
|
301
|
+
const statusTexts = {
|
|
302
|
+
200: 'OK',
|
|
303
|
+
301: 'Moved Permanently',
|
|
304
|
+
400: 'Bad Request',
|
|
305
|
+
404: 'Not Found',
|
|
306
|
+
500: 'Internal Server Error',
|
|
307
|
+
101: 'Switching Protocols',
|
|
308
|
+
};
|
|
309
|
+
return statusTexts[code] || 'Unknown Status';
|
|
310
|
+
},
|
|
311
|
+
head2line:(header)=>{
|
|
312
|
+
const headerLines = [];
|
|
313
|
+
for(const [key, value] of Object.entries(header)) {
|
|
314
|
+
if (Array.isArray(value)) {
|
|
315
|
+
value.forEach((val) => headerLines.push(`${key}: ${val}`));
|
|
316
|
+
} else {
|
|
317
|
+
headerLines.push(`${key}: ${value}`);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
return headerLines.join('\r\n');
|
|
321
|
+
},
|
|
322
|
+
createID:()=>{
|
|
323
|
+
return crypto.randomBytes(64)
|
|
324
|
+
.toString('base64')
|
|
325
|
+
.replace(/[^a-zA-Z0-9]/g, '')
|
|
326
|
+
.slice(0, 64);
|
|
327
|
+
}
|
|
328
|
+
};
|
|
329
|
+
|
|
330
|
+
module.exports = tools;
|
package/lib/websocket.js
ADDED
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
const tools = require('./tools');
|
|
2
|
+
|
|
3
|
+
module.exports = class{
|
|
4
|
+
status = 0
|
|
5
|
+
header = {}
|
|
6
|
+
cookie = {}
|
|
7
|
+
body = {}
|
|
8
|
+
host = ''
|
|
9
|
+
url = ''
|
|
10
|
+
query = {}
|
|
11
|
+
method = ''
|
|
12
|
+
callback = ()=>{}
|
|
13
|
+
constructor({req, socket, head}){
|
|
14
|
+
this.req = req;
|
|
15
|
+
this.socket = socket;
|
|
16
|
+
this.head = head;
|
|
17
|
+
|
|
18
|
+
if(this.req.headers.upgrade === 'websocket'){
|
|
19
|
+
this.socket.on('data', (buffer) => {
|
|
20
|
+
this.frame.decode(buffer);
|
|
21
|
+
})
|
|
22
|
+
.on('end', () => {
|
|
23
|
+
this.req.destroy();
|
|
24
|
+
})
|
|
25
|
+
.on('close', () => {
|
|
26
|
+
this.userOut();
|
|
27
|
+
this.callback(this, 'close');
|
|
28
|
+
})
|
|
29
|
+
.on('error', (e) => {
|
|
30
|
+
console.log(e);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
this.process();
|
|
34
|
+
this.readFrame();
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
process(){
|
|
38
|
+
if(!this.req.headers.host){
|
|
39
|
+
this.error(400, 'Invalid Host.');
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const findHost = this.req.headers.host.indexOf(':');
|
|
44
|
+
this.host = findHost === -1 ? this.req.headers.host : this.req.headers.host.slice(0, findHost);
|
|
45
|
+
|
|
46
|
+
this.method = this.req.headers.upgrade === 'websocket' ? 'websocket' : this.req.method.toLowerCase();
|
|
47
|
+
|
|
48
|
+
const url = this.req.url.split('?');
|
|
49
|
+
this.path = url[0];
|
|
50
|
+
if(url[1]){
|
|
51
|
+
this.query = tools.wwwform(url[1]);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if(this.req.headers.hasOwnProperty('cookie')){
|
|
55
|
+
let cookie = [];
|
|
56
|
+
if(Array.isArray(this.req.headers.cookie)){
|
|
57
|
+
cookie = this.req.headers.cookie;
|
|
58
|
+
}else{
|
|
59
|
+
cookie.push(this.req.headers.cookie);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
for(let x of cookie){
|
|
63
|
+
for(let i of x.split('; ')){
|
|
64
|
+
let index = i.indexOf('=');
|
|
65
|
+
let key = i.slice(0, index);
|
|
66
|
+
let value = i.slice(index + 1);
|
|
67
|
+
|
|
68
|
+
try{
|
|
69
|
+
key = decodeURIComponent(key);
|
|
70
|
+
value = decodeURIComponent(value);
|
|
71
|
+
}catch(e){}
|
|
72
|
+
|
|
73
|
+
this.cookie[key] = value;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
update(call){
|
|
80
|
+
this.callback = call;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
code(status){
|
|
84
|
+
this.status = status;
|
|
85
|
+
return this;
|
|
86
|
+
}
|
|
87
|
+
set(key, value){
|
|
88
|
+
if(this.header.hasOwnProperty(key)){
|
|
89
|
+
this.header[key].push(value);
|
|
90
|
+
}else{
|
|
91
|
+
this.header[key] = [value];
|
|
92
|
+
}
|
|
93
|
+
return this;
|
|
94
|
+
}
|
|
95
|
+
get(key){
|
|
96
|
+
if(this.req.headers.hasOwnProperty(key)){
|
|
97
|
+
let header = this.req.headers[key];
|
|
98
|
+
if(typeof header == 'string'){
|
|
99
|
+
return header;
|
|
100
|
+
}else if(Array.isArray(header)){
|
|
101
|
+
return header[header.length - 1];
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
look(keys){
|
|
107
|
+
for(let x of keys.split(/[\s]*,[\s]*/)){
|
|
108
|
+
if(!this.query.hasOwnProperty(x)){
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return true;
|
|
113
|
+
}
|
|
114
|
+
u(key){
|
|
115
|
+
if(this.query.hasOwnProperty(key)){
|
|
116
|
+
if(Array.isArray(this.query[key])){
|
|
117
|
+
return this.query[key][this.query[key].length - 1];
|
|
118
|
+
}else{
|
|
119
|
+
return this.query[key];
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
scookie = (name, value, option) => {
|
|
125
|
+
let text = [`${name}=${encodeURI((value || '').toString())}`];
|
|
126
|
+
for(let x in option){
|
|
127
|
+
if(['domain', 'expires', 'httponly', 'max-age', 'partitioned', 'path', 'secure', 'sameSite'].includes(x)){
|
|
128
|
+
if(['string','number'].includes(typeof option[x])){
|
|
129
|
+
text.push(`${x}=${encodeURI(option[x].toString())}`);
|
|
130
|
+
}else{
|
|
131
|
+
text.push(`${x}`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
this.set('set-cookie', text.join('; '));
|
|
136
|
+
return this;
|
|
137
|
+
}
|
|
138
|
+
rcookie(name, option){
|
|
139
|
+
if(typeof option != 'object'){
|
|
140
|
+
option = {};
|
|
141
|
+
}
|
|
142
|
+
option['max-age'] = '-1';
|
|
143
|
+
this.scookie(name, '', option);
|
|
144
|
+
return this;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
writeHead(){
|
|
148
|
+
this.header['x-powered-by'] = `R938`;
|
|
149
|
+
|
|
150
|
+
const head = tools.head2line(this.header);
|
|
151
|
+
const statusLine = `HTTP/1.1 ${this.status} ${tools.statusText(this.status)}`;
|
|
152
|
+
const headLine = `${statusLine}\r\n${head}\r\n\r\n`;
|
|
153
|
+
this.write(Buffer.from(headLine));
|
|
154
|
+
|
|
155
|
+
return this;
|
|
156
|
+
}
|
|
157
|
+
write(data, callback){
|
|
158
|
+
if(this.socket.writable){
|
|
159
|
+
this.socket.write(data, callback);
|
|
160
|
+
}else if(this.req.destroyed){
|
|
161
|
+
this.req.destroy();
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
end(data, callback){
|
|
165
|
+
if(this.socket.writable){
|
|
166
|
+
this.socket.end(data, callback);
|
|
167
|
+
}else if(this.req.destroyed){
|
|
168
|
+
this.req.destroy();
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
error(status){
|
|
172
|
+
this.code(status)
|
|
173
|
+
.writeHead()
|
|
174
|
+
.end();
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
readFrame(){
|
|
178
|
+
this.frame = new tools.decodeFrame((data)=>{
|
|
179
|
+
this.body = data;
|
|
180
|
+
|
|
181
|
+
if(data.type == 'utf8'){
|
|
182
|
+
if((data.payload[0] == '{' && data.payload[data.payload.length - 1] == '}') || (data.payload[0] == '[' && data.payload[data.payload.length - 1] == ']')){
|
|
183
|
+
try{
|
|
184
|
+
data.json = JSON.parse(data.payload);
|
|
185
|
+
}catch(e){
|
|
186
|
+
data.json = {};
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
this.callback(this, 'data');
|
|
190
|
+
}else if(data.type == 'binary'){
|
|
191
|
+
this.callback(this, 'data');
|
|
192
|
+
}else if(data.type == 'close'){
|
|
193
|
+
this.stop();
|
|
194
|
+
}else if(data.type == 'ping'){
|
|
195
|
+
this.callback(this, 'ping');
|
|
196
|
+
}else if(data.type == 'pong'){
|
|
197
|
+
this.callback(this, 'pong');
|
|
198
|
+
}else{
|
|
199
|
+
this.callback(this, 'error');
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
start(uid){
|
|
204
|
+
let seckey = this.get('sec-websocket-key');
|
|
205
|
+
let protocol = this.get('sec-websocket-protocol');
|
|
206
|
+
let version = parseInt(this.get('sec-websocket-version') || 0);
|
|
207
|
+
let acceptKey = tools.generateKey(seckey);
|
|
208
|
+
|
|
209
|
+
if(typeof seckey != 'string' || version != 13){
|
|
210
|
+
return this.error(400,`Accept key exchange failed.`);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
let checkUser = this.userHas(uid);
|
|
214
|
+
if(checkUser !== false){
|
|
215
|
+
checkUser.userOut();
|
|
216
|
+
checkUser.stop();
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
this.uid = uid;
|
|
220
|
+
this.userIn();
|
|
221
|
+
|
|
222
|
+
this.code(101)
|
|
223
|
+
.set('upgrade','websocket')
|
|
224
|
+
.set('connection','Upgrade')
|
|
225
|
+
.set('sec-websocket-accept',acceptKey);
|
|
226
|
+
|
|
227
|
+
if(protocol){
|
|
228
|
+
this.set('sec-websocket-protocol',protocol);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
this.writeHead();
|
|
232
|
+
}
|
|
233
|
+
stop(data, callback){
|
|
234
|
+
this.req.destroy();
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
userEach(callback){
|
|
238
|
+
if(!this.callback.users)return false;
|
|
239
|
+
for(let x in this.callback.users.member){
|
|
240
|
+
if(callback(this.callback.users.member[x])){
|
|
241
|
+
break;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
};
|
|
245
|
+
userLength(){
|
|
246
|
+
if(!this.callback.users)return 0;
|
|
247
|
+
return this.callback.users.length;
|
|
248
|
+
};
|
|
249
|
+
userHas(uid){
|
|
250
|
+
if(!this.callback.users)return false;
|
|
251
|
+
if(this.callback.users.member.hasOwnProperty(uid)){
|
|
252
|
+
return this.callback.users.member[uid];
|
|
253
|
+
}
|
|
254
|
+
return false;
|
|
255
|
+
}
|
|
256
|
+
userIn(){
|
|
257
|
+
if(!this.callback.users)return false;
|
|
258
|
+
if(!this.callback.users.member.hasOwnProperty(this.uid)){
|
|
259
|
+
this.callback.users.member[this.uid] = this;
|
|
260
|
+
this.callback.users.length++;
|
|
261
|
+
this.userIn = ()=>{};
|
|
262
|
+
return true;
|
|
263
|
+
}
|
|
264
|
+
return false;
|
|
265
|
+
}
|
|
266
|
+
userOut(){
|
|
267
|
+
if(!this.callback.users)return false;
|
|
268
|
+
if(this.callback.users.member.hasOwnProperty(this.uid)){
|
|
269
|
+
delete this.callback.users.member[this.uid];
|
|
270
|
+
this.callback.users.length--;
|
|
271
|
+
this.userOut = ()=>{};
|
|
272
|
+
return true;
|
|
273
|
+
}
|
|
274
|
+
return false;
|
|
275
|
+
}
|
|
276
|
+
pong(){
|
|
277
|
+
let encode = tools.encodeFrame('pong', 10);
|
|
278
|
+
this.write(encode);
|
|
279
|
+
};
|
|
280
|
+
send = (text, opcode = 1) => {
|
|
281
|
+
let encode = tools.encodeFrame(text, opcode);
|
|
282
|
+
this.write(encode);
|
|
283
|
+
};
|
|
284
|
+
sends = (text, opcode = 1) => {
|
|
285
|
+
let encode = tools.encodeFrame(text, opcode);
|
|
286
|
+
this.userEach((user)=>{
|
|
287
|
+
user.write(encode);
|
|
288
|
+
});
|
|
289
|
+
};
|
|
290
|
+
json(json){
|
|
291
|
+
this.send(JSON.stringify(json), 1);
|
|
292
|
+
}
|
|
293
|
+
jsons(json){
|
|
294
|
+
this.sends(JSON.stringify(json), 1);
|
|
295
|
+
}
|
|
296
|
+
sendto(uid, text, opcode = 1){
|
|
297
|
+
let user = this.userHas(uid);
|
|
298
|
+
if(user !== false){
|
|
299
|
+
user.send(text, opcode = 1);
|
|
300
|
+
return true;
|
|
301
|
+
}
|
|
302
|
+
return false;
|
|
303
|
+
};
|
|
304
|
+
jsonto(uid, json){
|
|
305
|
+
let user = this.userHas(uid);
|
|
306
|
+
if(user !== false){
|
|
307
|
+
user.json(json);
|
|
308
|
+
return true;
|
|
309
|
+
}
|
|
310
|
+
return false;
|
|
311
|
+
};
|
|
312
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name":"birdpack",
|
|
3
|
+
"description": "BirdPack framework is a tool for web server via TCP HTTP supporting websocket focusing on speed.",
|
|
4
|
+
"author":"R938",
|
|
5
|
+
"license":"r938-birdpack-2025",
|
|
6
|
+
"version":"1.0.0",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"main": "lib/server.js",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": "./lib/server.js"
|
|
11
|
+
},
|
|
12
|
+
"directories": {
|
|
13
|
+
},
|
|
14
|
+
"scripts": {
|
|
15
|
+
"dev": "node app.js -u http -p 8000"
|
|
16
|
+
}
|
|
17
|
+
}
|