upcore-tcp 0.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.md +318 -0
- package/index.js +1 -0
- package/lib/checkDirectory.js +7 -0
- package/lib/checkHeader.js +120 -0
- package/lib/checkPath.js +47 -0
- package/lib/checkType.js +161 -0
- package/lib/cookieDecode.js +15 -0
- package/lib/decodeFrame.js +180 -0
- package/lib/encodeFrame.js +31 -0
- package/lib/extension.js +7 -0
- package/lib/formdata.js +160 -0
- package/lib/headerDecode.js +28 -0
- package/lib/httpHeader.js +26 -0
- package/lib/parameterFile.js +47 -0
- package/lib/parsePath.js +10 -0
- package/lib/payloadDecode.js +39 -0
- package/lib/payloadType.js +111 -0
- package/lib/queryDecode.js +7 -0
- package/lib/routeContext.js +191 -0
- package/lib/server.js +712 -0
- package/lib/streamFile.js +70 -0
- package/lib/wwwform.js +51 -0
- package/mine/db.json +9342 -0
- package/mine/mine-type.js +86 -0
- package/package.json +19 -0
package/README.md
ADDED
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
# upcore
|
|
2
|
+
|
|
3
|
+
Framework Node.js ขนาดเล็กสำหรับ **HTTP + WebSocket**
|
|
4
|
+
ออกแบบมาให้เร็ว เบา และควบคุม flow ได้เองทั้งหมด
|
|
5
|
+
ใช้ **CommonJS** และรองรับทั้ง **Node.js** และ **Bun.js**
|
|
6
|
+
|
|
7
|
+
> Created by **R938**
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## ✨ ความสามารถหลัก
|
|
12
|
+
|
|
13
|
+
- สร้าง **HTTP Server + WebSocket Server** จาก instance เดียว
|
|
14
|
+
- Routing ครบ:
|
|
15
|
+
- static path
|
|
16
|
+
- dynamic params (`:id`)
|
|
17
|
+
- wildcard (`*`)
|
|
18
|
+
- Middleware chaining ด้วย `next()`
|
|
19
|
+
- Domain-based routing (`app.domain(...)`)
|
|
20
|
+
- Parse body ได้หลายรูปแบบ:
|
|
21
|
+
- `json`
|
|
22
|
+
- `formdata`
|
|
23
|
+
- `wwwform`
|
|
24
|
+
- `binary`
|
|
25
|
+
- ระบบ **Type & Validation** ในตัว
|
|
26
|
+
- Static file serving (`directory`)
|
|
27
|
+
- Auto route loader จากไฟล์ (`routes`)
|
|
28
|
+
- Helper ครบ:
|
|
29
|
+
- cookie
|
|
30
|
+
- query
|
|
31
|
+
- redirect
|
|
32
|
+
- file response
|
|
33
|
+
- ใช้งานได้ทั้ง **Node.js** และ **Bun.js**
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
## ติดตั้งใช้งาน
|
|
37
|
+
```bash
|
|
38
|
+
npm install upcore
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Quick Start
|
|
42
|
+
```js
|
|
43
|
+
const upcore = require('upcore');
|
|
44
|
+
|
|
45
|
+
const app = new upcore({
|
|
46
|
+
dev: true,
|
|
47
|
+
logger: true
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
app.get('/', (req) => {
|
|
51
|
+
req.html('<h1>Hello upcore</h1>');
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
app.listen();
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## โครงสร้างเริ่มต้น (แนะนำ)
|
|
58
|
+
```text
|
|
59
|
+
project/
|
|
60
|
+
routes/
|
|
61
|
+
get@.js
|
|
62
|
+
post@api-login.js
|
|
63
|
+
get@user-$id.js
|
|
64
|
+
public/
|
|
65
|
+
index.html
|
|
66
|
+
logo.png
|
|
67
|
+
app.js
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
ตัวอย่าง `app.js`:
|
|
71
|
+
```js
|
|
72
|
+
const upcore = require('upcore');
|
|
73
|
+
|
|
74
|
+
const app = new upcore({ port: 3000, dev: true, logger: true });
|
|
75
|
+
|
|
76
|
+
app
|
|
77
|
+
.add('json', ({ load, next }) => load('json', next))
|
|
78
|
+
.directory('./public', { pathname: '/static', type: ['png', 'jpg', 'html', 'css', 'js'] })
|
|
79
|
+
.routes('./routes');
|
|
80
|
+
|
|
81
|
+
app.listen();
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## การตั้งค่า `new upcore(options)`
|
|
85
|
+
- `hostname` default: `0.0.0.0`
|
|
86
|
+
- `port` default: `3000`
|
|
87
|
+
- `keepAlive` default: `true`
|
|
88
|
+
- `keepAliveInitialDelay` default: `5000`
|
|
89
|
+
- `noDelay` default: `true`
|
|
90
|
+
- `timeout` default: `30000`
|
|
91
|
+
- `dev` default: `false` (ถ้า `true` จะพิมพ์ข้อความตอน listen)
|
|
92
|
+
- `logger` default: `false` (ถ้า `true` จะ log traffic)
|
|
93
|
+
- `use: 'https'` ใช้ `tls` แทน `net` (ต้องส่ง options ที่จำเป็นสำหรับ TLS)
|
|
94
|
+
|
|
95
|
+
## API หลักของ App
|
|
96
|
+
- `app.listen(callback?)`
|
|
97
|
+
- `app.type(id, rule)`
|
|
98
|
+
- `app.types(rules)`
|
|
99
|
+
- `app.add(id, middleware)`
|
|
100
|
+
- `app.domain(domain | string[])`
|
|
101
|
+
- `app.next(...middlewareOrName)`
|
|
102
|
+
- `app.all(path, ...handler)`
|
|
103
|
+
- `app.get/post/put/patch/delete/options/head(path, ...handler)`
|
|
104
|
+
- `app.websocket(path, onStart, onData?, onClose?, onPing?, onPong?)`
|
|
105
|
+
- `app.directory(dir, options)`
|
|
106
|
+
- `app.routes(dir)`
|
|
107
|
+
|
|
108
|
+
## API ใน Handler (HTTP)
|
|
109
|
+
- Response: `req.code()`, `req.set()`, `req.http()`, `req.send()`, `req.html()`, `req.json()`, `req.redirect()`, `req.file()`, `req.error()`
|
|
110
|
+
- Request: `req.get(headerKey)`, `req.look(queryKeys)`, `req.u(queryKey)`, `req.load()`, `req.check()`, `req.q()`
|
|
111
|
+
- Cookie: `req.gcookie()`, `req.scookie()`, `req.rcookie()`
|
|
112
|
+
- Middleware flow: `req.next()`
|
|
113
|
+
- Validation: `req.test({ type, data, core })`
|
|
114
|
+
|
|
115
|
+
## API ใน Handler (WebSocket)
|
|
116
|
+
- `req.start(uid)` เริ่ม handshake + ลงทะเบียนผู้ใช้
|
|
117
|
+
- `req.stop()`
|
|
118
|
+
- `req.send()`, `req.sends()`, `req.sendto()`, `req.sendc()`
|
|
119
|
+
- `req.json()`, `req.jsons()`, `req.jsonto()`, `req.jsonc()`
|
|
120
|
+
- `req.ping()`, `req.pong()`
|
|
121
|
+
- `req.userEach()`, `req.userHas()`, `req.userLength()`
|
|
122
|
+
|
|
123
|
+
## ระบบ Type/Validation
|
|
124
|
+
รองรับ `type`:
|
|
125
|
+
- `int`
|
|
126
|
+
- `string`
|
|
127
|
+
- `boolean`
|
|
128
|
+
- `array`
|
|
129
|
+
- `object`
|
|
130
|
+
- `file` (object ที่มี `data` เป็น Buffer)
|
|
131
|
+
- `timestamp` (`YYYY-MM-DDTHH:mm:ss.sssZ`)
|
|
132
|
+
|
|
133
|
+
รองรับเงื่อนไขเสริม:
|
|
134
|
+
- `length: [min, max]` หรือ `length: exact`
|
|
135
|
+
- `format: 'user' | 'email' | 'hex' | 'decimal'`
|
|
136
|
+
- `list: [...]`
|
|
137
|
+
- `mime: [...]` (ใช้กับ `file`)
|
|
138
|
+
|
|
139
|
+
ตัวอย่าง:
|
|
140
|
+
```js
|
|
141
|
+
app
|
|
142
|
+
.type('id', { type: 'int', length: [1, 1000000] })
|
|
143
|
+
.type('email', { type: 'string', format: 'email', length: [6, 300] })
|
|
144
|
+
.type('avatar', { type: 'file', mime: ['image/png', 'image/jpeg'] });
|
|
145
|
+
.types({
|
|
146
|
+
userid:{ type: 'int', length: [1, 1000000] }
|
|
147
|
+
no:{ type: 'int', length: [1, 1000] }
|
|
148
|
+
});
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## ตัวอย่างการใช้งานทุกเคสหลัก
|
|
152
|
+
|
|
153
|
+
### 1) GET/POST พื้นฐาน
|
|
154
|
+
```js
|
|
155
|
+
app.get('/', (req) => req.send('ok'));
|
|
156
|
+
app.post('/echo', ({ load, json, body }) => {
|
|
157
|
+
load('json', () => json({ body }));
|
|
158
|
+
});
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### 2) Dynamic params และ wildcard
|
|
162
|
+
```js
|
|
163
|
+
app.get('/user/:id', ({ params, json }) => json({userid: params.id}));
|
|
164
|
+
// [GET] /user/1
|
|
165
|
+
// {"userid":1}
|
|
166
|
+
app.get('/files/*', ({ wildcard, send }) => send(wildcard));
|
|
167
|
+
// [GET] /files/asset/logo.svg
|
|
168
|
+
// /asset/logo.svg
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### 3) Query
|
|
172
|
+
```js
|
|
173
|
+
app.get('/search', (req) => {
|
|
174
|
+
if (!req.look('q')) return req.code(400).send('missing q');
|
|
175
|
+
req.json({ q: req.u('q') });
|
|
176
|
+
});
|
|
177
|
+
// [GET] /search?q=upcore
|
|
178
|
+
// {"q":"upcore"}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### 4) Middleware chain
|
|
182
|
+
```js
|
|
183
|
+
app.add('auth', (req) => {
|
|
184
|
+
if (!req.look('token')) return req.code(401).send('unauthorized');
|
|
185
|
+
req.next();
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
app.next('auth').get('/private', (req) => req.send('ok'));
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### 5) Parse body หลายประเภท
|
|
192
|
+
```js
|
|
193
|
+
app.add('json', ({ load, next }) => load('json', next));
|
|
194
|
+
app.add('form', ({ load, next }) => load(['wwwform', 'formdata'], next));
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### 6) Validation ด้วย `req.test`
|
|
198
|
+
```js
|
|
199
|
+
app.type('id', { type: 'int', length: [1, 999999] });
|
|
200
|
+
|
|
201
|
+
app.post('/user', 'json', (req) => {
|
|
202
|
+
const data = req.test({
|
|
203
|
+
type: 'json',
|
|
204
|
+
data: { id: { t: 'id', r: true } }
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
if (data !== false) req.json({ ok: true, data });
|
|
208
|
+
});
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### 7) Cookie
|
|
212
|
+
```js
|
|
213
|
+
app.get('/cookie/set', (req) => {
|
|
214
|
+
req.scookie('sid', 'abc123', { path: '/', httponly: true, 'max-age': 3600 });
|
|
215
|
+
req.send('set');
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
app.get('/cookie/get', (req) => req.json({ sid: req.gcookie('sid') || null }));
|
|
219
|
+
app.get('/cookie/remove', (req) => { req.rcookie('sid', { path: '/' }); req.send('removed'); });
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### 8) Redirect
|
|
223
|
+
```js
|
|
224
|
+
app.get('/go-doc', (req) => req.redirect('/docs')); // 301
|
|
225
|
+
app.get('/go-login', (req) => req.redirect(302, '/login'));
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
### 9) ส่งไฟล์ / template parameter
|
|
229
|
+
```js
|
|
230
|
+
app.get('/download', (req) => {
|
|
231
|
+
req.file({ file: './public/a.jpg', download: 'photo.jpg' });
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
app.get('/page', (req) => {
|
|
235
|
+
req.file({
|
|
236
|
+
file: './public/index.html',
|
|
237
|
+
parameter: { title: 'Hello', time: new Date().toISOString() }
|
|
238
|
+
});
|
|
239
|
+
});
|
|
240
|
+
```
|
|
241
|
+
ตัวอย่างไฟล์ /public/index.html
|
|
242
|
+
```html
|
|
243
|
+
<h1>{{title}}</h1>
|
|
244
|
+
<p>Time: {{time}}</p>
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
### 10) Static directory
|
|
248
|
+
```js
|
|
249
|
+
app.directory('./public', {
|
|
250
|
+
pathname: '/assets',
|
|
251
|
+
type: ['png', 'jpg', 'mp4', 'html', 'css', 'js']
|
|
252
|
+
});
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
### 11) Auto routes จากไฟล์
|
|
256
|
+
```js
|
|
257
|
+
app.routes('./routes');
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
หลักการตั้งชื่อไฟล์ของ `app.routes`:
|
|
261
|
+
- อ่านเฉพาะไฟล์ `.js` ในโฟลเดอร์นั้น (ไม่อ่านโฟลเดอร์ย่อย)
|
|
262
|
+
- รูปแบบหลัก: `method@path.js`
|
|
263
|
+
- ถ้าไม่มี `@` จะถือเป็น `GET` อัตโนมัติ เช่น `home.js` => `GET /home`
|
|
264
|
+
- `method` ที่ใช้ได้ตาม API: `get`, `post`, `put`, `patch`, `delete`, `options`, `head`, `all`, `websocket`
|
|
265
|
+
- `path` จะถูกแปลงอักขระพิเศษ:
|
|
266
|
+
- `-` => `/`
|
|
267
|
+
- `$` => `:` (path param)
|
|
268
|
+
- `\"` จะถูกลบออกจาก path
|
|
269
|
+
- ถ้าชื่อไฟล์มี `@` มากกว่า 1 ตัว (เช่น `get@a@b.js`) ระบบจะไม่ map route ไฟล์นั้น
|
|
270
|
+
|
|
271
|
+
ตัวอย่างชื่อไฟล์ -> endpoint:
|
|
272
|
+
- `get@home-admin.js` => `GET /home/admin`
|
|
273
|
+
- `get@user-$id.js` => `GET /user/:id`
|
|
274
|
+
- `post@auth-login.js` => `POST /auth/login`
|
|
275
|
+
- `health.js` => `GET /health`
|
|
276
|
+
- `all@api-v1-*.js` => `ALL /api/v1/*`
|
|
277
|
+
|
|
278
|
+
ตัวอย่างไฟล์ route:
|
|
279
|
+
```js
|
|
280
|
+
// routes/post@user-login.js
|
|
281
|
+
module.exports = (req) => {
|
|
282
|
+
req.json({ ok: true, body: req.body });
|
|
283
|
+
};
|
|
284
|
+
module.exports.next = 'json'; // ใช้ middleware ที่ add ไว้แล้ว
|
|
285
|
+
// module.exports.next = ['auth', 'json'];
|
|
286
|
+
// module.exports.next = ()=>{};
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
### 12) Domain-based routing
|
|
290
|
+
```js
|
|
291
|
+
app.domain(['api.local', 'localhost'])
|
|
292
|
+
.get('/health', (req) => req.json({ ok: true }));
|
|
293
|
+
|
|
294
|
+
// [GET] http://api.local/health
|
|
295
|
+
// [GET] http://localhost/health
|
|
296
|
+
|
|
297
|
+
// {"ok":true}
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
### 13) WebSocket
|
|
301
|
+
```js
|
|
302
|
+
app.websocket('/ws', async (req) => {
|
|
303
|
+
const ok = await req.start(req.u('uid') || String(Date.now()));
|
|
304
|
+
if (!ok) return req.stop();
|
|
305
|
+
req.send('connected');
|
|
306
|
+
}, (req) => {
|
|
307
|
+
req.send(req.body.payload); // echo
|
|
308
|
+
}, (req) => {
|
|
309
|
+
console.log('socket closed', req.uid);
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
// ws://127.0.0.1/ws?uid=upcore
|
|
313
|
+
```
|
|
314
|
+
## หมายเหตุสำคัญ
|
|
315
|
+
- ค่า `Host` ต้องตรงกับ domain ที่ลง route (หรือใช้ `*` ถ้าต้องการรับทุก host)
|
|
316
|
+
- ถ้า `req.load(...)` ประเภทไม่ตรงกับ `Content-Type` จะตอบ 400
|
|
317
|
+
- `req.test(...)` จะตอบ error อัตโนมัติเมื่อ type/format ไม่ผ่าน
|
|
318
|
+
- ถ้าใช้ `app.routes(dir)` โฟลเดอร์ต้องมีไฟล์ `.js` ตาม naming convention
|
package/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = require('./lib/server');
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
module.exports = function(chunk){
|
|
2
|
+
const len = chunk.length;
|
|
3
|
+
if (len < 16) return false;
|
|
4
|
+
let p = 0;
|
|
5
|
+
|
|
6
|
+
// ---------- METHOD ----------
|
|
7
|
+
let method = false;
|
|
8
|
+
|
|
9
|
+
switch((chunk[0] << 24) | (chunk[1] << 16) | (chunk[2] << 8) | chunk[3]){
|
|
10
|
+
case 0x47455420: method = 'get'; p = 4; break; // "GET "
|
|
11
|
+
case 0x504F5354: method = 'post'; p = 5; break; // "POST"
|
|
12
|
+
case 0x50555420: method = 'put'; p = 4; break; // "PUT "
|
|
13
|
+
case 0x48454144: method = 'head'; p = 5; break; // "HEAD"
|
|
14
|
+
case 0x44454C45: method = 'delete'; p = 7; break; // "DELE"
|
|
15
|
+
case 0x4F505449: method = 'options'; p = 8; break; // "OPTI"
|
|
16
|
+
case 0x50415443: method = 'patch'; p = 6; break; // "PATC"
|
|
17
|
+
default: return false;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// ---------- PATH ----------
|
|
21
|
+
const pathStart = p;
|
|
22
|
+
let searchPoint = -1;
|
|
23
|
+
|
|
24
|
+
while(p < len && chunk[p] !== 32){
|
|
25
|
+
if (chunk[p] === 63 && searchPoint === -1) searchPoint = p;
|
|
26
|
+
p++;
|
|
27
|
+
}
|
|
28
|
+
if (p >= len) return false;
|
|
29
|
+
|
|
30
|
+
const pathEnd = p++;
|
|
31
|
+
|
|
32
|
+
p += 10;
|
|
33
|
+
|
|
34
|
+
// ---------- HEADERS ----------
|
|
35
|
+
let header = [];
|
|
36
|
+
let host = false;
|
|
37
|
+
let port = false;
|
|
38
|
+
let length = 0;
|
|
39
|
+
let type = false;
|
|
40
|
+
let connection = 0;
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
while(p + 3 < len){
|
|
44
|
+
const lineStart = p;
|
|
45
|
+
|
|
46
|
+
if (chunk[p] === 13 && chunk[p+1] === 10) {
|
|
47
|
+
p += 2;
|
|
48
|
+
break;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
while(p < len && !(chunk[p] === 13 && chunk[p+1] === 10)) p++;
|
|
52
|
+
|
|
53
|
+
const h = chunk.subarray(lineStart, p);
|
|
54
|
+
|
|
55
|
+
switch((h[0] << 24) | (h[1] << 16) | (h[2] << 8) | h[3]){
|
|
56
|
+
case 0x436F6E74:{ // "Cont"
|
|
57
|
+
if(h[8] === 76 && h[14] === 58 && h[15] === 32){ // Content-Length:
|
|
58
|
+
length = parseInt(h.subarray(16));
|
|
59
|
+
break;
|
|
60
|
+
}else if(h[8] === 84 && h[12] === 58 && h[13] === 32){ // Content-Type:
|
|
61
|
+
type = h.subarray(14);
|
|
62
|
+
break;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
case 0x486F7374:{ // "Host"
|
|
66
|
+
if(h[4] === 58 && h[5] === 32){
|
|
67
|
+
let colon = -1;
|
|
68
|
+
for(let i = h.length - 1; i > 5; i--){
|
|
69
|
+
if(h[i] === 58){
|
|
70
|
+
colon = i;
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if(colon !== -1){
|
|
76
|
+
host = h.subarray(6, colon).toString();
|
|
77
|
+
port = h.subarray(colon + 1).toString();
|
|
78
|
+
}else{
|
|
79
|
+
host = h.subarray(6).toString();
|
|
80
|
+
}
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
case 0x436F6E6E:{ // "Conn"
|
|
85
|
+
// Connection
|
|
86
|
+
if(h[10] === 58 && h[11] === 32){
|
|
87
|
+
if(h[12] === 107 && h.length === 22){ // Connection: keep-alive
|
|
88
|
+
connection = 1
|
|
89
|
+
}else if(h[12] === 85 && h.length === 19){ // Connection: Upgrade
|
|
90
|
+
connection = 2
|
|
91
|
+
}
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
default:{
|
|
96
|
+
header.push(h);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
p += 2;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if(!host) return false;
|
|
104
|
+
|
|
105
|
+
const body = length ? chunk.subarray(p, p + length) : null;
|
|
106
|
+
if(length && body.length > length) return false;
|
|
107
|
+
|
|
108
|
+
return {
|
|
109
|
+
success:!length || body.length === length,
|
|
110
|
+
connection,
|
|
111
|
+
method,
|
|
112
|
+
host,
|
|
113
|
+
pathname: chunk.subarray(pathStart, searchPoint === -1 ? pathEnd : searchPoint),
|
|
114
|
+
search: searchPoint === -1 ? null : chunk.subarray(searchPoint + 1, pathEnd),
|
|
115
|
+
type,
|
|
116
|
+
length,
|
|
117
|
+
header,
|
|
118
|
+
payload: body ? [body] : []
|
|
119
|
+
};
|
|
120
|
+
}
|
package/lib/checkPath.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
module.exports = function checkPath(tree, req) {
|
|
2
|
+
let hostTree = tree[req.host] || tree['*'];
|
|
3
|
+
if (!hostTree) return false;
|
|
4
|
+
|
|
5
|
+
let node = hostTree[req.connection === 2 ? 'websocket' : req.method] || hostTree['*'];
|
|
6
|
+
if (!node) return false;
|
|
7
|
+
|
|
8
|
+
const path = req.pathname;
|
|
9
|
+
let start = 1;
|
|
10
|
+
req.params = {};
|
|
11
|
+
req.wildcard = {};
|
|
12
|
+
|
|
13
|
+
let cacheWildcard = false;
|
|
14
|
+
while (true) {
|
|
15
|
+
|
|
16
|
+
if(node.wildcard){
|
|
17
|
+
cacheWildcard = {
|
|
18
|
+
wildcard: path.slice(start - 1),
|
|
19
|
+
node:node.wildcard
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
let end = path.indexOf('/', start);
|
|
24
|
+
if (end === -1) end = path.length;
|
|
25
|
+
|
|
26
|
+
const part = path.slice(start, end);
|
|
27
|
+
|
|
28
|
+
if(node.static && node.static[part]){
|
|
29
|
+
node = node.static[part];
|
|
30
|
+
}else if (node.param){
|
|
31
|
+
req.params[node.param.id] = part;
|
|
32
|
+
node = node.param;
|
|
33
|
+
}else if(cacheWildcard !== false){
|
|
34
|
+
req.wildcard = cacheWildcard.wildcard;
|
|
35
|
+
return cacheWildcard.node.handler;
|
|
36
|
+
}else{
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (end === path.length) break;
|
|
41
|
+
start = end + 1;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (!node.handler) return false;
|
|
45
|
+
|
|
46
|
+
return node.handler;
|
|
47
|
+
}
|
package/lib/checkType.js
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
function isValidDecimal(s) {
|
|
2
|
+
for (let i = 0; i < s.length; i++) {
|
|
3
|
+
const c = s.charCodeAt(i);
|
|
4
|
+
|
|
5
|
+
if (
|
|
6
|
+
(c >= 48 && c <= 57) // 0-9
|
|
7
|
+
) continue;
|
|
8
|
+
|
|
9
|
+
return false;
|
|
10
|
+
}
|
|
11
|
+
return s.length > 0;
|
|
12
|
+
}
|
|
13
|
+
function isValidHex(s) {
|
|
14
|
+
for (let i = 0; i < s.length; i++) {
|
|
15
|
+
const c = s.charCodeAt(i);
|
|
16
|
+
|
|
17
|
+
if (
|
|
18
|
+
(c >= 48 && c <= 57) || // 0-9
|
|
19
|
+
(c >= 97 && c <= 102) // a-f
|
|
20
|
+
) continue;
|
|
21
|
+
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
return s.length > 0;
|
|
25
|
+
}
|
|
26
|
+
function isValidUser(s) {
|
|
27
|
+
for (let i = 0; i < s.length; i++) {
|
|
28
|
+
const c = s.charCodeAt(i);
|
|
29
|
+
|
|
30
|
+
if (
|
|
31
|
+
(c >= 48 && c <= 57) || // 0-9
|
|
32
|
+
(c >= 65 && c <= 90) || // A-Z
|
|
33
|
+
(c >= 97 && c <= 122) || // a-z
|
|
34
|
+
c === 95 || // _
|
|
35
|
+
c === 45 // -
|
|
36
|
+
) continue;
|
|
37
|
+
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
return s.length > 0;
|
|
41
|
+
}
|
|
42
|
+
function isValidEmail(s){
|
|
43
|
+
let at = -1;
|
|
44
|
+
let dot = -1;
|
|
45
|
+
|
|
46
|
+
for (let i = 0; i < s.length; i++) {
|
|
47
|
+
const c = s.charCodeAt(i);
|
|
48
|
+
|
|
49
|
+
if (c === 64) { // @
|
|
50
|
+
if (at !== -1) return false;
|
|
51
|
+
at = i;
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (c === 46) dot = i; // .
|
|
56
|
+
|
|
57
|
+
if (
|
|
58
|
+
(c >= 48 && c <= 57) || // 0-9
|
|
59
|
+
(c >= 65 && c <= 90) || // A-Z
|
|
60
|
+
(c >= 97 && c <= 122) || // a-z
|
|
61
|
+
c === 46 || c === 95 || // . _
|
|
62
|
+
c === 45 || c === 43 || // - +
|
|
63
|
+
c === 37 // %
|
|
64
|
+
) continue;
|
|
65
|
+
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return at > 0 && dot > at + 1 && dot < s.length - 1;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
module.exports = function({type, length, format, list, mime}, data){
|
|
73
|
+
let status = false;
|
|
74
|
+
let len = false;
|
|
75
|
+
|
|
76
|
+
if(type == 'int' && data === (data | 0)){
|
|
77
|
+
len = data;
|
|
78
|
+
status = true;
|
|
79
|
+
}else if(type == 'string' && typeof data === 'string'){
|
|
80
|
+
len = data.length;
|
|
81
|
+
status = true;
|
|
82
|
+
}else if(type == 'boolean' && typeof data === 'boolean'){
|
|
83
|
+
status = true;
|
|
84
|
+
}else if(type == 'array' && typeof data === 'object' && Array.isArray(data)){
|
|
85
|
+
status = true;
|
|
86
|
+
}else if(type == 'object' && typeof data === 'object' && !Array.isArray(data)){
|
|
87
|
+
status = true;
|
|
88
|
+
}else if(type == 'file' && typeof data === 'object' && Buffer.isBuffer(data?.data)){
|
|
89
|
+
len = data.data.length;
|
|
90
|
+
status = true;
|
|
91
|
+
}else if(type == 'timestamp' && typeof data === 'string' && data.length == 24 && /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/.test(data)){
|
|
92
|
+
len = new Date(data).getTime();
|
|
93
|
+
status = true;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if(status === false){
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if(mime !== undefined){
|
|
101
|
+
status = false;
|
|
102
|
+
if(mime.includes(data.type)){
|
|
103
|
+
status = true;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if(status === false){
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if(length !== undefined){
|
|
112
|
+
status = false;
|
|
113
|
+
if(Array.isArray(length) && length.length == 2){
|
|
114
|
+
if(length[0] <= len && length[1] >= len){
|
|
115
|
+
status = true;
|
|
116
|
+
}
|
|
117
|
+
}else if(length === (length | 0)){
|
|
118
|
+
if(len === length){
|
|
119
|
+
status = true;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if(status === false){
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if(format !== undefined){
|
|
129
|
+
status = false;
|
|
130
|
+
if(format == 'user'){
|
|
131
|
+
if(isValidUser(data)){
|
|
132
|
+
status = true;
|
|
133
|
+
}
|
|
134
|
+
}else if(format == 'email'){
|
|
135
|
+
if(isValidEmail(data)){
|
|
136
|
+
status = true;
|
|
137
|
+
}
|
|
138
|
+
}else if(format == 'hex'){
|
|
139
|
+
if(isValidHex(data)){
|
|
140
|
+
status = true;
|
|
141
|
+
}
|
|
142
|
+
}else if(format == 'decimal'){
|
|
143
|
+
if(isValidDecimal(data)){
|
|
144
|
+
status = true;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if(status === false){
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if(Array.isArray(list)){
|
|
154
|
+
if(!list.includes(data)){
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
return true;
|
|
161
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module.exports = function(cookie){
|
|
2
|
+
let output = [];
|
|
3
|
+
|
|
4
|
+
for(let i of cookie.split('; ')){
|
|
5
|
+
let index = i.indexOf('=');
|
|
6
|
+
|
|
7
|
+
try{
|
|
8
|
+
output[decodeURIComponent(i.slice(0, index))] = decodeURIComponent(i.slice(index + 1));
|
|
9
|
+
}catch(e){
|
|
10
|
+
console.log(e);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return output;
|
|
15
|
+
}
|