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 ¶
- func ClientIP(r *http.Request, trusted Proxies) string
- func ETag(body []byte) string
- func ErrorWriter[E Partser](e Envelope) func(http.ResponseWriter, *http.Request, E)
- func Health(checks ...Check) http.Handler
- func IfNoneMatch(r *http.Request, etag string) bool
- func Middleware(cfg Config) func(http.Handler) http.Handler
- func MustPrincipal[T any](ctx context.Context) T
- func Principal[T any](ctx context.Context) (T, bool)
- func RequestID(ctx context.Context) string
- func ResolveGuard(pattern string, pathValue func(name string) string) (string, bool)
- func ResolveGuards(patterns []string, pathValue func(name string) string) ([]string, bool)
- func SPA(fsys fs.FS, opts SPAOptions) http.Handler
- func SetFlag(ctx context.Context, name string)
- func SetPrincipal(ctx context.Context, principal any)
- func WithState(ctx context.Context, state *State) context.Context
- type Check
- type Config
- type Envelope
- type Failure
- type Partser
- type Proxies
- type Recorder
- type SPAOptions
- type State
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func ClientIP ¶
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 ErrorWriter ¶
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
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
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 ¶
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 ¶
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 ResolveGuard ¶
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 ¶
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
SetFlag marks the request with name, for example "skip-audit". The flag is visible to observers and middleware that share the State.
func SetPrincipal ¶
SetPrincipal records the authenticated principal on the request state.
Types ¶
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.
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 ¶
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 (*Recorder) Flush ¶
func (r *Recorder) Flush()
Flush implements http.Flusher when the underlying writer does.
func (*Recorder) Hijack ¶ added in v0.34.0
Hijack implements http.Hijacker when the underlying writer does, so a handler behind Middleware can still upgrade the connection.
func (*Recorder) Status ¶
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) WriteHeader ¶
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
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.