Skip to content

Commit 1605f96

Browse files
committed
feat(fetch): add HTTP proxy support (#273)
- Support HTTP_PROXY, HTTPS_PROXY, ALL_PROXY environment variables - Support NO_PROXY for bypassing specific hosts with port matching - Support proxy authentication via URL credentials - Default bypass for localhost, 127.0.0.1, ::1
1 parent b6a70bc commit 1605f96

File tree

7 files changed

+736
-31
lines changed

7 files changed

+736
-31
lines changed

Cargo.lock

Lines changed: 120 additions & 13 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

README.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -630,6 +630,40 @@ Initializes TLS connections in parallel during function init which significantly
630630

631631
Set the TLS version to be used for network connections. By default only TLS 1.2 is enabled. TLS 1.3 can also be enabled by setting this variable to `1.3`
632632

633+
### `HTTP_PROXY=url` / `HTTPS_PROXY=url`
634+
635+
Configure HTTP/HTTPS proxy servers for outgoing `fetch` requests. The URL can include authentication credentials.
636+
637+
- `HTTP_PROXY` - Proxy for HTTP requests
638+
- `HTTPS_PROXY` - Proxy for HTTPS requests
639+
- `ALL_PROXY` - Fallback proxy for both HTTP and HTTPS if specific proxy is not set
640+
641+
Examples:
642+
643+
```shell
644+
HTTP_PROXY=http://proxy.example.com:8080
645+
HTTPS_PROXY=http://user:[email protected]:8080
646+
ALL_PROXY=http://proxy.example.com:3128
647+
```
648+
649+
### `NO_PROXY=hosts`
650+
651+
Comma-separated list of hosts that should bypass the proxy. Supports:
652+
653+
- `*` - Bypass all hosts
654+
- `.example.com` - Bypass subdomains only (e.g., `sub.example.com` but not `example.com`)
655+
- `example.com` - Bypass domain and all subdomains
656+
- `example.com:8080` - Bypass only when port matches (pattern without port matches any port)
657+
- `[::1]:8080` - IPv6 with port (use brackets)
658+
659+
Default bypass hosts: `localhost`, `127.0.0.1`, `::1`
660+
661+
Example:
662+
663+
```shell
664+
NO_PROXY=localhost:3000,.internal.corp,api.example.com:443
665+
```
666+
633667
## Benchmark Methodology
634668

635669
Although Init Duration [reported by Lambda](https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtime-environment.html) is commonly used to understand cold start impact on overall request latency, this metric does not include the time needed to copy code into the Lambda sandbox.

modules/llrt_http/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,15 @@ native-roots = ["llrt_tls/native-roots"]
2323
[dependencies]
2424
bytes = { version = "1", default-features = false }
2525
http-body-util = { version = "0.1", default-features = false }
26+
headers = { version = "0.4", default-features = false }
2627
hyper = { version = "1", features = ["client"], default-features = false }
2728
hyper-util = { version = "0.1", default-features = false }
2829
hyper-rustls = { version = "0.27", features = [
2930
"ring",
3031
], default-features = false }
32+
hyper-http-proxy = { version = "1", default-features = false }
33+
percent-encoding = { version = "2", default-features = false, features = ["std"] }
34+
tracing = { version = "0.1", default-features = false }
3135
llrt_dns_cache = { version = "0.7.0-beta", path = "../../libs/llrt_dns_cache" }
3236
llrt_tls = { version = "0.7.0-beta", default-features = false, path = "../llrt_tls" }
3337
llrt_utils = { version = "0.7.0-beta", path = "../../libs/llrt_utils", default-features = false }

modules/llrt_http/src/agent.rs

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,23 @@
11
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
22
// SPDX-License-Identifier: Apache-2.0
3-
use std::convert::Infallible;
4-
5-
use bytes::Bytes;
6-
use http_body_util::combinators::BoxBody;
7-
use hyper_rustls::HttpsConnector;
8-
use hyper_util::client::legacy::{connect::HttpConnector, Client};
9-
use llrt_dns_cache::CachedDnsResolver;
103
use llrt_utils::result::ResultExt;
114
use llrt_utils::{any_of::AnyOf4, bytes::ObjectBytes, object::ObjectExt};
125
use rquickjs::{prelude::Opt, Ctx, Error, FromJs, Result, Value};
136

7+
use crate::{
8+
proxy::{ProxyConfig, PROXY_CONFIG},
9+
HyperClient,
10+
};
11+
1412
#[rquickjs::class]
1513
#[derive(rquickjs::JsLifetime, rquickjs::class::Trace)]
1614
pub struct Agent {
1715
#[qjs(skip_trace)]
18-
client: Client<HttpsConnector<HttpConnector<CachedDnsResolver>>, BoxBody<Bytes, Infallible>>,
16+
client: HyperClient,
1917
}
2018

2119
impl Agent {
22-
pub fn client(
23-
&self,
24-
) -> Client<HttpsConnector<HttpConnector<CachedDnsResolver>>, BoxBody<Bytes, Infallible>> {
20+
pub fn client(&self) -> HyperClient {
2521
self.client.clone()
2622
}
2723
}
@@ -47,8 +43,15 @@ impl Agent {
4743
ca,
4844
})
4945
.or_throw_msg(&ctx, "Failed to build TLS config")?;
50-
let client =
51-
crate::build_client(Some(config)).or_throw_msg(&ctx, "Failed to build HTTP client")?;
46+
47+
// Use global proxy config for the Agent's client
48+
let proxy_config: Option<&ProxyConfig> = if PROXY_CONFIG.is_enabled() {
49+
Some(&*PROXY_CONFIG)
50+
} else {
51+
None
52+
};
53+
let client = crate::build_client(Some(config), proxy_config)
54+
.or_throw_msg(&ctx, "Failed to build HTTP client")?;
5255

5356
Ok(Self { client })
5457
}

0 commit comments

Comments
 (0)