@@ -14,6 +14,8 @@ import * as vscode from "vscode"
1414import * as fs from "fs"
1515import { ProxyAgent } from "proxy-agent"
1616import * as https from "https"
17+ import type { Agent } from "http"
18+ import type { Agent as HttpsAgent } from "https"
1719
1820import {
1921 type ModelInfo ,
@@ -174,6 +176,9 @@ export class AwsBedrockHandler extends BaseProvider implements SingleCompletionH
174176 private client : BedrockRuntimeClient
175177 private arnInfo : any
176178
179+ // Static cache for CA certificates to avoid repeated file I/O
180+ private static cachedCertificates : Map < string , Buffer > = new Map ( )
181+
177182 constructor ( options : ProviderSettings ) {
178183 super ( )
179184 this . options = options
@@ -261,65 +266,35 @@ export class AwsBedrockHandler extends BaseProvider implements SingleCompletionH
261266
262267 /**
263268 * Create a NodeHttpHandler configured to work behind corporate proxies and with custom CAs.
264- * - Honors VS Code settings: `http.proxy` and `http.proxyStrictSSL`
265- * - Honors env vars: HTTPS_PROXY/HTTP_PROXY/ALL_PROXY, NODE_EXTRA_CA_CERTS, AWS_CA_BUNDLE
269+ *
270+ * The proxy-agent library automatically detects proxy settings from environment variables.
271+ * We only check VS Code settings to determine if a proxy is configured, then let proxy-agent
272+ * handle the actual proxy URL detection and protocol negotiation.
273+ *
274+ * Configuration Sources (in order of precedence):
275+ *
276+ * VS Code Settings:
277+ * - `http.proxy`: Proxy server URL (used to determine if proxy is configured)
278+ * - `http.proxyStrictSSL`: Whether to validate SSL certificates (default: true)
279+ *
280+ * Environment Variables (automatically detected by proxy-agent):
281+ * - `HTTPS_PROXY` / `https_proxy`: HTTPS proxy server URL
282+ * - `HTTP_PROXY` / `http_proxy`: HTTP proxy server URL
283+ * - `ALL_PROXY` / `all_proxy`: Fallback proxy for all protocols
284+ * - `NO_PROXY` / `no_proxy`: Comma-separated list of hosts to bypass proxy
285+ * - `NODE_EXTRA_CA_CERTS`: Path to additional CA certificates file
286+ * - `AWS_CA_BUNDLE`: Path to AWS-specific CA bundle (takes precedence over NODE_EXTRA_CA_CERTS)
287+ *
288+ * @returns {NodeHttpHandler | undefined } Configured handler for proxy/CA support, or undefined if initialization fails
289+ * @private
266290 */
267291 private createNodeHttpHandler ( ) : NodeHttpHandler | undefined {
268292 try {
269- const httpConfig = vscode . workspace . getConfiguration ( "http" )
270- const proxyUrl = (
271- httpConfig . get < string > ( "proxy" ) ||
272- process . env . HTTPS_PROXY ||
273- process . env . HTTP_PROXY ||
274- process . env . ALL_PROXY ||
275- ""
276- ) . trim ( )
277- const strictSSL = httpConfig . get < boolean > ( "proxyStrictSSL" , true )
278-
279- let ca : Buffer | undefined
280- const caPath = ( process . env . NODE_EXTRA_CA_CERTS || process . env . AWS_CA_BUNDLE || "" ) . trim ( )
281- if ( caPath ) {
282- try {
283- ca = fs . readFileSync ( caPath )
284- } catch ( e ) {
285- logger . warn ( "Failed to read custom CA bundle; continuing without it" , {
286- ctx : "bedrock" ,
287- error : e instanceof Error ? e . message : String ( e ) ,
288- caPath,
289- } )
290- }
291- }
293+ const proxyConfig = this . getProxyConfiguration ( )
294+ const tlsConfig = this . getTLSConfiguration ( )
295+ const agents = this . createHTTPAgents ( proxyConfig , tlsConfig )
292296
293- const agentOptions : https . AgentOptions = {
294- rejectUnauthorized : strictSSL ,
295- ...( ca ? { ca } : { } ) ,
296- }
297-
298- let httpAgent : any | undefined
299- let httpsAgent : any | undefined
300-
301- if ( proxyUrl ) {
302- // Use proxy-agent to support http/https/socks/PAC proxies seamlessly
303- // ProxyAgent constructor expects options object with proper TLS configuration
304- const proxyAgent = new ProxyAgent ( {
305- // The ProxyAgent will use the proxy URL from environment or options
306- httpsAgent : new https . Agent ( agentOptions ) ,
307- // Pass TLS options for proper certificate handling
308- rejectUnauthorized : strictSSL ,
309- ...( ca ? { ca } : { } ) ,
310- } )
311- // proxy-agent returns a single agent for both protocols
312- httpAgent = proxyAgent
313- httpsAgent = proxyAgent
314- } else {
315- // Direct connection agents with proper TLS options
316- httpsAgent = new https . Agent ( agentOptions )
317- }
318-
319- return new NodeHttpHandler ( {
320- httpAgent,
321- httpsAgent,
322- } )
297+ return new NodeHttpHandler ( agents )
323298 } catch ( err ) {
324299 logger . warn ( "Failed to initialize custom NodeHttpHandler; falling back to default" , {
325300 ctx : "bedrock" ,
@@ -329,6 +304,100 @@ export class AwsBedrockHandler extends BaseProvider implements SingleCompletionH
329304 }
330305 }
331306
307+ /**
308+ * Get proxy configuration from VS Code settings and environment variables.
309+ * Note: We only check if a proxy is configured; proxy-agent handles URL detection.
310+ */
311+ private getProxyConfiguration ( ) : { isProxyConfigured : boolean } {
312+ const httpConfig = vscode . workspace . getConfiguration ( "http" )
313+ const vscodeProxy = ( httpConfig . get < string > ( "proxy" ) || "" ) . trim ( )
314+
315+ // Check if any proxy configuration exists (VS Code or environment variables)
316+ const hasProxyConfig = ! ! (
317+ vscodeProxy ||
318+ process . env . HTTPS_PROXY ||
319+ process . env . https_proxy ||
320+ process . env . HTTP_PROXY ||
321+ process . env . http_proxy ||
322+ process . env . ALL_PROXY ||
323+ process . env . all_proxy
324+ )
325+
326+ return { isProxyConfigured : hasProxyConfig }
327+ }
328+
329+ /**
330+ * Get TLS configuration including SSL validation and CA certificates.
331+ */
332+ private getTLSConfiguration ( ) : { strictSSL : boolean ; ca ?: Buffer } {
333+ const httpConfig = vscode . workspace . getConfiguration ( "http" )
334+ const strictSSL = httpConfig . get < boolean > ( "proxyStrictSSL" , true )
335+
336+ const caPath = ( process . env . NODE_EXTRA_CA_CERTS || process . env . AWS_CA_BUNDLE || "" ) . trim ( )
337+ const ca = caPath ? this . loadCACertificate ( caPath ) : undefined
338+
339+ return { strictSSL, ca }
340+ }
341+
342+ /**
343+ * Load CA certificate from file system with caching to avoid repeated I/O operations.
344+ */
345+ private loadCACertificate ( caPath : string ) : Buffer | undefined {
346+ if ( ! caPath ) return undefined
347+
348+ // Check cache first
349+ if ( AwsBedrockHandler . cachedCertificates . has ( caPath ) ) {
350+ return AwsBedrockHandler . cachedCertificates . get ( caPath )
351+ }
352+
353+ try {
354+ const ca = fs . readFileSync ( caPath )
355+ AwsBedrockHandler . cachedCertificates . set ( caPath , ca )
356+ return ca
357+ } catch ( e ) {
358+ logger . warn ( "Failed to read custom CA bundle; continuing without it" , {
359+ ctx : "bedrock" ,
360+ error : e instanceof Error ? e . message : String ( e ) ,
361+ caPath,
362+ } )
363+ return undefined
364+ }
365+ }
366+
367+ /**
368+ * Create HTTP/HTTPS agents with proxy and TLS configuration.
369+ * ProxyAgent automatically detects proxy URLs from environment variables.
370+ */
371+ private createHTTPAgents (
372+ proxyConfig : { isProxyConfigured : boolean } ,
373+ tlsConfig : { strictSSL : boolean ; ca ?: Buffer } ,
374+ ) : { httpAgent ?: Agent ; httpsAgent ?: HttpsAgent } {
375+ const agentOptions : https . AgentOptions = {
376+ rejectUnauthorized : tlsConfig . strictSSL ,
377+ ...( tlsConfig . ca ? { ca : tlsConfig . ca } : { } ) ,
378+ }
379+
380+ if ( proxyConfig . isProxyConfigured ) {
381+ // ProxyAgent automatically detects proxy URLs from environment variables
382+ // We pass TLS configuration through the httpsAgent option
383+ const proxyAgent = new ProxyAgent ( {
384+ httpsAgent : new https . Agent ( agentOptions ) ,
385+ rejectUnauthorized : tlsConfig . strictSSL ,
386+ ...( tlsConfig . ca ? { ca : tlsConfig . ca } : { } ) ,
387+ } )
388+
389+ return {
390+ httpAgent : proxyAgent ,
391+ httpsAgent : proxyAgent ,
392+ }
393+ }
394+
395+ // Direct connection without proxy
396+ return {
397+ httpsAgent : new https . Agent ( agentOptions ) ,
398+ }
399+ }
400+
332401 // Helper to guess model info from custom modelId string if not in bedrockModels
333402 private guessModelInfoFromId ( modelId : string ) : Partial < ModelInfo > {
334403 // Define a mapping for model ID patterns and their configurations
0 commit comments