Skip to content

Commit f74f71d

Browse files
authored
fix: honor disabled proxy support for proxy settings (#1019)
Stop deriving proxy env vars and config from VS Code proxy settings when http.proxySupport is off, across SSH ProxyCommand and Coder API HTTP/WebSocket/SSE paths. Inherited proxy env vars are preserved. Watch http.proxySupport to reconnect streams and prompt SSH reload on change. Also deprecate coder.proxyBypass in favor of http.noProxy. Fixes #1017
1 parent 99745f5 commit f74f71d

9 files changed

Lines changed: 380 additions & 234 deletions

File tree

CHANGELOG.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,23 @@
55
from published versions since it shows up in the VS Code extension changelog
66
tab and is confusing to users. Add it back between releases if needed. -->
77

8+
## Unreleased
9+
10+
> **Breaking:** API requests now respect `http.proxySupport: off`. Previously
11+
> the extension applied VS Code's proxy settings (`http.proxy`, `http.noProxy`,
12+
> `coder.proxyBypass`) to API requests even when `http.proxySupport` was `off`.
13+
> Now `off` ignores those settings and only proxy environment variables are
14+
> used. If you set those settings and relied on them while proxy support was
15+
> `off`, either set `http.proxySupport` to `on` to keep using them, or move the
16+
> values into the `HTTP_PROXY`/`HTTPS_PROXY` (from `http.proxy`) and `NO_PROXY`
17+
> (from `http.noProxy`/`coder.proxyBypass`) environment variables.
18+
19+
### Fixed
20+
21+
- Honor `http.proxySupport: off` when deriving proxy settings for SSH and API
22+
connections, so VS Code's proxy settings are ignored while inherited proxy
23+
environment variables still apply.
24+
825
## [v1.15.1](https://github.com/coder/vscode-coder/releases/tag/v1.15.1) 2026-06-26
926

