httpclient

package
v0.62.0 Latest Latest
Warning

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

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

Documentation

Overview

Package httpclient bounds an API client's requests to its configured origin and owns the URL shapes adapters use to configure and address an API: base URLs, origins, and path segments.

Example
package main

import (
	"context"
	"errors"
	"fmt"
	"net/http"

	"github.com/Tangerg/scope/core/httpclient"
)

func main() {
	ctx := context.Background()
	host := &http.Client{}
	client, err := httpclient.New(ctx, httpclient.Config{
		HTTPClient: host,
		BaseURL:    "https://api.example/v1",
	})
	if err != nil {
		panic(err)
	}
	redirect, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://api.example:9443/result", nil)
	if err != nil {
		panic(err)
	}
	err = client.CheckRedirect(redirect, nil)
	fmt.Println("independent client:", client != host)
	fmt.Println("host transport unchanged:", host.Transport == nil)
	fmt.Println("other port rejected:", errors.Is(err, httpclient.ErrOriginNotAllowed))
}
Output:
independent client: true
host transport unchanged: true
other port rejected: true

Index

Examples

Constants

View Source
const DefaultMaxJSONResponseBytes = 32 << 20

Variables

View Source
var ErrOriginNotAllowed = errors.New("httpclient: request origin is not authorized")

ErrOriginNotAllowed prevents an API credential or request body from reaching an origin other than the one configured by its owner.

Functions

func New

func New(_ context.Context, config Config) (*http.Client, error)

New copies the client value before applying origin restrictions, so SDK initialization and redirect policy changes cannot mutate the host's client. The host retains ownership of the shared transport and cookie jar. Its redirect policy runs before the final destination and authority checks.

func NewDeliveryClient added in v0.62.0

func NewDeliveryClient(config DeliveryConfig) (*http.Client, error)

NewDeliveryClient returns a copy of the configured client that sends no cookie jar, never follows a redirect from HTTPS to HTTP, reaches only the Destination, and strips credentials outside CredentialOrigin.

func ParseBaseURL added in v0.62.0

func ParseBaseURL(raw string) (*url.URL, error)

ParseBaseURL admits a configured API base URL: an absolute HTTP(S) URL with a host and an optional base path. User information would carry a credential the binding does not own, and a query or fragment cannot prefix the resource paths appended to it. The raw text is checked because url.URL drops an empty fragment.

func PathSegment added in v0.62.0

func PathSegment(value string) string

PathSegment encodes value as exactly one URL path segment. Escaping alone leaves "." and ".." literal, and reference resolution would then drop the segment or climb to its parent, so their dots are percent-encoded too. An empty value encodes to an empty segment; callers own presence validation.

func Restrict added in v0.62.0

func Restrict(client *http.Client, allowed Origins) (*http.Client, error)

Restrict returns a copy of client whose requests, including every redirect, reach only allowed origins. A nil client is an empty client on the default transport. The host keeps ownership of the shared transport and cookie jar.

Types

type Config

type Config struct {
	// A nil Transport, including a typed nil, uses http.DefaultTransport.
	HTTPClient *http.Client
	BaseURL    string
	// RejectRedirects binds resource-scoped requests to the original URL, even
	// when a same-origin server redirects to a different tenant or resource.
	RejectRedirects bool
}

type DeliveryConfig added in v0.62.0

type DeliveryConfig struct {
	HTTPClient *http.Client
	// Destination authorizes every request and every redirect.
	Destination Destination
	// CredentialOrigin is the one origin that may still receive credentials,
	// such as an API that serves its own files; the zero value strips them
	// from every request.
	CredentialOrigin Origin
	// CredentialHeaders names provider credentials removed alongside
	// Authorization, Proxy-Authorization, and Cookie.
	CredentialHeaders []string
}

DeliveryConfig restricts a client that follows provider-issued output URLs. Such a URL is data, not a grant of the host's network or credentials.

