Skip to content

Feature Request: HTTP Polling Fallback for Corporate Proxy Environments #536

@ShijunDeng

Description

@ShijunDeng

Problem

In many corporate/enterprise network environments, HTTP proxies or firewalls block WebSocket (wss://) connections. This makes CloudCLI completely unusable for both the Chat interface and the Shell terminal, as both rely exclusively on WebSocket transport.

Affected scenarios:

  • Corporate HTTP proxies that only allow standard HTTP/HTTPS traffic
  • Firewalls with deep packet inspection that strip Upgrade: websocket headers
  • Restrictive network policies that whitelist only ports 80/443 with HTTP-only protocols
  • SSE (Server-Sent Events) is also blocked in some environments

Proposed Solution

Implement an automatic HTTP Long Polling fallback that transparently substitutes WebSocket when connections fail. The design should:

1. Auto-detection & transparent fallback

  • Track WebSocket connection failures (e.g., after 3 consecutive failures)
  • Automatically switch to HTTP polling mode without user intervention
  • No degradation for users with working WebSocket connections

2. Chat polling transport (/ws replacement)

  • POST /api/poll/connect — register a polling connection
  • GET /api/poll/messages — drain queued messages (with aggressive cache-busting headers)
  • POST /api/poll/send — send commands (claude-command, abort-session, etc.)
  • POST /api/poll/disconnect — cleanup
  • Adaptive polling interval: ~100ms during activity, ~500ms when idle
  • 10ms yield between message dispatches to prevent React setState batching issues

3. Shell polling transport (/shell replacement)

  • POST /api/poll/shell/connect — register shell polling + spawn PTY via handleShellConnection
  • GET /api/poll/shell/output — drain PTY output queue
  • POST /api/poll/shell/send — forward init/input/resize to PTY
  • POST /api/poll/shell/disconnect — cleanup
  • Faster polling interval (~50ms active / ~200ms idle) for interactive terminal feel
  • Server-side FakeShellWs (EventEmitter) that mimics WebSocket API for handleShellConnection compatibility

4. Frontend monkey-patch approach

  • Intercept new WebSocket(url) calls for /ws?token= and /shell?token= URLs
  • Replace with PollingWebSocket / PollingShellWebSocket classes that use fetch() internally
  • Maintain full WebSocket API compatibility (readyState, onopen, onmessage, onclose, send, close, addEventListener)

Implementation Notes

I have a working proof-of-concept deployed in a production environment behind an Nginx reverse proxy with SSL. Key learnings:

  1. Cache-busting is critical — Browser/proxy caching of poll responses causes stale data. Need Cache-Control: no-store, unique ETag, and _t=Date.now() query params.
  2. Auth must use headers, not query params — Passing JWT tokens as URL query parameters causes jwt malformed errors in some proxy configurations. Use Authorization: Bearer header instead.
  3. React setState batching — Dispatching multiple onmessage events synchronously causes React to only render the last message. A small await setTimeout(10ms) between dispatches fixes this.
  4. Service Worker interference — SW caching of index.html prevents users from getting updated polling code. Cache version bumping is needed.

Ideal Implementation Path

Rather than the monkey-patch approach (which works but is fragile), the proper implementation would be:

  1. Add polling transport as a first-class option in the React source code (e.g., usePollingWebSocket hook)
  2. Server-side polling endpoints as a separate Express router module
  3. Environment variable to force polling mode: FORCE_POLLING=true
  4. Auto-detection built into the WebSocket connection hook with configurable threshold

Environment

  • CloudCLI version: 1.25.2 (npm @siteboon/claude-code-ui)
  • Deployment: Linux server behind Nginx reverse proxy with SSL
  • Corporate network: HTTP proxy blocking WebSocket and SSE

Submitted by @ShijunDeng — happy to contribute a PR if the maintainers are interested in this direction.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions