Uber Fx module for net/http.Client with SOCKS5 proxy support (including per-host bypass) and custom root CA trust.
- Table of Contents
- About The Project
- Getting Started
- Usage
- Roadmap
- Contributing
- License
- Acknowledgments
httpfx is an Uber Fx module that provides a configured *http.Client and a Factory for creating additional client instances. It supports:
- SOCKS5 proxy via
golang.org/x/net/proxy(socks5://user:pass@host:port) - Per-host proxy bypass for hosts that should connect directly
- Custom root CA trust: append internal/corporate CAs to the system pool or replace it
- Per-client overrides via functional options on the
Factory - Transport tuning — idle connections, timeouts, pool sizes
- Go 1.25+
- An application using Uber Fx for dependency injection
go get github.com/go-core-fx/httpfx@latestimport (
"time"
"github.com/go-core-fx/httpfx"
"go.uber.org/fx"
)
func main() {
fx.New(
fx.Provide(func() httpfx.Config {
return httpfx.Config{
ProxyURL: "socks5://127.0.0.1:1080",
Bypass: "localhost,127.0.0.1",
Timeout: 30 * time.Second,
}
}),
httpfx.Module(),
// ... other modules
).Run()
}The module provides both a default *http.Client and a Factory for creating additional clients.
| Field | Type | Default | Description |
|---|---|---|---|
ProxyURL |
string |
"" |
SOCKS5 proxy URL (e.g. socks5://user:pass@host:port or socks5h://host:port). |
Bypass |
string |
"" |
Comma-separated hosts to bypass the proxy (e.g. localhost,127.0.0.1). |
Timeout |
time.Duration |
0 |
Client-level request timeout. Zero means no timeout. |
MaxIdleConns |
int |
0 |
Maximum idle (keep-alive) connections. Zero means no limit. |
MaxIdleConnsPerHost |
int |
0 |
Maximum idle connections per host. Zero means Go default (2). |
IdleConnTimeout |
time.Duration |
0 |
Maximum time a connection stays idle. Zero means no timeout. |
TLS.RootCAFile |
string |
"" |
Path to a PEM-encoded root CA file. Appended to the system pool unless replaced. |
TLS.RootCAPEM |
string |
"" |
Inline PEM-encoded root CA data. Merged with TLS.RootCAFile when both are set. |
TLS.RootCAReplaceSystem |
bool |
false |
When true, replace the system pool; only the configured root CAs are trusted. |
When ProxyURL is empty, clients inherit the base transport's proxy behavior (by default http.DefaultTransport uses http.ProxyFromEnvironment, i.e. the host's proxy environment variables). Configure a socks5:// or socks5h:// URL to switch to a SOCKS5 dialer (which disables any inherited HTTP proxy); plain HTTP CONNECT proxies (http://proxy:8080) are not supported.
Platform note: append mode builds on
x509.SystemCertPool(), which returns an empty pool on macOS/darwin (system roots load lazily at verify time), so append-mode behavior can differ by platform. Replace mode always trusts exactly the configured CAs.
Inject httpfx.Factory to create additional clients with shared base config but per-client overrides:
func Handler(f httpfx.Factory) error {
// Default client — uses factory base config
defaultClient, err := f.NewClient()
if err != nil {
return err
}
// Override timeout for a fast endpoint
apiClient, err := f.NewClient(httpfx.WithTimeout(5 * time.Second))
if err != nil {
return err
}
// Disable proxy for internal service calls
internalClient, err := f.NewClient(httpfx.WithProxyURL("", ""))
if err != nil {
return err
}
// Custom transport for a specific module
uploadClient, err := f.NewClient(
httpfx.WithTimeout(5 * time.Minute),
httpfx.WithMaxIdleConns(10),
)
if err != nil {
return err
}
_ = defaultClient
_ = apiClient
_ = internalClient
_ = uploadClient
return nil
}| Option | Description |
|---|---|
WithProxyURL(url, bypass) |
Override SOCKS5 proxy URL and bypass list |
WithTimeout(d) |
Override client timeout |
WithMaxIdleConns(n) |
Override max idle connections |
WithMaxIdleConnsPerHost(n) |
Override max idle connections per host |
WithIdleConnTimeout(d) |
Override idle connection timeout |
WithRootCAFile(path) |
Override root CA certificate file path |
WithRootCAPEM(pem) |
Override inline PEM root CA data |
WithRootCAReplaceSystem(v) |
Override system-pool replacement flag |
All proxying goes through golang.org/x/net/proxy via an explicit socks5:// or socks5h:// URL. Plain HTTP CONNECT proxies (http://proxy:8080) are not supported.
SOCKS5 with authentication:
httpfx.Config{
ProxyURL: "socks5://user:pass@127.0.0.1:1080",
}SOCKS5 with bypass for local addresses and CIDR ranges:
httpfx.Config{
ProxyURL: "socks5://127.0.0.1:1080",
Bypass: "localhost,127.0.0.1,192.168.0.0/16",
}By default, clients trust the system certificate pool. To trust an internal or corporate CA, configure Config.TLS; configured CAs are appended to the system pool unless RootCAReplaceSystem is set.
Internal CA via file path (Config literal):
httpfx.Config{
TLS: httpfx.TLSConfig{
RootCAFile: "/etc/corp/root-ca.pem",
},
}Inline PEM data (merged with the file when both are set):
httpfx.Config{
TLS: httpfx.TLSConfig{
RootCAFile: "/etc/corp/root-ca.pem",
RootCAPEM: "<PEM-encoded certificate data>",
},
}Replace mode - trust ONLY the configured CAs (strict internal environments):
httpfx.Config{
TLS: httpfx.TLSConfig{
RootCAFile: "/etc/corp/root-ca.pem",
RootCAReplaceSystem: true,
},
}Same settings via Factory per-client options:
corpClient, err := f.NewClient(
httpfx.WithRootCAFile("/etc/corp/root-ca.pem"),
)
if err != nil {
return err
}
strictClient, err := f.NewClient(
httpfx.WithRootCAPEM(pemData),
httpfx.WithRootCAReplaceSystem(true),
)
if err != nil {
return err
}Notes:
- Zero-value
httpfx.Config{}keeps the default Go TLS behavior (system roots only). - Invalid CA configuration (missing file, invalid PEM) makes
Factory.NewClientreturn an error; when clients are built throughModule, application startup fails with that error.
- SOCKS5 proxy support via
golang.org/x/net/proxy - Per-host proxy bypass
- Factory with per-client functional options
- Transport tuning (idle connections, timeouts)
- TLS configuration options
See the open issues for a full list of proposed features.
Contributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are greatly appreciated.
- Fork the Project
- Create your Feature Branch (
git checkout -b feature/AmazingFeature) - Commit your Changes (
git commit -m 'Add some AmazingFeature') - Push to the Branch (
git push origin feature/AmazingFeature) - Open a Pull Request
Distributed under the Apache License 2.0. See LICENSE for more information.
- Uber Fx — dependency injection framework
- golang.org/x/net/proxy — SOCKS5 and environment-based proxy dialers
- Best-README-Template — README structure