Cloudflare Snippets 免费部署VLESS不限流量节点:MiSub用户进阶教程,实现订阅聚合与优选

在上一篇 《MiSub - 一款强大的开源订阅转换与聚合管理工具》 中,我们不仅详细介绍了如何部署功能强大的 MiSub,还在开篇留下了一个悬念——Cloudflare Snippets。当时我们提到:“我们下次会介绍如何将它与本次的项目结合,实现一个非常酷的功能”。

现在,兑现承诺的时刻到了!

本篇教程将手把手带你利用 Cloudflare Snippets 这一“赛博大善人”的免费福利,从零开始部署一个属于你自己的、高性能不限量的 VLESS 代理节点,并将其无缝添加到你已经部署好的 MiSub 中,实现节点的统一管理与分发。

但是,在开始之前,有一个重要的前提。

前置条件:检查你是否是“天选之子”

Cloudflare Snippets 虽然官方说明它仅对付费计划(Pro、Business 和 Enterprise)提供,但 Cloudflare 部分免费(Free)计划的用户也有资格,大部分的免费域名,之前我们发过的.me域名,.pp.ua域名,.ggff域名,大部分都有资格的。

因此,在深入学习本教程之前,请务必先确认你是否拥有这个“白嫖”的权限。

检查路径:

  1. 登录 Cloudflare 仪表盘,选择你的域名。
  2. 在左侧导航栏中找到 **规则 (Rules)**,然后点击其下的 Snippets 选项。

检查 Cloudflare Snippets 访问权限

判断结果:

  • 如果你的界面如上图所示:能看到“创建片段”的按钮,那么恭喜你!本篇教程就是为你量身打造的,你可以继续阅读下去。
  • 如果你的 Snippets 选项中 显示 在边缘解锁自定义逻辑:如下图所示,Cloudflare Snippets控制您的边缘配置 — 短而强大的 JavaScript 片段,可动态定制您的网站或应用程序。Pro、Business 和 Enterprise 计划免费提供。则说明该功能尚未对你的域名开放。这种情况下,后续的步骤将无法进行。

在边缘解锁自定义逻辑


如果你已经确认自己是那个幸运儿,那么,让我们一起站在巨人的肩膀上,玩起来吧。

1:CF Snippet 部署源码

  • 感谢 CM 大佬以及多位大佬在 Snippet 部署上的探索与分享。下文贴出的源码均由 CM 大佬修改整合。由于代码可能随时更新,建议大家收藏原作者的发布页以获取最新动态:https://bp.sub.cmliussss.net/

  • 白嫖哥源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
const FIXED_UUID = '';// 建议修改为自己的规范化UUID,如不需要可留空
export default {
async fetch(request) {
try {
const url = new URL(request.url);
// 检查是否为 WebSocket 升级请求
const upgradeHeader = request.headers.get('Upgrade');
if (upgradeHeader !== 'websocket') {
// 将 request.cf 对象转换为 JSON 字符串
const jsonData = JSON.stringify(request.cf, null, 2); // 使用2个空格缩进,使输出更易读
// 创建 Response 对象,设置正确的 Content-Type 头
return new Response(jsonData, {
status: 200,
headers: {
'Content-Type': 'application/json;charset=UTF-8' // 标准 JSON 内容类型[6,7](@ref)
}
});
} else {
let socks5Address = url.searchParams.get('socks5') || url.searchParams.get('http') || null;
let parsedSocks5Address = {};
let enableSocks = null;
let enableGlobalSocks = url.searchParams.has('globalproxy');
let ProxyIP = '';
let ProxyPort = 443;
if ((url.pathname.toLowerCase().includes('/socks5=') || (url.pathname.includes('/s5=')) || (url.pathname.includes('/gs5=')))) {
socks5Address = url.pathname.split('5=')[1];
enableGlobalSocks = url.pathname.includes('/gs5=') ? true : enableGlobalSocks;
} if ((url.pathname.toLowerCase().includes('/http='))) {
socks5Address = url.pathname.split('/http=')[1];
} else if (url.pathname.toLowerCase().includes('/socks://') || url.pathname.toLowerCase().includes('/socks5://') || url.pathname.toLowerCase().includes('/http://')) {
socks5Address = url.pathname.split('://')[1].split('#')[0];
if (socks5Address.includes('@')) {
const lastAtIndex = socks5Address.lastIndexOf('@');
let userPassword = socks5Address.substring(0, lastAtIndex).replaceAll('%3D', '=');
const base64Regex = /^(?:[A-Z0-9+/]{4})*(?:[A-Z0-9+/]{2}==|[A-Z0-9+/]{3}=)?$/i;
if (base64Regex.test(userPassword) && !userPassword.includes(':')) userPassword = atob(userPassword);
socks5Address = `${userPassword}@${socks5Address.substring(lastAtIndex + 1)}`;
}
enableGlobalSocks = true;//开启全局SOCKS5
}

if (socks5Address) {
try {
parsedSocks5Address = socks5AddressParser(socks5Address);
enableSocks = url.pathname.includes('http://') ? 'http' : 'socks5';
} catch (err) {
enableSocks = null;
}
} else {
enableSocks = null;
}

if (url.searchParams.has('proxyip') || url.searchParams.has('ip')) {
ProxyIP = url.searchParams.get('proxyip') || url.searchParams.get('ip');
enableSocks = null;
} else if (url.pathname.toLowerCase().includes('/proxyip=')) {
ProxyIP = url.pathname.toLowerCase().split('/proxyip=')[1];
enableSocks = null;
} else if (url.pathname.toLowerCase().includes('/proxyip.')) {
ProxyIP = `proxyip.${url.pathname.toLowerCase().split("/proxyip.")[1]}`;
enableSocks = null;
} else if (url.pathname.toLowerCase().includes('/pyip=')) {
ProxyIP = url.pathname.toLowerCase().split('/pyip=')[1];
enableSocks = null;
} else if (url.pathname.toLowerCase().includes('/ip=')) {
ProxyIP = url.pathname.toLowerCase().split('/ip=')[1];
enableSocks = null;
}

return await handleSPESSWebSocket(request, {
parsedSocks5Address,
enableSocks,
enableGlobalSocks,
ProxyIP,
ProxyPort
});
}
} catch (err) {
return new Response(err && err.stack ? err.stack : String(err), { status: 500 });
}
},
};

async function handleSPESSWebSocket(request, config) {
const {
parsedSocks5Address,
enableSocks,
enableGlobalSocks,
ProxyIP,
ProxyPort
} = config;
const wsPair = new WebSocketPair();
const [clientWS, serverWS] = Object.values(wsPair);

serverWS.accept();

// WebSocket心跳机制,每30秒发送一次ping
let heartbeatInterval = setInterval(() => {
if (serverWS.readyState === WS_READY_STATE_OPEN) {
try {
serverWS.send('ping');
} catch (e) { }
}
}, 30000);
function clearHeartbeat() {
if (heartbeatInterval) {
clearInterval(heartbeatInterval);
heartbeatInterval = null;
}
}
serverWS.addEventListener('close', clearHeartbeat);
serverWS.addEventListener('error', clearHeartbeat);

// 处理 WebSocket 数据流
const earlyDataHeader = request.headers.get('sec-websocket-protocol') || '';
const wsReadable = createWebSocketReadableStream(serverWS, earlyDataHeader);
let remoteSocket = null;
let udpStreamWrite = null;
let isDns = false;

wsReadable.pipeTo(new WritableStream({
async write(chunk) {
if (isDns && udpStreamWrite) {
return udpStreamWrite(chunk);
}
if (remoteSocket) {
try {
const writer = remoteSocket.writable.getWriter();
await writer.write(chunk);
writer.releaseLock();
} catch (err) {
closeSocket(remoteSocket);
throw err;
}
return;
}
const result = parseVLESSHeader(chunk);
if (result.hasError) {
throw new Error(result.message);
}
const vlessRespHeader = new Uint8Array([result.vlessVersion[0], 0]);
const rawClientData = chunk.slice(result.rawDataIndex);
if (result.isUDP) {
if (result.portRemote === 53) {
isDns = true;
const { write } = await handleUDPOutBound(serverWS, vlessRespHeader);
udpStreamWrite = write;
udpStreamWrite(rawClientData);
return;
} else {
throw new Error('UDP代理仅支持DNS(端口53)');
}
}
async function connectAndWrite(address, port) {
const tcpSocket = await connect({ hostname: address, port: port }, { allowHalfOpen: true });
remoteSocket = tcpSocket;
const writer = tcpSocket.writable.getWriter();
await writer.write(rawClientData);
writer.releaseLock();
return tcpSocket;
}
async function connectAndWriteSocks(address, port) {
const tcpSocket = enableSocks === 'socks5'
? await socks5Connect(result.addressType, address, port, parsedSocks5Address)
: await httpConnect(result.addressType, address, port, parsedSocks5Address);
remoteSocket = tcpSocket;
const writer = tcpSocket.writable.getWriter();
await writer.write(rawClientData);
writer.releaseLock();
return tcpSocket;
}
async function retry() {
try {
let tcpSocket;
if (enableSocks === 'socks5') {
tcpSocket = await socks5Connect(result.addressType, result.addressRemote, result.portRemote, parsedSocks5Address);
} else if (enableSocks === 'http') {
tcpSocket = await httpConnect(result.addressType, result.addressRemote, result.portRemote, parsedSocks5Address);
} else {
const proxyConfig = await getProxyConfiguration(request.cf && request.cf.colo, result.addressRemote, result.portRemote, ProxyIP, ProxyPort);
tcpSocket = await connect({ hostname: proxyConfig.ip, port: proxyConfig.port }, { allowHalfOpen: true });
}
remoteSocket = tcpSocket;
const writer = tcpSocket.writable.getWriter();
await writer.write(rawClientData);
writer.releaseLock();
tcpSocket.closed.catch(() => { }).finally(() => {
if (serverWS.readyState === WS_READY_STATE_OPEN) {
serverWS.close(1000, '连接已关闭');
}
});
pipeRemoteToWebSocket(tcpSocket, serverWS, vlessRespHeader, null);
} catch (err) {
closeSocket(remoteSocket);
serverWS.close(1011, '代理连接失败: ' + (err && err.message ? err.message : err));
}
}
try {
if (enableGlobalSocks) {
const tcpSocket = await connectAndWriteSocks(result.addressRemote, result.portRemote);
pipeRemoteToWebSocket(tcpSocket, serverWS, vlessRespHeader, retry);
} else {
const tcpSocket = await connectAndWrite(result.addressRemote, result.portRemote);
pipeRemoteToWebSocket(tcpSocket, serverWS, vlessRespHeader, retry);
}
} catch (err) {
closeSocket(remoteSocket);
serverWS.close(1011, '连接失败: ' + (err && err.message ? err.message : err));
}
},
close() {
if (remoteSocket) {
closeSocket(remoteSocket);
}
}
})).catch(err => {
closeSocket(remoteSocket);
serverWS.close(1011, '内部错误: ' + (err && err.message ? err.message : err));
});

return new Response(null, {
status: 101,
webSocket: clientWS,
});
}

