http

package
v1.217.0-rc.2 Latest Latest
Warning

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

Go to latest
Published: Apr 27, 2026 License: Apache-2.0 Imports: 15 Imported by: 0

Documentation

Overview

Package http provides a configurable HTTP client with GitHub authentication support.

Creating a client

Use NewDefaultClient with functional options to configure the client:

client := http.NewDefaultClient(
    http.WithTimeout(30 * time.Second),
    http.WithGitHubToken("mytoken"),
)

Option ordering

The order in which options are applied matters when composing transport layers. Understanding the ordering rules prevents subtle authentication bugs.

## WithGitHubToken and WithTransport

WithGitHubToken wraps the current transport in a GitHubAuthenticatedTransport. WithTransport sets a new base transport. When applied after WithGitHubToken, WithTransport replaces the base (inner) transport while preserving the auth wrapper:

// Recommended: set transport first, then add authentication.
client := http.NewDefaultClient(
    http.WithTransport(myCustomTransport), // base transport
    http.WithGitHubToken("token"),          // wraps myCustomTransport
)

// Also valid: apply in reverse order — WithTransport after WithGitHubToken
// replaces the base of the existing GitHubAuthenticatedTransport.
client := http.NewDefaultClient(
    http.WithGitHubToken("token"),          // wraps http.DefaultTransport
    http.WithTransport(myCustomTransport),   // updates base to myCustomTransport
)

Triple-composition note: adding a second WithTransport after WithGitHubToken + the first WithTransport silently discards the first base transport:

// t1 is discarded; result is GitHubAuthenticatedTransport{Base: t2, Token: "x"}
client := http.NewDefaultClient(
    http.WithTransport(t1),
    http.WithGitHubToken("x"),
    http.WithTransport(t2), // t1 is gone
)

## WithGitHubHostMatcher

WithGitHubHostMatcher must be applied AFTER WithGitHubToken, because the host matcher is stored on the GitHubAuthenticatedTransport which is only created by WithGitHubToken. Applying it before WithGitHubToken has no effect.

// Correct: token first, then custom host matcher.
client := http.NewDefaultClient(
    http.WithGitHubToken("mytoken"),
    http.WithGitHubHostMatcher(func(host string) bool {
        return host == "github.mycorp.example.com" // GHES
    }),
)

// Incorrect: matcher applied before token — has no effect.
client := http.NewDefaultClient(
    http.WithGitHubHostMatcher(...), // no-op: no transport yet
    http.WithGitHubToken("mytoken"),
)

## GitHub Enterprise Server (GHES)

For GHES deployments set the GITHUB_API_URL environment variable to the GHES API base URL. The default host matcher ([isGitHubHost]) reads this variable and treats the configured hostname as an additional allowed host:

GITHUB_API_URL=https://github.mycorp.example.com

Alternatively, use WithGitHubHostMatcher for programmatic control.

Host-matcher precedence

The host predicate used to decide whether to inject Authorization follows this precedence order (highest to lowest):

  1. WithGitHubHostMatcher — an explicit custom predicate always wins.
  2. GITHUB_API_URL — when set and WithGitHubHostMatcher was NOT applied, the GHES hostname from the environment variable is added to the allowlist.
  3. Built-in allowlist — api.github.com, raw.githubusercontent.com, uploads.github.com.

If you need GHES support together with a custom matcher, include the GHES host in your custom predicate; WithGitHubHostMatcher bypasses the GITHUB_API_URL lookup:

ghesHost := "github.mycorp.example.com"
client := http.NewDefaultClient(
    http.WithGitHubToken("mytoken"),
    http.WithGitHubHostMatcher(func(host string) bool {
        return host == "api.github.com" || host == ghesHost
    }),
)

Security notes

Authorization headers are only injected when ALL of the following are true:

  • The request URL scheme is "https".
  • The request hostname matches the host predicate.
  • The Authorization header is not already set on the request.

Cross-host redirects: WithGitHubToken installs a CheckRedirect handler that strips the Authorization header from redirect requests that target a different host:port, preventing token leakage via open redirects.

This prevents accidental token leakage over unencrypted HTTP and ensures that caller-supplied Authorization headers are never overwritten.

Package http is a generated GoMock package.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Get

func Get(ctx context.Context, url string, client Client) ([]byte, error)

Get performs an HTTP GET request with context using the provided client.

func GetGitHubTokenFromEnv added in v1.203.0

func GetGitHubTokenFromEnv(v ...*viper.Viper) string

GetGitHubTokenFromEnv retrieves GitHub token from the global configuration. This function respects the standard Atmos precedence order:

  1. --github-token CLI flag (via viper, only available for toolchain commands)
  2. ATMOS_GITHUB_TOKEN environment variable
  3. GITHUB_TOKEN environment variable

