@cjwddz/mirror 0.0.18-alpha → 1.2.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 (65) hide show
  1. package/README.md +89 -222
  2. package/dist/cli/client.d.ts +19 -0
  3. package/dist/cli/client.d.ts.map +1 -0
  4. package/dist/cli/client.js +107 -0
  5. package/dist/cli/client.js.map +1 -0
  6. package/dist/cli/index.d.ts +1 -1
  7. package/dist/cli/index.js +88 -14
  8. package/dist/cli/index.js.map +1 -1
  9. package/dist/cli/server.d.ts +19 -0
  10. package/dist/cli/server.d.ts.map +1 -0
  11. package/dist/cli/server.js +82 -0
  12. package/dist/cli/server.js.map +1 -0
  13. package/dist/core/http-tunnel-client.d.ts +64 -0
  14. package/dist/core/http-tunnel-client.d.ts.map +1 -0
  15. package/dist/core/http-tunnel-client.js +315 -0
  16. package/dist/core/http-tunnel-client.js.map +1 -0
  17. package/dist/core/http-tunnel-protocol.d.ts +86 -0
  18. package/dist/core/http-tunnel-protocol.d.ts.map +1 -0
  19. package/dist/core/http-tunnel-protocol.js +29 -0
  20. package/dist/core/http-tunnel-protocol.js.map +1 -0
  21. package/dist/core/http-tunnel-server.d.ts +74 -0
  22. package/dist/core/http-tunnel-server.d.ts.map +1 -0
  23. package/dist/core/http-tunnel-server.js +317 -0
  24. package/dist/core/http-tunnel-server.js.map +1 -0
  25. package/dist/core/output-buffer.d.ts +42 -0
  26. package/dist/core/output-buffer.d.ts.map +1 -0
  27. package/dist/core/output-buffer.js +66 -0
  28. package/dist/core/output-buffer.js.map +1 -0
  29. package/dist/core/token-manager.d.ts +29 -0
  30. package/dist/core/token-manager.d.ts.map +1 -0
  31. package/dist/core/token-manager.js +90 -0
  32. package/dist/core/token-manager.js.map +1 -0
  33. package/dist/index.d.ts +4 -8
  34. package/dist/index.d.ts.map +1 -1
  35. package/dist/index.js +4 -8
  36. package/dist/index.js.map +1 -1
  37. package/package.json +12 -10
  38. package/dist/cli/host-impl.d.ts +0 -58
  39. package/dist/cli/host-impl.d.ts.map +0 -1
  40. package/dist/cli/host-impl.js +0 -547
  41. package/dist/cli/host-impl.js.map +0 -1
  42. package/dist/cli/host.d.ts +0 -9
  43. package/dist/cli/host.d.ts.map +0 -1
  44. package/dist/cli/host.js +0 -57
  45. package/dist/cli/host.js.map +0 -1
  46. package/dist/cli/link.d.ts +0 -6
  47. package/dist/cli/link.d.ts.map +0 -1
  48. package/dist/cli/link.js +0 -899
  49. package/dist/cli/link.js.map +0 -1
  50. package/dist/cli/ui/MirrorUI.d.ts +0 -22
  51. package/dist/cli/ui/MirrorUI.d.ts.map +0 -1
  52. package/dist/cli/ui/MirrorUI.js +0 -71
  53. package/dist/cli/ui/MirrorUI.js.map +0 -1
  54. package/dist/cli/ui/interactive-terminal.d.ts +0 -3
  55. package/dist/cli/ui/interactive-terminal.d.ts.map +0 -1
  56. package/dist/cli/ui/interactive-terminal.js +0 -150
  57. package/dist/cli/ui/interactive-terminal.js.map +0 -1
  58. package/dist/core/process-manager.d.ts +0 -19
  59. package/dist/core/process-manager.d.ts.map +0 -1
  60. package/dist/core/process-manager.js +0 -76
  61. package/dist/core/process-manager.js.map +0 -1
  62. package/dist/transport/pty.d.ts +0 -7
  63. package/dist/transport/pty.d.ts.map +0 -1
  64. package/dist/transport/pty.js +0 -26
  65. package/dist/transport/pty.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":";AAEA;;GAEG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAE3D,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,UAAU;AACV,MAAM,OAAO,GAAG,MAAM,iBAAiB,EAAE,CAAC;AAE1C,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,WAAW,CAAC,eAAe,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;AAErE,OAAO;KACJ,OAAO,CAAC,MAAM,CAAC;KACf,WAAW,CAAC,cAAc,CAAC;KAC3B,MAAM,CAAC,mBAAmB,EAAE,KAAK,EAAE,MAAM,CAAC;KAC1C,MAAM,CAAC,aAAa,EAAE,iBAAiB,CAAC;KACxC,MAAM,CAAC,iBAAiB,EAAE,4BAA4B,CAAC;KACvD,MAAM,CAAC,WAAW,CAAC,CAAC;AAEvB,OAAO;KACJ,OAAO,CAAC,MAAM,CAAC;KACf,WAAW,CAAC,YAAY,CAAC;KACzB,QAAQ,CAAC,OAAO,EAAE,8CAA8C,CAAC;KACjE,MAAM,CAAC,WAAW,CAAC,CAAC;AAEvB,OAAO,CAAC,KAAK,EAAE,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Mirror HTTP Tunnel Server CLI 命令
