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.Handlernatively, 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
ServeMuxhas 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.Handlerandfunc(http.Handler) http.Handlermiddleware - 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), nothttp.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.Ctxhandler signature -
net/httpmiddleware 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.HandlerviaServeHTTP
Cons:
- Custom
echo.Contexthandler signature - stdlib middleware requires
echo.WrapMiddlewareadapter - 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.Contexthandler signature, nothttp.Handlercompatible - 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).
Related
- httpserver MR: gitlab-org/labkit!339 (merged)
- httpserver design requirements: #4283 (closed)
- Chi discussion issue: #4311 (closed)
- Parent epic: gitlab-org/quality&360
- Artifact Registry PoC feedback: gitlab-org/gitlab#590332 (comment 3128897716)