Documentation
¶
Overview ¶
Package web is the HTTP driving adapter: routes, middleware, handlers and server lifecycle.
Index ¶
- Variables
- func Chain(h http.Handler, mws ...Middleware) http.Handler
- func Handler(cfg config.Config, log *slog.Logger, routes http.Handler) (http.Handler, error)
- func RequestIDFrom(ctx context.Context) string
- func UserFrom(ctx context.Context) (domain.User, bool)
- type App
- type Middleware
- func AccessLog(log *slog.Logger) Middleware
- func CrossOrigin(publicOrigin string) (Middleware, error)
- func Deadlines(body, response time.Duration) Middleware
- func MaxBody(limit int64) Middleware
- func Recover(log *slog.Logger) Middleware
- func RequestID() Middleware
- func SecurityHeaders(hsts bool) Middleware
- type Options
- type ReadinessCheck
- type Renderer
- type Server
- type Services
- type Static
Constants ¶
This section is empty.
Variables ¶
var ErrUnknownPage = errors.New("unknown page")
ErrUnknownPage is returned when no template exists for a requested page.
Functions ¶
func Chain ¶
func Chain(h http.Handler, mws ...Middleware) http.Handler
Chain applies mws to h. The first middleware is the outermost.
func Handler ¶
Handler wraps routes with the standard middleware, outermost first: request ID, access log, panic recovery, security headers, deadlines, the cross-origin check (CSRF protection), body size limit.
The access log sits outside recovery so it records the 500 that recovery writes. The security headers sit outside the two layers that refuse a request, the cross-origin check and the body limit, so a refusal carries them. The deadlines sit inside the security headers and outside those two layers, so a client cannot hold the connection open by being refused slowly; they also have to see the body before the body limit wraps it, because a wrapped body no longer says that a request has none. The cross-origin check comes before the body limit, so a request from another site is refused as such whatever it carries. The order of recovery, security headers and deadlines among themselves changes nothing that can be seen.
Pattern: Ordered Middleware Chain
func RequestIDFrom ¶
RequestIDFrom returns the request ID stored by RequestID, or "".
Types ¶
type App ¶
type App struct {
// contains filtered or unexported fields
}
App owns the shared dependencies of the handlers and builds the route table. It is the only place that knows about both HTTP and the use cases; wiring the use cases to their adapters happens outside, in internal/app.
func NewApp ¶
NewApp parses templates and loads static files from files, which must hold templates/ and static/ (see package assets).
func (*App) EndStreams ¶
func (a *App) EndStreams()
EndStreams ends every running reply: each is saved as stopped with the text so far, and its browser is told. Streams that start afterwards are sent away to try again, so a reply is never stopped before it began. Call it when the server starts shutting down.
type Middleware ¶
Middleware wraps a handler.
func AccessLog ¶
func AccessLog(log *slog.Logger) Middleware
AccessLog logs one line per request. Probes and static assets log at debug.
The line has seven named attributes and no more: request_id, method, path, status, bytes, duration and remote. The path is logged without its query, and no header, cookie or form value is logged, except the X-Request-ID that RequestID took for the request id.
Pattern: Log Allow-List
func CrossOrigin ¶
func CrossOrigin(publicOrigin string) (Middleware, error)
CrossOrigin rejects unsafe cross-origin browser requests (CSRF) using Sec-Fetch-Site and Origin. publicOrigin is trusted so the app keeps working behind a reverse proxy that rewrites Host.
func Deadlines ¶
func Deadlines(body, response time.Duration) Middleware
Deadlines bounds how long a client can hold a connection by being slow. Once the headers are in, net/http puts no limit on the rest: a client could send a body one byte a minute, or ask for a page and never read it, and each would keep a goroutine and a file descriptor for as long as it liked.
A body has body to arrive. net/http lifts that deadline itself when the body has been read, so a handler may take as long as it needs after that, and the request is not cancelled behind its back; a test holds it to that. A response has response to be taken; the event stream sets its own deadline for each write, after this one. A global ReadTimeout would do neither: it would cut every event stream.
Zero turns a limit off. Only code that builds its own config.HTTP or calls Deadlines itself can pass one: config.Load refuses zero for both timeouts, so a server started from the settings has both limits.
Pattern: Layered Deadlines
func MaxBody ¶
func MaxBody(limit int64) Middleware
MaxBody caps request bodies. A declared length over the limit is refused immediately; chunked bodies fail with *http.MaxBytesError while being read (see writeBodyError).
func Recover ¶
func Recover(log *slog.Logger) Middleware
Recover turns a handler panic into a logged 500 so one bad request cannot take the server down. http.ErrAbortHandler is passed through, as net/http documents.
func RequestID ¶
func RequestID() Middleware
RequestID keeps a well-formed incoming X-Request-ID or generates one, stores it in the request context, and echoes it on the response.
func SecurityHeaders ¶
func SecurityHeaders(hsts bool) Middleware
SecurityHeaders sets the standard browser hardening headers on every response. hsts adds Strict-Transport-Security and should be on only when the app is served over https.
Every answer is "no-store" unless its route says otherwise (static files, the event stream): a page holds one person's conversations, and the next person at the same computer must not get it back with the Back button.
type Options ¶
type Options struct {
// SecureCookies adds the Secure attribute to the session cookie (https).
SecureCookies bool
// FlushInterval is the shortest time between streamed updates.
FlushInterval time.Duration
// RateLimitRPS and RateLimitBurst set the token bucket for sending messages
// (per user) and signing in (per client address). Zero in either turns
// limiting off. Only code that builds Options itself can pass one:
// config.Load refuses zero for both settings, so a server started from the
// settings always limits.
RateLimitRPS float64
RateLimitBurst int
// ReadinessChecks are what GET /readyz looks at, in the order they are
// reported.
ReadinessChecks []ReadinessCheck
}
Options are the settings the handlers need.
type ReadinessCheck ¶
ReadinessCheck is one dependency /readyz looks at.
type Renderer ¶
type Renderer struct {
// contains filtered or unexported fields
}
Renderer renders html/template pages. Each page is the base layout, all partials, and one page file; pages define "page:title" and "page:main". Every request for a page gets the whole document: what htmx swaps in by itself are the partials (see Block).
func NewRenderer ¶
NewRenderer parses every page under templates/pages in fsys at startup, so a broken template fails the boot instead of a request.
type Server ¶
type Server struct {
// contains filtered or unexported fields
}
Server runs the HTTP handler and shuts it down gracefully.
It sets no global WriteTimeout on purpose: a server-wide write deadline would cut long-lived SSE streams. Streams manage their own deadlines.
func NewServer ¶
NewServer wires h into a server that honors cfg.App.ShutdownGrace. Each onShutdown function runs as shutdown begins, before the server waits for requests to finish: that is the moment to end streams (see App.EndStreams), which would otherwise keep the server waiting until the grace period is up.
Pattern: Shutdown Hook Before Wait
type Static ¶
type Static struct {
// contains filtered or unexported fields
}
Static serves the embedded static/ directory from memory. Vendored files live in version-named directories, so they are cached as immutable; our own files are revalidated with an ETag on every load. Text files are sent gzipped to a browser that takes it: a first load of about 400 KB of text is under 100 KB.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package render turns an agent's Markdown into safe HTML.
|
Package render turns an agent's Markdown into safe HTML. |
|
cmd/chromacss
command
Command chromacss writes the syntax-highlighting stylesheet.
|
Command chromacss writes the syntax-highlighting stylesheet. |