Help us shape the future of Go! Take the 2026 Go Developer Survey

httpx

package
v0.16.0 Latest Latest
Warning

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

Go to latest
Published: Oct 23, 2026 License: AGPL-3.0 Imports: 37 Imported by: 0

Documentation

Overview

Package httpx holds shared HTTP plumbing: middleware, the API response envelope, form binding/validation, the SSRF-safe client and the admin template renderer.

Index

Constants

View Source
const (
	CacheImmutable = "public, max-age=31536000, immutable"
	CacheShort     = "public, max-age=3600"
)

Cache-Control values for static assets.

View Source
const (
	ScalarScript    = "https://cdn.jsdelivr.net/npm/@scalar/api-reference@1.25.0"
	ScalarIntegrity = "sha384-VPR3QD7bdyIR9EHf6dRrFXz7NO0yJV7/UCVTLQ6e7a0wgzot5WaxvciVrrYOKEpU"
)

ScalarScript is the pinned Scalar API reference bundle served on /api/docs, and ScalarIntegrity its SRI hash. It is the only third-party script the built-in pages load, so the CSP allows exactly this URL on exactly that page.

View Source
const (

	// StaticPrefix is where admin/static is served.
	StaticPrefix = "/static/admin/"
	// LaunchCSSPath is the public URL for the embedded launch overlay stylesheet.
	// Downstream /start pages and prior Traefik mounts use this exact path.
	LaunchCSSPath = "/static/launch.css"
	// LaunchPolishPath is the public URL for the launch title/validation script.
	LaunchPolishPath = "/static/launch-polish.js"
)

Template layout (inside the FS given to NewTemplates):

admin/templates/base.html          shared defines ("head", "scripts")
admin/templates/layouts/<n>.html   full documents; call {{template "content" .}}
admin/templates/partials/**.html   {{define "partials/<name>"}} blocks
admin/templates/pages/**.html      {{define "title"}} + {{define "content"}}
admin/static/*                     served at /static/admin/ (see asset func)

Every file is parsed under its path relative to admin/templates. A page is executed through layouts/<layout>.html, or only its "content" template for HTMX requests (HX-Request: true).

View Source
const AccountPageCSS = `` /* 1232-byte string literal not displayed */

AccountPageCSS is shared presentation for standalone account add-on pages. Each enabled add-on serves it from its own route to avoid activation coupling.

Variables

View Source
var ErrRedirectRefused = errors.New("httpx: redirect refused")

ErrRedirectRefused is returned by a client built with NoRedirects when the server answers with a redirect.

Functions

func AccountSignInUnavailable added in v0.16.0

func AccountSignInUnavailable(w http.ResponseWriter, r *http.Request)

AccountSignInUnavailable explains why no public account sign-in is offered. Staff authentication remains separate and must not accept customer accounts.

func AssetCacheControl

func AssetCacheControl(r *http.Request, name string) string

AssetCacheControl returns CacheImmutable for asset URLs whose content cannot change under the same URL: a ?v= cache-busting query, or a file name carrying its release version. Everything else gets CacheShort.

func Bind

func Bind(r *http.Request, dst any) error

Bind fills the struct pointed to by dst from the request. JSON bodies (Content-Type application/json, max 1 MiB) use encoding/json; otherwise form values (URL-encoded or multipart) are matched by the `form` tag (default: lower-cased field name; "-" skips). Supported field kinds: string, bool ("on", "true", "1", "yes"), ints, uints, floats, []string and *string (nil when absent).

func ClientIP

func ClientIP(r *http.Request) string

ClientIP walks the forwarded chain only through explicitly trusted hops. A malformed chain is ignored as a whole; provider-specific headers are never trusted.

func ClientIPSource

func ClientIPSource(ctx context.Context) string

func Compress

func Compress(next http.Handler) http.Handler

Compress gzips text responses (see compressibleTypes) for clients that accept gzip. It leaves alone: responses that already have a Content-Encoding, range requests, bodiless statuses, and admin pages (/admin and /admin/...), which embed the session's CSRF token next to user-controlled text and so stay uncompressed to rule out BREACH-style length oracles. Admin static assets live under /static/admin/ and are compressed. It always adds Vary: Accept-Encoding to compressible paths.

func Error

func Error(w http.ResponseWriter, status int, code, msg string, fields any)

Error writes {"ok":false,"error":{"code","message","fields"}}; fields is omitted when nil.

func JSON

func JSON(w http.ResponseWriter, status int, data, meta any)

JSON writes {"ok":true,"data":data,"meta":meta}; meta is omitted when nil.

func Logger

func Logger(log *slog.Logger) func(http.Handler) http.Handler

Logger logs one line per request.

func NewSafeClient

func NewSafeClient(allowPrivate bool) *http.Client

NewSafeClient returns an HTTP client for fetching user-supplied URLs. Unless allowPrivate, its dialer refuses blocked addresses after DNS resolution (so DNS rebinding cannot bypass it). Redirects may only stay on the original host. Environment proxies are ignored.

func NewSafeClientOpts

func NewSafeClientOpts(o SafeClientOptions) *http.Client

