mirror of
https://github.com/zyycn/codex-proxy-rs.git
synced 2026-11-06 16:06:08 +00:00
feat: add codex tls client and headers
Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
Generated
+153
@@ -2,6 +2,12 @@
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "adler2"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
|
||||
|
||||
[[package]]
|
||||
name = "aead"
|
||||
version = "0.5.2"
|
||||
@@ -46,6 +52,21 @@ dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "alloc-no-stdlib"
|
||||
version = "2.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3"
|
||||
|
||||
[[package]]
|
||||
name = "alloc-stdlib"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece"
|
||||
dependencies = [
|
||||
"alloc-no-stdlib",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "allocator-api2"
|
||||
version = "0.2.21"
|
||||
@@ -95,6 +116,18 @@ dependencies = [
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-compression"
|
||||
version = "0.4.42"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e79b3f8a79cccc2898f31920fc69f304859b3bd567490f75ebf51ae1c792a9ac"
|
||||
dependencies = [
|
||||
"compression-codecs",
|
||||
"compression-core",
|
||||
"pin-project-lite",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-trait"
|
||||
version = "0.1.89"
|
||||
@@ -227,6 +260,27 @@ dependencies = [
|
||||
"hybrid-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "brotli"
|
||||
version = "8.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8119e4516436f5708bbc474a9d395bf12f1b5395e93a92a56e647ac3388c8610"
|
||||
dependencies = [
|
||||
"alloc-no-stdlib",
|
||||
"alloc-stdlib",
|
||||
"brotli-decompressor",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "brotli-decompressor"
|
||||
version = "5.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5962523e1b92ce1b5e793d9169b9943eece10d39f62550bc04bb605d75b94924"
|
||||
dependencies = [
|
||||
"alloc-no-stdlib",
|
||||
"alloc-stdlib",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.20.3"
|
||||
@@ -252,6 +306,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "556e016178bb5662a08681bbe0f00f8e17631781a4dfc8c45e466e4b185ec27f"
|
||||
dependencies = [
|
||||
"find-msvc-tools",
|
||||
"jobserver",
|
||||
"libc",
|
||||
"shlex",
|
||||
]
|
||||
|
||||
@@ -347,6 +403,26 @@ dependencies = [
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "compression-codecs"
|
||||
version = "0.4.38"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce2548391e9c1929c21bf6aa2680af86fe4c1b33e6cea9ac1cfeec0bd11218cf"
|
||||
dependencies = [
|
||||
"brotli",
|
||||
"compression-core",
|
||||
"flate2",
|
||||
"memchr",
|
||||
"zstd",
|
||||
"zstd-safe",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "compression-core"
|
||||
version = "0.4.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cc14f565cf027a105f7a44ccf9e5b424348421a1d8952a8fc9d499d313107789"
|
||||
|
||||
[[package]]
|
||||
name = "concurrent-queue"
|
||||
version = "2.5.0"
|
||||
@@ -500,6 +576,15 @@ version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "217698eaf96b4a3f0bc4f3662aaa55bdf913cd54d7204591faa790070c6d0853"
|
||||
|
||||
[[package]]
|
||||
name = "crc32fast"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-channel"
|
||||
version = "0.5.15"
|
||||
@@ -743,6 +828,16 @@ version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c"
|
||||
dependencies = [
|
||||
"crc32fast",
|
||||
"miniz_oxide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "flume"
|
||||
version = "0.12.0"
|
||||
@@ -1330,6 +1425,16 @@ version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
|
||||
|
||||
[[package]]
|
||||
name = "jobserver"
|
||||
version = "0.1.34"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33"
|
||||
dependencies = [
|
||||
"getrandom 0.3.4",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.100"
|
||||
@@ -1457,6 +1562,16 @@ version = "0.3.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.8.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
|
||||
dependencies = [
|
||||
"adler2",
|
||||
"simd-adler32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "1.2.1"
|
||||
@@ -2257,6 +2372,12 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "simd-adler32"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214"
|
||||
|
||||
[[package]]
|
||||
name = "similar"
|
||||
version = "2.7.0"
|
||||
@@ -2759,13 +2880,17 @@ version = "0.6.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840"
|
||||
dependencies = [
|
||||
"async-compression",
|
||||
"bitflags",
|
||||
"bytes",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"http",
|
||||
"http-body",
|
||||
"http-body-util",
|
||||
"pin-project-lite",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"tower",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
@@ -3575,3 +3700,31 @@ name = "zmij"
|
||||
version = "1.0.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
|
||||
|
||||
[[package]]
|
||||
name = "zstd"
|
||||
version = "0.13.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a"
|
||||
dependencies = [
|
||||
"zstd-safe",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zstd-safe"
|
||||
version = "7.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d"
|
||||
dependencies = [
|
||||
"zstd-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zstd-sys"
|
||||
version = "2.0.16+zstd.1.5.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "91e19ebc2adc8f83e43039e79776e3fda8ca919132d68a1fed6a5faca2683748"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"pkg-config",
|
||||
]
|
||||
|
||||
+1
-1
@@ -21,7 +21,7 @@ dotenvy = "0.15.7"
|
||||
futures = "0.3.32"
|
||||
hmac = "0.13.0"
|
||||
rand = "0.10.1"
|
||||
reqwest = { version = "=0.12.28", default-features = false, features = ["json", "stream", "cookies", "rustls-tls-native-roots"] }
|
||||
reqwest = { version = "=0.12.28", default-features = false, features = ["json", "stream", "cookies", "rustls-tls-native-roots", "gzip", "brotli", "zstd", "deflate"] }
|
||||
rustls = { version = "=0.23.36", default-features = false, features = ["ring", "std", "tls12"] }
|
||||
secrecy = "0.10.3"
|
||||
serde = { version = "1.0.228", features = ["derive"] }
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
- Commit `Cargo.lock` because this is an application.
|
||||
- Run `cargo update` in dedicated dependency-refresh changes.
|
||||
- Keep `reqwest = 0.12.28` and `rustls = 0.23.36` pinned until a TLS fingerprint review proves a newer pair matches real Codex Desktop behavior.
|
||||
- `reqwest` is built with `rustls-tls-native-roots`, `json`, `stream`, `cookies`, and response compression features used by the Codex client builder. Do not switch back to default native-tls.
|
||||
- Do not add unmaintained crates when a maintained community-standard crate exists.
|
||||
- Every dependency change must run:
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ Update this file before each feature commit.
|
||||
| Admin auth and client API keys | Completed | pending auth commit | `cargo test client_api_key_has_proxy_prefix_and_verifies_against_hash`; `cargo test admin_password_hash_is_not_a_client_api_key`; full `fmt/test/clippy` before commit | Admin passwords use Argon2id, client API keys use `cpr_` prefix plus HMAC-SHA256 with server-side pepper. |
|
||||
| Secret encryption | Completed | pending crypto commit | `cargo test secret_box_encrypts_and_decrypts_without_storing_plaintext`; full `fmt/test/clippy` before commit | AES-256-GCM `SecretBox` stores upstream secrets as `v1:<nonce>:<ciphertext>` without plaintext. |
|
||||
| Logging and pagination | Completed | pending logs commit | `cargo test event_logs_are_cursor_paginated`; full `fmt/test/clippy` before commit | Adds cursor pagination, `Page<T>` camelCase serialization, event log repository, and rotation config shell. |
|
||||
| TLS headers and fingerprint | Planned | | | |
|
||||
| TLS headers and fingerprint | Completed | pending codex headers commit | `cargo test codex_headers_include_desktop_identity_and_turn_state`; `cargo tree \| rg 'reqwest\|rustls'`; full `fmt/test/clippy` before commit | Adds Codex Desktop fingerprint model, exact identity/request headers, and pinned reqwest/rustls client builder. |
|
||||
| Cookie persistence | Planned | | | |
|
||||
| Account pool and refresh | Planned | | | |
|
||||
| Translation | Planned | | | |
|
||||
|
||||
@@ -1745,7 +1745,7 @@ git commit -m "feat: add paginated event logs"
|
||||
- Modify: `/home/zyy/桌面/Codes/codex-proxy-rs/src/lib.rs`
|
||||
- Test: `/home/zyy/桌面/Codes/codex-proxy-rs/tests/codex_headers_test.rs`
|
||||
|
||||
- [ ] **Step 1: Write failing header test**
|
||||
- [x] **Step 1: Write failing header test**
|
||||
|
||||
Create `/home/zyy/桌面/Codes/codex-proxy-rs/tests/codex_headers_test.rs`:
|
||||
|
||||
@@ -1769,7 +1769,7 @@ fn codex_headers_include_desktop_identity_and_turn_state() {
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Run test and verify RED**
|
||||
- [x] **Step 2: Run test and verify RED**
|
||||
|
||||
Run:
|
||||
|
||||
@@ -1779,7 +1779,7 @@ cargo test codex_headers_include_desktop_identity_and_turn_state
|
||||
|
||||
Expected: compile failure because `codex` and `fingerprint` modules do not exist.
|
||||
|
||||
- [ ] **Step 3: Implement fingerprint and headers**
|
||||
- [x] **Step 3: Implement fingerprint and headers**
|
||||
|
||||
Modify `/home/zyy/桌面/Codes/codex-proxy-rs/src/lib.rs`:
|
||||
|
||||
@@ -1908,7 +1908,7 @@ Create empty module files:
|
||||
touch src/fingerprint/updater.rs src/codex/sse.rs src/codex/types.rs src/codex/usage.rs src/codex/websocket.rs
|
||||
```
|
||||
|
||||
- [ ] **Step 4: Verify headers**
|
||||
- [x] **Step 4: Verify headers**
|
||||
|
||||
Run:
|
||||
|
||||
@@ -1919,7 +1919,7 @@ cargo tree | rg 'reqwest|rustls'
|
||||
|
||||
Expected: test passes; `cargo tree` shows `reqwest v0.12.28` and `rustls v0.23.36`.
|
||||
|
||||
- [ ] **Step 5: Commit TLS and headers foundation**
|
||||
- [x] **Step 5: Commit TLS and headers foundation**
|
||||
|
||||
```bash
|
||||
git add Cargo.toml src/codex src/fingerprint src/lib.rs tests/codex_headers_test.rs
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
use reqwest::Client;
|
||||
|
||||
pub fn build_reqwest_client(force_http11: bool) -> Result<Client, reqwest::Error> {
|
||||
// 中文注释:Codex Desktop 指纹依赖 reqwest/rustls 组合,升级前必须重新验证 TLS 行为。
|
||||
let builder = Client::builder()
|
||||
.use_rustls_tls()
|
||||
.no_proxy()
|
||||
.gzip(true)
|
||||
.brotli(true)
|
||||
.zstd(true)
|
||||
.deflate(true);
|
||||
let builder = if force_http11 {
|
||||
builder.http1_only()
|
||||
} else {
|
||||
builder
|
||||
};
|
||||
builder.build()
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use crate::fingerprint::model::Fingerprint;
|
||||
|
||||
pub fn build_codex_headers(
|
||||
fp: &Fingerprint,
|
||||
access_token: &str,
|
||||
account_id: Option<&str>,
|
||||
turn_state: Option<&str>,
|
||||
request_id: &str,
|
||||
) -> BTreeMap<String, String> {
|
||||
let mut headers = BTreeMap::new();
|
||||
headers.insert("accept".to_string(), "text/event-stream".to_string());
|
||||
headers.insert(
|
||||
"authorization".to_string(),
|
||||
format!("Bearer {access_token}"),
|
||||
);
|
||||
headers.insert("originator".to_string(), fp.originator.clone());
|
||||
headers.insert("user-agent".to_string(), fp.user_agent());
|
||||
headers.insert("x-client-request-id".to_string(), request_id.to_string());
|
||||
headers.insert(
|
||||
"x-openai-internal-codex-residency".to_string(),
|
||||
"global".to_string(),
|
||||
);
|
||||
if let Some(account_id) = account_id {
|
||||
headers.insert("chatgpt-account-id".to_string(), account_id.to_string());
|
||||
}
|
||||
if let Some(turn_state) = turn_state {
|
||||
headers.insert("x-codex-turn-state".to_string(), turn_state.to_string());
|
||||
}
|
||||
headers
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
pub mod client;
|
||||
pub mod headers;
|
||||
pub mod sse;
|
||||
pub mod types;
|
||||
pub mod usage;
|
||||
pub mod websocket;
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
pub mod model;
|
||||
pub mod updater;
|
||||
@@ -0,0 +1,35 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct Fingerprint {
|
||||
pub originator: String,
|
||||
pub app_version: String,
|
||||
pub build_number: String,
|
||||
pub platform: String,
|
||||
pub arch: String,
|
||||
pub chromium_version: String,
|
||||
pub user_agent_template: String,
|
||||
}
|
||||
|
||||
impl Fingerprint {
|
||||
pub fn default_for_tests() -> Self {
|
||||
Self {
|
||||
originator: "Codex Desktop".to_string(),
|
||||
app_version: "26.519.81530".to_string(),
|
||||
build_number: "3178".to_string(),
|
||||
platform: "darwin".to_string(),
|
||||
arch: "arm64".to_string(),
|
||||
chromium_version: "146".to_string(),
|
||||
user_agent_template:
|
||||
"Codex/{app_version} ({platform}; {arch}) Chromium/{chromium_version}".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn user_agent(&self) -> String {
|
||||
self.user_agent_template
|
||||
.replace("{app_version}", &self.app_version)
|
||||
.replace("{platform}", &self.platform)
|
||||
.replace("{arch}", &self.arch)
|
||||
.replace("{chromium_version}", &self.chromium_version)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
pub mod auth;
|
||||
pub mod codex;
|
||||
pub mod config;
|
||||
pub mod crypto;
|
||||
pub mod error;
|
||||
pub mod fingerprint;
|
||||
pub mod logs;
|
||||
pub mod pagination;
|
||||
pub mod state;
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
use codex_proxy_rs::{codex::headers::build_codex_headers, fingerprint::model::Fingerprint};
|
||||
|
||||
#[test]
|
||||
fn codex_headers_include_desktop_identity_and_turn_state() {
|
||||
let fp = Fingerprint::default_for_tests();
|
||||
let headers = build_codex_headers(
|
||||
&fp,
|
||||
"access-token",
|
||||
Some("acct_123"),
|
||||
Some("turn-state"),
|
||||
"rid_1",
|
||||
);
|
||||
|
||||
assert_eq!(headers.get("originator").unwrap(), "Codex Desktop");
|
||||
assert!(headers.get("user-agent").unwrap().contains("Codex"));
|
||||
assert_eq!(headers.get("authorization").unwrap(), "Bearer access-token");
|
||||
assert_eq!(headers.get("chatgpt-account-id").unwrap(), "acct_123");
|
||||
assert_eq!(headers.get("x-codex-turn-state").unwrap(), "turn-state");
|
||||
assert_eq!(headers.get("x-client-request-id").unwrap(), "rid_1");
|
||||
}
|
||||
Reference in New Issue
Block a user