Skip to content

Commit 932a793

Browse files
committed
fix: strip Accept-Encoding to get uncompressed SSE responses
SSE responses were gzip-compressed, causing the parser to fail. Also update README with clearer env var instructions for Windows.
1 parent 3db545c commit 932a793

2 files changed

Lines changed: 37 additions & 7 deletions

File tree

README.md

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,20 +45,34 @@ sudo update-ca-certificates
4545
export NODE_EXTRA_CA_CERTS=$(pwd)/ca.crt
4646
```
4747

48-
3. Run Claude Code through the proxy (in a separate terminal):
48+
3. Set the environment variables **in the terminal where you will run Claude Code** (not the proxy terminal):
4949

50-
**Linux/macOS:**
50+
**Linux/macOS (session only):**
5151
```bash
52-
HTTPS_PROXY=http://127.0.0.1:8080 NODE_EXTRA_CA_CERTS=$(pwd)/ca.crt claude
52+
export HTTPS_PROXY=http://127.0.0.1:8080
53+
export NODE_EXTRA_CA_CERTS=$(pwd)/ca.crt
54+
claude
5355
```
5456

55-
**Windows (PowerShell):**
57+
**Windows (PowerShell, session only):**
5658
```powershell
5759
$env:HTTPS_PROXY = "http://127.0.0.1:8080"
5860
$env:NODE_EXTRA_CA_CERTS = "$pwd\ca.crt"
5961
claude
6062
```
6163

64+
**To set them permanently (Windows):**
65+
```powershell
66+
[Environment]::SetEnvironmentVariable("HTTPS_PROXY", "http://127.0.0.1:8080", "User")
67+
[Environment]::SetEnvironmentVariable("NODE_EXTRA_CA_CERTS", "C:\path\to\ca.crt", "User")
68+
```
69+
70+
**To set them permanently (Linux/macOS):** add to `~/.bashrc` or `~/.zshrc`:
71+
```bash
72+
export HTTPS_PROXY=http://127.0.0.1:8080
73+
export NODE_EXTRA_CA_CERTS=/path/to/ca.crt
74+
```
75+
6276
4. Press Enter in the proxy terminal to launch the TUI and watch requests in real time.
6377

6478
## TUI Controls

src/proxy.rs

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,10 @@ async fn forward_and_intercept_inner(
224224
.version(parts.version);
225225

226226
for (name, value) in &parts.headers {
227+
// Strip Accept-Encoding so upstream sends uncompressed SSE we can parse
228+
if name.as_str().eq_ignore_ascii_case("accept-encoding") {
229+
continue;
230+
}
227231
upstream_req_builder = upstream_req_builder.header(name, value);
228232
}
229233

@@ -237,12 +241,18 @@ async fn forward_and_intercept_inner(
237241
.map_err(|e| anyhow::anyhow!("Upstream request failed: {}", e))?;
238242

239243
// Check if this is an SSE response
240-
let is_sse = upstream_res
244+
let content_type = upstream_res
241245
.headers()
242246
.get("content-type")
243247
.and_then(|v| v.to_str().ok())
244-
.map(|v| v.contains("text/event-stream"))
245-
.unwrap_or(false);
248+
.unwrap_or("none")
249+
.to_string();
250+
let is_sse = content_type.contains("text/event-stream");
251+
252+
tracing::info!(
253+
"Response for {} {} — content-type: {}, is_sse: {}, is_messages_api: {}",
254+
method, path, content_type, is_sse, is_messages_api
255+
);
246256

247257
if is_sse && is_messages_api {
248258
tee_sse_response(upstream_res, request_id, event_tx).await
@@ -291,12 +301,18 @@ async fn tee_sse_response(
291301

292302
tokio::spawn(async move {
293303
let mut parser = SseParser::new();
304+
tracing::info!("SSE tee started for req {}", request_id);
294305

295306
while let Some(frame_result) = body.frame().await {
296307
match frame_result {
297308
Ok(frame) => {
298309
if let Some(data) = frame.data_ref() {
310+
let preview = String::from_utf8_lossy(&data[..data.len().min(500)]);
311+
tracing::info!("SSE chunk for req {}: {} bytes — {:?}", request_id, data.len(), preview);
299312
let events = parser.feed(data);
313+
for event in &events {
314+
tracing::info!("SSE event for req {}: {:?}", request_id, event);
315+
}
300316
for event in events {
301317
match event {
302318
SseEvent::ContentBlockDelta { text } => {

0 commit comments

Comments
 (0)