Skip to content

Commit eee9659

Browse files
author
Patrick
committed
implemented suggestions
1 parent e27379e commit eee9659

File tree

1 file changed

+124
-55
lines changed

1 file changed

+124
-55
lines changed

src/api/providers/bedrock.ts

Lines changed: 124 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import * as vscode from "vscode"
1414
import * as fs from "fs"
1515
import { ProxyAgent } from "proxy-agent"
1616
import * as https from "https"
17+
import type { Agent } from "http"
18+
import type { Agent as HttpsAgent } from "https"
1719

1820
import {
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

Comments
 (0)