function createWebSocketReadableStream(ws, earlyDataHeader) {
return new ReadableStream({
start(controller) {
ws.addEventListener('message', event => {
controller.enqueue(event.data);
});

ws.addEventListener('close', () => {
controller.close();
});

ws.addEventListener('error', err => {
controller.error(err);
});

if (earlyDataHeader) {
try {
const decoded = atob(earlyDataHeader.replace(/-/g, '+').replace(/_/g, '/'));
const data = Uint8Array.from(decoded, c => c.charCodeAt(0));
controller.enqueue(data.buffer);
} catch (e) {
}
}
}
});
}

// 只允许固定UUID
function parseVLESSHeader(buffer) {
if (buffer.byteLength < 24) {
return { hasError: true, message: '无效的头部长度' };
}
const view = new DataView(buffer);
const version = new Uint8Array(buffer.slice(0, 1));
const uuid = formatUUID(new Uint8Array(buffer.slice(1, 17)));
if (FIXED_UUID && uuid !== FIXED_UUID) {
return { hasError: true, message: '无效的用户' };
}
const optionsLength = view.getUint8(17);
const command = view.getUint8(18 + optionsLength);
let isUDP = false;
if (command === 1) {
} else if (command === 2) {
isUDP = true;
} else {
return { hasError: true, message: '不支持的命令,仅支持TCP(01)和UDP(02)' };
}
let offset = 19 + optionsLength;
const port = view.getUint16(offset);
offset += 2;
const addressType = view.getUint8(offset++);
let address = '';
switch (addressType) {
case 1:
address = Array.from(new Uint8Array(buffer.slice(offset, offset + 4))).join('.');
offset += 4;
break;
case 2:
const domainLength = view.getUint8(offset++);
address = new TextDecoder().decode(buffer.slice(offset, offset + domainLength));
offset += domainLength;
break;
case 3:
const ipv6 = [];
for (let i = 0; i < 8; i++) {
ipv6.push(view.getUint16(offset).toString(16).padStart(4, '0'));
offset += 2;
}
address = ipv6.join(':').replace(/(^|:)0+(\w)/g, '$1$2');
break;
default:
return { hasError: true, message: '不支持的地址类型' };
}
return {
hasError: false,
addressRemote: address,
portRemote: port,
rawDataIndex: offset,
vlessVersion: version,
isUDP,
addressType
};
}

function pipeRemoteToWebSocket(remoteSocket, ws, vlessHeader, retry = null) {
let headerSent = false;
let hasIncomingData = false;

remoteSocket.readable.pipeTo(new WritableStream({
write(chunk) {
hasIncomingData = true;
if (ws.readyState === WS_READY_STATE_OPEN) {
if (!headerSent) {
const combined = new Uint8Array(vlessHeader.byteLength + chunk.byteLength);
combined.set(new Uint8Array(vlessHeader), 0);
combined.set(new Uint8Array(chunk), vlessHeader.byteLength);
ws.send(combined.buffer);
headerSent = true;
} else {
ws.send(chunk);
}
}
},
close() {
if (!hasIncomingData && retry) {
retry();
return;
}
if (ws.readyState === WS_READY_STATE_OPEN) {
ws.close(1000, '正常关闭');
}
},
abort() {
closeSocket(remoteSocket);
}
})).catch(err => {
closeSocket(remoteSocket);
if (ws.readyState === WS_READY_STATE_OPEN) {
ws.close(1011, '数据传输错误');
}
});
}

function closeSocket(socket) {
if (socket) {
try {
socket.close();
} catch (e) {
}
}
}

function formatUUID(bytes) {
const hex = Array.from(bytes, b => b.toString(16).padStart(2, '0')).join('');
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
}

function socks5AddressParser(address) {
// 使用 "@" 分割地址,分为认证部分和服务器地址部分
const lastAtIndex = address.lastIndexOf("@");
let [latter, former] = lastAtIndex === -1 ? [address, undefined] : [address.substring(lastAtIndex + 1), address.substring(0, lastAtIndex)];
let username, password, hostname, port;

// 如果存在 former 部分,说明提供了认证信息
if (former) {
const formers = former.split(":");
if (formers.length !== 2) {
throw new Error('无效的 SOCKS 地址格式:认证部分必须是 "username:password" 的形式');
}
[username, password] = formers;
}

// 解析服务器地址部分
const latters = latter.split(":");
// 检查是否是IPv6地址带端口格式 [xxx]:port
if (latters.length > 2 && latter.includes("]:")) {
// IPv6地址带端口格式:[2001:db8::1]:8080
port = Number(latter.split("]:")[1].replace(/[^\d]/g, ''));
hostname = latter.split("]:")[0] + "]"; // 正确提取hostname部分
} else if (latters.length === 2) {
// IPv4地址带端口或域名带端口
port = Number(latters.pop().replace(/[^\d]/g, ''));
hostname = latters.join(":");
} else {
port = 80;
hostname = latter;
}

if (isNaN(port)) {
throw new Error('无效的 SOCKS 地址格式:端口号必须是数字');
}

// 处理 IPv6 地址的特殊情况
// IPv6 地址包含多个冒号,所以必须用方括号括起来,如 [2001:db8::1]
const regex = /^\[.*\]$/;
if (hostname.includes(":") && !regex.test(hostname)) {
throw new Error('无效的 SOCKS 地址格式:IPv6 地址必须用方括号括起来,如 [2001:db8::1]');
}

//if (/^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/.test(hostname)) hostname = `${atob('d3d3Lg==')}${hostname}${atob('LmlwLjA5MDIyNy54eXo=')}`;
// 返回解析后的结果
return {
username, // 用户名,如果没有则为 undefined
password, // 密码,如果没有则为 undefined
hostname, // 主机名,可以是域名、IPv4 或 IPv6 地址
port, // 端口号,已转换为数字类型
}
}

// 修正socks5Connect函数,不再引用parsedSocks5Address
async function socks5Connect(addressType, addressRemote, portRemote, socks5Address) {
const { username, password, hostname, port } = socks5Address;
const socket = connect({
hostname,
port,
});
const socksGreeting = new Uint8Array([5, 2, 0, 2]);
const writer = socket.writable.getWriter();
await writer.write(socksGreeting);
const reader = socket.readable.getReader();
const encoder = new TextEncoder();
let res = (await reader.read()).value;
if (res[0] !== 0x05) {
throw new Error(`socks server version error: ${res[0]} expected: 5`);
}
if (res[1] === 0xff) {
throw new Error("no acceptable methods");
}
if (res[1] === 0x02) {
if (!username || !password) {
throw new Error("please provide username/password");
}
const authRequest = new Uint8Array([
1,
username.length,
...encoder.encode(username),
password.length,
...encoder.encode(password)
]);
await writer.write(authRequest);
res = (await reader.read()).value;
if (res[0] !== 0x01 || res[1] !== 0x00) {
throw new Error("fail to auth socks server");
}
}
let DSTADDR;
switch (addressType) {
case 1:
DSTADDR = new Uint8Array(
[1, ...addressRemote.split('.').map(Number)]
);
break;
case 2:
DSTADDR = new Uint8Array(
[3, addressRemote.length, ...encoder.encode(addressRemote)]
);
break;
case 3:
DSTADDR = new Uint8Array(
[4, ...addressRemote.split(':').flatMap(x => [parseInt(x.slice(0, 2), 16), parseInt(x.slice(2), 16)])]
);
break;
default:
throw new Error(`invalid addressType is ${addressType}`);
}
const socksRequest = new Uint8Array([5, 1, 0, ...DSTADDR, portRemote >> 8, portRemote & 0xff]);
await writer.write(socksRequest);
res = (await reader.read()).value;
if (res[1] === 0x00) {
} else {
throw new Error("fail to open socks connection");
}
writer.releaseLock();
reader.releaseLock();
return socket;
}

