holyterra commited on
Commit
f813dc0
·
verified ·
1 Parent(s): aff0190

Upload unified-server.js

Browse files
Files changed (1) hide show
  1. unified-server.js +98 -34
unified-server.js CHANGED
@@ -384,7 +384,7 @@ class ConnectionRegistry extends EventEmitter {
384
  if (queue) {
385
  this._routeMessage(parsedMessage, queue);
386
  } else {
387
- this.logger.warn(`[Server] 收到未知请求ID的消息: ${requestId}`);
388
  }
389
  } catch (error) {
390
  this.logger.error('[Server] 解析内部WebSocket消息失败');
@@ -526,17 +526,34 @@ class RequestHandler {
526
  return correctedDetails;
527
  }
528
 
529
- async _handleRequestFailureAndSwitch(errorDetails, res) {
530
- // --- START: MODIFICATION ---
531
- const correctedErrorDetails = this._parseAndCorrectErrorDetails(errorDetails);
532
- // --- END: MODIFICATION ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
533
 
534
- // 使用修正后的错误详情进行判断
535
- const isImmediateSwitch = this.config.immediateSwitchStatusCodes.includes(correctedErrorDetails.status);
536
 
537
  if (isImmediateSwitch) {
538
- this.logger.warn(`🔴 [Auth] 收到状态码 ${correctedErrorDetails.status} (已修正),触发立即切换账号...`);
539
- if (res) this._sendErrorChunkToClient(res, `收到状态码 ${correctedErrorDetails.status},正在尝试切换账号...`);
540
  try {
541
  await this._switchToNextAuth();
542
  if (res) this._sendErrorChunkToClient(res, `已切换到账号索引 ${this.currentAuthIndex},请重试`);
@@ -544,13 +561,13 @@ class RequestHandler {
544
  this.logger.error(`🔴 [Auth] 账号切换失败: ${switchError.message}`);
545
  if (res) this._sendErrorChunkToClient(res, `切换账号失败: ${switchError.message}`);
546
  }
547
- return; // End here after immediate switch attempt
548
  }
549
 
550
- // 使用 correctedErrorDetails.status
551
  if (this.config.failureThreshold > 0) {
552
  this.failureCount++;
553
- this.logger.warn(`⚠️ [Auth] 请求失败 - 失败计数: ${this.failureCount}/${this.config.failureThreshold} (当前账号索引: ${this.currentAuthIndex}, 状态码: ${correctedErrorDetails.status})`);
554
  if (this.failureCount >= this.config.failureThreshold) {
555
  this.logger.warn(`🔴 [Auth] 达到失败阈值!准备切换账号...`);
556
  if (res) this._sendErrorChunkToClient(res, `连续失败${this.failureCount}次,正在尝试切换账号...`);
@@ -563,7 +580,7 @@ class RequestHandler {
563
  }
564
  }
565
  } else {
566
- this.logger.warn(`[Auth] 请求失败 (状态码: ${correctedErrorDetails.status})。基于计数的自动切换已禁用 (failureThreshold=0)`);
567
  }
568
  }
569
 
@@ -617,26 +634,25 @@ class RequestHandler {
617
  this.logger.info(`[Request] 已向客户端发送标准错误信号: ${errorMessage}`);
618
  }
619
  }
620
- async _handlePseudoStreamResponse(proxyRequest, messageQueue, req, res) {
621
- res.status(200).set({ 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', 'Connection': 'keep-alive' });
622
- this.logger.info('[Request] 已向客户端发送初始响应头,假流式计时器已启动。');
 
623
  let connectionMaintainer = null;
624
  try {
625
- const keepAliveChunk = this._getKeepAliveChunk(req);
626
- connectionMaintainer = setInterval(() => { if (!res.writableEnded) { res.write(keepAliveChunk); } }, 1000);
 
 
627
  let lastMessage, requestFailed = false;
628
  for (let attempt = 1; attempt <= this.maxRetries; attempt++) {
629
  this.logger.info(`[Request] 请求尝试 #${attempt}/${this.maxRetries}...`);
630
  this._forwardRequest(proxyRequest);
631
  lastMessage = await messageQueue.dequeue();
632
  if (lastMessage.event_type === 'error' && lastMessage.status >= 400 && lastMessage.status <= 599) {
633
-
634
- // --- START: MODIFICATION ---
635
  const correctedMessage = this._parseAndCorrectErrorDetails(lastMessage);
636
  await this._handleRequestFailureAndSwitch(correctedMessage, res);
637
  const errorText = `收到 ${correctedMessage.status} 错误。${attempt < this.maxRetries ? `将在 ${this.retryDelay / 1000}秒后重试...` : '已达到最大重试次数。'}`;
638
- // --- END: MODIFICATION ---
639
-
640
  this._sendErrorChunkToClient(res, errorText);
641
  if (attempt < this.maxRetries) {
642
  await new Promise(resolve => setTimeout(resolve, this.retryDelay));
@@ -646,31 +662,73 @@ class RequestHandler {
646
  }
647
  break;
648
  }
649
- // --- START: MODIFICATION ---
650
  if (lastMessage.event_type === 'error' || requestFailed) {
651
  const finalError = this._parseAndCorrectErrorDetails(lastMessage);
652
- // 抛出错误,以便被外层 catch 块捕获,并使用修正后的信息
653
  throw new Error(`请求失败 (状态码: ${finalError.status}): ${finalError.message}`);
654
  }
655
- // --- END: MODIFICATION ---
656
 
657
  if (this.failureCount > 0) {
658
  this.logger.info(`✅ [Auth] 请求成功 - 失败计数已从 ${this.failureCount} 重置为 0`);
659
  }
660
  this.failureCount = 0;
 
661
  const dataMessage = await messageQueue.dequeue();
662
  const endMessage = await messageQueue.dequeue();
 
 
 
 
 
663
  if (dataMessage.data) {
664
- res.write(`data: ${dataMessage.data}\n\n`);
665
- this.logger.info('[Request] 已将完整响应体作为SSE事件发送。');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
666
  }
667
- if (endMessage.type !== 'STREAM_END') this.logger.warn('[Request] 未收到预期的流结束信号。');
 
 
 
 
 
 
 
 
 
668
  } finally {
669
  if (connectionMaintainer) clearInterval(connectionMaintainer);
670
  if (!res.writableEnded) res.end();
671
  this.logger.info('[Request] 假流式响应处理结束。');
672
  }
673
  }
 
674
  async _handleRealStreamResponse(proxyRequest, messageQueue, res) {
675
  let headerMessage, requestFailed = false;
676
  for (let attempt = 1; attempt <= this.maxRetries; attempt++) {
@@ -900,16 +958,22 @@ class ProxyServerSystem extends EventEmitter {
900
  let clientKey = null;
901
  let keySource = null;
902
 
 
903
  const headers = req.headers;
904
 
905
- if (headers['x-goog-api-key']) {
906
- clientKey = headers['x-goog-api-key'];
 
 
 
 
 
907
  keySource = 'x-goog-api-key Header';
908
- } else if (headers.authorization && headers.authorization.startsWith('Bearer ')) {
909
- clientKey = headers.authorization.substring(7);
910
  keySource = 'Authorization Header';
911
- } else if (headers['x-api-key']) {
912
- clientKey = headers['x-api-key'];
913
  keySource = 'X-API-Key Header';
914
  } else if (req.query.key) {
915
  clientKey = req.query.key;
 
384
  if (queue) {
385
  this._routeMessage(parsedMessage, queue);
386
  } else {
387
+ //this.logger.warn(`[Server] 收到未知请求ID的消息: ${requestId}`);
388
  }
389
  } catch (error) {
390
  this.logger.error('[Server] 解析内部WebSocket消息失败');
 
526
  return correctedDetails;
527
  }
528
 
529
+ async _handleRequestFailureAndSwitch(errorDetails, res) {
530
+ // 创建一个副本进行操作,并进行深度解析
531
+ const correctedDetails = { ...errorDetails };
532
+ if (correctedDetails.message && typeof correctedDetails.message === 'string') {
533
+ // 增强版正则表达式,能匹配 "HTTP 429" 或 JSON 中的 "code":429 等多种模式
534
+ const regex = /(?:HTTP|status code)\s*(\d{3})|"code"\s*:\s*(\d{3})/;
535
+ const match = correctedDetails.message.match(regex);
536
+
537
+ // match[1] 对应 (?:HTTP|status code)\s*(\d{3})
538
+ // match[2] 对应 "code"\s*:\s*(\d{3})
539
+ const parsedStatusString = match ? (match[1] || match[2]) : null;
540
+
541
+ if (parsedStatusString) {
542
+ const parsedStatus = parseInt(parsedStatusString, 10);
543
+ if (parsedStatus >= 400 && parsedStatus <= 599 && correctedDetails.status !== parsedStatus) {
544
+ this.logger.warn(`[Auth] 修正了错误状态码!原始: ${correctedDetails.status}, 从消息中解析得到: ${parsedStatus}`);
545
+ correctedDetails.status = parsedStatus;
546
+ }
547
+ }
548
+ }
549
+
550
+ // --- 后续逻辑使用修正后的 correctedDetails ---
551
 
552
+ const isImmediateSwitch = this.config.immediateSwitchStatusCodes.includes(correctedDetails.status);
 
553
 
554
  if (isImmediateSwitch) {
555
+ this.logger.warn(`🔴 [Auth] 收到状态码 ${correctedDetails.status} (已修正),触发立即切换账号...`);
556
+ if (res) this._sendErrorChunkToClient(res, `收到状态码 ${correctedDetails.status},正在尝试切换账号...`);
557
  try {
558
  await this._switchToNextAuth();
559
  if (res) this._sendErrorChunkToClient(res, `已切换到账号索引 ${this.currentAuthIndex},请重试`);
 
561
  this.logger.error(`🔴 [Auth] 账号切换失败: ${switchError.message}`);
562
  if (res) this._sendErrorChunkToClient(res, `切换账号失败: ${switchError.message}`);
563
  }
564
+ return; // 结束函数,外层循环将进行重试
565
  }
566
 
567
+ // 基于失败计数的切换逻辑
568
  if (this.config.failureThreshold > 0) {
569
  this.failureCount++;
570
+ this.logger.warn(`⚠️ [Auth] 请求失败 - 失败计数: ${this.failureCount}/${this.config.failureThreshold} (当前账号索引: ${this.currentAuthIndex}, 状态码: ${correctedDetails.status})`);
571
  if (this.failureCount >= this.config.failureThreshold) {
572
  this.logger.warn(`🔴 [Auth] 达到失败阈值!准备切换账号...`);
573
  if (res) this._sendErrorChunkToClient(res, `连续失败${this.failureCount}次,正在尝试切换账号...`);
 
580
  }
581
  }
582
  } else {
583
+ this.logger.warn(`[Auth] 请求失败 (状态码: ${correctedDetails.status})。基于计数的自动切换已禁用 (failureThreshold=0)`);
584
  }
585
  }
586
 
 
634
  this.logger.info(`[Request] 已向客户端发送标准错误信号: ${errorMessage}`);
635
  }
636
  }
637
+ async _handlePseudoStreamResponse(proxyRequest, messageQueue, req, res) {
638
+ // 注意:我们不再立即发送响应头,因为响应类型(SSE或JSON)尚未确定。
639
+ this.logger.info('[Request] 进入假流式处理流程,将根据原始请求路径决定最终响应格式。');
640
+
641
  let connectionMaintainer = null;
642
  try {
643
+ // 为了防止连接过早断开,可以先启动一个通用的心跳计时器
644
+ // 这个计时器只发送注释行,对SSE和JSON客户端都无害,但能保持连接
645
+ connectionMaintainer = setInterval(() => { if (!res.writableEnded) { res.write(': keep-alive\n\n'); } }, 15000);
646
+
647
  let lastMessage, requestFailed = false;
648
  for (let attempt = 1; attempt <= this.maxRetries; attempt++) {
649
  this.logger.info(`[Request] 请求尝试 #${attempt}/${this.maxRetries}...`);
650
  this._forwardRequest(proxyRequest);
651
  lastMessage = await messageQueue.dequeue();
652
  if (lastMessage.event_type === 'error' && lastMessage.status >= 400 && lastMessage.status <= 599) {
 
 
653
  const correctedMessage = this._parseAndCorrectErrorDetails(lastMessage);
654
  await this._handleRequestFailureAndSwitch(correctedMessage, res);
655
  const errorText = `收到 ${correctedMessage.status} 错误。${attempt < this.maxRetries ? `将在 ${this.retryDelay / 1000}秒后重试...` : '已达到最大重试次数。'}`;
 
 
656
  this._sendErrorChunkToClient(res, errorText);
657
  if (attempt < this.maxRetries) {
658
  await new Promise(resolve => setTimeout(resolve, this.retryDelay));
 
662
  }
663
  break;
664
  }
 
665
  if (lastMessage.event_type === 'error' || requestFailed) {
666
  const finalError = this._parseAndCorrectErrorDetails(lastMessage);
 
667
  throw new Error(`请求失败 (状态码: ${finalError.status}): ${finalError.message}`);
668
  }
 
669
 
670
  if (this.failureCount > 0) {
671
  this.logger.info(`✅ [Auth] 请求成功 - 失败计数已从 ${this.failureCount} 重置为 0`);
672
  }
673
  this.failureCount = 0;
674
+
675
  const dataMessage = await messageQueue.dequeue();
676
  const endMessage = await messageQueue.dequeue();
677
+ if (endMessage.type !== 'STREAM_END') this.logger.warn('[Request] 未收到预期的流结束信号。');
678
+
679
+ // 停止心跳计时器,因为我们即将发送最终数据
680
+ if (connectionMaintainer) clearInterval(connectionMaintainer);
681
+
682
  if (dataMessage.data) {
683
+ // ======================= START: CORE LOGIC CHANGE =======================
684
+ // 检查原始请求路径,判断客户端期望的是流还是普通JSON
685
+ const originalPath = req.path;
686
+ const isStreamRequest = originalPath.includes(':stream');
687
+
688
+ if (isStreamRequest) {
689
+ // 客户端想要一个流,我们模拟它 (保持原有逻辑)
690
+ this.logger.info(`[Request] 原始请求路径 "${originalPath}" 是流式请求,将模拟SSE响应。`);
691
+ res.status(200).set({
692
+ 'Content-Type': 'text/event-stream',
693
+ 'Cache-Control': 'no-cache',
694
+ 'Connection': 'keep-alive'
695
+ });
696
+ // 发送数据块
697
+ res.write(`data: ${dataMessage.data}\n\n`);
698
+ // 为提高兼容性,模拟一个 [DONE] 结束标志
699
+ res.write('data: [DONE]\n\n');
700
+ this.logger.info('[Request] 已将完整响应体作为模拟SSE事件发送。');
701
+ } else {
702
+ // 客户端想要一个普通JSON,我们直接返回它
703
+ this.logger.info(`[Request] 原始请求路径 "${originalPath}" 是非流式请求,将返回 application/json 响应。`);
704
+ try {
705
+ // 确保我们发送的是有效的JSON
706
+ const jsonData = JSON.parse(dataMessage.data);
707
+ res.status(200).json(jsonData);
708
+ } catch (e) {
709
+ this.logger.error(`[Request] 无法将来自浏览器的响应解析为JSON: ${e.message}`);
710
+ this._sendErrorResponse(res, 500, '代理内部错误:无法解析来自浏览器的响应。');
711
+ }
712
+ }
713
+ // ======================== END: CORE LOGIC CHANGE ========================
714
  }
715
+
716
+ } catch (error) {
717
+ // 如果出错时头还没发送,��们可以安全地发送一个错误状态码
718
+ if (!res.headersSent) {
719
+ this._handleRequestError(error, res);
720
+ } else {
721
+ // 如果头已发送(比如在模拟流时),我们只能在现有连接上发送错误块
722
+ this.logger.error(`[Request] 请求处理错误 (头已发送): ${error.message}`);
723
+ this._sendErrorChunkToClient(res, `处理失败: ${error.message}`);
724
+ }
725
  } finally {
726
  if (connectionMaintainer) clearInterval(connectionMaintainer);
727
  if (!res.writableEnded) res.end();
728
  this.logger.info('[Request] 假流式响应处理结束。');
729
  }
730
  }
731
+
732
  async _handleRealStreamResponse(proxyRequest, messageQueue, res) {
733
  let headerMessage, requestFailed = false;
734
  for (let attempt = 1; attempt <= this.maxRetries; attempt++) {
 
958
  let clientKey = null;
959
  let keySource = null;
960
 
961
+ // 在Express中, 所有请求头的键名都会被自动转换为小写。
962
  const headers = req.headers;
963
 
964
+ // 为了健壮性, 同时检查使用连字符(标准)和下划线(常见错误)的头。
965
+ const xGoogApiKey = headers['x-goog-api-key'] || headers['x_goog_api_key'];
966
+ const xApiKey = headers['x-api-key'] || headers['x_api_key'];
967
+ const authHeader = headers.authorization;
968
+
969
+ if (xGoogApiKey) {
970
+ clientKey = xGoogApiKey;
971
  keySource = 'x-goog-api-key Header';
972
+ } else if (authHeader && authHeader.startsWith('Bearer ')) {
973
+ clientKey = authHeader.substring(7);
974
  keySource = 'Authorization Header';
975
+ } else if (xApiKey) {
976
+ clientKey = xApiKey;
977
  keySource = 'X-API-Key Header';
978
  } else if (req.query.key) {
979
  clientKey = req.query.key;