Skip to content
→ All work

Case study 19 / 26

Spindrift

An HTTP/1.1 server with its own RFC 9112 protocol stack, a pipeline language instead of a config file, and reloads that never drop a request.

Status
Research
Period
2026
Domain
web · cloud
Language
Go
Last push
05 SEP 2026
License
MIT
Source of claims
README.md, docs/CONFIG.md, ARCHITECTURE.md

Requests are parsed by Spindrift's own strict HTTP/1.1 implementation — not net/http — then flow through a small declarative pipeline: middleware, routing, static files, reverse proxy, SSE streaming, rate limits, conditions and templates. Go standard library only.

01/The problem

Most servers are configured, and hide the request pipeline behind a large configuration surface and a larger codebase. Frameworks expose a pipeline but tie it to a runtime and a rebuild per change.

02/The system

Requests are parsed by Spindrift's own strict HTTP/1.1 implementation — not net/http — then flow through a small declarative pipeline: middleware, routing, static files, reverse proxy, SSE streaming, rate limits, conditions and templates. Go standard library only.

03/Implementation

  1. 01Request-line and header parsing with byte budgets, Content-Length and chunked bodies with trailers, pipelining, keep-alive and 100-continue.
  2. 02Strict rejection of bare LF, obs-fold, conflicting lengths and other malformed framing with the right status.
  3. 03A pipeline language: route and middleware rules, path params, conditions over request and environment, and templates.
  4. 04Handlers for respond, redirect, traversal-safe static files, streaming reverse proxy and chunked SSE; middleware for headers, auth gates, per-client token-bucket rate limits and CORS.
  5. 05Prometheus metrics, JSON access logs, atomic hot reload on SIGHUP and graceful drain on SIGINT/SIGTERM.

04/Engineering

Reloads keep in-flight requests on the old pipeline

The new pipeline is swapped atomically for the next request on every connection, and rate-limit buckets survive reloads for unchanged rules.

Drain that actually drains

Idle keep-alive connections close immediately; active responses — even long streams — finish before exit. Tested with a stream in flight.

Secrets stay out of the config

Conditions can read the environment, so an auth gate is one line and the token never lives in the file.

05/Interface

No product screenshots are published for this project. The visual above is a code-driven representation of how it behaves, built from the repository source — not a screenshot.

06/Tech stack

  • Go
  • Standard library only
  • RFC 9112
  • Prometheus metrics
  • Docker

07/Result

Verified outcomes

  • 30 protocol tests plus a load test (32 keep-alive clients × 200 requests) asserting zero failures and connection reuse; race detector clean.
  • Reported in the README (one run, loopback): ~75k requests/s with p99 ≈ 3.6 ms.

Known limitations

  • HTTP/1.1 only: no TLS termination, HTTP/2 or WebSocket upgrade; rate limiting is per process.

08/Links

Next case study

Emberline →