async function httpConnect(addressType, addressRemote, portRemote, socks5Address) {
const { username, password, hostname, port } = socks5Address;
const sock = await connect({
hostname: hostname,
port: port
});

// 构建HTTP CONNECT请求
let connectRequest = `CONNECT ${addressRemote}:${portRemote} HTTP/1.1\r\n`;
connectRequest += `Host: ${addressRemote}:${portRemote}\r\n`;

// 添加代理认证(如果需要)
if (username && password) {
const authString = `${username}:${password}`;
const base64Auth = btoa(authString);
connectRequest += `Proxy-Authorization: Basic ${base64Auth}\r\n`;
}

connectRequest += `User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36\r\n`;
connectRequest += `Proxy-Connection: Keep-Alive\r\n`;
connectRequest += `Connection: Keep-Alive\r\n`; // 添加标准 Connection 头
connectRequest += `\r\n`;

try {
// 发送连接请求
const writer = sock.writable.getWriter();
await writer.write(new TextEncoder().encode(connectRequest));
writer.releaseLock();
} catch (err) {
console.error('发送HTTP CONNECT请求失败:', err);
throw new Error(`发送HTTP CONNECT请求失败: ${err.message}`);
}

// 读取HTTP响应
const reader = sock.readable.getReader();
let respText = '';
let connected = false;
let responseBuffer = new Uint8Array(0);

try {
while (true) {
const { value, done } = await reader.read();
if (done) {
console.error('HTTP代理连接中断');
throw new Error('HTTP代理连接中断');
}

// 合并接收到的数据
const newBuffer = new Uint8Array(responseBuffer.length + value.length);
newBuffer.set(responseBuffer);
newBuffer.set(value, responseBuffer.length);
responseBuffer = newBuffer;

// 将收到的数据转换为文本
respText = new TextDecoder().decode(responseBuffer);

// 检查是否收到完整的HTTP响应头
if (respText.includes('\r\n\r\n')) {
// 分离HTTP头和可能的数据部分
const headersEndPos = respText.indexOf('\r\n\r\n') + 4;
const headers = respText.substring(0, headersEndPos);

// 检查响应状态
if (headers.startsWith('HTTP/1.1 200') || headers.startsWith('HTTP/1.0 200')) {
connected = true;

// 如果响应头之后还有数据,我们需要保存这些数据以便后续处理
if (headersEndPos < responseBuffer.length) {
const remainingData = responseBuffer.slice(headersEndPos);
// 创建一个缓冲区来存储这些数据,以便稍后使用
const dataStream = new ReadableStream({
start(controller) {
controller.enqueue(remainingData);
}
});

// 创建一个新的TransformStream来处理额外数据
const { readable, writable } = new TransformStream();
dataStream.pipeTo(writable).catch(err => console.error('处理剩余数据错误:', err));

// 替换原始readable流
// @ts-ignore
sock.readable = readable;
}
} else {
const errorMsg = `HTTP代理连接失败: ${headers.split('\r\n')[0]}`;
console.error(errorMsg);
throw new Error(errorMsg);
}
break;
}
}
} catch (err) {
reader.releaseLock();
throw new Error(`处理HTTP代理响应失败: ${err.message}`);
}

reader.releaseLock();

if (!connected) {
throw new Error('HTTP代理连接失败: 未收到成功响应');
}

return sock;
}
async function handleUDPOutBound(webSocket, vlessResponseHeader) {
let isVlessHeaderSent = false;
const transformStream = new TransformStream({
start(controller) {
},
transform(chunk, controller) {
for (let index = 0; index < chunk.byteLength;) {
const lengthBuffer = chunk.slice(index, index + 2);
const udpPacketLength = new DataView(lengthBuffer).getUint16(0);
const udpData = new Uint8Array(
chunk.slice(index + 2, index + 2 + udpPacketLength)
);
index = index + 2 + udpPacketLength;
controller.enqueue(udpData);
}
},
flush(controller) {
}
});

transformStream.readable.pipeTo(new WritableStream({
async write(chunk) {
const resp = await fetch('https://1.1.1.1/dns-query',
{
method: 'POST',
headers: {
'content-type': 'application/dns-message',
},
body: chunk,
})
const dnsQueryResult = await resp.arrayBuffer();
const udpSize = dnsQueryResult.byteLength;
const udpSizeBuffer = new Uint8Array([(udpSize >> 8) & 0xff, udpSize & 0xff]);

if (webSocket.readyState === WS_READY_STATE_OPEN) {
if (isVlessHeaderSent) {
webSocket.send(await new Blob([udpSizeBuffer, dnsQueryResult]).arrayBuffer());
} else {
webSocket.send(await new Blob([vlessResponseHeader, udpSizeBuffer, dnsQueryResult]).arrayBuffer());
isVlessHeaderSent = true;
}
}
}
})).catch((error) => {
});

const writer = transformStream.writable.getWriter();

return {
write(chunk) {
writer.write(chunk);
}
};
}

// ========== 必要常量和依赖 ==========
const WS_READY_STATE_OPEN = 1;
import { connect } from 'cloudflare:sockets';
async function getProxyConfiguration(colo, addressRemote, portRemote, ProxyIP, ProxyPort) {
if (!ProxyIP || ProxyIP == '') {
ProxyIP = 'proxyip.fxxk.dedyn.io';
} else if (ProxyIP.includes(']:')) {
ProxyPort = ProxyIP.split(']:')[1] || ProxyPort;
ProxyIP = ProxyIP.split(']:')[0] + "]" || ProxyIP;
} else if (ProxyIP.split(':').length === 2) {
ProxyPort = ProxyIP.split(':')[1] || ProxyPort;
ProxyIP = ProxyIP.split(':')[0] || ProxyIP;
}
if (ProxyIP.includes('.tp')) ProxyPort = ProxyIP.split('.tp')[1].split('.')[0] || ProxyPort;
return { ip: ProxyIP || addressRemote, port: ProxyPort || portRemote };
}
  • 天书12源码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
const FIXED_UUID = '';
//1. 天书版12.0重构版队列传输模式改,本版再次改变传输逻辑,大幅度提升传输稳定性,建议pages部署,这版是全功能开发者研究版,不适合小白部署
import { connect } from 'cloudflare:sockets';
//////////////////////////////////////////////////////////////////////////配置区块////////////////////////////////////////////////////////////////////////
let 反代IP = '' //反代IP或域名,反代IP端口一般情况下不用填写,如果你非要用非标反代的话,可以填'ts.hpc.tw:443'这样
let 启用SOCKS5反代 = null //如果启用此功能,原始反代将失效,很多S5不一定支持ipv6,启用则需禁用doh查询ipv6功能
let 启用SOCKS5全局反代 = false //选择是否启用SOCKS5全局反代,启用后所有访问都是S5的落地【无论你客户端选什么节点】,访问路径是客户端--CF--SOCKS5,当然启用此功能后延迟=CF+SOCKS5,带宽取决于SOCKS5的带宽,不再享受CF高速和随时满带宽的待遇
let 我的SOCKS5账号 = ''; //格式'账号:密码@地址:端口',示例admin:admin@127.0.0.1:443或admin:admin@[IPV6]:443,支持无账号密码示例@127.0.0.1:443

//////////////////////////////////////////////////////////////////////////个性化配置///////////////////////////////////////////////////////////////////////
//以下是适合开发者研究的个性化配置,如果你是小白保持默认别动就行
let 启用新版传输模式 = false //开启则使用队列传输方式,关闭则是原始管道流传输方式

let 启动控流机制 = false //仅新版传输方式有效,选择是否启动控流机制,true启动,false关闭,使用控流可降低CPU超时的概率,提升连接稳定性,适合轻度使用,日常使用应该绰绰有余
let 传输控流延迟 = 200; //单位毫秒,每传输2m数据暂停多少毫秒
//////////////////////////////////////////////////////////////////////////网页入口////////////////////////////////////////////////////////////////////////
export default {
async fetch(访问请求) {
if (访问请求.headers.get('Upgrade') === 'websocket') {
const url = new URL(访问请求.url);
我的SOCKS5账号 = url.searchParams.get('socks5') || url.searchParams.get('http');
启用SOCKS5全局反代 = url.searchParams.has('globalproxy');
if (url.pathname.toLowerCase().includes('/socks5=')) {
我的SOCKS5账号 = url.pathname.split('/socks5=')[1];
启用SOCKS5反代 = 'socks5';
} else if (url.pathname.toLowerCase().includes('/http=')) {
我的SOCKS5账号 = url.pathname.split('/http=')[1];
启用SOCKS5反代 = 'http';
} else if (url.pathname.toLowerCase().includes('/socks://') || url.pathname.toLowerCase().includes('/socks5://') || url.pathname.toLowerCase().includes('/http://')) {
启用SOCKS5反代 = (url.pathname.includes('/http://')) ? 'http' : 'socks5';
我的SOCKS5账号 = url.pathname.split('://')[1].split('#')[0];
if (我的SOCKS5账号.includes('@')) {
const lastAtIndex = 我的SOCKS5账号.lastIndexOf('@');
let userPassword = 我的SOCKS5账号.substring(0, lastAtIndex).replaceAll('%3D', '=');
const base64Regex = /^(?:[A-Z0-9+/]{4})*(?:[A-Z0-9+/]{2}==|[A-Z0-9+/]{3}=)?$/i;
if (base64Regex.test(userPassword) && !userPassword.includes(':')) userPassword = atob(userPassword);
我的SOCKS5账号 = `${userPassword}@${我的SOCKS5账号.substring(lastAtIndex + 1)}`;
}
启用SOCKS5全局反代 = true;//开启全局SOCKS5
}

if (我的SOCKS5账号 && 获取SOCKS5账号(我的SOCKS5账号)) {
try {
我的SOCKS5账号 = 我的SOCKS5账号;
启用SOCKS5反代 = url.searchParams.get('http') ? 'http' : 启用SOCKS5反代;
} catch (err) {
启用SOCKS5反代 = null;
}
} else {
启用SOCKS5反代 = null;
}

if (url.searchParams.has('proxyip')) {
反代IP = url.searchParams.get('proxyip');
启用SOCKS5反代 = null;
} else if (url.pathname.toLowerCase().includes('/proxyip=')) {
反代IP = url.pathname.toLowerCase().split('/proxyip=')[1];
启用SOCKS5反代 = null;
} else if (url.pathname.toLowerCase().includes('/proxyip.')) {
反代IP = `proxyip.${url.pathname.toLowerCase().split("/proxyip.")[1]}`;
启用SOCKS5反代 = null;
} else if (url.pathname.toLowerCase().includes('/pyip=')) {
反代IP = url.pathname.toLowerCase().split('/pyip=')[1];
启用SOCKS5反代 = null;
} else if (url.pathname.toLowerCase().includes('/ip=')) {
反代IP = url.pathname.toLowerCase().split('/ip=')[1];
启用SOCKS5反代 = null;
}

return await 升级WS请求(访问请求);
} else {
return new Response('Hello World!', { status: 200 });
}
}
};

