httpkit

package
v0.35.0 Latest Latest
Warning

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

Go to latest
Published: Oct 16, 2026 License: MIT Imports: 17 Imported by: 0

Documentation

Overview

Package httpkit is the glue an application usually writes around an onek-generated Go server: an error envelope, request state in the context, a response recorder and guard resolution. It depends only on the standard library and never imports generated code, so it works with every generated package, inline or shared runtime.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ClientIP

func ClientIP(r *http.Request, trusted Proxies) string

ClientIP returns the caller's address: the peer address of the connection, or, when that peer is a trusted proxy, the closest X-Forwarded-For entry that is not itself a trusted proxy.

func ETag added in v0.28.0

func ETag(body []byte) string

ETag returns a strong validator for body.

func ErrorWriter

func ErrorWriter[E Partser](e Envelope) func(http.ResponseWriter, *http.Request, E)

ErrorWriter adapts an Envelope to a generated server's error writer:

api.WithErrorWriter(httpkit.ErrorWriter[*api.ServerError](httpkit.Envelope{}))

func Health added in v0.28.0

func Health(checks ...Check) http.Handler

Health answers 200 {"status":"ok"} when every check passes and 503 {"status":"unavailable","failed":[names]} when one fails. It never sends the error text, which can name internal hosts.

func IfNoneMatch added in v0.28.0

func IfNoneMatch(r *http.Request, etag string) bool

IfNoneMatch reports whether the request's If-None-Match header matches etag, so the handler can answer 304. It understands lists, "*" and weak (W/) validators.

func Middleware

func Middleware(cfg Config) func(http.Handler) http.Handler

Middleware stores State in the request context, honors or creates the request id, resolves the client IP and records the response status and size.

func MustPrincipal

func MustPrincipal[T any](ctx context.Context) T

MustPrincipal is Principal for routes behind authentication: it panics when no principal of type T was set, which is a wiring bug, not a client error.

func Principal

func Principal[T any](ctx context.Context) (T, bool)

Principal returns the principal recorded by SetPrincipal when it has type T.

func RequestID

func RequestID(ctx context.Context) string

RequestID returns the request id, or "" when there is none.

func ResolveGuard

func ResolveGuard(pattern string, pathValue func(name string) string) (string, bool)

ResolveGuard fills the ":name" segments of a @guard pattern from the request's path values, so "object/level/:id" becomes "object/level/42". It reports false when a placeholder has no value.

func ResolveGuards

func ResolveGuards(patterns []string, pathValue func(name string) string) ([]string, bool)

ResolveGuards resolves every pattern. The patterns come from the route's RequestMetadata.Guards and the path values from the request:

guards, ok := httpkit.ResolveGuards(meta.Guards, r.PathValue)

func SPA added in v0.28.0

func SPA(fsys fs.FS, opts SPAOptions) http.Handler

SPA serves a single-page app from fsys: files that exist are served as they are, a path with no file extension that does not exist gets the index document (so client-side routes work), and a missing file with an extension is a 404 rather than index.html.

func SetFlag added in v0.28.0

func SetFlag(ctx context.Context, name string)

SetFlag marks the request with name, for example "skip-audit". The flag is visible to observers and middleware that share the State.

func SetPrincipal

func SetPrincipal(ctx context.Context, principal any)

SetPrincipal records the authenticated principal on the request state.

func WithState

func WithState(ctx context.Context, state *State) context.Context

WithState returns a context carrying state.

Types

type Check added in v0.28.0

type Check struct {
	Name string
	Run  func(ctx context.Context) error
}

Check is one named readiness probe for Health.

type Config

type Config struct {
	// RequestIDHeader is read from the request and echoed on the response.
	// It defaults to defaultRequestIDHeader.
	RequestIDHeader string
	// NewRequestID creates ids for requests that carry none. It defaults to a
	// random 16-byte hex string.
	NewRequestID func() string
	// TrustedProxies lists the addresses or prefixes (192.0.2.1, 10.0.0.0/8)
	// whose X-Forwarded-For header is believed. A request from any other peer
	// uses its own RemoteAddr.
	TrustedProxies []string
	// Now returns the current time. It defaults to time.Now.
	Now func() time.Time
}

Config configures Middleware.

type Envelope