NewSafeClientOpts is NewSafeClient with options (see SafeClientOptions).

func Nonce

func Nonce(ctx context.Context) string

Nonce returns the CSP nonce for inline <script nonce="..."> tags.

func RealIP

func RealIP(next http.Handler) http.Handler

RealIP stores ClientIP in the context (audit.WithIP) for audit, logging and login rate limiting.

func RealIPWithTrustedProxies

func RealIPWithTrustedProxies(prefixes []netip.Prefix) func(http.Handler) http.Handler

func Recover

func Recover(log *slog.Logger) func(http.Handler) http.Handler

Recover turns a handler panic into a logged 500.

func RedactPath

func RedactPath(p string) string

RedactPath replaces everything after a secret prefix with "…". Locale-prefixed public paths (/en/auth/magic/…, /ar/auth/magic/…) are treated the same as the unprefixed credential URL.

func RequestID

func RequestID(next http.Handler) http.Handler

RequestID reuses a well-formed incoming X-Request-ID or generates a ULID, stores it in the context and echoes it in the response header.

func RequestIDFromContext

func RequestIDFromContext(ctx context.Context) string

RequestIDFromContext returns the request id set by RequestID.

func RequestIsHTTPS

func RequestIsHTTPS(r *http.Request) bool

RequestIsHTTPS reports whether r arrived over https: TLS on this connection, or X-Forwarded-Proto "https" (first value) set by a trusted proxy peer (the same trust rule as ClientIP).

func SecureCookie

func SecureCookie(r *http.Request, configuredBaseURL string) bool

SecureCookie is the Secure flag for a cookie set while handling r: the request is https (RequestIsHTTPS), else the operator-configured base URL (GORUN_BASE_URL, never the site.base_url setting) is https.

func SecurityHeaders

func SecurityHeaders(publicScripts ...string) func(http.Handler) http.Handler

SecurityHeaders sets CSP (with a per-request nonce, see Nonce), X-Frame-Options (SAMEORIGIN for public pages, DENY for /admin and /api), nosniff, Referrer-Policy, and HSTS when the request itself is https (RequestIsHTTPS: TLS, or X-Forwarded-Proto from a trusted proxy). It never follows a database setting, so a wrong site.base_url cannot pin browsers to https on a plain-http deployment.

