Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion packages/js-sdk/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,8 @@
"glob": "^11.1.0",
"openapi-fetch": "^0.14.1",
"platform": "^1.3.6",
"tar": "^7.5.11"
"tar": "^7.5.11",
"undici": "^7.0.0"
},
"engines": {
"node": ">=20"
Expand Down
2 changes: 2 additions & 0 deletions packages/js-sdk/src/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { defaultHeaders } from './metadata'
import { ConnectionConfig } from '../connectionConfig'
import { AuthenticationError, RateLimitError, SandboxError } from '../errors'
import { createApiLogger } from '../logs'
import { getProxyFetch } from '../proxy'

export function handleApiError(
response: FetchResponse<any, any, any>,
Expand Down Expand Up @@ -74,6 +75,7 @@ class ApiClient {

this.api = createClient<paths>({
baseUrl: config.apiUrl,
fetch: getProxyFetch(),
// In HTTP 1.1, all connections are considered persistent unless declared otherwise
// keepalive: true,
headers: {
Expand Down
3 changes: 2 additions & 1 deletion packages/js-sdk/src/envd/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import createClient from 'openapi-fetch'
import type { components, paths } from './schema.gen'
import { ConnectionConfig } from '../connectionConfig'
import { createApiLogger } from '../logs'
import { getProxyFetch } from '../proxy'
import {
SandboxError,
InvalidArgumentError,
Expand Down Expand Up @@ -130,7 +131,7 @@ class EnvdApiClient {
) {
this.api = createClient({
baseUrl: config.apiUrl,
fetch: config?.fetch,
fetch: config?.fetch ?? getProxyFetch(),
headers: config?.headers,
// In HTTP 1.1, all connections are considered persistent unless declared otherwise
// keepalive: true,
Expand Down
41 changes: 41 additions & 0 deletions packages/js-sdk/src/proxy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { ProxyAgent } from 'undici'

import { getEnvVar } from './api/metadata'
import { runtime } from './utils'

function getProxyUrl(): string | undefined {
return (
getEnvVar('https_proxy') ||
getEnvVar('HTTPS_PROXY') ||
getEnvVar('http_proxy') ||
getEnvVar('HTTP_PROXY') ||
getEnvVar('all_proxy') ||
getEnvVar('ALL_PROXY') ||
undefined
)
}

let cachedProxyFetch: typeof fetch | null = null
let proxyResolved = false

/**
* Returns a proxy-aware fetch function if HTTPS_PROXY/HTTP_PROXY/ALL_PROXY
* env vars are set. In browser environments, returns undefined (browsers
* handle proxies natively via OS settings).
*
* Uses undici's ProxyAgent.
*/
export function getProxyFetch(): typeof fetch | undefined {
if (proxyResolved) return cachedProxyFetch ?? undefined
proxyResolved = true

if (runtime === 'browser') return undefined

const proxyUrl = getProxyUrl()
if (!proxyUrl) return undefined

const agent = new ProxyAgent(proxyUrl)
cachedProxyFetch = ((input: any, init?: any) =>
fetch(input, { ...init, dispatcher: agent })) as typeof fetch
return cachedProxyFetch
}
4 changes: 3 additions & 1 deletion packages/js-sdk/src/sandbox/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
} from '../connectionConfig'
import { EnvdApiClient, handleEnvdApiError } from '../envd/api'
import { createRpcLogger } from '../logs'
import { getProxyFetch } from '../proxy'
import { Commands, Pty } from './commands'
import { Filesystem } from './filesystem'
import { Git } from './git'
Expand Down Expand Up @@ -150,6 +151,7 @@ export class Sandbox extends SandboxApi {
'E2b-Sandbox-Port': this.envdPort.toString(),
}

const proxyFetch = getProxyFetch() ?? fetch
const rpcTransport = createConnectTransport({
baseUrl: this.envdApiUrl,
useBinaryFormat: false,
Expand Down Expand Up @@ -178,7 +180,7 @@ export class Sandbox extends SandboxApi {
redirect: 'follow',
}

return fetch(url, options)
return proxyFetch(url, options)
},
})

Expand Down
2 changes: 2 additions & 0 deletions packages/js-sdk/src/volume/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import createClient from 'openapi-fetch'
import type { components, paths } from './schema.gen'
import { defaultHeaders, getEnvVar } from '../api/metadata'
import { createApiLogger, Logger } from '../logs'
import { getProxyFetch } from '../proxy'
import type { Volume } from './index'

const FILE_TIMEOUT_MS = 3_600_000 // 1 hour
Expand Down Expand Up @@ -99,6 +100,7 @@ class VolumeApiClient {
constructor(config: VolumeConnectionConfig) {
this.api = createClient<paths>({
baseUrl: config.apiUrl,
fetch: getProxyFetch(),
headers: {
...defaultHeaders,
...(config.token && { Authorization: `Bearer ${config.token}` }),
Expand Down
4 changes: 2 additions & 2 deletions packages/python-sdk/e2b/api/client_async/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

from typing import Dict

from e2b.connection_config import ConnectionConfig
from e2b.connection_config import ConnectionConfig, _resolve_proxy
from e2b.api import limits, AsyncApiClient


Expand Down Expand Up @@ -45,7 +45,7 @@ def get_transport(config: ConnectionConfig) -> AsyncTransportWithLogger:

transport = AsyncTransportWithLogger(
limits=limits,
proxy=config.proxy,
proxy=_resolve_proxy(config.proxy),
)
AsyncTransportWithLogger._instances[loop_id] = transport
return transport
4 changes: 2 additions & 2 deletions packages/python-sdk/e2b/api/client_sync/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import logging

from e2b.api import ApiClient, limits
from e2b.connection_config import ConnectionConfig
from e2b.connection_config import ConnectionConfig, _resolve_proxy

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -44,7 +44,7 @@ def get_transport(config: ConnectionConfig) -> TransportWithLogger:

transport = TransportWithLogger(
limits=limits,
proxy=config.proxy,
proxy=_resolve_proxy(config.proxy),
)
TransportWithLogger.singleton = transport
return transport
19 changes: 19 additions & 0 deletions packages/python-sdk/e2b/connection_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,25 @@ def sandbox_headers(self):
}


def _resolve_proxy(proxy: Optional[ProxyTypes]) -> Optional[ProxyTypes]:
"""
Resolve the proxy to use for a request.
If an explicit proxy is provided, use it. Otherwise, fall back to
standard environment variables (HTTPS_PROXY, HTTP_PROXY, ALL_PROXY).
"""
if proxy is not None:
return proxy

return (
os.environ.get("https_proxy")
or os.environ.get("HTTPS_PROXY")
or os.environ.get("http_proxy")
or os.environ.get("HTTP_PROXY")
or os.environ.get("all_proxy")
or os.environ.get("ALL_PROXY")
)


Username = str
"""
User used for the operation in the sandbox.
Expand Down
3 changes: 2 additions & 1 deletion packages/python-sdk/e2b/volume/client_async/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from e2b.api.metadata import default_headers
from e2b.exceptions import AuthenticationException
from e2b.volume.client.client import AuthenticatedClient as AsyncVolumeApiClient
from e2b.connection_config import _resolve_proxy
from e2b.volume.connection_config import VolumeConnectionConfig

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -63,7 +64,7 @@ def get_transport(config: VolumeConnectionConfig) -> AsyncTransportWithLogger:

transport = AsyncTransportWithLogger(
limits=limits,
proxy=config.proxy,
proxy=_resolve_proxy(config.proxy),
)
AsyncTransportWithLogger.singleton = transport
return transport
3 changes: 2 additions & 1 deletion packages/python-sdk/e2b/volume/client_sync/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from e2b.api.metadata import default_headers
from e2b.exceptions import AuthenticationException
from e2b.volume.client.client import AuthenticatedClient as VolumeApiClient
from e2b.connection_config import _resolve_proxy
from e2b.volume.connection_config import VolumeConnectionConfig

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -63,7 +64,7 @@ def get_transport(config: VolumeConnectionConfig) -> TransportWithLogger:

transport = TransportWithLogger(
limits=limits,
proxy=config.proxy,
proxy=_resolve_proxy(config.proxy),
)
TransportWithLogger.singleton = transport
return transport
9 changes: 9 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.