jeasonstudio Claude Opus 4.6 commited on
Commit
34fceda
·
unverified ·
1 Parent(s): 175f068

feat: add force_http11 option for proxies without HTTP/2 support (#56)

Browse files

When using proxies like Clash/mihomo that don't support HTTP/2,
users encounter errors like:
"curl: (16) Remote peer returned unexpected data while we expected
SETTINGS frame."

This commit adds a `force_http11` config option to force HTTP/1.1
protocol in both curl CLI and libcurl FFI transports.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

README.md CHANGED
@@ -288,7 +288,23 @@ for await (const chunk of stream) {
288
  | `client` | `app_version`, `build_number`, `chromium_version` | 模拟的 Codex Desktop 版本与 Chromium 版本 |
289
  | `model` | `default`, `default_reasoning_effort`, `default_service_tier` | 默认模型、推理强度与速度模式 |
290
  | `auth` | `rotation_strategy`, `rate_limit_backoff_seconds` | 轮换策略与限流退避 |
291
- | `tls` | `curl_binary`, `impersonate_profile`, `proxy_url` | TLS 伪装与代理配置 |
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
292
 
293
  ### API 密钥 (proxy_api_key)
294
 
 
288
  | `client` | `app_version`, `build_number`, `chromium_version` | 模拟的 Codex Desktop 版本与 Chromium 版本 |
289
  | `model` | `default`, `default_reasoning_effort`, `default_service_tier` | 默认模型、推理强度与速度模式 |
290
  | `auth` | `rotation_strategy`, `rate_limit_backoff_seconds` | 轮换策略与限流退避 |
291
+ | `tls` | `curl_binary`, `impersonate_profile`, `proxy_url`, `force_http11` | TLS 伪装与代理配置 |
292
+
293
+ #### TLS 配置选项
294
+
295
+ ```yaml
296
+ tls:
297
+ curl_binary: auto # curl 二进制路径(auto 自动检测)
298
+ impersonate_profile: chrome136 # Chrome 伪装版本
299
+ proxy_url: null # 代理地址(null 自动检测本地代理)
300
+ force_http11: false # 强制使用 HTTP/1.1(解决代理不支持 HTTP/2 的问题)
301
+ ```
302
+
303
+ **`force_http11`**:当你的代理(如 Clash/mihomo)出现以下错误时启用:
304
+ ```
305
+ curl: (16) Remote peer returned unexpected data while we expected SETTINGS frame.
306
+ Perhaps, peer does not support HTTP/2 properly.
307
+ ```
308
 
309
  ### API 密钥 (proxy_api_key)
310
 
config/default.yaml CHANGED
@@ -34,3 +34,5 @@ tls:
34
  curl_binary: auto
35
  impersonate_profile: chrome144
36
  proxy_url: null
 
 
 
34
  curl_binary: auto
35
  impersonate_profile: chrome144
36
  proxy_url: null
37
+ # 当代理不支持 HTTP/2 时启用此选项(如遇到 SETTINGS frame 错误)
38
+ force_http11: true
src/config.ts CHANGED
@@ -49,6 +49,7 @@ const ConfigSchema = z.object({
49
  impersonate_profile: z.string().default("chrome136"),
50
  proxy_url: z.string().nullable().default(null),
51
  transport: z.enum(["auto", "curl-cli", "libcurl-ffi"]).default("auto"),
 
52
  }).default({}),
53
  });
54
 
 
49
  impersonate_profile: z.string().default("chrome136"),
50
  proxy_url: z.string().nullable().default(null),
51
  transport: z.enum(["auto", "curl-cli", "libcurl-ffi"]).default("auto"),
52
+ force_http11: z.boolean().default(false),
53
  }).default({}),
54
  });
55
 