type Destination added in v0.62.0

type Destination interface {
	ValidateRequest(request *http.Request) error
}

Destination authorizes the target of one request, including the authority written to the wire. Origins is the fixed-set Destination; a provider whose output hosts follow a rule supplies its own.

type Origin

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

Origin is an immutable HTTP origin. Only hierarchical URLs are admitted, so Opaque cannot replace the checked authority on the wire. Equality includes the scheme, host, and effective port; its zero value authorizes no requests.

func NewOrigin

func NewOrigin(endpoint *url.URL) (Origin, error)

NewOrigin takes the origin of an absolute, hierarchical HTTP(S) URL. Its path, query, and fragment name a resource and do not affect the origin.

func ParseOrigin added in v0.62.0

func ParseOrigin(raw string) (Origin, error)

ParseOrigin admits a configured origin: a base URL with no path beyond "/", so a value that names a resource cannot be mistaken for the authority it grants.

func (Origin) ValidateRequest

func (o Origin) ValidateRequest(request *http.Request) error

ValidateRequest checks both the destination URL and the authority sent on the wire, because Request.Host can override URL.Host without changing the URL.

type Origins added in v0.62.0

type Origins []Origin

Origins is a set of authorized origins. A request is authorized when its destination, including the authority written to the wire, is one of them.

func (Origins) ValidateRequest added in v0.62.0

func (o Origins) ValidateRequest(request *http.Request) error

type ResponseBodyLimit added in v0.61.0

type ResponseBodyLimit int64

ResponseBodyLimit admits bytes as they are read, before a decoder can allocate for an untrusted response. The HTTP client still owns retries and body closure.

func (ResponseBodyLimit) Apply added in v0.61.0

func (r ResponseBodyLimit) Apply(request *http.Request, next func(*http.Request) (*http.Response, error)) (*http.Response, error)

func (ResponseBodyLimit) ApplyErrors added in v0.61.0

func (r ResponseBodyLimit) ApplyErrors(request *http.Request, next func(*http.Request) (*http.Response, error)) (*http.Response, error)

Successful streaming bodies have a per-event contract, so their error envelopes must be bounded without imposing a limit on the entire stream.

func (ResponseBodyLimit) ApplyEventStream added in v0.61.0

func (r ResponseBodyLimit) ApplyEventStream(request *http.Request, next func(*http.Request) (*http.Response, error)) (*http.Response, error)

For SDKs with LF/CRLF SSE framing, each wire event has its own budget, including comments, BOM, and delimiters. Empty lines reset admission, so long-lived streams retain backpressure without a total byte ceiling. This wrapper preserves bytes; the SDK remains responsible for interpreting them.

type ResponseBudget added in v0.62.0

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

ResponseBudget is one aggregate byte grant shared by every response of an operation, such as the pages of a listing, so a scan that follows a server's pagination cannot read more in total than its caller granted. Each body is read in full against what remains; a successful body must be one JSON value. It is not safe for concurrent use.

func NewResponseBudget added in v0.62.0

func NewResponseBudget(maxBytes int64) (*ResponseBudget, error)

func (*ResponseBudget) Apply added in v0.62.0

func (r *ResponseBudget) Apply(request *http.Request, next func(*http.Request) (*http.Response, error)) (*http.Response, error)

type ResponseError added in v0.60.0

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

ResponseError exposes transport evidence without turning a remote response body into public diagnostics. It makes no claim about provider execution.

func NewResponseError added in v0.60.0

func NewResponseError(statusCode int, header http.Header) *ResponseError

func (*ResponseError) Error added in v0.60.0

func (r *ResponseError) Error() string

func (*ResponseError) HTTPStatus added in v0.60.0

func (r *ResponseError) HTTPStatus() int

func (*ResponseError) RequestID added in v0.60.0

func (r *ResponseError) RequestID() string

Jump to

Keyboard shortcuts

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