azureutils: honor VS Code proxy settings in Azure clients#2356
azureutils: honor VS Code proxy settings in Azure clients#2356alexweininger wants to merge 1 commit into
Conversation
There was a problem hiding this comment.
Pull request overview
This PR adds proxy/TLS handling that honors VS Code’s http.* settings for Azure SDK clients created via azureutils, and exposes a helper to apply the same behavior to non-pipeline HTTP clients.
Changes:
- Introduces a new
ProxyAgentPolicyplus agetProxyAgent(requestUrl)helper that resolves proxy/TLS settings from VS Code configuration (with env-var fallback). - Wires the new policy into
addAzExtPipelineso it applies to Azure/generic clients consistently. - Adds tests and new runtime dependencies for proxy agent implementations, and exports the new public API.
Show a summary per file
| File | Description |
|---|---|
| azure/test/proxyAgent.test.ts | Adds coverage for isHostBypassed, getProxyAgent, and ProxyAgentPolicy behaviors. |
| azure/src/utils/ProxyAgentPolicy.ts | New proxy/TLS resolution logic + pipeline policy + exported helper. |
| azure/src/index.ts | Exports getProxyAgent from the package entrypoint. |
| azure/src/createAzureClient.ts | Installs ProxyAgentPolicy into the shared Azure pipeline setup. |
| azure/package.json | Adds http-proxy-agent and https-proxy-agent runtime deps. |
| azure/package-lock.json | Locks new proxy agent dependencies. |
| azure/index.d.ts | Declares the new getProxyAgent public API. |
Review details
Tip
Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Files not reviewed (1)
- azure/package-lock.json: Generated file
- Files reviewed: 5/7 changed files
- Comments generated: 5
- Review effort level: Low
| function resolveProxyUrl(config: HttpProxyConfig): string | undefined { | ||
| if (config.proxyUrl) { | ||
| return config.proxyUrl; | ||
| } | ||
| return process.env.HTTPS_PROXY ?? process.env.https_proxy | ||
| ?? process.env.ALL_PROXY ?? process.env.all_proxy | ||
| ?? process.env.HTTP_PROXY ?? process.env.http_proxy | ||
| ?? undefined; | ||
| } |
There was a problem hiding this comment.
Good catch. Made resolveProxyUrl protocol-aware in 3ddf477: http: requests now prefer HTTP_PROXY, https: prefer HTTPS_PROXY, with ALL_PROXY as a cross-protocol fallback. VS Code http.proxy still takes precedence over all env vars. Added a regression test asserting HTTP_PROXY wins for http: URLs.
| const config = getHttpProxyConfig(); | ||
| // `http.proxySupport: off` is an explicit opt-out: inject no proxySettings or agent. | ||
| if (!config.proxySupportEnabled) { | ||
| return; | ||
| } | ||
| const isTls = url.protocol === 'https:'; | ||
| const bypassed = isHostBypassed(url.hostname, config.noProxy); | ||
| const insecure = isTls && config.strictSSL === false; | ||
|
|
||
| // Secure proxy from VS Code config: hand off to the built-in policy so it builds/caches the | ||
| // agent and honors NODE_EXTRA_CA_CERTS. Env-var proxies are already handled by that policy. | ||
| if (config.proxyUrl && !bypassed && !insecure) { | ||
| request.proxySettings ??= getDefaultProxySettings(config.proxyUrl); | ||
| return; | ||
| } | ||
|
|
||
| // A TLS override is needed (strictSSL: false), so own the agent — with or without a proxy. | ||
| if (insecure) { | ||
| const proxyUrl = bypassed ? undefined : resolveProxyUrl(config); | ||
| request.agent = proxyUrl | ||
| ? getProxyAgentForUrl(proxyUrl, isTls, /* rejectUnauthorized */ false) | ||
| : getInsecureTlsAgent(); | ||
| } |
There was a problem hiding this comment.
Fixed in 3ddf477. applyProxy now delegates to the same resolveProxyForRequest() helper, which skips non-http(s) URLs (no proxySettings/agent injected) and resolves env-var proxies protocol-aware for the insecure path. Added a policy test asserting an ftp: request URL is left untouched.
f2467ba to
9453727
Compare
3ddf477 to
cb40d5c
Compare
nturinski
left a comment
There was a problem hiding this comment.
Personally I feel like 80% of the comments copilot writes are way too verbose. Maybe run a session to make them more concise or just remove some of the more self explanatory ones all together?
| /** | ||
| * Adds the policy to `pipeline` (before the built-in proxy policy) unless it is already present. | ||
| */ | ||
| public static addIfNeeded(pipeline: Pipeline): void { |
There was a problem hiding this comment.
I don't really care, but we've never done a pattern of addIfNeeded since it's typically all added at one time anyway. My impression was that this was only added if the user actually had proxy settings though so might be redundant.
There was a problem hiding this comment.
Good point, done in 538bc9f. Dropped the addIfNeeded static method and its dedup guard (which was redundant since addAzExtPipeline always runs on a fresh pipeline) and just add the policy inline like the sibling policies: pipeline.addPolicy(new ProxyAgentPolicy(), { beforePolicies: [proxyPolicyName] }). To clarify the redundancy you flagged: the policy is always added; it decides per-request whether any proxy/TLS handling applies (unset/off/noProxy all short-circuit to a no-op), so there's no cost when the user has no proxy configured.
The Azure SDK's built-in proxy policy reads only proxy environment variables (HTTPS_PROXY/HTTP_PROXY/NO_PROXY) and never consults VS Code's `http.proxy` / `http.proxyStrictSSL` / `http.noProxy` settings. Its proxy agent also discards TLS settings. On corporate networks that require an explicit VS Code proxy, ARM and generic requests therefore bypass the proxy and can fail. Add a shared ProxyAgentPolicy and two exported helpers that resolve proxy/TLS configuration from VS Code's http settings, falling back to the standard proxy env vars: - getProxyAgent(requestUrl): an http/https Agent for clients that accept a raw agent (also carries an http.proxyStrictSSL:false TLS override). - getProxySettings(requestUrl): a ProxySettings object for pipeline-based Azure SDK data-plane clients that accept proxyOptions (e.g. Storage's BlobServiceClient/ShareServiceClient); no TLS override since proxyOptions cannot express one. The policy is wired into addAzExtPipeline so it applies to createGenericClient, createAzureClient, and createAzureSubscriptionClient. TLS validation is only relaxed when http.proxyStrictSSL is explicitly false, and http.proxySupport: off makes the policy a no-op. CA trust continues to rely on NODE_EXTRA_CA_CERTS (documented separately in vscode-azureresourcegroups#1537), which Node honors globally including through proxy agents. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
cb40d5c to
538bc9f
Compare
|
Thanks for the review @nturinski. Addressed both notes in 538bc9f:
build/lint/test all green (57 passing). |
Summary
The Azure SDK's built-in proxy policy (
@azure/core-rest-pipelinevia@typespec/ts-http-runtime) only reads the proxy environment variables (HTTPS_PROXY/HTTP_PROXY/NO_PROXY). It never consults VS Code'shttp.*settings, and its proxy agent additionally discards TLS settings. On corporate networks where the user configures a proxy through VS Code (rather than env vars), ARM management calls (createAzureClient/createAzureSubscriptionClient) and generic requests (createGenericClient) bypass the proxy and can fail.This PR closes that proxy-settings gap.
What changed
ProxyAgentPolicy(azure/src/utils/ProxyAgentPolicy.ts) that resolves proxy/TLS configuration from VS Code'shttp.proxy,http.proxyStrictSSL,http.noProxy, andhttp.proxySupportsettings, falling back to the standard proxy environment variables. Resolution is protocol-aware (http:prefersHTTP_PROXY,https:prefersHTTPS_PROXY, withALL_PROXYas a cross-protocol fallback) and only applies tohttp(s):request URLs.addAzExtPipeline, so it applies tocreateGenericClient,createAzureClient, andcreateAzureSubscriptionClientacross every extension that depends on azureutils (fix once, apply everywhere).getProxyAgent(for clients that accept anhttp.Agent) andgetProxySettings(for pipeline-based data-plane clients that acceptproxyOptions: ProxySettings, e.g. StorageBlobServiceClient/ShareServiceClient, and later Cosmos / Event Hubs / Service Bus).Public API
getProxyAgentreturns anhttp/httpsAgentconfigured from VS Code's proxy settings (or the proxy env vars) for the given request URL, orundefinedwhen no proxy or TLS override applies. It carries theproxyStrictSSL: falseTLS override.getProxySettingsreturns aProxySettingsobject (host/port/username/password) for pipeline-based Azure SDK data-plane clients that acceptproxyOptions. It shares the same resolver asgetProxyAgentbut, unlike it, does not carry a TLS (proxyStrictSSL: false) override, sinceproxyOptionscannot express that.Behavior
proxyStrictSSLtrue (default): injectsrequest.proxySettingsand lets the built-in policy build/cache the agent, soNODE_EXTRA_CA_CERTSis still honored.proxyStrictSSL: false(only when explicitly set): owns the agent so the TLS override actually applies to the destination handshake inside the CONNECT tunnel (the built-in agent, andhttps-proxy-agent's constructor, otherwise drop the destinationrejectUnauthorized). Never relaxed by default.http.noProxy(merged withNO_PROXY): no proxy applied.http(s):request URLs (e.g.ftp:): left untouched, no proxy applied.request.agent(e.g.sendRequestWithTimeout): left untouched.http.proxySupport: off: this policy is a full no-op and bothgetProxyAgentandgetProxySettingsreturnundefined, even ifhttp.proxyis set. Note that standard proxy environment variables still flow through the Azure SDK's own built-in proxy policy in this case;offopts out of VS Code's setting-based proxy handling, matching the VS Code extension-host default, and fully suppressing env-var proxies is deliberately out of scope for this slice. Default (override) preserves current behavior.undefined.Known limitations
http.noProxyonly bypasses proxies that this policy applies. When a proxy comes solely from an environment variable (e.g.HTTPS_PROXY) andhttp.proxyis unset, the SDK's built-in policy still proxies the request because it consults onlyNO_PROXY, not VS Code'shttp.noProxylist.CA trust
CA-certificate trust is intentionally out of scope here and continues to rely on
NODE_EXTRA_CA_CERTS(plushttp.systemCertificates), which Node honors globally including through proxy agents. That path is documented separately in microsoft/vscode-azureresourcegroups#1537. This change only closes the separate "SDK ignores the configured proxy" gap.Testing
npm run build,npm run lint(0 warnings), andnpm test(vscode-test) all pass inazure/(57 passing).isHostBypassed,getProxyAgent,getProxySettings, andProxyAgentPolicy: secure proxy, insecure TLS override, protocol-aware env-var precedence,ALL_PROXYfallback, non-http(s):schemes,noProxybypass, caller-set agent, and theproxySupport: offno-op.CONNECTproxy and assert the request succeeds through the proxy whenhttp.proxyStrictSSL: falseand fails with a self-signed-certificate error by default, for both thegetProxyAgentexport and the agent the policy sets on the request.Related issues
Related to microsoft/vscode-azurefunctions#5123 and microsoft/vscode-azureappservice#2899 (management half). Motivated by microsoft/vscode-azureresourcegroups#1536.