Update documentation with server package details
All checks were successful
CI / test (push) Successful in 30s
All checks were successful
CI / test (push) Successful in 30s
Add server package description, component table, and usage example to README. Document server architecture, middleware chain, and test conventions in CLAUDE.md. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
16
CLAUDE.md
16
CLAUDE.md
@@ -13,6 +13,8 @@ go vet ./... # static analysis
|
|||||||
## Architecture
|
## Architecture
|
||||||
|
|
||||||
- **Module**: `git.codelab.vc/pkg/httpx`, Go 1.24, zero external dependencies
|
- **Module**: `git.codelab.vc/pkg/httpx`, Go 1.24, zero external dependencies
|
||||||
|
|
||||||
|
### Client
|
||||||
- **Core pattern**: middleware is `func(http.RoundTripper) http.RoundTripper`
|
- **Core pattern**: middleware is `func(http.RoundTripper) http.RoundTripper`
|
||||||
- **Chain assembly order** (client.go): Logging → User MW → Retry → CB → Balancer → Transport
|
- **Chain assembly order** (client.go): Logging → User MW → Retry → CB → Balancer → Transport
|
||||||
- Retry wraps CB+Balancer so each attempt can hit a different endpoint
|
- Retry wraps CB+Balancer so each attempt can hit a different endpoint
|
||||||
@@ -21,10 +23,20 @@ go vet ./... # static analysis
|
|||||||
- **balancer.Transport** returns `(Middleware, *Closer)` — Closer must be tracked for health checker shutdown
|
- **balancer.Transport** returns `(Middleware, *Closer)` — Closer must be tracked for health checker shutdown
|
||||||
- **Client.Close()** stops the health checker goroutine
|
- **Client.Close()** stops the health checker goroutine
|
||||||
|
|
||||||
|
### Server (`server/`)
|
||||||
|
- **Core pattern**: middleware is `func(http.Handler) http.Handler`
|
||||||
|
- **Server** wraps `http.Server` with `net.Listener`, graceful shutdown via signal handling, lifecycle hooks
|
||||||
|
- **Router** wraps `http.ServeMux` — supports groups with prefix + middleware inheritance, `Mount` for sub-handlers
|
||||||
|
- **Middleware chain** via `Chain(A, B, C)` — A outermost, C innermost (same as client side)
|
||||||
|
- **statusWriter** wraps `http.ResponseWriter` to capture status; implements `Unwrap()` for `http.ResponseController`
|
||||||
|
- **Defaults()** preset: RequestID → Recovery → Logging + production timeouts
|
||||||
|
- **HealthHandler** exposes `GET /healthz` (liveness) and `GET /readyz` (readiness with pluggable checkers)
|
||||||
|
|
||||||
## Conventions
|
## Conventions
|
||||||
|
|
||||||
- Functional options for all configuration
|
- Functional options for all configuration (client and server)
|
||||||
- Test helpers: `mockTransport(fn)` wrapping `middleware.RoundTripperFunc`
|
- Test helpers: `mockTransport(fn)` wrapping `middleware.RoundTripperFunc` (client), `httptest.NewRecorder`/`httptest.NewRequest` (server)
|
||||||
|
- Server tests use `waitForAddr(t, srv)` helper to poll until server is ready
|
||||||
- No external test frameworks — stdlib only
|
- No external test frameworks — stdlib only
|
||||||
- Thread safety required (`sync.Mutex`/`atomic`)
|
- Thread safety required (`sync.Mutex`/`atomic`)
|
||||||
- `internal/clock` for deterministic time testing
|
- `internal/clock` for deterministic time testing
|
||||||
|
|||||||
43
README.md
43
README.md
@@ -1,6 +1,6 @@
|
|||||||
# httpx
|
# httpx
|
||||||
|
|
||||||
HTTP client for Go microservices. Retry, load balancing, circuit breaking, all as `http.RoundTripper` middleware. stdlib only, zero external deps.
|
HTTP client and server toolkit for Go microservices. Client side: retry, load balancing, circuit breaking — all as `http.RoundTripper` middleware. Server side: routing, middleware (request ID, recovery, logging), health checks, graceful shutdown. stdlib only, zero external deps.
|
||||||
|
|
||||||
```
|
```
|
||||||
go get git.codelab.vc/pkg/httpx
|
go get git.codelab.vc/pkg/httpx
|
||||||
@@ -33,7 +33,9 @@ resp.JSON(&user)
|
|||||||
|
|
||||||
## Packages
|
## Packages
|
||||||
|
|
||||||
Everything is a `func(http.RoundTripper) http.RoundTripper`. Use them with `httpx.Client` or plug into a plain `http.Client`.
|
### Client
|
||||||
|
|
||||||
|
Client middleware is `func(http.RoundTripper) http.RoundTripper`. Use them with `httpx.Client` or plug into a plain `http.Client`.
|
||||||
|
|
||||||
| Package | What it does |
|
| Package | What it does |
|
||||||
|---------|-------------|
|
|---------|-------------|
|
||||||
@@ -42,6 +44,20 @@ Everything is a `func(http.RoundTripper) http.RoundTripper`. Use them with `http
|
|||||||
| `circuitbreaker` | Per-host state machine (closed/open/half-open). Stops hammering dead endpoints. |
|
| `circuitbreaker` | Per-host state machine (closed/open/half-open). Stops hammering dead endpoints. |
|
||||||
| `middleware` | Logging (slog), default headers, bearer/basic auth, panic recovery. |
|
| `middleware` | Logging (slog), default headers, bearer/basic auth, panic recovery. |
|
||||||
|
|
||||||
|
### Server
|
||||||
|
|
||||||
|
Server middleware is `func(http.Handler) http.Handler`. The `server` package provides a production-ready HTTP server.
|
||||||
|
|
||||||
|
| Component | What it does |
|
||||||
|
|-----------|-------------|
|
||||||
|
| `server.Server` | Wraps `http.Server` with graceful shutdown, signal handling, lifecycle logging. |
|
||||||
|
| `server.Router` | Lightweight wrapper around `http.ServeMux` with groups, prefix routing, sub-router mounting. |
|
||||||
|
| `server.RequestID` | Assigns/propagates `X-Request-Id` (UUID v4 via `crypto/rand`). |
|
||||||
|
| `server.Recovery` | Recovers panics, returns 500, logs stack trace. |
|
||||||
|
| `server.Logging` | Structured request logging (method, path, status, duration, request ID). |
|
||||||
|
| `server.HealthHandler` | Liveness (`/healthz`) and readiness (`/readyz`) endpoints with pluggable checkers. |
|
||||||
|
| `server.Defaults` | Production preset: RequestID → Recovery → Logging + sensible timeouts. |
|
||||||
|
|
||||||
The client assembles them in this order:
|
The client assembles them in this order:
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -90,6 +106,29 @@ httpClient := &http.Client{
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Server
|
||||||
|
|
||||||
|
```go
|
||||||
|
logger := slog.Default()
|
||||||
|
|
||||||
|
r := server.NewRouter()
|
||||||
|
r.HandleFunc("GET /hello", func(w http.ResponseWriter, _ *http.Request) {
|
||||||
|
w.Write([]byte("world"))
|
||||||
|
})
|
||||||
|
|
||||||
|
// Groups with middleware
|
||||||
|
api := r.Group("/api/v1", authMiddleware)
|
||||||
|
api.HandleFunc("GET /users/{id}", getUser)
|
||||||
|
|
||||||
|
// Health checks
|
||||||
|
r.Mount("/", server.HealthHandler(
|
||||||
|
func() error { return db.Ping() },
|
||||||
|
))
|
||||||
|
|
||||||
|
srv := server.New(r, server.Defaults(logger)...)
|
||||||
|
log.Fatal(srv.ListenAndServe()) // graceful shutdown on SIGINT/SIGTERM
|
||||||
|
```
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
Go 1.24+, stdlib only.
|
Go 1.24+, stdlib only.
|
||||||
|
|||||||
Reference in New Issue
Block a user