1027
### Added

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,8 @@
123123
"scope": "machine"
124124
},
125125
"coder.proxyBypass": {
126-
"markdownDescription": "If not set, will inherit from the `no_proxy` or `NO_PROXY` environment variables. `http.proxySupport` must be set to `on` or `off`, otherwise VS Code will override the proxy agent set by the plugin.",
126+
"markdownDescription": "If not set, will inherit from the `no_proxy` or `NO_PROXY` environment variables. Has no effect when `http.proxySupport` is set to `off`. With values other than `on`, VS Code will override the proxy agent set by the plugin.",
127+
"markdownDeprecationMessage": "Deprecated: prefer `http.noProxy`.",
127128
"type": "string",
128129
"default": "",
129130
"scope": "machine"

src/api/coderApi.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ const webSocketConfigSettings = [
8282
"coder.tlsCaFile",
8383
"coder.tlsAltHost",
8484
"http.proxy",
85+
"http.proxySupport",
8586
"coder.proxyBypass",
8687
"http.noProxy",
8788
"http.proxyAuthorization",

src/api/utils.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,16 @@ export async function createHttpAgent(
2828
): Promise<ProxyAgent> {
2929
const insecure = cfg.get<boolean>("coder.insecure", false);
3030
const proxyStrictSSL = cfg.get<boolean>("http.proxyStrictSSL", true);
31-
const proxyAuthorization = cfg.get<string | null>("http.proxyAuthorization");
32-
const httpNoProxy = cfg.get<string[]>("http.noProxy");
31+
// "off" ignores VS Code proxy config; inherited env proxies still apply.
32+
const proxyEnabled = cfg.get<string>("http.proxySupport") !== "off";
33+
const proxySetting = <T>(key: string) =>
34+
proxyEnabled ? cfg.get<T>(key) : undefined;
35+
const proxyAuthorization = proxySetting<string | null>(
36+
"http.proxyAuthorization",
37+
);
38+
const httpProxy = proxySetting<string | null>("http.proxy");
39+
const coderProxyBypass = proxySetting<string | null>("coder.proxyBypass");
40+
const httpNoProxy = proxySetting<string[]>("http.noProxy");
3341

3442
const certFile = expandPath(
3543
String(cfg.get("coder.tlsCertFile") ?? "").trim(),
@@ -54,8 +62,8 @@ export async function createHttpAgent(
5462
getProxyForUrl: (url: string) => {
5563
return getProxyForUrl(
5664
url,
57-
cfg.get("http.proxy"),
58-
cfg.get("coder.proxyBypass"),
65+
httpProxy,
66+
coderProxyBypass,
5967
joinNoProxy(httpNoProxy),
6068
);
6169
},

src/remote/environment.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ export const SSH_PROXY_SETTINGS: ReadonlyArray<{
1919
title: string;
2020
}> = [
2121
{ setting: "http.proxy", title: "HTTP Proxy" },
22+
{ setting: "http.proxySupport", title: "HTTP Proxy Support" },
2223
{ setting: "http.noProxy", title: "HTTP No Proxy" },
2324
{ setting: "coder.proxyBypass", title: "Proxy Bypass" },
2425
];
@@ -65,6 +66,10 @@ export function applySshEnvironment(
6566
export function getSshProxyEnvironment(
6667
cfg: Pick<WorkspaceConfiguration, "get">,
6768
): SshEnvironment {
69+
if (cfg.get<string>("http.proxySupport") === "off") {
70+
return {};
71+
}
72+
6873
const httpProxy = trimmed(cfg.get<string | null>("http.proxy"));
6974
const noProxy =
7075
trimmed(cfg.get<string | null>("coder.proxyBypass")) ??

test/mocks/testHelpers.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,25 @@ export class MockConfigurationProvider {
149149
}
150150
}
151151

152+
export type Settings = Record<string, unknown>;
153+
154+
/** Proxy URL for proxy-config tests. */
155+
export const PROXY_URL = "http://proxy.example.com:8080";
156+
157+
/** A MockConfigurationProvider seeded with the given settings. */
158+
export function config(settings: Settings = {}): MockConfigurationProvider {
159+
const cfg = new MockConfigurationProvider();
160+
for (const [key, value] of Object.entries(settings)) {
161+
cfg.set(key, value);
162+
}
163+
return cfg;
164+
}
165+
166+
/** Settings with http.proxy set. */
167+
export function withProxy(settings: Settings = {}): Settings {
168+
return { "http.proxy": PROXY_URL, ...settings };
169+
}
170+
152171
/**
153172
* Mock progress reporter that integrates with vscode.window.withProgress.
154173
* Use this to control progress reporting behavior and cancellation in tests.

test/unit/api/coderApi.test.ts

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -923,25 +923,31 @@ describe("CoderApi", () => {
923923
expect(sockets).toHaveLength(1);
924924
});
925925

926-
it("reconnects sockets in DISCONNECTED state when config changes", async () => {
927-
mockConfig.set("coder.insecure", false);
928-
const { sockets, handlers } = setupAutoOpeningWebSocket();
929-
api = createApi(CODER_URL, AXIOS_TOKEN);
930-
await api.watchAgentMetadata(AGENT_ID);
931-
await tick();
932-
933-
// Trigger close with unrecoverable code to put socket in DISCONNECTED
934-
handlers["close"]?.({ code: 1002, reason: "Protocol error" });
935-
await tick();
936-
937-
mockConfig.set("coder.insecure", true);
938-
await new Promise((resolve) =>
939-
setTimeout(resolve, CONFIG_CHANGE_DEBOUNCE_MS + 50),
940-
);
926+
it.each([
927+
["coder.insecure", false, true],
928+
["http.proxySupport", "on", "off"],
929+
])(
930+
"reconnects sockets in DISCONNECTED state when %s changes",
931+
async (setting, before, after) => {
932+
mockConfig.set(setting, before);
933+
const { sockets, handlers } = setupAutoOpeningWebSocket();
934+
api = createApi(CODER_URL, AXIOS_TOKEN);
935+
await api.watchAgentMetadata(AGENT_ID);
936+
await tick();
937+
938+
// Trigger close with unrecoverable code to put socket in DISCONNECTED
939+
handlers["close"]?.({ code: 1002, reason: "Protocol error" });
940+
await tick();
941+
942+
mockConfig.set(setting, after);
943+
await new Promise((resolve) =>
944+
setTimeout(resolve, CONFIG_CHANGE_DEBOUNCE_MS + 50),
945+
);
941946

942-
// Only DISCONNECTED sockets get reconnected by config changes
943-
expect(sockets).toHaveLength(2);
944-
});
947+
// Only DISCONNECTED sockets get reconnected by config changes
948+
expect(sockets).toHaveLength(2);
949+
},
950+
);
945951
});
946952
});
947953

0 commit comments

Comments
 (0)