Scaffolds the server module and everything Phase 0 of PLAN.md calls for: a binary that boots, serves its discovery endpoints and nothing else yet. - config: one YAML file plus per-field WARP_* environment overrides, unknown fields rejected, validation that reports every problem at once - logging and metrics: slog, a Prometheus registry, per-route HTTP instrumentation labelled by pattern rather than path - httputil: the Matrix error envelope, error-returning handlers, CORS, request logging, panic recovery, and a router over http.ServeMux - endpoints: /_matrix/client/versions, /.well-known/matrix/client, /.well-known/matrix/server (gated), /healthz, /metrics, and M_UNRECOGNIZED for everything else - storage: driver registry, shared database/sql base, checksummed migration runner, sqlite and postgres drivers, pocketbase stub - settings: server_settings table behind a cached service, so the federation and registration switches change without a restart - federation: the gate that makes federation a runtime mode - cmd/warp: serve, migrate, generate-keys, version - Makefile, golangci-lint v2, GitHub Actions CI (Linux and Windows tests, race on Linux, storage tests against PostgreSQL, lint, boot smoke test), Dockerfile, example config and docs Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
51 lines
1.6 KiB
Go
51 lines
1.6 KiB
Go
package httputil
|
|
|
|
import (
|
|
"net/http"
|
|
)
|
|
|
|
// Instrumenter wraps a handler with per-route instrumentation. The route is
|
|
// the ServeMux pattern the handler is registered under; it is passed
|
|
// explicitly because the pattern ServeMux records on a request is not
|
|
// visible to middleware wrapping the mux from the outside.
|
|
type Instrumenter func(route string, h http.Handler) http.Handler
|
|
|
|
// Router is a thin layer over http.ServeMux that applies an Instrumenter to
|
|
// every route and knows how to answer unrouted requests the Matrix way.
|
|
//
|
|
// Patterns use Go 1.22 syntax, method included:
|
|
//
|
|
// r.HandleFunc("GET /_matrix/client/versions", versions)
|
|
type Router struct {
|
|
mux *http.ServeMux
|
|
instrument Instrumenter
|
|
}
|
|
|
|
// NewRouter returns a router. A nil Instrumenter means no instrumentation.
|
|
func NewRouter(instrument Instrumenter) *Router {
|
|
if instrument == nil {
|
|
instrument = func(_ string, h http.Handler) http.Handler { return h }
|
|
}
|
|
mux := http.NewServeMux()
|
|
r := &Router{mux: mux, instrument: instrument}
|
|
// Anything that matches no other pattern is M_UNRECOGNIZED, per the
|
|
// Client-Server API's "Unsupported endpoints" rule.
|
|
r.Handle("/", NotFoundHandler())
|
|
return r
|
|
}
|
|
|
|
// Handle registers a handler for a pattern.
|
|
func (r *Router) Handle(pattern string, h http.Handler) {
|
|
r.mux.Handle(pattern, r.instrument(pattern, h))
|
|
}
|
|
|
|
// HandleFunc registers an error-returning handler for a pattern.
|
|
func (r *Router) HandleFunc(pattern string, h Handler) {
|
|
r.Handle(pattern, Wrap(h))
|
|
}
|
|
|
|
// ServeHTTP implements http.Handler.
|
|
func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|
r.mux.ServeHTTP(w, req)
|
|
}
|