3
+ * 启动 HTTP 隧道服务端,接收 HTTP 请求并通过 WebSocket 隧道转发
4
+ */
5
+ /**
6
+ * 服务器配置选项
7
+ */
8
+ export interface ServerOptions {
9
+ port: number;
10
+ host: string;
11
+ tunnelPort: number;
12
+ token?: string;
13
+ timeout: number;
14
+ }
15
+ /**
16
+ * 启动 HTTP 隧道服务端
17
+ */
18
+ export declare function serverCommand(options: ServerOptions): Promise<void>;
19
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/cli/server.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,wBAAsB,aAAa,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CA2FzE"}
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Mirror HTTP Tunnel Server CLI 命令
3
+ * 启动 HTTP 隧道服务端,接收 HTTP 请求并通过 WebSocket 隧道转发
4
+ */
5
+ import { HttpTunnelServer } from '../core/http-tunnel-server.js';
6
+ import { WebSocketServer } from 'ws';
7
+ /**
8
+ * 启动 HTTP 隧道服务端
9
+ */
10
+ export async function serverCommand(options) {
11
+ const { port, host, tunnelPort, token, timeout } = options;
12
+ console.log('Mirror HTTP Tunnel Server');
13
+ console.log('=========================\n');
14
+ // 创建 WebSocket 服务器(用于隧道连接)
15
+ const wss = new WebSocketServer({ port: tunnelPort, host });
16
+ console.log(`[*] WebSocket tunnel server listening on ${host}:${tunnelPort}`);
17
+ if (token) {
18
+ console.log(`[*] Authentication token: ${token}`);
19
+ }
20
+ // 创建 HTTP 隧道服务端
21
+ const tunnelServer = new HttpTunnelServer({
22
+ port,
23
+ host,
24
+ timeout,
25
+ });
26
+ // 启动 HTTP 服务器
27
+ await tunnelServer.start();
28
+ console.log(`[*] HTTP server listening on http://${host}:${port}\n`);
29
+ console.log('[*] Waiting for client connection...\n');
30
+ let clientConnected = false;
31
+ // 处理 WebSocket 隧道连接
32
+ wss.on('connection', (ws, req) => {
33
+ // 验证 token(如果配置了)
34
+ const url = new URL(req.url || '', `http://${req.headers.host}`);
35
+ const clientToken = url.searchParams.get('token');
36
+ if (token && clientToken !== token) {
37
+ console.log('[!] Client rejected: invalid token');
38
+ ws.close(1008, 'Invalid token');
39
+ return;
40
+ }
41
+ const clientIp = req.socket.remoteAddress || 'unknown';
42
+ console.log(`[✓] Client connected from ${clientIp}`);
43
+ if (!clientConnected) {
44
+ clientConnected = true;
45
+ console.log('[*] HTTP tunnel is now active\n');
46
+ }
47
+ // 设置隧道连接
48
+ tunnelServer.setTunnelConnection(ws);
49
+ // 处理客户端断开
50
+ ws.on('close', () => {
51
+ console.log('[!] Client disconnected');
52
+ console.log('[*] HTTP tunnel is inactive\n');
53
+ clientConnected = false;
54
+ });
55
+ ws.on('error', (error) => {
56
+ console.error('[!] Client error:', error);
57
+ });
58
+ // 发送欢迎消息
59
+ ws.send(JSON.stringify({
60
+ type: 'WELCOME',
61
+ message: 'Connected to Mirror HTTP Tunnel Server',
62
+ serverVersion: '1.2.0',
63
+ }));
64
+ });
65
+ // 处理 WebSocket 服务器错误
66
+ wss.on('error', (error) => {
67
+ console.error('[!] WebSocket server error:', error);
68
+ process.exit(1);
69
+ });
70
+ // 处理进程退出
71
+ const cleanup = async () => {
72
+ console.log('\n[*] Shutting down...');
73
+ await tunnelServer.stop();
74
+ wss.close();
75
+ process.exit(0);
76
+ };
77
+ process.on('SIGINT', cleanup);
78
+ process.on('SIGTERM', cleanup);
79
+ // 防止进程退出
80
+ await new Promise(() => { });
81
+ }
82
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/cli/server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,64 @@
1
+ /**
2
+ * HTTP 隧道客户端
3
+ * 接收来自服务端的 HTTP 请求,转发到本地服务,并将响应返回
4
+ */
5
+ import { WebSocket } from 'ws';
6
+ /**
7
+ * HTTP 隧道客户端配置
8
+ */
9
+ export interface HttpTunnelClientOptions {
10
+ targetUrl: string;
11
+ tunnelWs: WebSocket;
12
+ }
13
+ /**
14
+ * HTTP 隧道客户端类
15
+ */
16
+ export declare class HttpTunnelClient {
17
+ private targetUrl;
18
+ private tunnelWs;
19
+ private httpAgent;
20
+ private httpsAgent;
21
+ private wsClients;
22
+ constructor(options: HttpTunnelClientOptions);
23
+ /**
24
+ * 处理隧道消息
25
+ */
26
+ private handleTunnelMessage;
27
+ /**
28
+ * 处理 HTTP 请求
29
+ */
30
+ private handleHttpRequest;
31
+ /**
32
+ * 处理 WebSocket 升级请求
33
+ */
34
+ private handleWsUpgrade;
35
+ /**
36
+ * 处理 WebSocket 数据消息
37
+ */
38
+ private handleWsData;
39
+ /**
40
+ * 处理 WebSocket 关闭消息
41
+ */
42
+ private handleWsClose;
43
+ /**
44
+ * 发送 HTTP 请求到本地服务
45
+ */
46
+ private sendHttpRequest;
47
+ /**
48
+ * 构建目标 URL
49
+ */
50
+ private buildTargetUrl;
51
+ /**
52
+ * 标准化响应头
53
+ */
54
+ private normalizeHeaders;
55
+ /**
56
+ * 判断 Buffer 是否为 UTF-8 编码
57
+ */
58
+ private isUtf8;
59
+ /**
60
+ * 关闭客户端
61
+ */
62
+ close(): void;
63
+ }
64
+ //# sourceMappingURL=http-tunnel-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http-tunnel-client.d.ts","sourceRoot":"","sources":["../../src/core/http-tunnel-client.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AAW/B;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED;;GAEG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,SAAS,CAAM;IACvB,OAAO,CAAC,QAAQ,CAAY;IAC5B,OAAO,CAAC,SAAS,CAAa;IAC9B,OAAO,CAAC,UAAU,CAAc;IAChC,OAAO,CAAC,SAAS,CAAqC;gBAE1C,OAAO,EAAE,uBAAuB;IAuC5C;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAsC3B;;OAEG;YACW,iBAAiB;IA6C/B;;OAEG;YACW,eAAe;IA+D7B;;OAEG;IACH,OAAO,CAAC,YAAY;IAWpB;;OAEG;IACH,OAAO,CAAC,aAAa;IASrB;;OAEG;IACH,OAAO,CAAC,eAAe;IAuDvB;;OAEG;IACH,OAAO,CAAC,cAAc;IAkCtB;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAYxB;;OAEG;IACH,OAAO,CAAC,MAAM;IASd;;OAEG;IACH,KAAK,IAAI,IAAI;CASd"}
@@ -0,0 +1,315 @@
1
+ /**
2
+ * HTTP 隧道客户端
3
+ * 接收来自服务端的 HTTP 请求,转发到本地服务,并将响应返回
4
+ */
5
+ import http from 'http';
6
+ import https from 'https';
7
+ import { WebSocket } from 'ws';
8
+ import { isHttpTunnelMessage, } from './http-tunnel-protocol.js';
9
+ /**
10
+ * HTTP 隧道客户端类
11
+ */
12
+ export class HttpTunnelClient {
13
+ targetUrl;
14
+ tunnelWs;
15
+ httpAgent;
16
+ httpsAgent;
17
+ wsClients = new Map(); // requestId -> WebSocket client
18
+ constructor(options) {
19
+ this.targetUrl = new URL(options.targetUrl);
20
+ this.tunnelWs = options.tunnelWs;
21
+ // 创建 HTTP 客户端代理
22
+ this.httpAgent = new http.Agent({
23
+ keepAlive: true,
24
+ keepAliveMsecs: 1000,
25
+ maxSockets: Infinity,
26
+ maxFreeSockets: 256,
27
+ });
28
+ this.httpsAgent = new https.Agent({
29
+ keepAlive: true,
30
+ keepAliveMsecs: 1000,
31
+ maxSockets: Infinity,
32
+ maxFreeSockets: 256,
33
+ rejectUnauthorized: false, // 允许自签名证书
34
+ });
35
+ // 处理隧道消息
36
+ this.tunnelWs.on('message', (data) => {
37
+ this.handleTunnelMessage(data);
38
+ });
39
+ // 处理隧道错误
40
+ this.tunnelWs.on('error', (error) => {
41
+ console.error('[HTTP Tunnel Client] Tunnel error:', error);
42
+ });
43
+ // 处理隧道关闭
44
+ this.tunnelWs.on('close', () => {
45
+ console.log('[HTTP Tunnel Client] Tunnel closed');
46
+ // 关闭所有 WebSocket 客户端
47
+ this.wsClients.forEach((ws) => ws.close());
48
+ this.wsClients.clear();
49
+ });
50
+ }
51
+ /**
52
+ * 处理隧道消息
53
+ */
54
+ handleTunnelMessage(data) {
55
+ try {
56
+ const message = JSON.parse(data.toString());
57
+ if (!isHttpTunnelMessage(message)) {
58
+ console.warn('[HTTP Tunnel Client] Invalid message received');
59
+ return;
60
+ }
61
+ switch (message.type) {
62
+ case 'WELCOME':
63
+ // 忽略欢迎消息
64
+ break;
65
+ case 'HTTP_REQUEST':
66
+ this.handleHttpRequest(message);
67
+ break;
68
+ case 'WS_UPGRADE':
69
+ this.handleWsUpgrade(message);
70
+ break;
71
+ case 'WS_DATA':
72
+ this.handleWsData(message);
73
+ break;
74
+ case 'WS_CLOSE':
75
+ this.handleWsClose(message);
76
+ break;
77
+ default:
78
+ console.warn('[HTTP Tunnel Client] Unexpected message type:', message.type);
79
+ }
80
+ }
81
+ catch (error) {
82
+ console.error('[HTTP Tunnel Client] Error handling tunnel message:', error);
83
+ }
84
+ }
85
+ /**
86
+ * 处理 HTTP 请求
87
+ */
88
+ async handleHttpRequest(message) {
89
+ try {
90
+ const { requestId, method, url: requestUrl, headers, body } = message;
91
+ // 构建完整的目标 URL
92
+ const targetUrl = this.buildTargetUrl(requestUrl);
93
+ // 请求体
94
+ const bodyBuffer = body ? Buffer.from(body, 'base64') : undefined;
95
+ // 发送请求到本地服务
96
+ const response = await this.sendHttpRequest(method, targetUrl, headers, bodyBuffer);
97
+ // 构建响应消息
98
+ const responseMessage = {
99
+ type: 'HTTP_RESPONSE',
100
+ requestId,
101
+ statusCode: response.statusCode,
102
+ statusMessage: response.statusMessage || 'OK',
103
+ headers: this.normalizeHeaders(response.headers),
104
+ body: response.body ? response.body.toString('base64') : undefined,
105
+ };
106
+ // 发送响应回隧道
107
+ this.tunnelWs.send(JSON.stringify(responseMessage));
108
+ }
109
+ catch (error) {
110
+ console.error('[HTTP Tunnel Client] Error handling HTTP request:', error);
111
+ // 发送错误响应
112
+ const errorMessage = {
113
+ type: 'TUNNEL_ERROR',
114
+ requestId: message.requestId,
115
+ error: error instanceof Error ? error.message : 'Unknown error',
116
+ code: 'REQUEST_ERROR',
117
+ };
118
+ this.tunnelWs.send(JSON.stringify(errorMessage));
119
+ }
120
+ }
121
+ /**
122
+ * 处理 WebSocket 升级请求
123
+ */
124
+ async handleWsUpgrade(message) {
125
+ try {
126
+ const { requestId, url: requestUrl, headers } = message;
127
+ // 构建完整的目标 URL
128
+ const targetUrl = this.buildTargetUrl(requestUrl, 'ws:');
129
+ // 创建 WebSocket 客户端连接
130
+ const wsClient = new WebSocket(targetUrl, { headers });
131
+ this.wsClients.set(requestId, wsClient);
132
+ // 处理 WebSocket 客户端消息
133
+ wsClient.on('message', (data) => {
134
+ const message = {
135
+ type: 'WS_DATA',
136
+ requestId,
137
+ data: data.toString('base64'),
138
+ isBinary: data instanceof Buffer && !this.isUtf8(data),
139
+ };
140
+ this.tunnelWs.send(JSON.stringify(message));
141
+ });
142
+ // 处理 WebSocket 客户端关闭
143
+ wsClient.on('close', (code, reason) => {
144
+ const message = {
145
+ type: 'WS_CLOSE',
146
+ requestId,
147
+ code,
148
+ reason: reason ? reason.toString() : undefined,
149
+ };
150
+ this.tunnelWs.send(JSON.stringify(message));
151
+ this.wsClients.delete(requestId);
152
+ });
153
+ // 处理 WebSocket 客户端错误
154
+ wsClient.on('error', (error) => {
155
+ console.error('[HTTP Tunnel Client] WebSocket client error:', error);
156
+ const message = {
157
+ type: 'WS_CLOSE',
158
+ requestId,
159
+ code: 1011,
160
+ reason: error.message,
161
+ };
162
+ this.tunnelWs.send(JSON.stringify(message));
163
+ this.wsClients.delete(requestId);
164
+ });
165
+ console.log(`[HTTP Tunnel Client] WebSocket connection established: ${targetUrl}`);
166
+ }
167
+ catch (error) {
168
+ console.error('[HTTP Tunnel Client] Error handling WebSocket upgrade:', error);
169
+ const errorMessage = {
170
+ type: 'TUNNEL_ERROR',
171
+ requestId: message.requestId,
172
+ error: error instanceof Error ? error.message : 'Unknown error',
173
+ code: 'WS_ERROR',
174
+ };
175
+ this.tunnelWs.send(JSON.stringify(errorMessage));
176
+ }
177
+ }
178
+ /**
179
+ * 处理 WebSocket 数据消息
180
+ */
181
+ handleWsData(message) {
182
+ const wsClient = this.wsClients.get(message.requestId);
183
+ if (wsClient && wsClient.readyState === WebSocket.OPEN) {
184
+ const data = message.isBinary ? Buffer.from(message.data, 'base64') : message.data;
185
+ wsClient.send(data);
186
+ }
187
+ else {
188
+ console.warn('[HTTP Tunnel Client] WebSocket client not found for:', message.requestId);
189
+ }
190
+ }
191
+ /**
192
+ * 处理 WebSocket 关闭消息
193
+ */
194
+ handleWsClose(message) {
195
+ const wsClient = this.wsClients.get(message.requestId);
196
+ if (wsClient) {
197
+ wsClient.close(message.code, message.reason);
198
+ this.wsClients.delete(message.requestId);
199
+ }
200
+ }
201
+ /**
202
+ * 发送 HTTP 请求到本地服务
203
+ */
204
+ sendHttpRequest(method, targetUrl, headers, body) {
205
+ return new Promise((resolve, reject) => {
206
+ const urlObj = new URL(targetUrl);
207
+ const isHttps = urlObj.protocol === 'https:';
208
+ const httpModule = isHttps ? https : http;
209
+ // 移除可能导致问题的头部
210
+ const safeHeaders = { ...headers };
211
+ delete safeHeaders['host'];
212
+ delete safeHeaders['content-length'];
213
+ const options = {
214
+ hostname: urlObj.hostname,
215
+ port: urlObj.port || (isHttps ? 443 : 80),
216
+ path: urlObj.pathname + urlObj.search,
217
+ method,
218
+ headers: safeHeaders,
219
+ agent: isHttps ? this.httpsAgent : this.httpAgent,
220
+ rejectUnauthorized: false,
221
+ };
222
+ const req = httpModule.request(options, (res) => {
223
+ const chunks = [];
224
+ res.on('data', (chunk) => {
225
+ chunks.push(chunk);
226
+ });
227
+ res.on('end', () => {
228
+ resolve({
229
+ statusCode: res.statusCode || 200,
230
+ statusMessage: res.statusMessage,
231
+ headers: res.headers,
232
+ body: chunks.length > 0 ? Buffer.concat(chunks) : undefined,
233
+ });
234
+ });
235
+ });
236
+ req.on('error', (error) => {
237
+ reject(error);
238
+ });
239
+ if (body) {
240
+ req.write(body);
241
+ }
242
+ req.end();
243
+ });
244
+ }
245
+ /**
246
+ * 构建目标 URL
247
+ */
248
+ buildTargetUrl(requestUrl, protocolOverride) {
249
+ // 解析请求 URL(可能包含完整 URL 或仅路径)
250
+ let requestPath = '';
251
+ let requestSearch = '';
252
+ // 检查是否为完整 URL
253
+ if (requestUrl.startsWith('http://') || requestUrl.startsWith('https://')) {
254
+ const urlObj = new URL(requestUrl);
255
+ requestPath = urlObj.pathname;
256
+ requestSearch = urlObj.search;
257
+ }
258
+ else {
259
+ // 处理相对路径
260
+ const queryIndex = requestUrl.indexOf('?');
261
+ if (queryIndex !== -1) {
262
+ requestPath = requestUrl.substring(0, queryIndex);
263
+ requestSearch = requestUrl.substring(queryIndex);
264
+ }
265
+ else {
266
+ requestPath = requestUrl;
267
+ }
268
+ }
269
+ // 使用目标协议和主机
270
+ const protocol = protocolOverride || this.targetUrl.protocol || 'http:';
271
+ const hostname = this.targetUrl.hostname || 'localhost';
272
+ const port = this.targetUrl.port || (protocol === 'https:' ? '443' : '80');
273
+ // 构建完整 URL
274
+ const portStr = (protocol === 'http:' && port === '80') || (protocol === 'https:' && port === '443')
275
+ ? ''
276
+ : `:${port}`;
277
+ return `${protocol}//${hostname}${portStr}${requestPath}${requestSearch}`;
278
+ }
279
+ /**
280
+ * 标准化响应头
281
+ */
282
+ normalizeHeaders(headers) {
283
+ const normalized = {};
284
+ for (const [key, value] of Object.entries(headers)) {
285
+ if (value !== undefined) {
286
+ normalized[key.toLowerCase()] = Array.isArray(value) ? value.join(', ') : value;
287
+ }
288
+ }
289
+ return normalized;
290
+ }
291
+ /**
292
+ * 判断 Buffer 是否为 UTF-8 编码
293
+ */
294
+ isUtf8(buffer) {
295
+ try {
296
+ buffer.toString('utf8');
297
+ return true;
298
+ }
299
+ catch {
300
+ return false;
301
+ }
302
+ }
303
+ /**
304
+ * 关闭客户端
305
+ */
306
+ close() {
307
+ // 关闭所有 WebSocket 客户端
308
+ this.wsClients.forEach((ws) => ws.close());
309
+ this.wsClients.clear();
310
+ // 关闭代理
311
+ this.httpAgent.destroy();
312
+ this.httpsAgent.destroy();
313
+ }
314
+ }
315
+ //# sourceMappingURL=http-tunnel-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http-tunnel-client.js","sourceRoot":"","sources":["../../src/core/http-tunnel-client.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AAC/B,OAAO,EAOL,mBAAmB,GACpB,MAAM,2BAA2B,CAAC;AAUnC;;GAEG;AACH,MAAM,OAAO,gBAAgB;IACnB,SAAS,CAAM;IACf,QAAQ,CAAY;IACpB,SAAS,CAAa;IACtB,UAAU,CAAc;IACxB,SAAS,GAA2B,IAAI,GAAG,EAAE,CAAC,CAAC,gCAAgC;IAEvF,YAAY,OAAgC;QAC1C,IAAI,CAAC,SAAS,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QAC5C,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;QAEjC,gBAAgB;QAChB,IAAI,CAAC,SAAS,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC;YAC9B,SAAS,EAAE,IAAI;YACf,cAAc,EAAE,IAAI;YACpB,UAAU,EAAE,QAAQ;YACpB,cAAc,EAAE,GAAG;SACpB,CAAC,CAAC;QAEH,IAAI,CAAC,UAAU,GAAG,IAAI,KAAK,CAAC,KAAK,CAAC;YAChC,SAAS,EAAE,IAAI;YACf,cAAc,EAAE,IAAI;YACpB,UAAU,EAAE,QAAQ;YACpB,cAAc,EAAE,GAAG;YACnB,kBAAkB,EAAE,KAAK,EAAE,UAAU;SACtC,CAAC,CAAC;QAEH,SAAS;QACT,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,IAAY,EAAE,EAAE;YAC3C,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAC;QACjC,CAAC,CAAC,CAAC;QAEH,SAAS;QACT,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;YAClC,OAAO,CAAC,KAAK,CAAC,oCAAoC,EAAE,KAAK,CAAC,CAAC;QAC7D,CAAC,CAAC,CAAC;QAEH,SAAS;QACT,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;YAC7B,OAAO,CAAC,GAAG,CAAC,oCAAoC,CAAC,CAAC;YAClD,qBAAqB;YACrB,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,CAAC;YAC3C,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,CAAC;QACzB,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;OAEG;IACK,mBAAmB,CAAC,IAAY;QACtC,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAsB,CAAC;YAEjE,IAAI,CAAC,mBAAmB,CAAC,OAAO,CAAC,EAAE,CAAC;gBAClC,OAAO,CAAC,IAAI,CAAC,+CAA+C,CAAC,CAAC;gBAC9D,OAAO;YACT,CAAC;YAED,QAAQ,OAAO,CAAC,IAAI,EAAE,CAAC;gBACrB,KAAK,SAAS;oBACZ,SAAS;oBACT,MAAM;gBAER,KAAK,cAAc;oBACjB,IAAI,CAAC,iBAAiB,CAAC,OAA6B,CAAC,CAAC;oBACtD,MAAM;gBAER,KAAK,YAAY;oBACf,IAAI,CAAC,eAAe,CAAC,OAA2B,CAAC,CAAC;oBAClD,MAAM;gBAER,KAAK,SAAS;oBACZ,IAAI,CAAC,YAAY,CAAC,OAAwB,CAAC,CAAC;oBAC5C,MAAM;gBAER,KAAK,UAAU;oBACb,IAAI,CAAC,aAAa,CAAC,OAAyB,CAAC,CAAC;oBAC9C,MAAM;gBAER;oBACE,OAAO,CAAC,IAAI,CAAC,+CAA+C,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;YAChF,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,qDAAqD,EAAE,KAAK,CAAC,CAAC;QAC9E,CAAC;IACH,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,iBAAiB,CAAC,OAA2B;QACzD,IAAI,CAAC;YACH,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC;YAEtE,cAAc;YACd,MAAM,SAAS,GAAG,IAAI,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC;YAElD,MAAM;YACN,MAAM,UAAU,GAAG,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;YAElE,YAAY;YACZ,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,eAAe,CACzC,MAAM,EACN,SAAS,EACT,OAAO,EACP,UAAU,CACX,CAAC;YAEF,SAAS;YACT,MAAM,eAAe,GAAwB;gBAC3C,IAAI,EAAE,eAAe;gBACrB,SAAS;gBACT,UAAU,EAAE,QAAQ,CAAC,UAAU;gBAC/B,aAAa,EAAE,QAAQ,CAAC,aAAa,IAAI,IAAI;gBAC7C,OAAO,EAAE,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,OAAO,CAAC;gBAChD,IAAI,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,SAAS;aACnE,CAAC;YAEF,UAAU;YACV,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,eAAe,CAAC,CAAC,CAAC;QACtD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,mDAAmD,EAAE,KAAK,CAAC,CAAC;YAE1E,SAAS;YACT,MAAM,YAAY,GAAG;gBACnB,IAAI,EAAE,cAAuB;gBAC7B,SAAS,EAAE,OAAO,CAAC,SAAS;gBAC5B,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe;gBAC/D,IAAI,EAAE,eAAe;aACtB,CAAC;YAEF,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC,CAAC;QACnD,CAAC;IACH,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,eAAe,CAAC,OAAyB;QACrD,IAAI,CAAC;YACH,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,UAAU,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;YAExD,cAAc;YACd,MAAM,SAAS,GAAG,IAAI,CAAC,cAAc,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;YAEzD,qBAAqB;YACrB,MAAM,QAAQ,GAAG,IAAI,SAAS,CAAC,SAAS,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC;YAEvD,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;YAExC,qBAAqB;YACrB,QAAQ,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,IAAY,EAAE,EAAE;gBACtC,MAAM,OAAO,GAAkB;oBAC7B,IAAI,EAAE,SAAS;oBACf,SAAS;oBACT,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC;oBAC7B,QAAQ,EAAE,IAAI,YAAY,MAAM,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC;iBACvD,CAAC;gBACF,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC;YAC9C,CAAC,CAAC,CAAC;YAEH,qBAAqB;YACrB,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE;gBACpC,MAAM,OAAO,GAAmB;oBAC9B,IAAI,EAAE,UAAU;oBAChB,SAAS;oBACT,IAAI;oBACJ,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,SAAS;iBAC/C,CAAC;gBACF,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC;gBAC5C,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;YACnC,CAAC,CAAC,CAAC;YAEH,qBAAqB;YACrB,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;gBAC7B,OAAO,CAAC,KAAK,CAAC,8CAA8C,EAAE,KAAK,CAAC,CAAC;gBACrE,MAAM,OAAO,GAAmB;oBAC9B,IAAI,EAAE,UAAU;oBAChB,SAAS;oBACT,IAAI,EAAE,IAAI;oBACV,MAAM,EAAE,KAAK,CAAC,OAAO;iBACtB,CAAC;gBACF,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC;gBAC5C,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;YACnC,CAAC,CAAC,CAAC;YAEH,OAAO,CAAC,GAAG,CAAC,0DAA0D,SAAS,EAAE,CAAC,CAAC;QACrF,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,wDAAwD,EAAE,KAAK,CAAC,CAAC;YAE/E,MAAM,YAAY,GAAG;gBACnB,IAAI,EAAE,cAAuB;gBAC7B,SAAS,EAAE,OAAO,CAAC,SAAS;gBAC5B,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe;gBAC/D,IAAI,EAAE,UAAU;aACjB,CAAC;YAEF,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC,CAAC;QACnD,CAAC;IACH,CAAC;IAED;;OAEG;IACK,YAAY,CAAC,OAAsB;QACzC,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QAEvD,IAAI,QAAQ,IAAI,QAAQ,CAAC,UAAU,KAAK,SAAS,CAAC,IAAI,EAAE,CAAC;YACvD,MAAM,IAAI,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC;YACnF,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACtB,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,IAAI,CAAC,sDAAsD,EAAE,OAAO,CAAC,SAAS,CAAC,CAAC;QAC1F,CAAC;IACH,CAAC;IAED;;OAEG;IACK,aAAa,CAAC,OAAuB;QAC3C,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QAEvD,IAAI,QAAQ,EAAE,CAAC;YACb,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;YAC7C,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QAC3C,CAAC;IACH,CAAC;IAED;;OAEG;IACK,eAAe,CACrB,MAAc,EACd,SAAiB,EACjB,OAA+B,EAC/B,IAAa;QAEb,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,SAAS,CAAC,CAAC;YAClC,MAAM,OAAO,GAAG,MAAM,CAAC,QAAQ,KAAK,QAAQ,CAAC;YAC7C,MAAM,UAAU,GAAG,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;YAE1C,cAAc;YACd,MAAM,WAAW,GAAG,EAAE,GAAG,OAAO,EAAE,CAAC;YACnC,OAAO,WAAW,CAAC,MAAM,CAAC,CAAC;YAC3B,OAAO,WAAW,CAAC,gBAAgB,CAAC,CAAC;YAErC,MAAM,OAAO,GAA+C;gBAC1D,QAAQ,EAAE,MAAM,CAAC,QAAQ;gBACzB,IAAI,EAAE,MAAM,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;gBACzC,IAAI,EAAE,MAAM,CAAC,QAAQ,GAAG,MAAM,CAAC,MAAM;gBACrC,MAAM;gBACN,OAAO,EAAE,WAAW;gBACpB,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS;gBACjD,kBAAkB,EAAE,KAAK;aAC1B,CAAC;YAEF,MAAM,GAAG,GAAG,UAAU,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;gBAC9C,MAAM,MAAM,GAAa,EAAE,CAAC;gBAE5B,GAAG,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE;oBACvB,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;gBACrB,CAAC,CAAC,CAAC;gBAEH,GAAG,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE;oBACjB,OAAO,CAAC;wBACN,UAAU,EAAE,GAAG,CAAC,UAAU,IAAI,GAAG;wBACjC,aAAa,EAAE,GAAG,CAAC,aAAa;wBAChC,OAAO,EAAE,GAAG,CAAC,OAAO;wBACpB,IAAI,EAAE,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS;qBAC5D,CAAC,CAAC;gBACL,CAAC,CAAC,CAAC;YACL,CAAC,CAAC,CAAC;YAEH,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;gBACxB,MAAM,CAAC,KAAK,CAAC,CAAC;YAChB,CAAC,CAAC,CAAC;YAEH,IAAI,IAAI,EAAE,CAAC;gBACT,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAClB,CAAC;YAED,GAAG,CAAC,GAAG,EAAE,CAAC;QACZ,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;OAEG;IACK,cAAc,CAAC,UAAkB,EAAE,gBAAyB;QAClE,4BAA4B;QAC5B,IAAI,WAAW,GAAG,EAAE,CAAC;QACrB,IAAI,aAAa,GAAG,EAAE,CAAC;QAEvB,cAAc;QACd,IAAI,UAAU,CAAC,UAAU,CAAC,SAAS,CAAC,IAAI,UAAU,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;YAC1E,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,UAAU,CAAC,CAAC;YACnC,WAAW,GAAG,MAAM,CAAC,QAAQ,CAAC;YAC9B,aAAa,GAAG,MAAM,CAAC,MAAM,CAAC;QAChC,CAAC;aAAM,CAAC;YACN,SAAS;YACT,MAAM,UAAU,GAAG,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YAC3C,IAAI,UAAU,KAAK,CAAC,CAAC,EAAE,CAAC;gBACtB,WAAW,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC;gBAClD,aAAa,GAAG,UAAU,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;YACnD,CAAC;iBAAM,CAAC;gBACN,WAAW,GAAG,UAAU,CAAC;YAC3B,CAAC;QACH,CAAC;QAED,YAAY;QACZ,MAAM,QAAQ,GAAG,gBAAgB,IAAI,IAAI,CAAC,SAAS,CAAC,QAAQ,IAAI,OAAO,CAAC;QACxE,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,IAAI,WAAW,CAAC;QACxD,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,IAAI,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAE3E,WAAW;QACX,MAAM,OAAO,GAAG,CAAC,QAAQ,KAAK,OAAO,IAAI,IAAI,KAAK,IAAI,CAAC,IAAI,CAAC,QAAQ,KAAK,QAAQ,IAAI,IAAI,KAAK,KAAK,CAAC;YAClG,CAAC,CAAC,EAAE;YACJ,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC;QAEf,OAAO,GAAG,QAAQ,KAAK,QAAQ,GAAG,OAAO,GAAG,WAAW,GAAG,aAAa,EAAE,CAAC;IAC5E,CAAC;IAED;;OAEG;IACK,gBAAgB,CAAC,OAAiC;QACxD,MAAM,UAAU,GAA2B,EAAE,CAAC;QAE9C,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;YACnD,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;gBACxB,UAAU,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;YAClF,CAAC;QACH,CAAC;QAED,OAAO,UAAU,CAAC;IACpB,CAAC;IAED;;OAEG;IACK,MAAM,CAAC,MAAc;QAC3B,IAAI,CAAC;YACH,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;YACxB,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED;;OAEG;IACH,KAAK;QACH,qBAAqB;QACrB,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,CAAC;QAC3C,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,CAAC;QAEvB,OAAO;QACP,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,CAAC;QACzB,IAAI,CAAC,UAAU,CAAC,OAAO,EAAE,CAAC;IAC5B,CAAC;CACF"}
@@ -0,0 +1,86 @@
1
+ /**
2
+ * HTTP 隧道通信协议定义
3
+ * 用于通过 WebSocket 传输完整的 HTTP 请求和响应
4
+ */
5
+ import type { OutgoingHttpHeaders } from 'http';
6
+ export type HttpTunnelMessageType = 'WELCOME' | 'HTTP_REQUEST' | 'HTTP_RESPONSE' | 'WS_UPGRADE' | 'WS_DATA' | 'WS_CLOSE' | 'TUNNEL_ERROR';
7
+ export interface HttpTunnelBaseMessage {
8
+ type: HttpTunnelMessageType;
9
+ requestId: string;
10
+ }
11
+ /**
12
+ * HTTP 请求消息
13
+ */
14
+ export interface HttpRequestMessage extends HttpTunnelBaseMessage {
15
+ type: 'HTTP_REQUEST';
16
+ requestId: string;
17
+ method: string;
18
+ url: string;
19
+ headers: Record<string, string>;
20
+ body?: string;
21
+ }
22
+ /**
23
+ * HTTP 响应消息
24
+ */
25
+ export interface HttpResponseMessage extends HttpTunnelBaseMessage {
26
+ type: 'HTTP_RESPONSE';
27
+ requestId: string;
28
+ statusCode: number;
29
+ statusMessage: string;
30
+ headers: OutgoingHttpHeaders;
31
+ body?: string;
32
+ }
33
+ /**
34
+ * WebSocket 升级请求消息
35
+ */
36
+ export interface WsUpgradeMessage extends HttpTunnelBaseMessage {
37
+ type: 'WS_UPGRADE';
38
+ requestId: string;
39
+ url: string;
40
+ headers: Record<string, string>;
41
+ }
42
+ /**
43
+ * WebSocket 数据消息
44
+ */
45
+ export interface WsDataMessage extends HttpTunnelBaseMessage {
46
+ type: 'WS_DATA';
47
+ requestId: string;
48
+ data: string;
49
+ isBinary?: boolean;
50
+ }
51
+ /**
52
+ * WebSocket 关闭消息
53
+ */
54
+ export interface WsCloseMessage extends HttpTunnelBaseMessage {
55
+ type: 'WS_CLOSE';
56
+ requestId: string;
57
+ code?: number;
58
+ reason?: string;
59
+ }
60
+ /**
61
+ * 隧道错误消息
62
+ */
63
+ export interface TunnelErrorMessage extends HttpTunnelBaseMessage {
64
+ type: 'TUNNEL_ERROR';
65
+ requestId: string;
66
+ error: string;
67
+ code?: string;
68
+ }
69
+ /**
70
+ * 欢迎消息(服务端发送给客户端的握手消息)
71
+ */
72
+ export interface WelcomeMessage {
73
+ type: 'WELCOME';
74
+ message: string;
75
+ serverVersion: string;
76
+ }
77
+ export type HttpTunnelMessage = HttpRequestMessage | HttpResponseMessage | WsUpgradeMessage | WsDataMessage | WsCloseMessage | TunnelErrorMessage | WelcomeMessage;
78
+ /**
79
+ * 判断对象是否为有效的消息对象(宽松验证)
80
+ */
81
+ export declare function isHttpTunnelMessage(obj: unknown): obj is HttpTunnelMessage;
82
+ /**
83
+ * 生成唯一的请求 ID
84
+ */
85
+ export declare function generateRequestId(): string;
86
+ //# sourceMappingURL=http-tunnel-protocol.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http-tunnel-protocol.d.ts","sourceRoot":"","sources":["../../src/core/http-tunnel-protocol.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,MAAM,CAAC;AAEhD,MAAM,MAAM,qBAAqB,GAC7B,SAAS,GACT,cAAc,GACd,eAAe,GACf,YAAY,GACZ,SAAS,GACT,UAAU,GACV,cAAc,CAAC;AAEnB,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,qBAAqB,CAAC;IAC5B,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,kBAAmB,SAAQ,qBAAqB;IAC/D,IAAI,EAAE,cAAc,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,MAAM,WAAW,mBAAoB,SAAQ,qBAAqB;IAChE,IAAI,EAAE,eAAe,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,mBAAmB,CAAC;IAC7B,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,MAAM,WAAW,gBAAiB,SAAQ,qBAAqB;IAC7D,IAAI,EAAE,YAAY,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACjC;AAED;;GAEG;AACH,MAAM,WAAW,aAAc,SAAQ,qBAAqB;IAC1D,IAAI,EAAE,SAAS,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,cAAe,SAAQ,qBAAqB;IAC3D,IAAI,EAAE,UAAU,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,kBAAmB,SAAQ,qBAAqB;IAC/D,IAAI,EAAE,cAAc,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,SAAS,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,MAAM,iBAAiB,GACzB,kBAAkB,GAClB,mBAAmB,GACnB,gBAAgB,GAChB,aAAa,GACb,cAAc,GACd,kBAAkB,GAClB,cAAc,CAAC;AAEnB;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,iBAAiB,CAkB1E;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,CAE1C"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * HTTP 隧道通信协议定义
3
+ * 用于通过 WebSocket 传输完整的 HTTP 请求和响应
4
+ */
5
+ /**
6
+ * 判断对象是否为有效的消息对象(宽松验证)
7
+ */
8
+ export function isHttpTunnelMessage(obj) {
9
+ if (typeof obj !== 'object' || obj === null) {
10
+ return false;
11
+ }
12
+ const message = obj;
13
+ // 必须有 type 字段
14
+ if (typeof message.type !== 'string') {
15
+ return false;
16
+ }
17
+ // 必须有 requestId 字段(除了 WELCOME 消息)
18
+ if (message.type !== 'WELCOME' && typeof message.requestId !== 'string') {
19
+ return false;
20
+ }
21
+ return true;
22
+ }
23
+ /**
24
+ * 生成唯一的请求 ID
25
+ */
26
+ export function generateRequestId() {
27
+ return `${Date.now()}-${Math.random().toString(36).substring(2, 15)}`;
28
+ }
29
+ //# sourceMappingURL=http-tunnel-protocol.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http-tunnel-protocol.js","sourceRoot":"","sources":["../../src/core/http-tunnel-protocol.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAoGH;;GAEG;AACH,MAAM,UAAU,mBAAmB,CAAC,GAAY;IAC9C,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;QAC5C,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,OAAO,GAAG,GAA8B,CAAC;IAE/C,cAAc;IACd,IAAI,OAAO,OAAO,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QACrC,OAAO,KAAK,CAAC;IACf,CAAC;IAED,kCAAkC;IAClC,IAAI,OAAO,CAAC,IAAI,KAAK,SAAS,IAAI,OAAO,OAAO,CAAC,SAAS,KAAK,QAAQ,EAAE,CAAC;QACxE,OAAO,KAAK,CAAC;IACf,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,iBAAiB;IAC/B,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC;AACxE,CAAC"}