////////////////////////////////////////////////////////////////////////脚本主要架构//////////////////////////////////////////////////////////////////////
//第一步,读取和构建基础访问结构
async function 升级WS请求(访问请求) {
const 创建WS接口 = new WebSocketPair();
const [客户端, WS接口] = Object.values(创建WS接口);
WS接口.accept();
WS接口.send(new Uint8Array([0, 0]));
处理WS请求(访问请求, WS接口)
return new Response(null, { status: 101, webSocket: 客户端 }); //一切准备就绪后,回复客户端WS连接升级成功
}
async function 处理WS请求(访问请求, WS接口) {
let 转换二进制数据;
const 读取WS数据头 = 访问请求.headers.get('sec-websocket-protocol'); //读取访问标头中的WS通信数据
if (!读取WS数据头) {
转换二进制数据 = await new Promise(resolve => {
WS接口.addEventListener('message', event => {
resolve(new Uint8Array(event.data));
}, { once: true });
});
} else {
转换二进制数据 = 转换WS数据头为二进制数据(读取WS数据头); //解码目标访问数据,传递给TCP握手进程
}
解析VL标头(转换二进制数据, WS接口); //解析VL数据并进行TCP握手
}
function 转换WS数据头为二进制数据(WS数据头) {
const base64URL转换为标准base64 = WS数据头.replace(/-/g, '+').replace(/_/g, '/');
const 解码base64 = atob(base64URL转换为标准base64);
const 转换为二进制数组 = Uint8Array.from(解码base64, c => c.charCodeAt(0));
return 转换为二进制数组;
}
//第二步,解读VL协议数据,创建TCP握手
async function 解析VL标头(二进制数据, WS接口, TCP接口) {
let 识别地址类型, 访问地址, 地址长度;
try {
const 获取数据定位 = 二进制数据[17];
const 提取端口索引 = 18 + 获取数据定位 + 1;
const 访问端口 = new DataView(二进制数据.buffer, 提取端口索引, 2).getUint16(0);
if (访问端口 === 53) { //这个处理是应对某些客户端优先强制查询dns的情况,也是通过加密通道udp over tcp的,放心使用
const 提取DNS查询报文 = 二进制数据.slice(提取端口索引 + 9);
const 查询DOH结果 = await fetch('https://1.1.1.1/dns-query', { //不是所有doh都支持二进制查询,默认的是最快的地址,想更换可以换成"https://dns.google/dns-query"
method: 'POST',
headers: {
'content-type': 'application/dns-message',
},
body: 提取DNS查询报文
})
const 提取DOH结果 = await 查询DOH结果.arrayBuffer();
const 构建长度头部 = new Uint8Array([(提取DOH结果.byteLength >> 8) & 0xff, 提取DOH结果.byteLength & 0xff]);
WS接口.send(await new Blob([构建长度头部, 提取DOH结果, new TextEncoder().encode(生成随机字符串())]).arrayBuffer());
return;
}
const 提取地址索引 = 提取端口索引 + 2;
识别地址类型 = 二进制数据[提取地址索引];
let 地址信息索引 = 提取地址索引 + 1;
switch (识别地址类型) {
case 1:
地址长度 = 4;
访问地址 = 二进制数据.slice(地址信息索引, 地址信息索引 + 地址长度).join('.');
break;
case 2:
地址长度 = 二进制数据[地址信息索引];
地址信息索引 += 1;
访问地址 = new TextDecoder().decode(二进制数据.slice(地址信息索引, 地址信息索引 + 地址长度));
break;
case 3:
地址长度 = 16;
const ipv6 = [];
const 读取IPV6地址 = new DataView(二进制数据.buffer, 地址信息索引, 16);
for (let i = 0; i < 8; i++) ipv6.push(读取IPV6地址.getUint16(i * 2).toString(16));
访问地址 = ipv6.join(':');
break;
default:
throw new Error('无效的访问地址');
}
if (FIXED_UUID && 验证VL的密钥(二进制数据.slice(1, 17)) !== FIXED_UUID) throw new Error('UUID验证失败');
if (启用SOCKS5反代 == 'socks5' && 启用SOCKS5全局反代) {
TCP接口 = await 创建SOCKS5接口(识别地址类型, 访问地址, 访问端口);
} if (启用SOCKS5反代 == 'http' && 启用SOCKS5全局反代) {
TCP接口 = await httpConnect(访问地址, 访问端口);
} else {
try {
if (识别地址类型 === 3) {
const 转换IPV6地址 = `[${访问地址}]`
TCP接口 = connect({ hostname: 转换IPV6地址, port: 访问端口 });
} else {
TCP接口 = connect({ hostname: 访问地址, port: 访问端口 });
}
await TCP接口.opened;
} catch {
if (启用SOCKS5反代 == 'socks5') {
TCP接口 = await 创建SOCKS5接口(识别地址类型, 访问地址, 访问端口);
} else if (启用SOCKS5反代 == 'http') {
TCP接口 = await httpConnect(访问地址, 访问端口);
} else {
let [反代IP地址, 反代IP端口] = 解析地址端口(反代IP);
TCP接口 = connect({ hostname: 反代IP地址, port: 反代IP端口 });
}
}
}
await TCP接口.opened;
const 写入初始数据 = 二进制数据.slice(地址信息索引 + 地址长度)
const 传输数据 = TCP接口.writable.getWriter();
if (写入初始数据.length > 0) {
await 传输数据.write(写入初始数据);
}
if (启用新版传输模式) {
const 读取数据 = TCP接口.readable.getReader();
队列传输管道(访问地址, 传输数据, 读取数据, WS接口); //建立WS接口与TCP接口的传输管道
} else {
原始传输管道(传输数据, TCP接口, WS接口)
}
} catch (e) {
return new Response(`连接握手失败: ${e}`, { status: 500 });
}
}
function 验证VL的密钥(字节数组, 起始位置 = 0) {
const 十六进制表 = Array.from({ length: 256 }, (_, 值) =>
(值 + 256).toString(16).slice(1)
);
const 分段结构 = [4, 2, 2, 2, 6];
let 当前索引 = 起始位置;
const 格式化UUID = 分段结构
.map(段长度 =>
Array.from({ length: 段长度 }, () => 十六进制表[字节数组[当前索引++]]).join('')
)
.join('-')
.toLowerCase();
return 格式化UUID;
}

globalThis.DNS缓存记录 = globalThis.DNS缓存记录 ??= new Map();