type Envelope struct {
	// Logger receives the cause of 5xx failures. Nil uses slog.Default().
	Logger *slog.Logger
	// ContentType defaults to "application/json".
	ContentType string
	// RequestIDHeader names the response header that carries the request id
	// when Middleware is not installed, as generated servers set it with
	// WithRequestID. It defaults to defaultRequestIDHeader.
	RequestIDHeader string
	// Wrap customizes the body. It receives the default body map and may
	// return any value to encode instead.
	Wrap func(f Failure, requestID string, body map[string]any) any
}

Envelope writes failures as {"error": {"code", "message", "request_id"}}. Violations and the offending field are included when present. The cause of a 5xx failure is logged and never sent to the client.

func (Envelope) Write

func (e Envelope) Write(w http.ResponseWriter, r *http.Request, f Failure)

Write sends f as the response.

type Failure

type Failure struct {
	Status     int
	Code       string
	Message    string
	Field      string
	Violations []string
	Cause      error
}

Failure is what an error writer knows about a failed request. A generated ServerError provides the same values through its Parts method.

type Partser

type Partser interface {
	Parts() (status int, code, message, field string, violations []string, cause error)
}

Partser is satisfied by the generated ServerError.

type Proxies

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

Proxies is a parsed trusted-proxy list.

func ParseProxies

func ParseProxies(entries []string) Proxies

ParseProxies parses addresses and CIDR prefixes. Entries that do not parse are ignored.

type Recorder

type Recorder struct {
	http.ResponseWriter
	// contains filtered or unexported fields
}

Recorder wraps a ResponseWriter and records the status and the number of body bytes. Middleware installs one; it also works standalone.

func NewRecorder

func NewRecorder(w http.ResponseWriter) *Recorder

NewRecorder wraps w.

func (*Recorder) Bytes

func (r *Recorder) Bytes() int64

Bytes is the number of body bytes written.

func (*Recorder) Flush

func (r *Recorder) Flush()

Flush implements http.Flusher when the underlying writer does.

func (*Recorder) Hijack added in v0.34.0

func (r *Recorder) Hijack() (net.Conn, *bufio.ReadWriter, error)

Hijack implements http.Hijacker when the underlying writer does, so a handler behind Middleware can still upgrade the connection.

func (*Recorder) Status

func (r *Recorder) Status() int

Status is the status code sent, or 200 once a body was written without one, or 0 before anything was sent.

func (*Recorder) Unwrap

func (r *Recorder) Unwrap() http.ResponseWriter

Unwrap lets http.ResponseController reach the underlying writer.

func (*Recorder) Write

func (r *Recorder) Write(data []byte) (int, error)

func (*Recorder) WriteHeader

func (r *Recorder) WriteHeader(status int)

type SPAOptions added in v0.28.0

type SPAOptions struct {
	// Index is the document served for client-side routes. It defaults to
	// "index.html".
	Index string
	// Immutable reports whether a file name is content-hashed and can be
	// cached forever. It defaults to names under "assets/".
	Immutable func(name string) bool
	// OnServe runs before every response, with the file about to be served, to
	// set per-request headers such as a Content-Security-Policy.
	OnServe func(w http.ResponseWriter, r *http.Request, name string)
}

SPAOptions configures SPA.

type State

type State struct {
	RequestID string
	Start     time.Time
	ClientIP  string
	// contains filtered or unexported fields
}

State is the per-request state Middleware stores in the context. It is shared by pointer, so values set while handling the request (the authenticated principal, for example) are visible to an observer that runs after the handler returns.

func EnsureState added in v0.28.0

func EnsureState(ctx context.Context, r *http.Request, cfg Config) (context.Context, *State)

EnsureState returns a context carrying the request State, creating it from r when there is none. A request observer calls it in RequestStarted so the State exists before the handler chain runs and is still readable in RequestFinished; Middleware then reuses it.

func StateFrom

func StateFrom(ctx context.Context) *State

StateFrom returns the request state, or nil outside Middleware.

func (*State) Bytes

func (s *State) Bytes() int64

Bytes is the number of response body bytes written so far.

func (*State) Flag added in v0.28.0

func (s *State) Flag(name string) bool

Flag reports whether SetFlag marked the request with name.

func (*State) Principal added in v0.28.0

func (s *State) Principal() any

Principal returns the principal recorded with SetPrincipal, or nil.

func (*State) Status

func (s *State) Status() int

Status is the response status captured so far (0 before the header is sent).

Jump to

Keyboard shortcuts

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