Skip to content

Commit 7ff3022

Browse files
committed
Bedrock: honor VS Code proxy + custom CA for TLS
1 parent dcbb7a6 commit 7ff3022

File tree

3 files changed

+80
-13
lines changed

3 files changed

+80
-13
lines changed

src/api/providers/bedrock.ts

Lines changed: 78 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,19 @@
11
import {
2-
BedrockRuntimeClient,
3-
ConverseStreamCommand,
4-
ConverseCommand,
5-
BedrockRuntimeClientConfig,
6-
ContentBlock,
7-
Message,
8-
SystemContentBlock,
2+
BedrockRuntimeClient,
3+
ConverseStreamCommand,
4+
ConverseCommand,
5+
BedrockRuntimeClientConfig,
6+
ContentBlock,
7+
Message,
8+
SystemContentBlock,
99
} from "@aws-sdk/client-bedrock-runtime"
10+
import { NodeHttpHandler } from "@aws-sdk/node-http-handler"
1011
import { fromIni } from "@aws-sdk/credential-providers"
1112
import { Anthropic } from "@anthropic-ai/sdk"
13+
import * as vscode from "vscode"
14+
import * as fs from "fs"
15+
import { ProxyAgent } from "proxy-agent"
16+
import * as https from "https"
1217

1318
import {
1419
type ModelInfo,
@@ -225,15 +230,17 @@ export class AwsBedrockHandler extends BaseProvider implements SingleCompletionH
225230
this.options.awsBedrockEndpointEnabled && { endpoint: this.options.awsBedrockEndpoint }),
226231
}
227232

233+
// Build a Node HTTP handler that respects VS Code proxy and strict SSL settings
234+
const httpHandler = this.createNodeHttpHandler()
235+
if (httpHandler) {
236+
clientConfig.requestHandler = httpHandler
237+
}
238+
228239
if (this.options.awsUseApiKey && this.options.awsApiKey) {
229240
// Use API key/token-based authentication if enabled and API key is set
230241
clientConfig.token = { token: this.options.awsApiKey }
231242
clientConfig.authSchemePreference = ["httpBearerAuth"] // Otherwise there's no end of credential problems.
232-
clientConfig.requestHandler = {
233-
// This should be the default anyway, but without setting something
234-
// this provider fails to work with LiteLLM passthrough.
235-
requestTimeout: 0,
236-
}
243+
// If we created a NodeHttpHandler above, ensure request timeout behavior is set there.
237244
} else if (this.options.awsUseProfile && this.options.awsProfile) {
238245
// Use profile-based credentials if enabled and profile is set
239246
clientConfig.credentials = fromIni({
@@ -252,6 +259,65 @@ export class AwsBedrockHandler extends BaseProvider implements SingleCompletionH
252259
this.client = new BedrockRuntimeClient(clientConfig)
253260
}
254261

262+
/**
263+
* 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
266+
*/
267+
private createNodeHttpHandler(): NodeHttpHandler | undefined {
268+
try {
269+
const httpConfig = vscode.workspace.getConfiguration("http")
270+
const proxyUrl = (httpConfig.get<string>("proxy") || process.env.HTTPS_PROXY || process.env.HTTP_PROXY || process.env.ALL_PROXY || "").trim()
271+
const strictSSL = httpConfig.get<boolean>("proxyStrictSSL", true)
272+
273+
let ca: Buffer | undefined
274+
const caPath = (process.env.NODE_EXTRA_CA_CERTS || process.env.AWS_CA_BUNDLE || "").trim()
275+
if (caPath) {
276+
try {
277+
ca = fs.readFileSync(caPath)
278+
} catch (e) {
279+
logger.warn("Failed to read custom CA bundle; continuing without it", {
280+
ctx: "bedrock",
281+
error: e instanceof Error ? e.message : String(e),
282+
caPath,
283+
})
284+
}
285+
}
286+
287+
const agentOptions: https.AgentOptions = {
288+
rejectUnauthorized: strictSSL,
289+
...(ca ? { ca } : {}),
290+
}
291+
292+
let httpAgent: any | undefined
293+
let httpsAgent: any | undefined
294+
295+
if (proxyUrl) {
296+
// Use proxy-agent to support http/https/socks/PAC proxies seamlessly
297+
const proxyAgent = new ProxyAgent(proxyUrl) as unknown as https.Agent
298+
// proxy-agent returns a single agent for both protocols
299+
httpAgent = proxyAgent
300+
httpsAgent = proxyAgent
301+
} else {
302+
// Direct connection agents with proper TLS options
303+
httpsAgent = new https.Agent(agentOptions)
304+
}
305+
306+
return new NodeHttpHandler({
307+
httpAgent,
308+
httpsAgent,
309+
// 0 = no timeout (let Bedrock stream indefinitely until our own AbortController cancels)
310+
requestTimeout: 0,
311+
})
312+
} catch (err) {
313+
logger.warn("Failed to initialize custom NodeHttpHandler; falling back to default", {
314+
ctx: "bedrock",
315+
error: err instanceof Error ? err.message : String(err),
316+
})
317+
return undefined
318+
}
319+
}
320+
255321
// Helper to guess model info from custom modelId string if not in bedrockModels
256322
private guessModelInfoFromId(modelId: string): Partial<ModelInfo> {
257323
// Define a mapping for model ID patterns and their configurations

src/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -422,6 +422,7 @@
422422
"@anthropic-ai/vertex-sdk": "^0.7.0",
423423
"@aws-sdk/client-bedrock-runtime": "^3.848.0",
424424
"@aws-sdk/credential-providers": "^3.848.0",
425+
"@aws-sdk/node-http-handler": "^3.848.0",
425426
"@google/genai": "^1.0.0",
426427
"@lmstudio/sdk": "^1.1.1",
427428
"@mistralai/mistralai": "^1.3.6",
@@ -465,6 +466,7 @@
465466
"pdf-parse": "^1.1.1",
466467
"pkce-challenge": "^5.0.0",
467468
"pretty-bytes": "^7.0.0",
469+
"proxy-agent": "^6.5.0",
468470
"proper-lockfile": "^4.1.2",
469471
"ps-tree": "^1.2.0",
470472
"puppeteer-chromium-resolver": "^24.0.0",

src/tsconfig.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
{
22
"compilerOptions": {
3-
"types": ["vitest/globals"],
43
"esModuleInterop": true,
54
"experimentalDecorators": true,
65
"forceConsistentCasingInFileNames": true,

0 commit comments

Comments
 (0)