edge

package
v0.0.2 Latest Latest
Warning

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

Go to latest
Published: Nov 11, 2026 License: AGPL-3.0 Imports: 15 Imported by: 0

Documentation

Overview

Package edge holds the HTTP edge code shared by the frontend and the backend process: working out who the visitor is behind a reverse proxy, the PROXY protocol, and request limiting. It must not import the store, so the frontend can use it without database access.

Index

Constants

View Source
const ContentSecurityPolicy = "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; " +
	"img-src 'self' data:; frame-ancestors 'none'; base-uri 'none'; form-action 'self'; object-src 'none'"

contentSecurityPolicy only allows our own scripts. Styles may be inline because a few templates size inputs with a style attribute and htmx injects its indicator rules.

Variables

This section is empty.

Functions

func ClientIP

func ClientIP(r *http.Request) string

ClientIP is the address of the peer, after proxy headers were settled by Proxy.Headers.

func Compress

func Compress(next http.Handler) http.Handler

Compress gzips text responses for clients that accept it.

func ExternalURL

func ExternalURL(r *http.Request, path string) string

ExternalURL builds an absolute URL for this server as seen by the browser. The configured base URL wins; without one the request's own host is used, which a proxy must pass on unchanged.

func IPKey

func IPKey(r *http.Request) string

IPKey identifies the client for rate limiting. An IPv6 client is counted by its /64, because a single subscriber usually controls the whole prefix and could otherwise rotate addresses freely.

func IsSecure

func IsSecure(r *http.Request) bool

IsSecure reports whether the visitor reached the app over HTTPS: directly, through a trusted proxy that said so, or because the configured base URL is https.

func LastForwarded

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

LastForwarded returns the last value of a forwarding header: the one the nearest proxy added.

func LimitBody

func LimitBody(next http.Handler) http.Handler

LimitBody rejects oversized form posts before any handler parses them, which would otherwise buffer up to 32 MiB of multipart data in memory and temp files for anonymous clients.

func LogRequests

func LogRequests(next http.Handler) http.Handler

LogRequests logs every request at debug level.

func NoStore

func NoStore(h http.Header)

NoStore tells every cache that the response is for one visitor only. Cache-Control is what browsers and well-behaved proxies read. CDN-Cache-Control (RFC 9213, read by Cloudflare, Fastly and Akamai) and Surrogate-Control speak to CDNs directly, and a CDN rule such as "cache everything" may override Cache-Control but not these.

func ParseBaseURL

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

ParseBaseURL checks the public address of the app, such as https://dexa.example.com. It must be an origin: the app serves from the root of its host and cannot live under a sub-path.

func ParseTrustedProxies

func ParseTrustedProxies(items []string) ([]netip.Prefix, error)

ParseTrustedProxies turns addresses, CIDR ranges and the keywords "loopback" and "private" into prefixes. "private" covers loopback, the RFC 1918 ranges and IPv6 unique local addresses.

func ProxyListener

func ProxyListener(ln net.Listener, policy func() (enabled bool, trusted []netip.Prefix)) net.Listener

ProxyListener applies the PROXY protocol policy to a listener. policy is asked for every new connection and reports whether the protocol is enabled and which peers may send the header. Wrap a listener once at start; it costs nothing while the setting is off. The HTTPS listener uses it too: the header comes before the TLS handshake.

func SecureHeaders

func SecureHeaders(next http.Handler) http.Handler

SecureHeaders adds the browser hardening headers to every response and keeps pages that can hold personal data (or a freshly minted token) out of shared and back/forward caches.

func Trusted

func Trusted(trusted []netip.Prefix, a netip.Addr) bool

Trusted reports whether a is one of the trusted proxies.

func ValidHost

func ValidHost(h string) bool

ValidHost reports whether h is plausible as a Host header value.

func WithBaseURL

func WithBaseURL(ctx context.Context, base *url.URL) context.Context

WithBaseURL records the configured public address, which wins over the request's own host.

func WithSecure

func WithSecure(ctx context.Context, secure bool) context.Context

WithSecure records whether the visitor reached the app over HTTPS.

Types

type Limiter

type Limiter struct {
	// MaxInFlight is the number of requests served at once. 0 means no cap.
	MaxInFlight int
	// MaxQueue is how many more requests may wait for a slot. Beyond that they are refused at once.
	MaxQueue int
	// MaxWait bounds how long a queued request waits for a slot.
	MaxWait time.Duration
	// Rate and Burst configure the per-key token bucket (requests per second, bucket size).
	// Rate 0 turns it off.
	Rate  float64
	Burst int
	// Key names the bucket a request belongs to. nil means IPKey.
	Key func(*http.Request) string
	// Skip exempts a request (cheap static files, health checks).
	Skip func(*http.Request) bool
	// contains filtered or unexported fields
}

Limiter sheds load before it reaches the handlers behind it. It combines

  • a cap on requests being served at once, with a bounded queue and a bounded wait, so a flood is answered with a cheap 503 instead of piling up work; and
  • an optional token bucket per key (normally the client address), so one client cannot take the whole budget.

The zero values of the optional fields turn the matching part off.

func (*Limiter) Acquire

func (l *Limiter) Acquire(ctx context.Context) (release func(), ok bool)

Acquire reserves a slot. It reports false when the request must be refused: the queue is full or the wait ran out. The returned release must be called when the request is done.

func (*Limiter) Allow

func (l *Limiter) Allow(key string) bool

Allow takes one token from key's bucket and reports whether there was one.

func (*Limiter) Middleware

func (l *Limiter) Middleware(next http.Handler) http.Handler

Middleware applies the limiter. Refused requests get 429 (over the per-key rate) or 503 (the server is at capacity), both with Retry-After, before any handler work is done.

type Proxy

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

Proxy settles who the visitor is behind a reverse proxy. The zero value is ready to use.

func (*Proxy) Headers

func (p *Proxy) Headers(settings Settings, next http.Handler) http.Handler

Headers settles who the visitor is and how they reached us, once per request, so the rest of the app never reads the forwarding headers itself.

type Settings

type Settings func() (base *url.URL, trusted []netip.Prefix)

Settings returns the configured public address (nil if none) and the trusted proxies. It is asked on every request, so a change on the Admin page applies at once.

Jump to

Keyboard shortcuts

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