Skip to content

Allow specifying an HTTP agent / proxy for WebSocket connections #493

@leachiM2k

Description

@leachiM2k

Proposed changes

Add a public way to route the SDK's WebSocket connections
(listen.v2.connect, speak.v1.connect, agent.v1.connect, etc.)
through a custom HTTP agent — e.g. an HttpsProxyAgent — so streaming
traffic can transit an HTTP/HTTPS proxy.

Today this is not possible:

  • BaseClientOptions.fetch / BaseClientOptions.fetcher only affect
    REST requests; the WebSocket upgrade in core/websocket/ws.js is built
    directly via the ws package and never goes through the fetcher.
  • ReconnectingWebSocket.Options already declares WebSocket?: any,
    which would be a perfect injection point, but the connect() methods
    hard-code the options object and never expose it.

Context

We run the SDK behind a corporate egress proxy. All other outbound
traffic from our service (REST APIs, WebSockets from other providers via a custom
ws agent, etc.) honours HTTPS_PROXY. The Deepgram SDK is the only
client that cannot, which forced direct DNS resolution for
api.eu.deepgram.com from inside our cluster. When that hostname's
in-cluster DNS resolution stuttered, every Deepgram session failed with
getaddrinfo EAI_AGAIN api.eu.deepgram.com and the ReconnectingWebSocket
retried in a tight loop — a routine outage we would not have hit if the
SDK had taken the proxy route the rest of the egress already uses.

Anyone running the SDK from a Node service in a network-policy-controlled
environment (Kubernetes with egress proxies, enterprise networks, regional
gateways) hits the same wall.

Possible Implementation

Any one of these would resolve it:

  1. Forward agent on ConnectArgs. Most surgical: pipe the value
    through to the underlying new ws.WebSocket(url, protocols, { agent })
    call.
  2. Pass through a WebSocket override on ConnectArgs to
    ReconnectingWebSocket.options.WebSocket. The slot already exists,
    only the plumbing is missing.
  3. Honour HTTPS_PROXY / HTTP_PROXY automatically in the Node
    branch of getGlobalWebSocket() — e.g. attach an https-proxy-agent
    when those env vars are set, similar to how undici honours them.

(1) keeps the surface tiny and is symmetric with the agent option ws
already accepts.

Other information

Workaround we currently use: patch ws.WebSocket at process startup with
a subclass that injects options.agent = new HttpsProxyAgent(process.env.HTTPS_PROXY)
for Deepgram URLs. The patch has to reach the real
module.exports.WebSocket via module.createRequire("ws"), because
import * as ws from "ws" produces a frozen / copied namespace whose
mutation does not propagate back to other consumers — including the SDK
itself.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions