Skip to content

feat: allow custom http agent for streaming websocket connections#504

Open
valeriudev wants to merge 1 commit into
deepgram:mainfrom
valeriudev:vg/ws-proxy-agent
Open

feat: allow custom http agent for streaming websocket connections#504
valeriudev wants to merge 1 commit into
deepgram:mainfrom
valeriudev:vg/ws-proxy-agent

Conversation

@valeriudev

Copy link
Copy Markdown

Proposed changes

Closes #493.

Streaming WebSocket connections (listen.v1/listen.v2, speak.v1, agent.v1) can now be routed through a custom Node http/https agent — e.g. an HttpsProxyAgent — so streaming traffic transits an HTTP/HTTPS egress proxy, exactly like the issue describes for proxy-controlled environments (Kubernetes egress proxies, enterprise networks, regional gateways).

Before this, only REST honored a custom fetcher; the WebSocket upgrade went straight out via ws with no way to inject an agent. This implements the issue author's preferred option (forward agent on the connect args), delivered through the WebSocket?: any slot they identified as "the perfect injection point":

  • New agent option on the client (new DeepgramClient({ apiKey, agent })) applies to every streaming connection.
  • New per-connection agent on connect() / createConnection() overrides the client-level default.
  • The agent is injected by substituting a small ws subclass into ReconnectingWebSocket's WebSocket slot, which adds agent to the underlying new ws.WebSocket(url, protocols, { ...opts, agent }) call. No Fern-generated code is patched, so it survives regenerations, and reconnects continue to use the proxy (the original failure mode was a tight getaddrinfo EAI_AGAIN retry loop when in-cluster DNS stuttered — with the proxy resolving the host, that's bypassed).
import { DeepgramClient } from "@deepgram/sdk";
import { HttpsProxyAgent } from "https-proxy-agent";

const agent = new HttpsProxyAgent(process.env.HTTPS_PROXY!);
const client = new DeepgramClient({ apiKey: "YOUR_API_KEY", agent });
// or per connection (overrides the client-level agent):
const socket = await client.listen.v1.createConnection({ model: "nova-3", agent });

The option is Node-only (ignored in browser/web-worker runtimes, which use the native WebSocket that cannot accept an agent), excluded from query params, and ignored when a transportFactory is supplied (custom transports own their own networking).

Types of changes

  • New feature (non-breaking change which adds functionality)

Checklist

  • I have read the CONTRIBUTING doc
  • I have lint'ed all of my code using repo standards
  • I have added tests that prove my fix is effective or that my feature works
  • I have added necessary documentation (if appropriate)

Further comments

Why this approach. The issue listed three options. I implemented option 1 (explicit agent) because it keeps the public surface tiny and is symmetric with the agent option ws already accepts. I deliberately did not implement option 3 (auto-honoring HTTPS_PROXY/HTTP_PROXY): the SDK's REST layer uses bare fetch and does not auto-honor proxy env vars either, so there's no existing parity to match, and adding it would pull ~4 runtime deps into a currently single-dependency (ws) SDK plus force a Node-18 version pin on https-proxy-agent. Happy to follow up with an opt-in proxy: \"env\" flag in a separate PR if maintainers want it (and want to weigh whether REST should get symmetric treatment).

Testing. Added unit coverage for agent injection, the client-vs-per-connection override, cross-service wiring, and query-param exclusion. Also added a dependency-free manual smoke test (tests/manual/ws-proxy-smoke.ts) that stands up a local CONNECT proxy and opens a real Deepgram listen socket through it — verified end-to-end that the WebSocket opens and the traffic transits the proxy. make build, make test (552 + 132), and make lint are green.

Closes deepgram#493.

Streaming WebSocket connections (listen, speak, agent) now accept an
optional Node http/https `agent`, so traffic can be routed through an
HTTP/HTTPS proxy such as HttpsProxyAgent. Set `agent` on the client to
apply it to every connection, or pass it to connect() /
createConnection() to override the client-level default. Node-only;
ignored in browser and web-worker runtimes, which use the native
WebSocket and cannot accept a custom agent.

The agent is injected by wrapping the ws class that ReconnectingWebSocket
instantiates, so no Fern-generated code is patched. Adds unit coverage
for the injection and override behavior, plus a self-contained manual
smoke test (tests/manual/ws-proxy-smoke.ts) that opens a real Deepgram
connection through a local CONNECT proxy.
@valeriudev valeriudev requested a review from GregHolmes as a code owner June 3, 2026 17:43
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.

Allow specifying an HTTP agent / proxy for WebSocket connections

1 participant