httpfx

package module
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 31, 2026 License: Apache-2.0 Imports: 13 Imported by: 0

README

Contributors Forks Stargazers Issues Apache 2.0 License


httpfx

Uber Fx module for net/http.Client with SOCKS5 proxy support (including per-host bypass) and custom root CA trust.

Report Bug · Request Feature

Table of Contents


About The Project

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

Built With

  • Go
  • Uber Fx
  • x/net

(back to top)


Getting Started

Prerequisites

  • Go 1.25+
  • An application using Uber Fx for dependency injection

Installation

go get github.com/go-core-fx/httpfx@latest

(back to top)


Usage

Module Setup

import (
    "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.

Configuration Reference

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.

Factory & Per-Client Options

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
}
Available Options
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

Proxy Examples

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",
}

TLS & Root CA Examples

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.NewClient return an error; when clients are built through Module, application startup fails with that error.

(back to top)


Roadmap

  • 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.

(back to top)


Contributing

Contributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are greatly appreciated.

  1. Fork the Project
  2. Create your Feature Branch (git checkout -b feature/AmazingFeature)
  3. Commit your Changes (git commit -m 'Add some AmazingFeature')
  4. Push to the Branch (git push origin feature/AmazingFeature)
  5. Open a Pull Request

(back to top)


License

Distributed under the Apache License 2.0. See LICENSE for more information.

(back to top)


Acknowledgments

(back to top)

Documentation

Index

Constants

View Source
const ModuleName = "httpfx"

Variables

View Source
var (
	// ErrInvalidConfig is returned when the HTTP client configuration is invalid.
	ErrInvalidConfig = errors.New("invalid config")

	// ErrInvalidProxyURL is returned when the proxy URL cannot be parsed.
	ErrInvalidProxyURL = errors.New("invalid proxy URL")

	// ErrProxyDialFailed is returned when the proxy dialer cannot be created.
	ErrProxyDialFailed = errors.New("proxy dialer creation failed")

	// ErrCertPoolFailed is returned when a root CA file cannot be read or the
	// root CA certificate pool cannot be built.
	ErrCertPoolFailed = errors.New("root CA cert pool creation failed")

	// ErrEmptyCertPEM is returned when PEM data contains no valid certificates.
	ErrEmptyCertPEM = errors.New("no valid certificates found in PEM data")
)

Functions

func Module

func Module() fx.Option

Types

type Config

type Config struct {
	// ProxyURL is an explicit SOCKS5 proxy URL (e.g., "socks5://user:pass@host:port").
	// Empty means inherit [http.DefaultTransport]'s value.
	ProxyURL string

	// Bypass is a comma-separated list of hosts that bypass the proxy
	// (e.g., "localhost,127.0.0.1").
	Bypass string

	// Timeout is the HTTP client-level timeout. Zero means inherit
	// [http.DefaultClient]'s timeout (none).
	Timeout time.Duration

	// MaxIdleConns is the maximum number of idle (keep-alive) connections.
	// Zero means inherit [http.DefaultTransport]'s value.
	MaxIdleConns int

	// MaxIdleConnsPerHost is the maximum idle connections per host.
	// Zero means inherit [http.DefaultTransport]'s value.
	MaxIdleConnsPerHost int

	// IdleConnTimeout is the maximum time an idle connection is kept alive.
	// Zero means inherit [http.DefaultTransport]'s value.
	IdleConnTimeout time.Duration

	// TLS configures root CA trust for TLS connections; see [TLSConfig].
	// The zero value keeps the default behavior (system certificate pool only).
	TLS TLSConfig
}

Config holds the HTTP client configuration.

type Factory

type Factory interface {
	// NewClient creates a new [http.Client] using the factory's base configuration,
	// with optional per-client overrides via [Option]. It returns an error when
	// the resulting configuration is invalid (e.g. unusable root CA settings).
	NewClient(opts ...Option) (*http.Client, error)
}

Factory creates http.Client instances with shared proxy and transport configuration.

func NewFactory

func NewFactory(config Config) Factory

NewFactory creates a new Factory from the provided configuration.

type Option

type Option func(*clientOptions)

Option configures per-client overrides on Factory.NewClient.

func WithIdleConnTimeout

func WithIdleConnTimeout(t time.Duration) Option

WithIdleConnTimeout overrides the idle connection timeout for this client.

func WithMaxIdleConns

func WithMaxIdleConns(n int) Option

WithMaxIdleConns overrides the maximum idle connections for this client.

func WithMaxIdleConnsPerHost

func WithMaxIdleConnsPerHost(n int) Option

WithMaxIdleConnsPerHost overrides the maximum idle connections per host for this client.

func WithProxyURL

func WithProxyURL(rawURL string, bypass string) Option

WithProxyURL overrides the SOCKS5 proxy URL for this client.

func WithRootCAFile added in v0.1.0

func WithRootCAFile(path string) Option

WithRootCAFile overrides the root CA certificate file path for this client. The file must contain PEM-encoded certificates; all of them are added to the trust pool. An empty path clears the base configuration's CA file.

func WithRootCAPEM added in v0.1.0

func WithRootCAPEM(pem string) Option

WithRootCAPEM overrides the inline PEM-encoded root CA data for this client. Multiple certificates in one string are all added to the trust pool. An empty string clears the base configuration's CA PEM data.

func WithRootCAReplaceSystem added in v0.1.0

func WithRootCAReplaceSystem(replace bool) Option

WithRootCAReplaceSystem overrides whether this client replaces the system certificate pool instead of appending to it. When true, only the root CAs configured for this client are trusted.

func WithTimeout

func WithTimeout(t time.Duration) Option

WithTimeout overrides the client-level timeout for this client.

type TLSConfig added in v0.1.0

type TLSConfig struct {
	// RootCAFile is a path to a PEM-encoded root CA certificate file.
	// Multiple certificates in one file are all added to the pool.
	RootCAFile string

	// RootCAPEM is PEM-encoded root CA certificate data. Multiple
	// certificates in one string are all added to the pool.
	RootCAPEM string

	// RootCAReplaceSystem replaces the system certificate pool instead of
	// appending to it. When true, only the configured root CAs are trusted.
	//
	// Note: append mode relies 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.
	RootCAReplaceSystem bool
}

TLSConfig holds root CA trust configuration for TLS connections.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL