@kevisual/router 0.0.41 → 0.0.43
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/dist/router-browser.js +66 -48
- package/dist/router-sign.js +3 -1
- package/dist/router-simple.js +50 -30
- package/dist/router.d.ts +166 -80
- package/dist/router.js +5134 -4872
- package/package.json +5 -2
- package/src/app.ts +21 -16
- package/src/index.ts +5 -2
- package/src/router-simple.ts +1 -0
- package/src/server/handle-server.ts +1 -1
- package/src/server/index.ts +2 -1
- package/src/server/parse-body.ts +44 -33
- package/src/server/server-base.ts +265 -0
- package/src/server/server-bun.ts +244 -0
- package/src/server/server-type.ts +78 -0
- package/src/server/server.ts +25 -177
- package/src/server/ws-server.ts +35 -103
- package/src/test/listen-ip.ts +2 -2
- package/src/utils/is-engine.ts +5 -1
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @title Bun Server Implementation
|
|
3
|
+
* @description Bun 服务器实现,提供基于 Bun.serve 的 HTTP 和 WebSocket 功能
|
|
4
|
+
* @tags bun, server, websocket, http
|
|
5
|
+
* @createdAt 2025-12-20
|
|
6
|
+
*/
|
|
7
|
+
import { ServerType, type ServerOpts, type Cors, RouterRes, RouterReq } from './server-type.ts';
|
|
8
|
+
import { ServerBase } from './server-base.ts';
|
|
9
|
+
|
|
10
|
+
export class BunServer extends ServerBase implements ServerType {
|
|
11
|
+
declare _server: any;
|
|
12
|
+
declare _callback: any;
|
|
13
|
+
declare cors: Cors;
|
|
14
|
+
constructor(opts?: ServerOpts) {
|
|
15
|
+
super(opts);
|
|
16
|
+
}
|
|
17
|
+
customListen(...args: any[]): void {
|
|
18
|
+
this.listenWithBun(...args);
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Bun 运行时的 listen 实现
|
|
22
|
+
*/
|
|
23
|
+
private listenWithBun(...args: any[]) {
|
|
24
|
+
// @ts-ignore - Bun 全局 API
|
|
25
|
+
if (typeof Bun === 'undefined' || !Bun.serve) {
|
|
26
|
+
throw new Error('Bun runtime not detected');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
let port: number = 3000;
|
|
30
|
+
let hostname: string = 'localhost';
|
|
31
|
+
let callback: (() => void) | undefined;
|
|
32
|
+
|
|
33
|
+
// 解析参数
|
|
34
|
+
if (typeof args[0] === 'number') {
|
|
35
|
+
port = args[0];
|
|
36
|
+
if (typeof args[1] === 'string') {
|
|
37
|
+
hostname = args[1];
|
|
38
|
+
callback = args[2] || args[3];
|
|
39
|
+
} else if (typeof args[1] === 'function') {
|
|
40
|
+
callback = args[1];
|
|
41
|
+
} else {
|
|
42
|
+
callback = args[2];
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const requestCallback = this.createCallback() as unknown as (req: RouterReq, res: RouterRes) => void;
|
|
47
|
+
const wsPath = this.path;
|
|
48
|
+
// @ts-ignore
|
|
49
|
+
this._server = Bun.serve({
|
|
50
|
+
port,
|
|
51
|
+
hostname,
|
|
52
|
+
idleTimeout: 0, // 4 minutes idle timeout (max 255 seconds)
|
|
53
|
+
fetch: async (request: Bun.BunRequest, server: any) => {
|
|
54
|
+
const host = request.headers.get('host') || 'localhost';
|
|
55
|
+
const clientInfo = server.requestIP(request); // 返回 { address: string, port: number } 或 null
|
|
56
|
+
const url = new URL(request.url, `http://${host}`);
|
|
57
|
+
// 处理 WebSocket 升级请求
|
|
58
|
+
if (request.headers.get('upgrade') === 'websocket') {
|
|
59
|
+
const listenPath = this.listeners.map((item) => item.path).filter((item) => item);
|
|
60
|
+
if (listenPath.includes(url.pathname) || url.pathname === wsPath) {
|
|
61
|
+
const token = url.searchParams.get('token') || '';
|
|
62
|
+
const id = url.searchParams.get('id') || '';
|
|
63
|
+
const upgraded = server.upgrade(request, {
|
|
64
|
+
data: { url: url, pathname: url.pathname, token, id },
|
|
65
|
+
});
|
|
66
|
+
if (upgraded) {
|
|
67
|
+
return undefined; // WebSocket 连接成功
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return new Response('WebSocket upgrade failed', { status: 400 });
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// 将 Bun 的 Request 转换为 Node.js 风格的 req/res
|
|
74
|
+
return new Promise((resolve) => {
|
|
75
|
+
const req: RouterReq = {
|
|
76
|
+
url: url.pathname + url.search,
|
|
77
|
+
method: request.method,
|
|
78
|
+
headers: Object.fromEntries(request.headers.entries()),
|
|
79
|
+
socket: {
|
|
80
|
+
// @ts-ignore
|
|
81
|
+
remoteAddress: request?.remoteAddress || request?.ip || clientInfo?.address || '',
|
|
82
|
+
remotePort: clientInfo?.port || 0,
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const res: RouterRes = {
|
|
87
|
+
statusCode: 200,
|
|
88
|
+
headersSent: false,
|
|
89
|
+
writableEnded: false,
|
|
90
|
+
_headers: {} as Record<string, string | string[]>,
|
|
91
|
+
_bodyChunks: [] as any[],
|
|
92
|
+
writeHead(statusCode: number, headers: Record<string, string | string[]>) {
|
|
93
|
+
this.statusCode = statusCode;
|
|
94
|
+
for (const key in headers) {
|
|
95
|
+
this._headers[key] = headers[key];
|
|
96
|
+
}
|
|
97
|
+
this.headersSent = true;
|
|
98
|
+
},
|
|
99
|
+
setHeader(name: string, value: string | string[]) {
|
|
100
|
+
this._headers[name] = value;
|
|
101
|
+
},
|
|
102
|
+
cookie(name: string, value: string, options?: any) {
|
|
103
|
+
let cookieString = `${name}=${value}`;
|
|
104
|
+
if (options) {
|
|
105
|
+
if (options.maxAge) {
|
|
106
|
+
cookieString += `; Max-Age=${options.maxAge}`;
|
|
107
|
+
}
|
|
108
|
+
if (options.domain) {
|
|
109
|
+
cookieString += `; Domain=${options.domain}`;
|
|
110
|
+
}
|
|
111
|
+
if (options.path) {
|
|
112
|
+
cookieString += `; Path=${options.path}`;
|
|
113
|
+
}
|
|
114
|
+
if (options.expires) {
|
|
115
|
+
cookieString += `; Expires=${options.expires.toUTCString()}`;
|
|
116
|
+
}
|
|
117
|
+
if (options.httpOnly) {
|
|
118
|
+
cookieString += `; HttpOnly`;
|
|
119
|
+
}
|
|
120
|
+
if (options.secure) {
|
|
121
|
+
cookieString += `; Secure`;
|
|
122
|
+
}
|
|
123
|
+
if (options.sameSite) {
|
|
124
|
+
cookieString += `; SameSite=${options.sameSite}`;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
this.setHeader('Set-Cookie', cookieString);
|
|
128
|
+
},
|
|
129
|
+
write(chunk: any, encoding?: string | Function, callback?: Function) {
|
|
130
|
+
if (typeof encoding === 'function') {
|
|
131
|
+
callback = encoding;
|
|
132
|
+
encoding = 'utf8';
|
|
133
|
+
}
|
|
134
|
+
if (!this._bodyChunks) {
|
|
135
|
+
this._bodyChunks = [];
|
|
136
|
+
}
|
|
137
|
+
this._bodyChunks.push(chunk);
|
|
138
|
+
if (callback) callback();
|
|
139
|
+
return true;
|
|
140
|
+
},
|
|
141
|
+
pipe(stream: any) {
|
|
142
|
+
this.writableEnded = true;
|
|
143
|
+
|
|
144
|
+
// 如果是 ReadableStream,直接使用
|
|
145
|
+
if (stream instanceof ReadableStream) {
|
|
146
|
+
resolve(
|
|
147
|
+
new Response(stream, {
|
|
148
|
+
status: this.statusCode,
|
|
149
|
+
headers: this._headers as any,
|
|
150
|
+
})
|
|
151
|
+
);
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// 如果是 Node.js 流,转换为 ReadableStream
|
|
156
|
+
const readableStream = new ReadableStream({
|
|
157
|
+
start(controller) {
|
|
158
|
+
stream.on('data', (chunk: any) => {
|
|
159
|
+
controller.enqueue(chunk);
|
|
160
|
+
});
|
|
161
|
+
stream.on('end', () => {
|
|
162
|
+
controller.close();
|
|
163
|
+
});
|
|
164
|
+
stream.on('error', (err: any) => {
|
|
165
|
+
controller.error(err);
|
|
166
|
+
});
|
|
167
|
+
},
|
|
168
|
+
cancel() {
|
|
169
|
+
if (stream.destroy) {
|
|
170
|
+
stream.destroy();
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
resolve(
|
|
176
|
+
new Response(readableStream, {
|
|
177
|
+
status: this.statusCode,
|
|
178
|
+
headers: this._headers as any,
|
|
179
|
+
})
|
|
180
|
+
);
|
|
181
|
+
},
|
|
182
|
+
end(data?: string) {
|
|
183
|
+
this.writableEnded = true;
|
|
184
|
+
|
|
185
|
+
// 合并所有写入的数据块
|
|
186
|
+
let responseData: string | Buffer = data;
|
|
187
|
+
if (this._bodyChunks && this._bodyChunks.length > 0) {
|
|
188
|
+
if (data) this._bodyChunks.push(data);
|
|
189
|
+
// 处理 Buffer 和字符串混合的情况
|
|
190
|
+
const hasBuffer = this._bodyChunks.some(chunk => chunk instanceof Buffer || chunk instanceof Uint8Array);
|
|
191
|
+
if (hasBuffer) {
|
|
192
|
+
// 如果有 Buffer,转换所有内容为 Buffer 后合并
|
|
193
|
+
const buffers = this._bodyChunks.map(chunk => {
|
|
194
|
+
if (chunk instanceof Buffer) return chunk;
|
|
195
|
+
if (chunk instanceof Uint8Array) return Buffer.from(chunk);
|
|
196
|
+
return Buffer.from(String(chunk));
|
|
197
|
+
});
|
|
198
|
+
responseData = Buffer.concat(buffers);
|
|
199
|
+
} else {
|
|
200
|
+
// 纯字符串,直接拼接
|
|
201
|
+
responseData = this._bodyChunks.map(chunk => String(chunk)).join('');
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
resolve(
|
|
206
|
+
new Response(responseData as any, {
|
|
207
|
+
status: this.statusCode,
|
|
208
|
+
headers: this._headers as any,
|
|
209
|
+
})
|
|
210
|
+
);
|
|
211
|
+
},
|
|
212
|
+
};
|
|
213
|
+
// 处理请求体
|
|
214
|
+
if (request.method !== 'GET' && request.method !== 'HEAD') {
|
|
215
|
+
request.text().then((body) => {
|
|
216
|
+
(req as any).body = body;
|
|
217
|
+
requestCallback(req, res);
|
|
218
|
+
});
|
|
219
|
+
} else {
|
|
220
|
+
requestCallback(req, res);
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
},
|
|
224
|
+
websocket: {
|
|
225
|
+
open: (ws: any) => {
|
|
226
|
+
ws.send('connected');
|
|
227
|
+
},
|
|
228
|
+
message: async (ws: any, message: string | Buffer) => {
|
|
229
|
+
const pathname = ws.data.pathname || '';
|
|
230
|
+
const token = ws.data.token || '';
|
|
231
|
+
const id = ws.data.id || '';
|
|
232
|
+
await this.onWebSocket({ ws, message, pathname, token, id });
|
|
233
|
+
},
|
|
234
|
+
close: (ws: any) => {
|
|
235
|
+
// WebSocket 连接关闭
|
|
236
|
+
},
|
|
237
|
+
},
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
if (callback) {
|
|
241
|
+
callback();
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import * as http from 'http';
|
|
2
|
+
|
|
3
|
+
export type Listener = {
|
|
4
|
+
id?: string;
|
|
5
|
+
io?: boolean;
|
|
6
|
+
path?: string;
|
|
7
|
+
fun: (...args: any[]) => Promise<void> | void;
|
|
8
|
+
}
|
|
9
|
+
export type ListenerFun = (...args: any[]) => Promise<void> | void;
|
|
10
|
+
export type OnListener = Listener | ListenerFun | (Listener | ListenerFun)[];
|
|
11
|
+
export type Cors = {
|
|
12
|
+
/**
|
|
13
|
+
* @default '*''
|
|
14
|
+
*/
|
|
15
|
+
origin?: string | undefined;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export type ServerOpts<T = {}> = {
|
|
19
|
+
/**path default `/api/router` */
|
|
20
|
+
path?: string;
|
|
21
|
+
/**handle Fn */
|
|
22
|
+
handle?: (msg?: { path: string; key?: string;[key: string]: any }, ctx?: { req: http.IncomingMessage; res: http.ServerResponse }) => any;
|
|
23
|
+
cors?: Cors;
|
|
24
|
+
io?: boolean;
|
|
25
|
+
} & T;
|
|
26
|
+
|
|
27
|
+
export interface ServerType {
|
|
28
|
+
path?: string;
|
|
29
|
+
server?: any;
|
|
30
|
+
handle: ServerOpts['handle'];
|
|
31
|
+
setHandle(handle?: any): void;
|
|
32
|
+
listeners: Listener[];
|
|
33
|
+
listen(port: number, hostname?: string, backlog?: number, listeningListener?: () => void): void;
|
|
34
|
+
listen(port: number, hostname?: string, listeningListener?: () => void): void;
|
|
35
|
+
listen(port: number, backlog?: number, listeningListener?: () => void): void;
|
|
36
|
+
listen(port: number, listeningListener?: () => void): void;
|
|
37
|
+
listen(path: string, backlog?: number, listeningListener?: () => void): void;
|
|
38
|
+
listen(path: string, listeningListener?: () => void): void;
|
|
39
|
+
listen(handle: any, backlog?: number, listeningListener?: () => void): void;
|
|
40
|
+
listen(handle: any, listeningListener?: () => void): void;
|
|
41
|
+
/**
|
|
42
|
+
* 兜底监听,当除开 `/api/router` 之外的请求,框架只监听一个api,所以有其他的请求都执行其他的监听
|
|
43
|
+
* @description 主要是为了兼容其他的监听
|
|
44
|
+
* @param listener
|
|
45
|
+
*/
|
|
46
|
+
on(listener: OnListener): void;
|
|
47
|
+
onWebSocket({ ws, message, pathname, token, id }: { ws: WS; message: string | Buffer; pathname: string, token?: string, id?: string }): void;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
type WS = {
|
|
51
|
+
send: (data: any) => void;
|
|
52
|
+
close: () => void;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export type RouterReq<T = {}> = {
|
|
56
|
+
url: string;
|
|
57
|
+
method: string;
|
|
58
|
+
headers: Record<string, string>;
|
|
59
|
+
socket?: {
|
|
60
|
+
remoteAddress?: string;
|
|
61
|
+
remotePort?: number;
|
|
62
|
+
};
|
|
63
|
+
cookies?: Record<string, string>;
|
|
64
|
+
} & T;
|
|
65
|
+
|
|
66
|
+
export type RouterRes<T = {}> = {
|
|
67
|
+
statusCode: number;
|
|
68
|
+
headersSent: boolean;
|
|
69
|
+
_headers: Record<string, string | string[]>;
|
|
70
|
+
_bodyChunks: any[];
|
|
71
|
+
writableEnded: boolean;
|
|
72
|
+
writeHead: (statusCode: number, headers?: Record<string, string>) => void;
|
|
73
|
+
setHeader: (name: string, value: string | string[]) => void;
|
|
74
|
+
cookie: (name: string, value: string, options?: any) => void;
|
|
75
|
+
write: (chunk: any) => void;
|
|
76
|
+
pipe: (stream: any) => void;
|
|
77
|
+
end: (data?: any) => void;
|
|
78
|
+
} & T;
|
package/src/server/server.ts
CHANGED
|
@@ -1,64 +1,22 @@
|
|
|
1
|
-
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
2
1
|
import http from 'node:http';
|
|
3
2
|
import https from 'node:https';
|
|
4
3
|
import http2 from 'node:http2';
|
|
5
|
-
import {
|
|
6
|
-
import
|
|
7
|
-
|
|
4
|
+
import { isBun } from '../utils/is-engine.ts';
|
|
5
|
+
import { ServerType, Listener, ServerOpts } from './server-type.ts';
|
|
6
|
+
import { ServerBase } from './server-base.ts';
|
|
7
|
+
import { WsServer } from './ws-server.ts';
|
|
8
8
|
|
|
9
|
-
type CookieFn = (name: string, value: string, options?: cookie.SerializeOptions, end?: boolean) => void;
|
|
10
|
-
|
|
11
|
-
export type HandleCtx = {
|
|
12
|
-
req: IncomingMessage & { cookies: Record<string, string> };
|
|
13
|
-
res: ServerResponse & {
|
|
14
|
-
/**
|
|
15
|
-
* cookie 函数, end 参数用于设置是否立即设置到响应头,设置了后面的cookie再设置会覆盖前面的
|
|
16
|
-
*/
|
|
17
|
-
cookie: CookieFn; //
|
|
18
|
-
};
|
|
19
|
-
};
|
|
20
|
-
// 实现函数
|
|
21
|
-
export function createHandleCtx(req: IncomingMessage, res: ServerResponse): HandleCtx {
|
|
22
|
-
// 用于存储所有的 Set-Cookie 字符串
|
|
23
|
-
const cookies: string[] = [];
|
|
24
|
-
let handReq = req as HandleCtx['req'];
|
|
25
|
-
let handRes = res as HandleCtx['res'];
|
|
26
|
-
// 扩展 res.cookie 方法
|
|
27
|
-
const cookieFn: CookieFn = (name, value, options = {}, end = true) => {
|
|
28
|
-
// 序列化新的 Cookie
|
|
29
|
-
const serializedCookie = cookie.serialize(name, value, options);
|
|
30
|
-
cookies.push(serializedCookie); // 将新的 Cookie 添加到数组
|
|
31
|
-
if (end) {
|
|
32
|
-
// 如果设置了 end 参数,则立即设置到响应头
|
|
33
|
-
res.setHeader('Set-Cookie', cookies);
|
|
34
|
-
}
|
|
35
|
-
};
|
|
36
|
-
// 解析请求中的现有 Cookie
|
|
37
|
-
const parsedCookies = cookie.parse(req.headers.cookie || '');
|
|
38
|
-
handReq.cookies = parsedCookies;
|
|
39
|
-
handRes.cookie = cookieFn;
|
|
40
|
-
// 返回扩展的上下文
|
|
41
|
-
return {
|
|
42
|
-
req: handReq,
|
|
43
|
-
res: handRes,
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
9
|
export type Cors = {
|
|
47
10
|
/**
|
|
48
11
|
* @default '*''
|
|
49
12
|
*/
|
|
50
13
|
origin?: string | undefined;
|
|
51
14
|
};
|
|
52
|
-
export type
|
|
53
|
-
/**path default `/api/router` */
|
|
54
|
-
path?: string;
|
|
55
|
-
/**handle Fn */
|
|
56
|
-
handle?: (msg?: { path: string; key?: string;[key: string]: any }, ctx?: { req: http.IncomingMessage; res: http.ServerResponse }) => any;
|
|
57
|
-
cors?: Cors;
|
|
15
|
+
export type ServerNodeOpts = ServerOpts<{
|
|
58
16
|
httpType?: 'http' | 'https' | 'http2';
|
|
59
17
|
httpsKey?: string;
|
|
60
18
|
httpsCert?: string;
|
|
61
|
-
}
|
|
19
|
+
}>;
|
|
62
20
|
export const resultError = (error: string, code = 500) => {
|
|
63
21
|
const r = {
|
|
64
22
|
code: code,
|
|
@@ -67,41 +25,39 @@ export const resultError = (error: string, code = 500) => {
|
|
|
67
25
|
return JSON.stringify(r);
|
|
68
26
|
};
|
|
69
27
|
|
|
70
|
-
export class
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
private _callback: any;
|
|
75
|
-
private cors: Cors;
|
|
76
|
-
private hasOn = false;
|
|
28
|
+
export class ServerNode extends ServerBase implements ServerType {
|
|
29
|
+
declare _server: http.Server | https.Server | http2.Http2SecureServer;
|
|
30
|
+
declare _callback: any;
|
|
31
|
+
declare cors: Cors;
|
|
77
32
|
private httpType = 'http';
|
|
33
|
+
declare listeners: Listener[];
|
|
78
34
|
private options = {
|
|
79
35
|
key: '',
|
|
80
36
|
cert: '',
|
|
81
37
|
};
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
this.cors = opts?.cors;
|
|
38
|
+
io: WsServer | undefined;
|
|
39
|
+
constructor(opts?: ServerNodeOpts) {
|
|
40
|
+
super(opts);
|
|
86
41
|
this.httpType = opts?.httpType || 'http';
|
|
87
42
|
this.options = {
|
|
88
43
|
key: opts?.httpsKey || '',
|
|
89
44
|
cert: opts?.httpsCert || '',
|
|
90
45
|
};
|
|
46
|
+
const io = opts?.io ?? false;
|
|
47
|
+
if (io) {
|
|
48
|
+
this.io = new WsServer(this);
|
|
49
|
+
}
|
|
91
50
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
listen(path: string, backlog?: number, listeningListener?: () => void): void;
|
|
97
|
-
listen(path: string, listeningListener?: () => void): void;
|
|
98
|
-
listen(handle: any, backlog?: number, listeningListener?: () => void): void;
|
|
99
|
-
listen(handle: any, listeningListener?: () => void): void;
|
|
100
|
-
listen(...args: any[]) {
|
|
51
|
+
customListen(...args: any[]): void {
|
|
52
|
+
if (isBun) {
|
|
53
|
+
throw new Error('Use BunServer from server-bun module for Bun runtime');
|
|
54
|
+
}
|
|
101
55
|
this._server = this.createServer();
|
|
102
56
|
const callback = this.createCallback();
|
|
103
57
|
this._server.on('request', callback);
|
|
104
58
|
this._server.listen(...args);
|
|
59
|
+
|
|
60
|
+
this.io?.listen();
|
|
105
61
|
}
|
|
106
62
|
createServer() {
|
|
107
63
|
let server: http.Server | https.Server | http2.Http2SecureServer;
|
|
@@ -132,112 +88,4 @@ export class Server {
|
|
|
132
88
|
server = http.createServer();
|
|
133
89
|
return server;
|
|
134
90
|
}
|
|
135
|
-
|
|
136
|
-
this.handle = handle;
|
|
137
|
-
}
|
|
138
|
-
/**
|
|
139
|
-
* get callback
|
|
140
|
-
* @returns
|
|
141
|
-
*/
|
|
142
|
-
createCallback() {
|
|
143
|
-
const path = this.path;
|
|
144
|
-
const handle = this.handle;
|
|
145
|
-
const cors = this.cors;
|
|
146
|
-
const _callback = async (req: IncomingMessage, res: ServerResponse) => {
|
|
147
|
-
// only handle /api/router
|
|
148
|
-
if (req.url === '/favicon.ico') {
|
|
149
|
-
return;
|
|
150
|
-
}
|
|
151
|
-
if (res.headersSent) {
|
|
152
|
-
// 程序已经在其他地方响应了
|
|
153
|
-
return;
|
|
154
|
-
}
|
|
155
|
-
if (this.hasOn && !req.url.startsWith(path)) {
|
|
156
|
-
// 其他监听存在,不判断不是当前路径的请求,
|
|
157
|
-
// 也就是不处理!url.startsWith(path)这个请求了
|
|
158
|
-
// 交给其他监听处理
|
|
159
|
-
return;
|
|
160
|
-
}
|
|
161
|
-
if (cors) {
|
|
162
|
-
res.setHeader('Access-Control-Allow-Origin', cors?.origin || '*'); // 允许所有域名的请求访问,可以根据需要设置具体的域名
|
|
163
|
-
res.setHeader('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept, Authorization');
|
|
164
|
-
res.setHeader('Access-Control-Allow-Methods', 'GET, POST');
|
|
165
|
-
if (req.method === 'OPTIONS') {
|
|
166
|
-
res.end();
|
|
167
|
-
return;
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
const url = req.url;
|
|
171
|
-
if (!url.startsWith(path)) {
|
|
172
|
-
res.end(resultError(`not path:[${path}]`));
|
|
173
|
-
return;
|
|
174
|
-
}
|
|
175
|
-
const messages = await handleServer(req, res);
|
|
176
|
-
if (!handle) {
|
|
177
|
-
res.end(resultError('no handle'));
|
|
178
|
-
return;
|
|
179
|
-
}
|
|
180
|
-
try {
|
|
181
|
-
const end = await handle(messages as any, { req, res });
|
|
182
|
-
if (res.writableEnded) {
|
|
183
|
-
// 如果响应已经结束,则不进行任何操作
|
|
184
|
-
return;
|
|
185
|
-
}
|
|
186
|
-
res.setHeader('Content-Type', 'application/json; charset=utf-8');
|
|
187
|
-
if (typeof end === 'string') {
|
|
188
|
-
res.end(end);
|
|
189
|
-
} else {
|
|
190
|
-
res.end(JSON.stringify(end));
|
|
191
|
-
}
|
|
192
|
-
} catch (e) {
|
|
193
|
-
console.error(e);
|
|
194
|
-
res.setHeader('Content-Type', 'application/json; charset=utf-8');
|
|
195
|
-
if (e.code && typeof e.code === 'number') {
|
|
196
|
-
res.end(resultError(e.message || `Router Server error`, e.code));
|
|
197
|
-
} else {
|
|
198
|
-
res.end(resultError('Router Server error'));
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
};
|
|
202
|
-
this._callback = _callback;
|
|
203
|
-
return _callback;
|
|
204
|
-
}
|
|
205
|
-
get handleServer() {
|
|
206
|
-
return this._callback;
|
|
207
|
-
}
|
|
208
|
-
set handleServer(fn: any) {
|
|
209
|
-
this._callback = fn;
|
|
210
|
-
}
|
|
211
|
-
/**
|
|
212
|
-
* 兜底监听,当除开 `/api/router` 之外的请求,框架只监听一个api,所以有其他的请求都执行其他的监听
|
|
213
|
-
* @description 主要是为了兼容其他的监听
|
|
214
|
-
* @param listener
|
|
215
|
-
*/
|
|
216
|
-
on(listener: Listener | Listener[]) {
|
|
217
|
-
this._server = this._server || this.createServer();
|
|
218
|
-
this._server.removeAllListeners('request');
|
|
219
|
-
this.hasOn = true;
|
|
220
|
-
if (Array.isArray(listener)) {
|
|
221
|
-
listener.forEach((l) => this._server.on('request', l));
|
|
222
|
-
} else {
|
|
223
|
-
this._server.on('request', listener);
|
|
224
|
-
}
|
|
225
|
-
const callbackListener = this._callback || this.createCallback();
|
|
226
|
-
this._server.on('request', callbackListener);
|
|
227
|
-
return () => {
|
|
228
|
-
if (Array.isArray(listener)) {
|
|
229
|
-
listener.forEach((l) => this._server.removeListener('request', l as Listener));
|
|
230
|
-
} else {
|
|
231
|
-
this._server.removeListener('request', listener as Listener);
|
|
232
|
-
}
|
|
233
|
-
this.hasOn = false;
|
|
234
|
-
this._server.removeListener('request', callbackListener);
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
get callback() {
|
|
238
|
-
return this._callback || this.createCallback();
|
|
239
|
-
}
|
|
240
|
-
get server() {
|
|
241
|
-
return this._server;
|
|
242
|
-
}
|
|
243
|
-
}
|
|
91
|
+
}
|