src/tls/curl-binary.ts CHANGED
@@ -165,6 +165,7 @@ function detectImpersonateSupport(binary: string): string[] {
165
  * Get Chrome TLS profile args to prepend to curl commands.
166
  * Returns empty array when using system curl (args are curl-impersonate specific).
167
  * Uses --impersonate flag when available, otherwise falls back to manual CHROME_TLS_ARGS.
 
168
  */
169
  export function getChromeTlsArgs(): string[] {
170
  // Ensure binary is resolved first
@@ -173,7 +174,13 @@ export function getChromeTlsArgs(): string[] {
173
  if (!_tlsArgs) {
174
  _tlsArgs = detectImpersonateSupport(_resolved!);
175
  }
176
- return [..._tlsArgs];
 
 
 
 
 
 
177
  }
178
 
179
  /**
 
165
  * Get Chrome TLS profile args to prepend to curl commands.
166
  * Returns empty array when using system curl (args are curl-impersonate specific).
167
  * Uses --impersonate flag when available, otherwise falls back to manual CHROME_TLS_ARGS.
168
+ * When force_http11 is enabled, adds --http1.1 to force HTTP/1.1 protocol.
169
  */
170
  export function getChromeTlsArgs(): string[] {
171
  // Ensure binary is resolved first
 
174
  if (!_tlsArgs) {
175
  _tlsArgs = detectImpersonateSupport(_resolved!);
176
  }
177
+ const args = [..._tlsArgs];
178
+ // Force HTTP/1.1 when configured (for proxies that don't support HTTP/2)
179
+ const config = getConfig();
180
+ if (config.tls.force_http11) {
181
+ args.push("--http1.1");
182
+ }
183
+ return args;
184
  }
185
 
186
  /**
src/tls/libcurl-ffi-transport.ts CHANGED
@@ -14,6 +14,7 @@ import type { IKoffiLib, IKoffiCType, IKoffiRegisteredCallback, KoffiFunction }
14
  import type { TlsTransport, TlsTransportResponse } from "./transport.js";
15
  import { getProxyUrl, getResolvedProfile } from "./curl-binary.js";
16
  import { getBinDir } from "../paths.js";
 
17
 
18
  // ── libcurl constants ──────────────────────────────────────────────
19
 
@@ -30,6 +31,7 @@ const CURLOPT_PROXY = 10004;
30
  const CURLOPT_CAINFO = 10065;
31
  const CURLOPT_ACCEPT_ENCODING = 10102;
32
  const CURLOPT_HTTP_VERSION = 84;
 
33
  const CURL_HTTP_VERSION_2_0 = 3;
34
  const CURLINFO_RESPONSE_CODE = 0x200002;
35
  const CURLM_OK = 0;
@@ -462,7 +464,11 @@ export class LibcurlFfiTransport implements TlsTransport {
462
 
463
  b.curl_easy_setopt_str(easy, CURLOPT_URL, url);
464
  b.curl_easy_setopt_long(easy, CURLOPT_NOSIGNAL, 1);
465
- b.curl_easy_setopt_long(easy, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0);
 
 
 
 
466
 
467
  // Accept-Encoding — let libcurl handle decompression
468
  b.curl_easy_setopt_str(easy, CURLOPT_ACCEPT_ENCODING, "");
 
14
  import type { TlsTransport, TlsTransportResponse } from "./transport.js";
15
  import { getProxyUrl, getResolvedProfile } from "./curl-binary.js";
16
  import { getBinDir } from "../paths.js";
17
+ import { getConfig } from "../config.js";
18
 
19
  // ── libcurl constants ──────────────────────────────────────────────
20
 
 
31
  const CURLOPT_CAINFO = 10065;
32
  const CURLOPT_ACCEPT_ENCODING = 10102;
33
  const CURLOPT_HTTP_VERSION = 84;
34
+ const CURL_HTTP_VERSION_1_1 = 2;
35
  const CURL_HTTP_VERSION_2_0 = 3;
36
  const CURLINFO_RESPONSE_CODE = 0x200002;
37
  const CURLM_OK = 0;
 
464
 
465
  b.curl_easy_setopt_str(easy, CURLOPT_URL, url);
466
  b.curl_easy_setopt_long(easy, CURLOPT_NOSIGNAL, 1);
467
+
468
+ // HTTP version: use HTTP/1.1 when force_http11 is enabled (for proxies that don't support HTTP/2)
469
+ const config = getConfig();
470
+ const httpVersion = config.tls.force_http11 ? CURL_HTTP_VERSION_1_1 : CURL_HTTP_VERSION_2_0;
471
+ b.curl_easy_setopt_long(easy, CURLOPT_HTTP_VERSION, httpVersion);
472
 
473
  // Accept-Encoding — let libcurl handle decompression
474
  b.curl_easy_setopt_str(easy, CURLOPT_ACCEPT_ENCODING, "");