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):
- WithGitHubHostMatcher — an explicit custom predicate always wins.
- GITHUB_API_URL — when set and WithGitHubHostMatcher was NOT applied, the GHES hostname from the environment variable is added to the allowlist.
- 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 GetGitHubTokenFromEnv ¶ added in v1.203.0
GetGitHubTokenFromEnv retrieves GitHub token from the global configuration. This function respects the standard Atmos precedence order:
- --github-token CLI flag (via viper, only available for toolchain commands)
- ATMOS_GITHUB_TOKEN environment variable
- 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.
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.
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) 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.