function 解析地址端口(反代IP) {
const proxyIP = 反代IP.toLowerCase();
let 地址 = proxyIP, 端口 = 443;
if (!proxyIP || proxyIP == '') {
地址 = 'proxyip.fxxk.dedyn.io'; //默认反代
} else if (proxyIP.includes(']:')) {
端口 = proxyIP.split(']:')[1] || 端口;
地址 = proxyIP.split(']:')[0] + "]" || 地址;
} else if (proxyIP.split(':').length === 2) {
端口 = proxyIP.split(':')[1] || 端口;
地址 = proxyIP.split(':')[0] || 地址;
}
if (proxyIP.includes('.tp')) 端口 = proxyIP.split('.tp')[1].split('.')[0] || 端口;
return [地址, 端口];
}
//第三步,创建客户端WS-CF-目标的传输通道并监听状态
async function 原始传输管道(传输数据, TCP接口, WS接口) {
WS接口.addEventListener('message', async event => { try { await 传输数据.write(new Uint8Array(event.data)) } catch { }; }); //监听客户端WS接口后续数据,推送给TCP接口
TCP接口.readable.pipeTo(new WritableStream({ async write(返回数据) { WS接口.send(返回数据) } })); //将TCP接口返回的数据回写至客户端WS接口
}
async function 队列传输管道(访问地址, 传输数据, 读取数据, WS接口, 传输队列 = Promise.resolve(), 开始传输时间 = performance.now(), 字节计数 = 0, 累计传输字节数 = 0, 大数据包 = false, 已结束 = false) {
WS接口.addEventListener('message', event => 传输队列 = 传输队列.then(async () => {
let 分段初值 = 0
let 分段大小 = 4 * 1024;
const WS数据 = new Uint8Array(event.data);
while (分段初值 < WS数据.length) {
const 数据块 = WS数据.slice(分段初值, 分段初值 + 分段大小);
try { await 传输数据.write(数据块) } catch { };
累计传输字节数 += 数据块.length;
分段初值 += 分段大小;
}
累计传输字节数 += WS数据.length;
}).catch());
const 保活 = setInterval(async () => {
if (已结束) {
clearInterval(保活);
} else {
传输队列 = 传输队列.then(async () => await 传输数据.write(new Uint8Array(0))).catch();
}
}, 30000);
while (true) {
const { done: 流结束, value: 返回数据 } = await 读取数据.read();
if (返回数据 && 返回数据.length > 0) {
if (!大数据包 && 返回数据.length >= 4096) {
let TCP缓存 = [];
let TCP缓存长度 = 0;
大数据包 = true;
TCP缓存.push(返回数据);
TCP缓存长度 += 返回数据.length;
while (true) {
const { done: 流结束, value: 返回数据 } = await 读取数据.read();
if (流结束) 已结束 = true;
if (返回数据 && 返回数据.length > 0) {
TCP缓存.push(返回数据);
TCP缓存长度 += 返回数据.length;
if (返回数据.length < 4096 || TCP缓存长度 >= 512 * 1024) {
let 合并偏移 = 0;
let 待推送数据 = new Uint8Array(TCP缓存长度);
for (const 数据块 of TCP缓存) {
待推送数据.set(数据块, 合并偏移);
合并偏移 += 数据块.length;
}
传输队列 = 传输队列.then(() => WS接口.send(待推送数据)).catch();
累计传输字节数 += 待推送数据.length;
大数据包 = false;
break;
}
}
}
} else {
传输队列 = 传输队列.then(() => WS接口.send(返回数据)).catch();
累计传输字节数 += 返回数据.length;
}
if (启动控流机制 && (累计传输字节数 - 字节计数) > 2 * 1024 * 1024) {
传输队列 = 传输队列.then(async () => await new Promise(resolve => setTimeout(resolve, 传输控流延迟))).catch();
字节计数 = 累计传输字节数;
}
}
if (流结束 || 已结束) { 已结束 = true; break; }
}
}
//////////////////////////////////////////////////////////////////////////SOCKS5部分//////////////////////////////////////////////////////////////////////
async function 创建SOCKS5接口(识别地址类型, 访问地址, 访问端口, 转换访问地址, 传输数据, 读取数据) {
let SOCKS5接口, 账号, 密码, 地址, 端口;
try {
({ 账号, 密码, 地址, 端口 } = await 获取SOCKS5账号(我的SOCKS5账号));
SOCKS5接口 = connect({ hostname: 地址, port: 端口 });
await SOCKS5接口.opened;
传输数据 = SOCKS5接口.writable.getWriter();
读取数据 = SOCKS5接口.readable.getReader();
const 转换数组 = new TextEncoder(); //把文本内容转换为字节数组,如账号,密码,域名,方便与S5建立连接
const 构建S5认证 = new Uint8Array([5, 2, 0, 2]); //构建认证信息,支持无认证和用户名/密码认证
await 传输数据.write(构建S5认证); //发送认证信息,确认目标是否需要用户名密码认证
const 读取认证要求 = (await 读取数据.read()).value;
if (读取认证要求[1] === 0x02) { //检查是否需要用户名/密码认证
if (!账号 || !密码) {
throw new Error(`未配置账号密码`);
}
const 构建账号密码包 = new Uint8Array([1, 账号.length, ...转换数组.encode(账号), 密码.length, ...转换数组.encode(密码)]); //构建账号密码数据包,把字符转换为字节数组
await 传输数据.write(构建账号密码包); //发送账号密码认证信息
const 读取账号密码认证结果 = (await 读取数据.read()).value;
if (读取账号密码认证结果[0] !== 0x01 || 读取账号密码认证结果[1] !== 0x00) { //检查账号密码认证结果,认证失败则退出
throw new Error(`账号密码错误`);
}
}
switch (识别地址类型) {
case 1: // IPv4
转换访问地址 = new Uint8Array([1, ...访问地址.split('.').map(Number)]);
break;
case 2: // 域名
转换访问地址 = new Uint8Array([3, 访问地址.length, ...转换数组.encode(访问地址)]);
break;
case 3: // IPv6
转换访问地址 = 转换为Socks5IPv6地址(访问地址);
function 转换为Socks5IPv6地址(原始地址) {
const 去括号地址 = 原始地址.startsWith('[') && 原始地址.endsWith(']')
? 原始地址.slice(1, -1)
: 原始地址;
const 分段 = 去括号地址.split('::');
const 前缀 = 分段[0] ? 分段[0].split(':').filter(Boolean) : [];
const 后缀 = 分段[1] ? 分段[1].split(':').filter(Boolean) : [];
const 填充数量 = 8 - (前缀.length + 后缀.length);
if (填充数量 < 0) throw new Error('IPv6地址格式错误');
const 完整分段 = [...前缀, ...Array(填充数量).fill('0'), ...后缀];
const IPv6字节 = 完整分段.flatMap(字段 => {
const 数值 = parseInt(字段 || '0', 16);
return [(数值 >> 8) & 0xff, 数值 & 0xff];
});
return new Uint8Array([0x04, ...IPv6字节]);
}
break;
}
const 构建转换后的访问地址 = new Uint8Array([5, 1, 0, ...转换访问地址, 访问端口 >> 8, 访问端口 & 0xff]); //构建转换好的地址消息
await 传输数据.write(构建转换后的访问地址); //发送转换后的地址
const 检查返回响应 = (await 读取数据.read()).value;
if (检查返回响应[0] !== 0x05 || 检查返回响应[1] !== 0x00) {
throw new Error(`目标地址连接失败,访问地址: ${访问地址},地址类型: ${识别地址类型}`);
}
传输数据.releaseLock();
读取数据.releaseLock();
return SOCKS5接口;
} catch { };
传输数据?.releaseLock();
读取数据?.releaseLock();
await SOCKS5接口?.close();
throw new Error(`所有SOCKS5账号失效`);
}
async function 获取SOCKS5账号(SOCKS5) {
const 分隔账号 = SOCKS5.includes("@") ? SOCKS5.lastIndexOf("@") : -1;
const 账号段 = SOCKS5.slice(0, 分隔账号);
const 地址段 = 分隔账号 !== -1 ? SOCKS5.slice(分隔账号 + 1) : SOCKS5;
const [账号, 密码] = [账号段.slice(0, 账号段.lastIndexOf(":")), 账号段.slice(账号段.lastIndexOf(":") + 1)];
const [地址, 端口] = 解析地址端口(地址段);
return { 账号, 密码, 地址, 端口 };
}
//////////////////////////////////////////////////////////////////////////订阅页面////////////////////////////////////////////////////////////////////////

function 生成随机字符串(最小长度 = 8, 最大长度 = 24) {
const 字符集 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
const 长度 = 最小长度 + Math.floor(Math.random() * (最大长度 - 最小长度 + 1));
const 结果 = [];
const 随机字节 = new Uint8Array(长度);
crypto.getRandomValues(随机字节);
for (let i = 0; i < 长度; i++) {
const 随机索引 = 随机字节[i] % 字符集.length;
结果.push(字符集[随机索引]);
}
return 结果.join('');
}

async function httpConnect(addressRemote, portRemote) {
const parsedSocks5Address = await 获取SOCKS5账号(我的SOCKS5账号);
const { username, password, hostname, port } = parsedSocks5Address;
const sock = await connect({
hostname: hostname,
port: port
});

// 构建HTTP CONNECT请求
let connectRequest = `CONNECT ${addressRemote}:${portRemote} HTTP/1.1\r\n`;
connectRequest += `Host: ${addressRemote}:${portRemote}\r\n`;

// 添加代理认证(如果需要)
if (username && password) {
const authString = `${username}:${password}`;
const base64Auth = btoa(authString);
connectRequest += `Proxy-Authorization: Basic ${base64Auth}\r\n`;
}

connectRequest += `User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36\r\n`;
connectRequest += `Proxy-Connection: Keep-Alive\r\n`;
connectRequest += `Connection: Keep-Alive\r\n`; // 添加标准 Connection 头
connectRequest += `\r\n`;

try {
// 发送连接请求
const writer = sock.writable.getWriter();
await writer.write(new TextEncoder().encode(connectRequest));
writer.releaseLock();
} catch (err) {
console.error('发送HTTP CONNECT请求失败:', err);
throw new Error(`发送HTTP CONNECT请求失败: ${err.message}`);
}

// 读取HTTP响应
const reader = sock.readable.getReader();
let respText = '';
let connected = false;
let responseBuffer = new Uint8Array(0);

try {
while (true) {
const { value, done } = await reader.read();
if (done) {
console.error('HTTP代理连接中断');
throw new Error('HTTP代理连接中断');
}

// 合并接收到的数据
const newBuffer = new Uint8Array(responseBuffer.length + value.length);
newBuffer.set(responseBuffer);
newBuffer.set(value, responseBuffer.length);
responseBuffer = newBuffer;

// 将收到的数据转换为文本
respText = new TextDecoder().decode(responseBuffer);

// 检查是否收到完整的HTTP响应头
if (respText.includes('\r\n\r\n')) {
// 分离HTTP头和可能的数据部分
const headersEndPos = respText.indexOf('\r\n\r\n') + 4;
const headers = respText.substring(0, headersEndPos);

// 检查响应状态
if (headers.startsWith('HTTP/1.1 200') || headers.startsWith('HTTP/1.0 200')) {
connected = true;

// 如果响应头之后还有数据,我们需要保存这些数据以便后续处理
if (headersEndPos < responseBuffer.length) {
const remainingData = responseBuffer.slice(headersEndPos);
// 创建一个缓冲区来存储这些数据,以便稍后使用
const dataStream = new ReadableStream({
start(controller) {
controller.enqueue(remainingData);
}
});

// 创建一个新的TransformStream来处理额外数据
const { readable, writable } = new TransformStream();
dataStream.pipeTo(writable).catch(err => console.error('处理剩余数据错误:', err));

// 替换原始readable流
// @ts-ignore
sock.readable = readable;
}
} else {
const errorMsg = `HTTP代理连接失败: ${headers.split('\r\n')[0]}`;
console.error(errorMsg);
throw new Error(errorMsg);
}
break;
}
}
} catch (err) {
reader.releaseLock();
throw new Error(`处理HTTP代理响应失败: ${err.message}`);
}

reader.releaseLock();

if (!connected) {
throw new Error('HTTP代理连接失败: 未收到成功响应');
}

return sock;
}
  • ymyuuu源码(支持xhttp)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
