labkit/v2/httpserver: Investigate and standardize HTTP router for LabKit v2 httpserver

Context

MR gitlab-org/labkit!339 (merged) proposes a v2/httpserver package with an abstract Router interface backed by Go 1.22+ stdlib net/http.ServeMux. The interface is designed so the underlying router can be swapped without breaking consumers (design requirements). Before v2/httpserver lands, we need to decide which router to standardize on as the default for all GitLab Go services adopting LabKit v2.

Problem

Without a standard recommendation, each service will pick its own HTTP framework, fragmenting the ecosystem and complicating shared middleware, documentation, and onboarding.

Evaluation attributes

When comparing candidates, consider:

  • stdlib compatibility: Does it use native http.Handler / http.HandlerFunc?
  • Middleware pattern: Does it support func(http.Handler) http.Handler natively, or require adapters?
  • Route grouping: Can routes be grouped with shared middleware (e.g. auth group, API group)?
  • Sub-routers / mounting: Can independent route trees be composed (e.g. Mount("/api", apiRouter))?
  • Path parameter ergonomics: How are URL parameters extracted and accessed?
  • Dependency footprint: Number and weight of transitive dependencies
  • Maintenance posture: Release cadence, response to CVEs, bus factor
  • Ecosystem adoption: Usage across the Go community, production references
  • Migration cost: Effort to adopt from the current stdlib default in the proposed v2/httpserver
  • Performance: Routing throughput for realistic workloads (not micro-benchmarks)

Candidates

Go stdlib net/http.ServeMux (Go 1.22+)

The default in the proposed v2/httpserver (!339). Zero dependencies. Supports method-based routing and path parameters since Go 1.22.

Pros:

  • Zero external dependencies
  • Maintained by the Go team, guaranteed long-term support
  • Native http.Handler, no adapters needed
  • No learning curve for Go developers

Cons:

  • No route grouping or sub-router support (must be built manually)
  • No middleware chaining API
  • Nested ServeMux has unexpected pattern matching behavior
  • Pattern conflicts cause panics rather than errors
  • LabKit would need to build grouping/chaining abstractions on top

chi (github.com/go-chi/chi/v5)

~21,800 GitHub stars. 100% http.Handler compatible. Zero external dependencies.

Pros:

  • Native http.Handler and func(http.Handler) http.Handler middleware
  • Built-in route grouping (r.Group, r.Route) and sub-routers (r.Mount)
  • Zero external dependencies, core router is <1000 LOC
  • Rich built-in middleware package (logger, recoverer, timeout, compress)
  • Battle-tested at Cloudflare, Heroku, 99Designs
  • Design philosophy (stdlib-first, composable) aligns with LabKit's goals

Cons:

  • Release cadence has slowed (mature, but fewer releases)
  • Historical vulnerability in RedirectSlashes middleware (fixed in v5)
  • Third-party dependency (even if lightweight)

httprouter (github.com/julienschmidt/httprouter)

~17,100 GitHub stars. Radix-tree based, extremely fast.

Pros:

  • Very fast routing performance
  • Zero dependencies

Cons:

  • Custom handler signature: func(w, r, httprouter.Params), not http.Handler
  • No middleware support, no route grouping, no sub-routers
  • Effectively unmaintained (last release ~2020, v2 stalled)
  • Go 1.22 stdlib now covers its core value proposition

Fiber (github.com/gofiber/fiber)

~39,300 GitHub stars. Express.js-inspired, built on fasthttp.

Pros:

  • Very high raw throughput
  • Active development, large community
  • Route grouping and sub-routers

Cons:

  • Built on fasthttp, NOT net/http. Fundamentally incompatible
  • Custom fiber.Ctx handler signature
  • net/http middleware requires lossy adaptor with known streaming bugs
  • Pools/reuses request contexts (values must not escape handler)
  • Heavy dependency footprint

Echo (github.com/labstack/echo)

~32,200 GitHub stars. Full-featured framework, v5 released.

Pros:

  • Active maintenance
  • Route grouping, sub-routers, built-in middleware suite
  • Echo instance implements http.Handler via ServeHTTP

Cons:

  • Custom echo.Context handler signature
  • stdlib middleware requires echo.WrapMiddleware adapter
  • Framework wants to own request lifecycle, conflicts with LabKit's wrapper role
  • External dependencies (golang.org/x/crypto, golang.org/x/net)

Gin (github.com/gin-gonic/gin)

~88,200 GitHub stars. Most popular Go framework.

Pros:

  • Largest ecosystem and community
  • Route grouping, JSON binding, validation

Cons:

  • Custom *gin.Context handler signature, not http.Handler compatible
  • stdlib middleware requires wrapping
  • Full framework, not a router (adds weight LabKit does not need)
  • Built on httprouter (unmaintained) as transitive dependency
  • Historical maintenance gaps

Deliverable

A pro/con recommendation with a decision on which router becomes the default Router implementation in v2/httpserver. The chosen router should be adoptable by all GitLab Go services (go-service-template, Artifact Registry, and future services).

Edited by Doug Barrett