The viper binding is configured in cmd/toolchain/toolchain.go for toolchain commands. For non-toolchain commands, we fall back to direct environment variable lookup.

An optional *viper.Viper instance may be passed; when provided it is used instead of the global viper singleton. This is primarily useful in tests to avoid mutating shared global state.

Types

type Client

type Client interface {
	// Do performs an HTTP request and returns the response.
	Do(req *http.Request) (*http.Response, error)
}

Client defines the interface for making HTTP requests. This interface allows for easy mocking in tests.

See the package documentation (doc.go) for guidance on option ordering when composing ClientOption values such as WithTransport and WithGitHubToken.

type ClientOption added in v1.203.0

type ClientOption func(*DefaultClient)

ClientOption is a functional option for configuring the DefaultClient. Options are applied in the order they are passed to NewDefaultClient. For composition rules when mixing WithTransport and WithGitHubToken, see the "Option ordering" section in the package documentation (doc.go).

func WithGitHubHostMatcher

func WithGitHubHostMatcher(matcher func(string) bool) ClientOption

WithGitHubHostMatcher sets a custom host-matching predicate on the GitHub authenticated transport. The predicate receives the request hostname (without port) and returns true when the host should receive GitHub authentication headers.

This is useful for GitHub Enterprise Server (GHES) deployments or custom GitHub proxies where the API is hosted on a non-standard domain.

Example usage:

client := NewDefaultClient(
    WithGitHubToken("token"),
    WithGitHubHostMatcher(func(host string) bool {
        return host == "github.mycorp.example.com"
    }),
)

If this option is applied before WithGitHubToken, it has no effect because there is no transport to configure yet. Apply it after WithGitHubToken.

func WithGitHubToken added in v1.203.0

func WithGitHubToken(token string) ClientOption

WithGitHubToken sets the GitHub token for authenticated requests. Wraps the existing transport instead of replacing it to allow composition with WithTransport. It also installs a CheckRedirect handler that strips the managed Authorization header when a redirect crosses to a different host, preventing token leakage.

Triple-composition caveat: if a second WithTransport call follows this option, the earlier base transport is silently replaced. See WithTransport for details.

func WithTimeout added in v1.203.0

func WithTimeout(timeout time.Duration) ClientOption

WithTimeout sets the HTTP client timeout.

func WithTransport added in v1.203.0

func WithTransport(transport http.RoundTripper) ClientOption

WithTransport sets a custom HTTP transport. If a GitHubAuthenticatedTransport has already been applied (e.g., via WithGitHubToken), the provided transport is set as its Base rather than replacing the auth wrapper. This preserves GitHub authentication regardless of option order.

Triple-composition note: when a second WithTransport call follows WithGitHubToken, the earlier base transport (from the first WithTransport) is silently replaced by the new one. Example: WithTransport(t1), WithGitHubToken("x"), WithTransport(t2) Result: GitHubAuthenticatedTransport{Base: t2, Token: "x"}; t1 is discarded.

type DefaultClient

type DefaultClient struct {
	// contains filtered or unexported fields
}

DefaultClient is the default HTTP client implementation.

func NewDefaultClient

func NewDefaultClient(opts ...ClientOption) *DefaultClient

NewDefaultClient creates a new DefaultClient with optional configuration.

func (*DefaultClient) Do

func (c *DefaultClient) Do(req *http.Request) (*http.Response, error)

Do implements Client.Do.

type GitHubAuthenticatedTransport added in v1.203.0

type GitHubAuthenticatedTransport struct {
	Base        http.RoundTripper
	GitHubToken string
	// contains filtered or unexported fields
}

GitHubAuthenticatedTransport wraps an http.Transport to add GitHub token authentication.

func (*GitHubAuthenticatedTransport) RoundTrip added in v1.203.0

RoundTrip implements http.RoundTripper interface.

type MockClient

type MockClient struct {
	// contains filtered or unexported fields
}

MockClient is a mock of Client interface.

func NewMockClient

func NewMockClient(ctrl *gomock.Controller) *MockClient

NewMockClient creates a new mock instance.

func (*MockClient) Do

func (m *MockClient) Do(req *http.Request) (*http.Response, error)

Do mocks base method.

func (*MockClient) EXPECT

func (m *MockClient) EXPECT() *MockClientMockRecorder

EXPECT returns an object that allows the caller to indicate expected use.

type MockClientMockRecorder

type MockClientMockRecorder struct {
	// contains filtered or unexported fields
}

MockClientMockRecorder is the mock recorder for MockClient.

func (*MockClientMockRecorder) Do

func (mr *MockClientMockRecorder) Do(req any) *gomock.Call

Do indicates an expected call of Do.

Jump to

Keyboard shortcuts

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