const FIXED_UUID = '';// 建议修改为自己的规范化UUID,如不需要可留空
import { connect } from 'cloudflare:sockets';
let 反代IP = '';
let 启用SOCKS5反代 = null;
let 启用SOCKS5全局反代 = false;
let 我的SOCKS5账号 = '';
export default {
async fetch(request) {
const url = new URL(request.url);
我的SOCKS5账号 = url.searchParams.get('socks5') || url.searchParams.get('http');
启用SOCKS5全局反代 = url.searchParams.has('globalproxy');
if (url.pathname.toLowerCase().includes('/socks5=')) {
我的SOCKS5账号 = url.pathname.split('/socks5=')[1];
启用SOCKS5反代 = 'socks5';
} else if (url.pathname.toLowerCase().includes('/http=')) {
我的SOCKS5账号 = url.pathname.split('/http=')[1];
启用SOCKS5反代 = 'http';
} else if (url.pathname.toLowerCase().includes('/socks://') || url.pathname.toLowerCase().includes('/socks5://') || url.pathname.toLowerCase().includes('/http://')) {
启用SOCKS5反代 = (url.pathname.includes('/http://')) ? 'http' : 'socks5';
我的SOCKS5账号 = url.pathname.split('://')[1].split('#')[0];
if (我的SOCKS5账号.includes('@')) {
const lastAtIndex = 我的SOCKS5账号.lastIndexOf('@');
let userPassword = 我的SOCKS5账号.substring(0, lastAtIndex).replaceAll('%3D', '=');
const base64Regex = /^(?:[A-Z0-9+/]{4})*(?:[A-Z0-9+/]{2}==|[A-Z0-9+/]{3}=)?$/i;
if (base64Regex.test(userPassword) && !userPassword.includes(':')) userPassword = atob(userPassword);
我的SOCKS5账号 = `${userPassword}@${我的SOCKS5账号.substring(lastAtIndex + 1)}`;
}
启用SOCKS5全局反代 = true;//开启全局SOCKS5
}

if (我的SOCKS5账号) {
try {
获取SOCKS5账号(我的SOCKS5账号);
启用SOCKS5反代 = url.searchParams.get('http') ? 'http' : 启用SOCKS5反代;
} catch (err) {
启用SOCKS5反代 = null;
}
} else {
启用SOCKS5反代 = null;
}

if (url.searchParams.has('proxyip')) {
反代IP = url.searchParams.get('proxyip');
启用SOCKS5反代 = null;
} else if (url.pathname.toLowerCase().includes('/proxyip=')) {
反代IP = url.pathname.toLowerCase().split('/proxyip=')[1];
启用SOCKS5反代 = null;
} else if (url.pathname.toLowerCase().includes('/proxyip.')) {
反代IP = `proxyip.${url.pathname.toLowerCase().split("/proxyip.")[1]}`;
启用SOCKS5反代 = null;
} else if (url.pathname.toLowerCase().includes('/pyip=')) {
反代IP = url.pathname.toLowerCase().split('/pyip=')[1];
启用SOCKS5反代 = null;
} else if (url.pathname.toLowerCase().includes('/ip=')) {
反代IP = url.pathname.toLowerCase().split('/ip=')[1];
启用SOCKS5反代 = null;
}

// WebSocket 处理
if (request.headers.get('Upgrade')?.toLowerCase() === 'websocket') {
return handleWebSocket(request);
} else {
if (request.method === 'POST') {
// XHTTP 处理
return handleXhttp(request);
} else {
return new Response(`${解析地址端口(反代IP)}`, { status: 200 });
}
}
}
};

// XHTTP 处理函数
async function handleXhttp(request) {
const vlessHeader = await readVlessHeader(request.body.getReader());
if (!vlessHeader) return new Response('Invalid VLESS header', {
status: 400
});

// 连接到远程
let remote = null;
if (启用SOCKS5反代 == 'socks5' && 启用SOCKS5全局反代) {
remote = await socks5Connect(vlessHeader.hostname, vlessHeader.port);
} else if (启用SOCKS5反代 == 'http' && 启用SOCKS5全局反代) {
remote = await httpConnect(vlessHeader.hostname, vlessHeader.port);
} else {
try {
remote = connect({ hostname: vlessHeader.hostname, port: vlessHeader.port });
await remote.opened;
} catch {
if (启用SOCKS5反代 == 'socks5') {
remote = await socks5Connect(vlessHeader.hostname, vlessHeader.port);
} else if (启用SOCKS5反代 == 'http') {
remote = await httpConnect(vlessHeader.hostname, vlessHeader.port);
} else {
let [反代IP地址, 反代IP端口] = 解析地址端口(反代IP);
remote = connect({ hostname: 反代IP地址, port: 反代IP端口 });
}
}
}
await remote.opened;

if (!remote) return new Response('Connect failed', {
status: 502
});

// 创建双向数据流
const {
readable,
writable
} = new TransformStream();

// 上传流:请求 -> 远程
const uploadPipe = async () => {
const writer = remote.writable.getWriter();
if (vlessHeader.data?.length) await writer.write(vlessHeader.data);

const reader = vlessHeader.reader;
try {
while (true) {
const {
value,
done
} = await reader.read();
if (done) break;
if (value) await writer.write(value);
}
} catch { }
writer.close();
};

// 下载流:远程 -> 响应
const downloadPipe = new TransformStream({
start(ctrl) {
ctrl.enqueue(vlessHeader.resp);
}
});

remote.readable.pipeTo(downloadPipe.writable).catch(() => { });
uploadPipe().catch(() => { });

return new Response(downloadPipe.readable, {
headers: {
'Content-Type': 'application/grpc',
'X-Accel-Buffering': 'no',
'Cache-Control': 'no-store'
}
});
}