Scripts come from the site itself or carry the nonce. The admin adds 'unsafe-eval' because its Alpine directives are inline expressions that only the standard (eval-based) Alpine build evaluates. Public pages get no eval and no CDN: the bundled themes self-host everything (the default theme's Tailwind runtime included, which does not eval). publicScripts are the operator's extra public script sources (config.PublicScriptSources) for custom themes that still load a CDN or self-hosted Alpine. Inline styles serve Tailwind's runtime and HTMX indicators; blob: images serve upload previews.

func SecurityHeadersWith

func SecurityHeadersWith(o SecurityOptions) func(http.Handler) http.Handler

SecurityHeadersWith is SecurityHeaders with options.

func SetFlash

func SetFlash(w http.ResponseWriter, kind, message string)

SetFlash stores a one-shot message (kind: success, error, info; message: an i18n key) shown by the next rendered page.

func TrustedProxyAddress

func TrustedProxyAddress(r *http.Request, ip netip.Addr) bool

func WarnUntrustedForwarding

func WarnUntrustedForwarding(log *slog.Logger) func(http.Handler) http.Handler

WarnUntrustedForwarding returns middleware that logs (at most once per hour, first occurrence immediately) when forwarding headers arrive from a peer outside the trusted proxies. It must run after RealIPWithTrustedProxies, which puts the trusted networks in the request context. It never changes the request.

func WithTrustedProxies

func WithTrustedProxies(ctx context.Context, prefixes []netip.Prefix) context.Context

WithTrustedProxies is set only by the application from validated operator configuration.

Types

type FieldErrors

type FieldErrors map[string]string

FieldErrors maps a field's form name to a translated message.

func Validate

func Validate(ctx context.Context, b *i18n.Bundle, v any) FieldErrors

Validate checks `validate` struct tags and returns messages translated for i18n.Locale(ctx), keyed like Bind's form names. Rules (comma-separated, first failure wins): required, email, min=N / max=N (string length in characters, or numeric value), oneof=a b c. Optional empty fields skip the other rules.

type Flash

type Flash struct{ Kind, Message string }

Flash is a one-shot message set by SetFlash; Message is an i18n key.

type Funcs

type Funcs struct {
	PinnedNav     func(ctx context.Context) []NavItem // authorized personal shortcuts, in saved order
	NavGroupOrder func(ctx context.Context) []string
	Nav           func(ctx context.Context) []NavItem               // already permission-filtered and sorted
	AllNav        func(ctx context.Context) []NavItem               // authorized items before personal visibility preferences
	Can           func(ctx context.Context, permission string) bool // nil denies permission
	User          func(ctx context.Context) any
	CSRF          func(ctx context.Context) string
	Setting       func(ctx context.Context, key string) string
}

Funcs supplies request-scoped data to the template funcs. Any may be nil.

type Meta

type Meta struct {
	Total   int `json:"total"`
	Page    int `json:"page"`
	PerPage int `json:"per_page"`
}

Meta is the pagination block of a list response.

type NavGroup struct {
	Key, LabelKey, Label string
	Items                []NavItem
}

NavGroup is one sidebar section returned by the nav template func. Label is LabelKey translated for the request locale; for a group without a catalog entry it is the key's last segment title-cased ("field_kit" → "Field Kit"), never the raw key.

type NavItem struct {
	ID, LabelKey, Icon, Href, Permission, Group string
	Position                                    int
}

NavItem mirrors NavItem field for field, so callers convert with httpx.NavItem(k). httpx cannot import kernel: kernel installs httpx's middleware on the root mux.

type NavigationSection struct {
	Key, LabelKey, Icon, Href string
	Active                    bool
	Groups                    []NavigationTabGroup
}

NavigationSection presents the authorized, personalized registry without changing IDs, routes, permissions or stored preferences.

type NavigationTabGroup struct {
	Key, LabelKey, Href string
	Active              bool
	Items               []NavItem
}

type Pagination

type Pagination struct {
	Page, PerPage, Total, Pages, Prev, Next int
	HasPrev, HasNext                        bool
}

Pagination is returned by the pagination template func.

func NewPagination

func NewPagination(page, perPage, total int) Pagination

NewPagination computes page links; page and perPage are clamped to ≥ 1.

type QuickAccessItem added in v0.9.0

type QuickAccessItem struct {
	NavItem
	Category string
	Active   bool
}

QuickAccessItem contains only a currently authorized registry item. Categories share the main navigation's semantic colors; no saved label/URL is trusted.

type SafeClientOptions

type SafeClientOptions struct {
	// AllowPrivate disables the private-network dial check (tests, or an
	// explicitly trusted internal endpoint).
	AllowPrivate bool
	// FollowCrossHost lets redirects leave the original host. Every hop still
	// dials through the private-network check.
	FollowCrossHost bool
	// Timeout bounds the whole request including redirects (default 30s).
	Timeout time.Duration
	// MaxRedirects caps the redirect chain (default 5).
	MaxRedirects int
	// NoRedirects refuses every redirect with ErrRedirectRefused (signed
	// webhook deliveries, which must reach exactly the configured URL).
	NoRedirects bool
}

SafeClientOptions configures NewSafeClientOpts. Zero values mean the defaults: private addresses blocked, same-host redirects only, 30s timeout, at most 5 redirects. An https request is never redirected to http (that would resend the body and headers in cleartext).

type SecurityOptions

type SecurityOptions struct {
	// PublicScripts are extra public-page script sources (see SecurityHeaders).
	PublicScripts []string
	// HSTSIncludeSubdomains and HSTSPreload extend the HSTS header
	// (config.HSTS*). Both are off by default: includeSubDomains would force
	// https on every subdomain of the site's host, and preload is a
	// browser-wide commitment that is slow to undo.
	HSTSIncludeSubdomains, HSTSPreload bool
}

SecurityOptions configure SecurityHeadersWith.

func (SecurityOptions) HSTSValue

func (o SecurityOptions) HSTSValue() string

HSTSValue is the Strict-Transport-Security value for o: one year, plus includeSubDomains and preload when enabled.

type SidebarBrand

type SidebarBrand struct {
	Title    string
	LogoPath string
}

type Templates

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

Templates renders admin pages. In dev every request re-parses the FS when a source can change on disk; in prod, or when every source is embedded, everything is parsed once by NewTemplates.

func NewTemplates

func NewTemplates(fsys fs.FS, dev bool, b *i18n.Bundle, f Funcs, log *slog.Logger) (*Templates, error)

NewTemplates parses fsys (see the layout above) and fails on any template error, so prod boot catches broken templates.

func (*Templates) RegisterExtension

func (t *Templates) RegisterExtension(fsys fs.FS) error

RegisterExtension attaches module-owned admin pages and asset hashes during module registration, before serving requests. Extensions cannot override core templates or other extensions. The owning module serves its static assets.

func (*Templates) Render

func (t *Templates) Render(w http.ResponseWriter, r *http.Request, page string, data any)

Render renders page inside layouts/admin.html (content only for HTMX).

func (*Templates) RenderError

func (t *Templates) RenderError(w http.ResponseWriter, r *http.Request, status int)

RenderError renders pages/errors/<status>.html (403, 404, else 500) in the auth layout, falling back to plain text.

func (*Templates) RenderPartial

func (t *Templates) RenderPartial(w http.ResponseWriter, r *http.Request, name string, data any)

RenderPartial executes a single named template (e.g. "partials/flash") without any layout.

func (*Templates) RenderWith

func (t *Templates) RenderWith(w http.ResponseWriter, r *http.Request, status int, layout, page string, data any)

RenderWith renders page with the given status inside layouts/<layout>.html, or only its "content" template when layout is "" or for HTMX requests. Errors are logged and answered with a plain 500.

func (*Templates) SetSidebarBrandResolver

func (t *Templates) SetSidebarBrandResolver(resolve func(context.Context) SidebarBrand)

SetSidebarBrandResolver connects the site's identity service during module registration.

Jump to

Keyboard shortcuts

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