Skip to content

Commit 6c37194

Browse files
js-sdk: Add support for proxy environment variables like HTTPS_PROXY
This adds support for the following proxy environment variables: - https_proxy - HTTPS_PROXY - http_proxy - HTTP_PROXY - all_proxy - ALL_PROXY Lowercase version takes precedence as that seems to be standard across cURL, Python, and wget -- yes, I agree -- it is nuts! For a reference on the used environment variables and their precedence see: https://superuser.com/a/1690537
1 parent d254261 commit 6c37194

5 files changed

Lines changed: 55 additions & 2 deletions

File tree

packages/js-sdk/src/api/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { defaultHeaders } from './metadata'
55
import { ConnectionConfig } from '../connectionConfig'
66
import { AuthenticationError, RateLimitError, SandboxError } from '../errors'
77
import { createApiLogger } from '../logs'
8+
import { getProxyFetch } from '../proxy'
89

910
export function handleApiError(
1011
response: FetchResponse<any, any, any>,
@@ -74,6 +75,7 @@ class ApiClient {
7475

7576
this.api = createClient<paths>({
7677
baseUrl: config.apiUrl,
78+
fetch: getProxyFetch(),
7779
// In HTTP 1.1, all connections are considered persistent unless declared otherwise
7880
// keepalive: true,
7981
headers: {

packages/js-sdk/src/envd/api.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import createClient from 'openapi-fetch'
33
import type { components, paths } from './schema.gen'
44
import { ConnectionConfig } from '../connectionConfig'
55
import { createApiLogger } from '../logs'
6+
import { getProxyFetch } from '../proxy'
67
import {
78
SandboxError,
89
InvalidArgumentError,
@@ -130,7 +131,7 @@ class EnvdApiClient {
130131
) {
131132
this.api = createClient({
132133
baseUrl: config.apiUrl,
133-
fetch: config?.fetch,
134+
fetch: config?.fetch ?? getProxyFetch(),
134135
headers: config?.headers,
135136
// In HTTP 1.1, all connections are considered persistent unless declared otherwise
136137
// keepalive: true,

packages/js-sdk/src/proxy.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import { getEnvVar } from './api/metadata'
2+
import { runtime } from './utils'
3+
4+
function getProxyUrl(): string | undefined {
5+
return (
6+
getEnvVar('HTTPS_PROXY') ||
7+
getEnvVar('https_proxy') ||
8+
getEnvVar('HTTP_PROXY') ||
9+
getEnvVar('http_proxy') ||
10+
getEnvVar('ALL_PROXY') ||
11+
getEnvVar('all_proxy') ||
12+
undefined
13+
)
14+
}
15+
16+
let cachedProxyFetch: typeof fetch | null = null
17+
let proxyResolved = false
18+
19+
/**
20+
* Returns a proxy-aware fetch function if HTTPS_PROXY/HTTP_PROXY/ALL_PROXY
21+
* env vars are set. In browser environments, returns undefined (browsers
22+
* handle proxies natively via OS settings).
23+
*
24+
* Uses undici's ProxyAgent, which is built into Node.js 20+.
25+
*/
26+
export function getProxyFetch(): typeof fetch | undefined {
27+
if (proxyResolved) return cachedProxyFetch ?? undefined
28+
proxyResolved = true
29+
30+
if (runtime === 'browser') return undefined
31+
32+
const proxyUrl = getProxyUrl()
33+
if (!proxyUrl) return undefined
34+
35+
try {
36+
// eslint-disable-next-line @typescript-eslint/no-var-requires
37+
const { ProxyAgent } = require('undici')
38+
const agent = new ProxyAgent(proxyUrl)
39+
cachedProxyFetch = ((input: any, init?: any) =>
40+
fetch(input, { ...init, dispatcher: agent })) as typeof fetch
41+
return cachedProxyFetch
42+
} catch {
43+
// undici not available (e.g. edge runtimes, older Node.js) — fall back to default fetch
44+
return undefined
45+
}
46+
}

packages/js-sdk/src/sandbox/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
} from '../connectionConfig'
1010
import { EnvdApiClient, handleEnvdApiError } from '../envd/api'
1111
import { createRpcLogger } from '../logs'
12+
import { getProxyFetch } from '../proxy'
1213
import { Commands, Pty } from './commands'
1314
import { Filesystem } from './filesystem'
1415
import { Git } from './git'
@@ -150,6 +151,7 @@ export class Sandbox extends SandboxApi {
150151
'E2b-Sandbox-Port': this.envdPort.toString(),
151152
}
152153

154+
const proxyFetch = getProxyFetch() ?? fetch
153155
const rpcTransport = createConnectTransport({
154156
baseUrl: this.envdApiUrl,
155157
useBinaryFormat: false,
@@ -178,7 +180,7 @@ export class Sandbox extends SandboxApi {
178180
redirect: 'follow',
179181
}
180182

181-
return fetch(url, options)
183+
return proxyFetch(url, options)
182184
},
183185
})
184186

packages/js-sdk/src/volume/client.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import createClient from 'openapi-fetch'
33
import type { components, paths } from './schema.gen'
44
import { defaultHeaders, getEnvVar } from '../api/metadata'
55
import { createApiLogger, Logger } from '../logs'
6+
import { getProxyFetch } from '../proxy'
67
import type { Volume } from './index'
78

89
const FILE_TIMEOUT_MS = 3_600_000 // 1 hour
@@ -99,6 +100,7 @@ class VolumeApiClient {
99100
constructor(config: VolumeConnectionConfig) {
100101
this.api = createClient<paths>({
101102
baseUrl: config.apiUrl,
103+
fetch: getProxyFetch(),
102104
headers: {
103105
...defaultHeaders,
104106
...(config.token && { Authorization: `Bearer ${config.token}` }),

0 commit comments

Comments
 (0)