// WebSocket 处理函数(保持原有逻辑)
async function handleWebSocket(request) {
const [client, ws] = Object.values(new WebSocketPair());
ws.accept();

let remote = null,
udpWriter = null,
isDNS = false;

new ReadableStream({
start(ctrl) {
ws.addEventListener('message', e => ctrl.enqueue(e.data));
ws.addEventListener('close', () => {
remote?.close();
ctrl.close();
});
ws.addEventListener('error', () => {
remote?.close();
ctrl.error();
});

const early = request.headers.get('sec-websocket-protocol');
if (early) {
try {
ctrl.enqueue(Uint8Array.from(atob(early.replace(/-/g, '+').replace(/_/g, '/')),
c => c.charCodeAt(0)).buffer);
} catch { }
}
}
}).pipeTo(new WritableStream({
async write(data) {
if (isDNS) return udpWriter?.write(data);
if (remote) {
const w = remote.writable.getWriter();
await w.write(data);
w.releaseLock();
return;
}

if (data.byteLength < 24) return;

// UUID验证
if (FIXED_UUID) {
const uuidBytes = new Uint8Array(data.slice(1, 17));
const expectedUUID = FIXED_UUID.replace(/-/g, '');
for (let i = 0; i < 16; i++) {
if (uuidBytes[i] !== parseInt(expectedUUID.substr(i * 2, 2), 16)) return;
}
}

const view = new DataView(data);
const optLen = view.getUint8(17);
const cmd = view.getUint8(18 + optLen);
if (cmd !== 1 && cmd !== 2) return;

let pos = 19 + optLen;
const port = view.getUint16(pos);
const type = view.getUint8(pos + 2);
pos += 3;

let addr = '';
if (type === 1) {
addr =
`${view.getUint8(pos)}.${view.getUint8(pos + 1)}.${view.getUint8(pos + 2)}.${view.getUint8(pos + 3)}`;
pos += 4;
} else if (type === 2) {
const len = view.getUint8(pos++);
addr = new TextDecoder().decode(data.slice(pos, pos + len));
pos += len;
} else if (type === 3) {
const ipv6 = [];
for (let i = 0; i < 8; i++, pos += 2) ipv6.push(view.getUint16(pos).toString(16));
addr = ipv6.join(':');
} else return;

const header = new Uint8Array([data[0], 0]);
const payload = data.slice(pos);

// UDP DNS
if (cmd === 2) {
if (port !== 53) return;
isDNS = true;
let sent = false;
const {
readable,
writable
} = new TransformStream({
transform(chunk, ctrl) {
for (let i = 0; i < chunk.byteLength;) {
const len = new DataView(chunk.slice(i, i + 2)).getUint16(0);
ctrl.enqueue(chunk.slice(i + 2, i + 2 + len));
i += 2 + len;
}
}
});

readable.pipeTo(new WritableStream({
async write(query) {
try {
const resp = await fetch('https://1.1.1.1/dns-query', {
method: 'POST',
headers: {
'content-type': 'application/dns-message'
},
body: query
});
if (ws.readyState === 1) {
const result = new Uint8Array(await resp
.arrayBuffer());
ws.send(new Uint8Array([...(sent ? [] : header),
result.length >> 8, result.length &
0xff, ...result
]));
sent = true;
}
} catch { }
}
}));
udpWriter = writable.getWriter();
return udpWriter.write(payload);
}

// TCP连接
let sock = null;
if (启用SOCKS5反代 == 'socks5' && 启用SOCKS5全局反代) {
sock = await socks5Connect(addr, port);
} else if (启用SOCKS5反代 == 'http' && 启用SOCKS5全局反代) {
sock = await httpConnect(addr, port);
} else {
try {
sock = connect({ hostname: addr, port: port });
await sock.opened;
} catch {
if (启用SOCKS5反代 == 'socks5') {
sock = await socks5Connect(addr, port);
} else if (启用SOCKS5反代 == 'http') {
sock = await httpConnect(addr, port);
} else {
let [反代IP地址, 反代IP端口] = 解析地址端口(反代IP);
sock = connect({ hostname: 反代IP地址, port: 反代IP端口 });
}
}
}
await sock.opened;
if (!sock) return;

remote = sock;
const w = sock.writable.getWriter();
await w.write(payload);
w.releaseLock();

let sent = false;
sock.readable.pipeTo(new WritableStream({
write(chunk) {
if (ws.readyState === 1) {
ws.send(sent ? chunk : new Uint8Array([...header, ...
new Uint8Array(chunk)
]));
sent = true;
}
},
close: () => ws.readyState === 1 && ws.close(),
abort: () => ws.readyState === 1 && ws.close()
})).catch(() => { });
}
})).catch(() => { });

return new Response(null, {
status: 101,
webSocket: client
});
}

// UUID 工具函数
function parseUUID(uuid) {
return uuid.replaceAll('-', '').match(/.{2}/g).map(x => parseInt(x, 16));
}

// SOCKS5连接
async function socks5Connect(targetHost, targetPort) {
const parsedSocks5Address = await 获取SOCKS5账号(我的SOCKS5账号);
const { username, password, host, port } = parsedSocks5Address;
const sock = connect({
hostname: host,
port: port
});
await sock.opened;
const w = sock.writable.getWriter();
const r = sock.readable.getReader();
await w.write(new Uint8Array([5, 2, 0, 2]));
const auth = (await r.read()).value;
if (auth[1] === 2 && username) {
const user = new TextEncoder().encode(username);
const pass = new TextEncoder().encode(password);
await w.write(new Uint8Array([1, user.length, ...user, pass.length, ...pass]));
await r.read();
}
const domain = new TextEncoder().encode(targetHost);
await w.write(new Uint8Array([5, 1, 0, 3, domain.length, ...domain,
targetPort >> 8, targetPort & 0xff
]));
await r.read();
w.releaseLock();
r.releaseLock();
return sock;
}

// VLESS Header 解析
async function readVlessHeader(reader) {
let buffer = new Uint8Array(1024);
let offset = 0;

while (true) {
const {
value,
done
} = await reader.read();
if (done) {
if (offset === 0) return null;
break;
}

if (offset + value.length > buffer.length) {
const newBuf = new Uint8Array(offset + value.length);
newBuf.set(buffer.slice(0, offset));
buffer = newBuf;
}
buffer.set(value, offset);
offset += value.length;

// 跳过版本和UUID(17字节),读取addon长度
if (offset < 18) continue;

// UUID验证
if (FIXED_UUID) {
const uuidBytes = parseUUID(FIXED_UUID);
if (!buffer.slice(1, 17).every((b, i) => b === uuidBytes[i])) {
return null;
}
}

const addonLen = buffer[17];
if (offset < 18 + addonLen + 1) continue;
const cmd = buffer[18 + addonLen];
if (cmd !== 1) return null;

const portIndex = 18 + addonLen + 1;
const port = (buffer[portIndex] << 8) | buffer[portIndex + 1];
const atypeIndex = portIndex + 2;
const atype = buffer[atypeIndex];
let hostIndex = atypeIndex + 1,
hostLen = 0,
hostname = '';

switch (atype) {
case 1: // IPv4
hostLen = 4;
if (offset < hostIndex + hostLen) continue;
hostname = Array.from(buffer.slice(hostIndex, hostIndex + hostLen)).join('.');
break;
case 2: // 域名
hostLen = buffer[hostIndex];
if (offset < hostIndex + 1 + hostLen) continue;
hostname = new TextDecoder().decode(buffer.slice(hostIndex + 1, hostIndex + 1 + hostLen));
hostLen++;
break;
case 3: // IPv6
hostLen = 16;
if (offset < hostIndex + hostLen) continue;
const dv = new DataView(buffer.buffer, hostIndex, 16);
const parts = [];
for (let i = 0; i < 8; i++) parts.push(dv.getUint16(i * 2).toString(16));
hostname = parts.join(':');
break;
default:
return null;
}

const headerLen = hostIndex + hostLen;
const data = buffer.slice(headerLen, offset);
return {
hostname,
port,
data,
resp: new Uint8Array([buffer[0], 0]),
reader
};
}
return null;
}

async function 获取SOCKS5账号(address) {
// 使用 "@" 分割地址,分为认证部分和服务器地址部分
const lastAtIndex = address.lastIndexOf("@");
let [latter, former] = lastAtIndex === -1 ? [address, undefined] : [address.substring(lastAtIndex + 1), address.substring(0, lastAtIndex)];
let username, password, hostname, port;

// 如果存在 former 部分,说明提供了认证信息
if (former) {
const formers = former.split(":");
if (formers.length !== 2) {
throw new Error('无效的 SOCKS 地址格式:认证部分必须是 "username:password" 的形式');
}
[username, password] = formers;
}

// 解析服务器地址部分
const latters = latter.split(":");
// 检查是否是IPv6地址带端口格式 [xxx]:port
if (latters.length > 2 && latter.includes("]:")) {
// IPv6地址带端口格式:[2001:db8::1]:8080
port = Number(latter.split("]:")[1].replace(/[^\d]/g, ''));
hostname = latter.split("]:")[0] + "]"; // 正确提取hostname部分
} else if (latters.length === 2) {
// IPv4地址带端口或域名带端口
port = Number(latters.pop().replace(/[^\d]/g, ''));
hostname = latters.join(":");
} else {
port = 80;
hostname = latter;
}

if (isNaN(port)) {
throw new Error('无效的 SOCKS 地址格式:端口号必须是数字');
}

// 处理 IPv6 地址的特殊情况
// IPv6 地址包含多个冒号,所以必须用方括号括起来,如 [2001:db8::1]
const regex = /^\[.*\]$/;
if (hostname.includes(":") && !regex.test(hostname)) {
throw new Error('无效的 SOCKS 地址格式:IPv6 地址必须用方括号括起来,如 [2001:db8::1]');
}

//if (/^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/.test(hostname)) hostname = `${atob('d3d3Lg==')}${hostname}${atob('LmlwLjA5MDIyNy54eXo=')}`;
// 返回解析后的结果
return {
username, // 用户名,如果没有则为 undefined
password, // 密码,如果没有则为 undefined
hostname, // 主机名,可以是域名、IPv4 或 IPv6 地址
port, // 端口号,已转换为数字类型
}
}
function 解析地址端口(反代IP) {
const proxyIP = 反代IP.toLowerCase();
let 地址 = proxyIP, 端口 = 443;
if (!proxyIP || proxyIP == '') {
地址 = 'proxyip.fxxk.dedyn.io'; //默认反代
} else if (proxyIP.includes(']:')) {
端口 = proxyIP.split(']:')[1] || 端口;
地址 = proxyIP.split(']:')[0] + "]" || 地址;
} else if (proxyIP.split(':').length === 2) {
端口 = proxyIP.split(':')[1] || 端口;
地址 = proxyIP.split(':')[0] || 地址;
}
if (proxyIP.includes('.tp')) 端口 = proxyIP.split('.tp')[1].split('.')[0] || 端口;
return [地址, 端口];
}

