Skip to content

Improve proxy wrapper: HTTP forwarding, NO_PROXY support, tests#13

Merged
a5m0 merged 4 commits intomainfrom
claude/compare-proxy-setup-L5i7J
Feb 22, 2026
Merged

Improve proxy wrapper: HTTP forwarding, NO_PROXY support, tests#13
a5m0 merged 4 commits intomainfrom
claude/compare-proxy-setup-L5i7J

Conversation

@a5m0
Copy link
Owner

@a5m0 a5m0 commented Feb 22, 2026

Summary

  • Rewrite proxy wrapper to support plain HTTP request forwarding (not just CONNECT tunnels), NO_PROXY/HTTP_PROXY env vars, dynamic port allocation, and proper HTTP error responses (400/502/504) back to Chrome
  • Fix localhost bypass bug where localhost/127.0.0.1/::1 was unreachable when NO_PROXY was empty due to an early return
  • Add *.domain glob pattern support for NO_PROXY matching — the real Claude Code web environment uses *.googleapis.com style patterns, not just .domain suffix syntax
  • Fix shutdown race condition in stop_proxy_wrapper where the accept thread referenced a global that was set to None before the thread exited, causing AttributeError
  • Add 48 tests covering all proxy wrapper functions end-to-end: NO_PROXY matching, auth header encoding/injection, env var parsing, server lifecycle, and integration tests with a mock upstream proxy

Test plan

  • pytest tests/test_proxy_wrapper.py — 48 tests passing
  • Manual verification in Claude Code web environment with real upstream proxy

…r handling

Inspired by simonw/research go-rod-cli proxy approach, this improves our
Python proxy wrapper with several missing capabilities:

- Add plain HTTP request forwarding (previously only CONNECT tunnels worked)
- Add HTTP_PROXY and NO_PROXY environment variable support
- Use dynamic port allocation instead of hardcoded port 18080
- Send proper HTTP error responses (400/502/504) to Chrome instead of silently dropping connections
- Replace bare except blocks with typed OSError catches and logging
- Extract shared helpers (_build_auth_header, _inject_auth_header, _send_error, _forward_data)
- Reduce startup sleep from 500ms to 100ms

https://claude.ai/code/session_01G9pCRRAvGKFduD1eosqDA5
The localhost/127.0.0.1/::1 bypass was unreachable when NO_PROXY was
empty because of an early return. Move the localhost check before
the no_proxy emptiness guard.

https://claude.ai/code/session_01G9pCRRAvGKFduD1eosqDA5
The real NO_PROXY env in Claude Code web uses glob syntax like
*.googleapis.com and *.svc.cluster.local. Previously only the
.domain suffix syntax was handled. Now both forms work:
- "*.example.com" matches "sub.example.com" and "example.com"
- ".example.com" matches "sub.example.com" and "example.com"

Verified with live tests against the real upstream proxy.

https://claude.ai/code/session_01G9pCRRAvGKFduD1eosqDA5
- Add 48 unit/integration tests covering: _should_bypass_proxy (NO_PROXY
  matching), _build_auth_header, _inject_auth_header, _send_error,
  get_proxy_config, is_claude_code_web_environment, start/stop lifecycle,
  and end-to-end handle_client with mock upstream proxy
- Fix race in stop_proxy_wrapper: accept loop referenced the global
  _wrapper_server which was set to None before the thread exited,
  causing AttributeError. Now captures a local reference and joins
  the thread on shutdown.

https://claude.ai/code/session_01G9pCRRAvGKFduD1eosqDA5
@a5m0 a5m0 merged commit c0b97b4 into main Feb 22, 2026
1 check passed
@a5m0 a5m0 deleted the claude/compare-proxy-setup-L5i7J branch February 22, 2026 21:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants