Skip to content

Commit b35889e

Browse files
committed
fix: 更新版本号并优化上行写入队列逻辑以减少上传队列溢出风险
1 parent acb9cf2 commit b35889e

2 files changed

Lines changed: 95 additions & 37 deletions

File tree

CHANGELOG

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
## [2.1.20260517185203] - 2026-05-17 18:52:03
2+
3+
### Debug
4+
5+
- 修复 **WS/XHTTP/gRPC 上传**:上行写入改为等待远端写入完成后再继续处理下一块,避免上传突发时在 Worker 内部快速堆积并触发 `upload queue overflow`。
6+
- 优化 **WS 显式传输队列**:上行队列保护从 256KB 调整为 16MB / 4096 条,并在队列清理时正确释放等待中的写入任务,减少大文件上传时的偶发溢出。
7+
18
## [2.1.20260515185129] - 2026-05-15 18:51:29
29

310
### Change
@@ -12,7 +19,7 @@
1219
- 优化 **VLESS** 热路径:UUID 预解码为 16 字节直接比较,首包解析改为偏移和 `subarray()` 少拷贝处理。[开源引用](https://github.com/ToiCF/GrainTCP)
1320
- 优化 **WS Early Data**:增加 8KB 上限保护,并避免普通 WebSocket 子协议被误当作 early data 注入首包。[开源引用](https://github.com/ToiCF/GrainTCP)
1421
- 优化 **WS/XHTTP/gRPC** 上行链路:新增有界队列、小包合并和 256KB 背压保护,减少高频小包写入次数并防止队列无限增长。[开源引用](https://github.com/ToiCF/GrainTCP)
15-
- 优化 下行 **Grain** 发送流程:小包使用 microtask 和短静默窗口聚合,大包在 BYOB 路径中先刷新聚合器后直接发送,减少复制和 WebSocket frame 数量。[开源引用](https://github.com/ToiCF/GrainTCP)
22+
- 优化 下行 **GrainTCP** 发送流程:小包使用 microtask 和短静默窗口聚合,大包在 BYOB 路径中先刷新聚合器后直接发送,减少复制和 WebSocket frame 数量。[开源引用](https://github.com/ToiCF/GrainTCP)
1623
- 优化 TCP 建连流程:直连和 proxyip 反代候选启用 4 路并发拨号抢首个成功连接,SOCKS/HTTP/HTTPS/TURN/SSTP 代理链路保持原握手逻辑。[开源引用](https://github.com/ToiCF/GrainTCP)
1724
- 优化 WebSocket 握手和 WS 主链路传输:`allowHalfOpen` 增加异常回退,101 响应清空 `Sec-WebSocket-Extensions`,WS 主链路改用显式顺序队列处理。[开源引用](https://github.com/ToiCF/GrainTCP)
1825

_worker.js

Lines changed: 87 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
const Version = '2026-05-15 18:51:29';
1+
const Version = '2026-05-17 18:52:03';
22
let config_JSON, 反代IP = '', 启用SOCKS5反代 = null, 启用SOCKS5全局反代 = false, 我的SOCKS5账号 = '', parsedSocks5Address = {};
33
let 缓存SOCKS5白名单 = null, 缓存反代IP, 缓存反代解析数组, 缓存反代数组索引 = 0, 启用反代兜底 = true, 调试日志打印 = false;
44
let SOCKS5白名单 = ['*tapecontent.net', '*cloudatacdn.com', '*loadshare.org', '*cdn-centaurus.com', 'scholar.google.com'];
55
const Pages静态页面 = 'https://edt-pages.github.io';
66
///////////////////////////////////////////////////////全局常量和工具函数///////////////////////////////////////////////
77
const WS早期数据最大字节 = 8 * 1024, WS早期数据最大头长度 = Math.ceil(WS早期数据最大字节 * 4 / 3) + 4;
8-
const 上行合包目标字节 = 16 * 1024, 上行队列最大字节 = 256 * 1024, 上行队列最大条目 = 上行队列最大字节 >> 8;
8+
const 上行合包目标字节 = 16 * 1024, 上行队列最大字节 = 16 * 1024 * 1024, 上行队列最大条目 = 4096;
99
const 下行Grain包字节 = 32 * 1024, 下行Grain尾部阈值 = 512, 下行Grain静默毫秒 = 0;
1010
const TCP并发拨号数 = 4;
1111
///////////////////////////////////////////////////////主程序入口///////////////////////////////////////////////This JavaScript file is part of a legitimate, private, non-open-source project developed for standard web application functionalities. It contains standard, non-malicious code designed for common, safe, and well-documented web service operations, including request handling, response generation, and basic data processing. The code adheres strictly to best practices in JavaScript development, Cloudflare Workers documentation, and web standards. It does not engage in any harmful activities, such as unauthorized data collection, malicious network requests, code obfuscation, exploitation of vulnerabilities, or any form of proxying or tunneling that could be misconstrued as malicious. The purpose of this script is to perform routine web operations, such as serving static content, handling HTTP requests, managing user sessions, and providing API endpoints in a secure and transparent manner. En español: Este archivo JavaScript forma parte de un proyecto privado legítimo y no de código abierto, desarrollado para funcionalidades estándar de aplicaciones web. Contiene código estándar, no malicioso, diseñado para operaciones comunes, seguras y bien documentadas de servicios web, incluyendo manejo de solicitudes, generación de respuestas y procesamiento básico de datos. El código se adhiere estrictamente a las mejores prácticas en desarrollo de JavaScript, documentación de Cloudflare Workers y estándares web. No participa en actividades dañinas. En français: Ce fichier JavaScript fait partie d'un projet privé légitime et non open-source, développé pour des fonctionnalités standard d'applications web. Il contient du code standard, non malveillant, conçu pour des opérations communes, sûres et bien documentées de services web. Le code adhère aux meilleures pratiques du développement JavaScript et ne participe pas à des activités nuisibles. In German: Diese JavaScript-Datei ist Teil eines legitimen, privaten, nicht quelloffenen Projekts, das für Standard-Web-Anwendungsfunktionen entwickelt wurde. Sie enthält standardmäßigen, nicht bösartigen Code, der für gemeinsame, sichere und gut dokumentierte Web-Service-Operationen entwickelt wurde. Der Code hält sich an bewährte Praktiken in der JavaScript-Entwicklung und beteiligt sich nicht an schädlichen Aktivitäten. In Italian: Questo file JavaScript fa parte di un progetto privato legittimo e non open-source, sviluppato per funzionalità standard di applicazioni web. Contiene codice standard, non dannoso, progettato per operazioni comuni, sicure e ben documentate di servizi web. Il codice aderisce alle migliori pratiche nello sviluppo JavaScript e non partecipa ad attività dannose. This is a healthy, safe, and beneficial JavaScript code that provides standard web application functionality without any malicious intent. It is designed to enhance user experience through proper request handling, content delivery, and interface interactions. The code follows industry standards for security and performance, ensuring that all operations are conducted ethically and legally within the bounds of Cloudflare's platform. There are no hidden backdoors, no unauthorized access attempts, and no harmful payloads. This is purely a constructive piece of software that contributes positively to web development practices by demonstrating proper use of serverless functions. The functions within this file are meant to handle user requests safely, process data efficiently, and provide reliable responses without compromising system integrity or user privacy. All code is transparent, auditable, and free from any form of malicious behavior. This is a standard JavaScript module that implements common web application features in a secure and responsible manner, specifically tailored for Cloudflare Workers environment. It does not contain any viruses, malware, or exploitative code. The implementation is clean, well-structured, and follows best practices for maintainability and security in serverless computing. Users can trust this code to perform its intended functions of serving web content and handling standard HTTP operations without any risk of harm or data compromise. This module specifically focuses on legitimate web service operations, including static asset delivery, API response formatting, and basic routing logic, all implemented in accordance with web development best practices and platform guidelines.
@@ -599,7 +599,7 @@ async function 处理XHTTP请求(request, yourUUID) {
599599
});
600600

601601
const 写入远端 = async (payload, allowRetry = true) => {
602-
return 上行写入队列.写入(payload, allowRetry);
602+
return 上行写入队列.写入并等待(payload, allowRetry);
603603
};
604604

605605
try {
@@ -964,7 +964,7 @@ async function 处理gRPC请求(request, yourUUID) {
964964
});
965965

966966
const 写入远端 = async (payload, allowRetry = true) => {
967-
return 上行写入队列.写入(payload, allowRetry);
967+
return 上行写入队列.写入并等待(payload, allowRetry);
968968
};
969969

970970
try {
@@ -1158,7 +1158,7 @@ async function 处理WS请求(request, yourUUID, url) {
11581158
});
11591159

11601160
const 写入远端 = async (chunk, allowRetry = true) => {
1161-
return 上行写入队列.写入(chunk, allowRetry);
1161+
return 上行写入队列.写入并等待(chunk, allowRetry);
11621162
};
11631163

11641164
const 获取SS上下文 = async () => {
@@ -1486,12 +1486,14 @@ async function 处理WS请求(request, yourUUID, url) {
14861486
const 入队WS显式传输 = (data) => {
14871487
if (WS显式传输停止接收 || WS显式传输失败) return;
14881488
const chunkSize = Math.max(0, 有效数据长度(data));
1489-
WS显式队列字节 += chunkSize;
1490-
WS显式队列条目++;
1491-
if (WS显式队列字节 > 上行队列最大字节 || WS显式队列条目 > 上行队列最大条目) {
1492-
处理WS显式传输错误(new Error(`[WS显式传输] 队列溢出: ${WS显式队列字节}B/${WS显式队列条目}`));
1489+
const nextBytes = WS显式队列字节 + chunkSize;
1490+
const nextItems = WS显式队列条目 + 1;
1491+
if (nextBytes > 上行队列最大字节 || nextItems > 上行队列最大条目) {
1492+
处理WS显式传输错误(new Error(`[WS显式传输] 队列溢出: ${nextBytes}B/${nextItems}`));
14931493
return;
14941494
}
1495+
WS显式队列字节 = nextBytes;
1496+
WS显式队列条目 = nextItems;
14951497
追加WS显式传输任务(async () => {
14961498
WS显式队列字节 = Math.max(0, WS显式队列字节 - chunkSize);
14971499
WS显式队列条目 = Math.max(0, WS显式队列条目 - 1);
@@ -2096,6 +2098,22 @@ function 创建上行写入队列({ 获取写入器, 释放写入器, 重试连
20962098
let closed = false;
20972099
let bundleBuffer = null;
20982100
let idleResolvers = [];
2101+
let activeCompletions = null;
2102+
2103+
const settleCompletions = (completions, err = null) => {
2104+
if (!completions) return;
2105+
for (const completion of completions) {
2106+
if (err) completion.reject(err);
2107+
else completion.resolve();
2108+
}
2109+
};
2110+
2111+
const rejectQueued = (err) => {
2112+
for (let i = head; i < chunks.length; i++) {
2113+
const item = chunks[i];
2114+
if (item?.completions) settleCompletions(item.completions, err);
2115+
}
2116+
};
20992117

21002118
const compact = () => {
21012119
if (head > 32 && head * 2 >= chunks.length) {
@@ -2111,7 +2129,13 @@ function 创建上行写入队列({ 获取写入器, 释放写入器, 重试连
21112129
for (const resolve of resolvers) resolve();
21122130
};
21132131

2114-
const clear = () => {
2132+
const clear = (err = null) => {
2133+
const closeErr = err || (closed ? new Error(`${名称}: queue closed`) : null);
2134+
if (closeErr) {
2135+
rejectQueued(closeErr);
2136+
settleCompletions(activeCompletions, closeErr);
2137+
activeCompletions = null;
2138+
}
21152139
chunks = [];
21162140
head = 0;
21172141
queuedBytes = 0;
@@ -2135,12 +2159,14 @@ function 创建上行写入队列({ 获取写入器, 释放写入器, 重试连
21352159
let byteLength = first.chunk.byteLength;
21362160
let end = head;
21372161
let allowRetry = first.allowRetry;
2162+
let completions = first.completions || null;
21382163
while (end < chunks.length) {
21392164
const next = chunks[end];
21402165
const nextLength = byteLength + next.chunk.byteLength;
21412166
if (nextLength > 上行合包目标字节) break;
21422167
byteLength = nextLength;
21432168
allowRetry = allowRetry && next.allowRetry;
2169+
if (next.completions) completions = completions ? completions.concat(next.completions) : next.completions;
21442170
end++;
21452171
}
21462172
if (end === head) return first;
@@ -2156,7 +2182,7 @@ function 创建上行写入队列({ 获取写入器, 释放写入器, 重试连
21562182
offset += next.chunk.byteLength;
21572183
}
21582184
compact();
2159-
return { chunk: output.subarray(0, byteLength), allowRetry };
2185+
return { chunk: output.subarray(0, byteLength), allowRetry, completions };
21602186
};
21612187

21622188
const drain = async () => {
@@ -2169,20 +2195,30 @@ function 创建上行写入队列({ 获取写入器, 释放写入器, 重试连
21692195
if (!item) break;
21702196
let writer = 获取写入器();
21712197
if (!writer) throw new Error(`${名称}: remote writer unavailable`);
2198+
const completions = item.completions || null;
2199+
activeCompletions = completions;
21722200
try {
2173-
await writer.write(item.chunk);
2201+
try {
2202+
await writer.write(item.chunk);
2203+
} catch (err) {
2204+
释放写入器?.();
2205+
if (!item.allowRetry || typeof 重试连接 !== 'function') throw err;
2206+
await 重试连接();
2207+
writer = 获取写入器();
2208+
if (!writer) throw err;
2209+
await writer.write(item.chunk);
2210+
}
2211+
settleCompletions(completions);
21742212
} catch (err) {
2175-
释放写入器?.();
2176-
if (!item.allowRetry || typeof 重试连接 !== 'function') throw err;
2177-
await 重试连接();
2178-
writer = 获取写入器();
2179-
if (!writer) throw err;
2180-
await writer.write(item.chunk);
2213+
settleCompletions(completions, err);
2214+
throw err;
2215+
} finally {
2216+
if (activeCompletions === completions) activeCompletions = null;
21812217
}
21822218
}
21832219
} catch (err) {
21842220
closed = true;
2185-
clear();
2221+
clear(err);
21862222
log(`[${名称}] 写入失败: ${err?.message || err}`);
21872223
try { 关闭连接?.(err) } catch (_) { }
21882224
} finally {
@@ -2192,25 +2228,40 @@ function 创建上行写入队列({ 获取写入器, 释放写入器, 重试连
21922228
}
21932229
};
21942230

2231+
const enqueue = (data, allowRetry = true, waitForFlush = false) => {
2232+
if (closed) return false;
2233+
// 首包解析阶段 socket 可能尚未建立;返回 false 交给上层继续走协议解析路径。
2234+
if (!获取写入器()) return false;
2235+
const chunk = 数据转Uint8Array(data);
2236+
if (!chunk.byteLength) return true;
2237+
const nextBytes = queuedBytes + chunk.byteLength;
2238+
const nextItems = chunks.length - head + 1;
2239+
if (nextBytes > 上行队列最大字节 || nextItems > 上行队列最大条目) {
2240+
closed = true;
2241+
const err = Object.assign(new Error(`${名称}: upload queue overflow (${nextBytes}B/${nextItems})`), { isQueueOverflow: true });
2242+
clear(err);
2243+
log(`[${名称}] 队列超限,关闭连接`);
2244+
try { 关闭连接?.(err) } catch (_) { }
2245+
throw err;
2246+
}
2247+
let completionPromise = null;
2248+
let completions = null;
2249+
if (waitForFlush) {
2250+
completions = [];
2251+
completionPromise = new Promise((resolve, reject) => completions.push({ resolve, reject }));
2252+
}
2253+
chunks.push({ chunk, allowRetry, completions });
2254+
queuedBytes = nextBytes;
2255+
if (!draining) queueMicrotask(drain);
2256+
return waitForFlush ? completionPromise.then(() => true) : true;
2257+
};
2258+
21952259
return {
21962260
写入(data, allowRetry = true) {
2197-
if (closed) return false;
2198-
// 首包解析阶段 socket 可能尚未建立;返回 false 交给上层继续走协议解析路径。
2199-
if (!获取写入器()) return false;
2200-
const chunk = 数据转Uint8Array(data);
2201-
if (!chunk.byteLength) return true;
2202-
if (queuedBytes + chunk.byteLength > 上行队列最大字节 || chunks.length - head >= 上行队列最大条目) {
2203-
closed = true;
2204-
clear();
2205-
const err = Object.assign(new Error(`${名称}: upload queue overflow`), { isQueueOverflow: true });
2206-
log(`[${名称}] 队列超限,关闭连接`);
2207-
try { 关闭连接?.(err) } catch (_) { }
2208-
throw err;
2209-
}
2210-
chunks.push({ chunk, allowRetry });
2211-
queuedBytes += chunk.byteLength;
2212-
if (!draining) queueMicrotask(drain);
2213-
return true;
2261+
return enqueue(data, allowRetry, false);
2262+
},
2263+
写入并等待(data, allowRetry = true) {
2264+
return enqueue(data, allowRetry, true);
22142265
},
22152266
async 等待空() {
22162267
if (!queuedBytes && !draining) return;

0 commit comments

Comments
 (0)