Spaces:
Paused
Paused
feat: add force_http11 option for proxies without HTTP/2 support (#56)
Browse filesWhen 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 +17 -1
- config/default.yaml +2 -0
- src/config.ts +1 -0
- src/tls/curl-binary.ts +8 -1
- src/tls/libcurl-ffi-transport.ts +7 -1
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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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, "");
|