async function httpConnect(addressRemote, portRemote) {
const parsedSocks5Address = await 获取SOCKS5账号(我的SOCKS5账号);
const { username, password, hostname, port } = parsedSocks5Address;
const sock = await connect({
hostname: hostname,
port: port
});

// 构建HTTP CONNECT请求
let connectRequest = `CONNECT ${addressRemote}:${portRemote} HTTP/1.1\r\n`;
connectRequest += `Host: ${addressRemote}:${portRemote}\r\n`;

// 添加代理认证(如果需要)
if (username && password) {
const authString = `${username}:${password}`;
const base64Auth = btoa(authString);
connectRequest += `Proxy-Authorization: Basic ${base64Auth}\r\n`;
}

connectRequest += `User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36\r\n`;
connectRequest += `Proxy-Connection: Keep-Alive\r\n`;
connectRequest += `Connection: Keep-Alive\r\n`; // 添加标准 Connection 头
connectRequest += `\r\n`;

try {
// 发送连接请求
const writer = sock.writable.getWriter();
await writer.write(new TextEncoder().encode(connectRequest));
writer.releaseLock();
} catch (err) {
console.error('发送HTTP CONNECT请求失败:', err);
throw new Error(`发送HTTP CONNECT请求失败: ${err.message}`);
}

// 读取HTTP响应
const reader = sock.readable.getReader();
let respText = '';
let connected = false;
let responseBuffer = new Uint8Array(0);

try {
while (true) {
const { value, done } = await reader.read();
if (done) {
console.error('HTTP代理连接中断');
throw new Error('HTTP代理连接中断');
}

// 合并接收到的数据
const newBuffer = new Uint8Array(responseBuffer.length + value.length);
newBuffer.set(responseBuffer);
newBuffer.set(value, responseBuffer.length);
responseBuffer = newBuffer;

// 将收到的数据转换为文本
respText = new TextDecoder().decode(responseBuffer);

// 检查是否收到完整的HTTP响应头
if (respText.includes('\r\n\r\n')) {
// 分离HTTP头和可能的数据部分
const headersEndPos = respText.indexOf('\r\n\r\n') + 4;
const headers = respText.substring(0, headersEndPos);

// 检查响应状态
if (headers.startsWith('HTTP/1.1 200') || headers.startsWith('HTTP/1.0 200')) {
connected = true;

// 如果响应头之后还有数据,我们需要保存这些数据以便后续处理
if (headersEndPos < responseBuffer.length) {
const remainingData = responseBuffer.slice(headersEndPos);
// 创建一个缓冲区来存储这些数据,以便稍后使用
const dataStream = new ReadableStream({
start(controller) {
controller.enqueue(remainingData);
}
});

// 创建一个新的TransformStream来处理额外数据
const { readable, writable } = new TransformStream();
dataStream.pipeTo(writable).catch(err => console.error('处理剩余数据错误:', err));

// 替换原始readable流
// @ts-ignore
sock.readable = readable;
}
} else {
const errorMsg = `HTTP代理连接失败: ${headers.split('\r\n')[0]}`;
console.error(errorMsg);
throw new Error(errorMsg);
}
break;
}
}
} catch (err) {
reader.releaseLock();
throw new Error(`处理HTTP代理响应失败: ${err.message}`);
}

reader.releaseLock();

if (!connected) {
throw new Error('HTTP代理连接失败: 未收到成功响应');
}

return sock;
}

2:部署 Cloudflare Snippet 并 创建 VLESS 节点

2.1:部署 Cloudflare Snippet

第一步:创建并粘贴源码

  1. 登录 Cloudflare,选择你的域名,并进入 规则 → Snippets 页面。
  2. 点击 创建片段
    创建片段
  3. 在左上角的 片段名称 处,为你即将创建的片段设置一个方便识别的名称。
  4. 任意选择一个上文提供的源码进行复制,然后粘贴到左侧的 snippet.js 代码编辑器中。
    粘贴源码到编辑器

第二步:设置 UUID (可选)

为了增强安全性,建议为你的服务设置一个 UUID(即验证密码)。当然,你也可以跳过此步。

  1. 打开 v2rayN 软件,点击左上角的 配置文件添加[VLESS]配置文件
    在v2rayN中添加VLESS配置文件
  2. 在弹出的窗口中,点击 生成 按钮来创建一个新的 UUID,并复制它。
    生成并复制UUID
  3. 回到 Cloudflare Snippet 编辑器,将刚刚复制的 UUID 粘贴到源码中指定的位置。
    将UUID粘贴到源码中

第三步:配置片段触发规则

  1. 完成源码编辑后,点击右上角的 片段规则
  2. 在规则设置中,选择 自定义筛选表达式,并按以下方式配置:
    • 字段: 选择 主机名
    • 运算符: 选择 等于
    • : 填写你计划用于此服务的域名(例如 ts.ddff.me请务必替换成你自己的域名)。
  3. 点击 完成 保存规则。
    设置片段触发规则

第四步:部署 Snippet 并创建 DNS 记录

  1. 点击页面右上角的 部署 按钮。
  2. 在弹出的确认窗口中,系统会提示 创建新代理 DNS 记录
  3. IPv4 地址 字段中,填写一个占位地址:192.0.2.1
  4. 最后,点击 创建记录和部署规则
    创建DNS记录并部署
  5. 稍等片刻,看到右侧显示 活动 状态,部署成功。
    部署成功

提示:本教程以天书大佬的源码为例。你也可以尝试部署其他作者(如白嫖哥)的源码,部署流程完全相同,可以自行测试选择最适合你的版本。


2.2:创建 VLESS 客户端配置

现在,我们需要在客户端(如 v2rayN)中手动配置一个 VLESS 节点来连接刚刚部署好的服务。

  1. 在 v2rayN 中,参照下图填写必要的连接参数:
    • 别名:为你的节点设置一个自定义名称。
    • UUID必须填写你之前在源码中设置的那个 UUID。如果未设置,则可以随意生成。
    • 地址 / 端口:地址填写你上面设置的子域名,端口填写 443。也可以使用优选域名。
    • 传输协议:选择 ws (WebSocket)。
    • 伪装域名:填写你上面设置的子域名。
    • 传输层安全:选择 tls
    • SNI:填写你上面设置的-子域名。
      VLESS节点基础配置
      VLESS节点传输配置
  2. 填写完毕后,点击 确定 保存配置。

2.3:测试连接

  1. 回到 v2rayN 主界面,右键点击刚刚创建的节点。
  2. 选择 测试服务器真连接延迟
  3. 如果延迟测试成功并显示数值,则表示节点已经可以正常使用。
    测试节点真连接延迟

3:完美联动!将新节点添加到 MiSub

最后,也是最激动人心的一步:我们将这个亲手创建的免费节点,加入到我们的 MiSub 订阅项目中!

3.1:添加节点

  1. 在 v2rayN 中,选中刚刚创建并测试成功的节点,按 Ctrl+C 复制节点的分享链接(vless://...)。
  2. 打开你部署好的 MiSub 管理页面,并使用管理员密码登录。
  3. 在左侧导航栏选择 手动节点
  4. 在输入框中 粘贴 刚刚复制的 vless:// 链接,然后点击 添加节点。MiSub 会自动解析链接并添加。
    添加节点
  5. 切换到左侧的 订阅分组,选择一个你想添加此节点的分组(或新建一个)。
  6. 在编辑分组的页面,从右侧的“手动节点”列表中 勾选 你刚刚添加的节点,然后界面上方点保存。
    选择节点
  7. 大功告成! 现在,所有使用这个订阅分组链接的客户端,在更新订阅后,都会自动看到这个全新的、由你亲手打造的 VLESS 节点!

3.2:添加优选订阅

你亲手创建的 VLESS 节点虽然只有一个,但它的潜力远不止于此。通过 Cloudflare 的“节点优选”技术,我们可以将这个单节点“裂变”成包含数百个高速 IP 的订阅,让你的客户端自动选择当前网络环境下最快的线路。

这不仅能显著提升连接速度和稳定性,还能在遇到网络波动时自动切换,体验更佳。

操作步骤:

  1. 获取原始节点链接
    在 v2rayN 中,右键点击我们刚刚创建并测试成功的 VLESS 节点,选择“复制分享链接”,获取到 vless://... 格式的链接。

  2. 生成优选订阅
    打开 优选订阅生成器 网站:https://zrf.zrf.me/

    • “节点链接” 输入框中,粘贴 刚刚复制的 vless:// 链接。
    • 点击 “生成优选订阅” 按钮。
  3. 复制生成的订阅链接
    页面会生成一条全新的 优选订阅地址。这条地址就是我们最终的“成果”,它内部包含了大量经过优选的 Cloudflare IP 节点。请完整复制它。
    生成优选订阅

  4. 将优选订阅添加至 MiSub
    现在,回到你的 MiSub 管理界面。这次我们不是添加“手动节点”,而是要添加“订阅”。

    • 进入 “订阅” 管理页面。
    • 将刚刚复制的优-选订阅地址粘贴进去,添加并保存。
      添加优选订阅
    • 别忘了在 “订阅分组” 中勾选这个新的优选订阅。

现在,当你的客户端更新 MiSub 提供的订阅链接时,你得到的就不再是一个孤零零的节点,而是成百上千个已经优选好的高速节点!
更新 MiSub

3.3:更进一步:聚合多个优选订阅

一个优选订阅器还不够?没问题!网络上有多位大佬都在维护各自的优选订阅服务,你可以将它们全部利用起来。

  1. 访问 CloudFlare 优选订阅器汇总 页面。
  2. 将你的 vless:// 链接分别粘贴到列表中的多个订阅器里,生成多条不同的优选订阅链接。
  3. 将所有这些生成的优选订阅链接,全部 添加到你的 MiSub 的“订阅”列表中。

通过这种方式,你的 MiSub 就成了一个强大的“超级优选聚合器”,节点多到用不完,真正实现网络自由。