This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Elton is a Go web framework (module github.com/vicanso/elton/v2, requires Go 1.24+) inspired by koa/echo. Two packages: the core framework at the repo root, and middleware/ with all built-in middleware. Documentation is primarily in Chinese; commit messages follow conventional commits in lowercase English (feat:, fix:, refactor!: for breaking changes).
make test # go test -race -cover ./...
go test -race -run TestProxy ./middleware/ # single test
make bench # benchmarks
make lint # golangci-lint run
go test -fuzz=FuzzNewCacheResponse -fuzztime 10s ./middleware/ # fuzz a binary decoderAlways run tests with -race (CI does). Verify gofmt -l . is clean before finishing.
Everything is a Handler func(*Context) error. At route registration, global middleware (e.Use at that moment) is snapshotted with route handlers into a fixed chain; each handler calls c.Next() (stable boundNext → chainNext, no per-request closure). Request flows inward, response outward. Errors return up the chain instead of writing responses directly. Context.Committed short-circuits: once true, remaining processing is skipped and the response is considered written. Call Use before registering routes that need those middlewares.
Response contract: handlers set c.Body (any) and/or c.StatusCode; a responder middleware (e.g. middleware.NewDefaultResponder) converts Body to c.BodyBuffer (*bytes.Buffer), which the framework writes out in elton.go after the chain returns. If Body is an io.Reader it is streamed via Pipe; if it implements io.Closer and is not piped (error path, or BodyBuffer set), the framework closes it (Context.closeReaderBody).
Key core files: elton.go (Elton instance, route registration, lifecycle ListenAndServe/Shutdown/GracefulClose, events OnBefore/OnDone/OnError, Context pooling via sync.Pool), context.go (Context; generic GetContextValue[T]; signed cookies via keygrip), route.go (path normalize, param names, edgeWriter for single ServeMux match + custom 404/405), fresh.go (zero-alloc HTTP freshness check). Routing uses stdlib http.ServeMux (Go 1.22+ patterns: {name}, {name...}, method match); c.Param / RouteParams are filled from Request.PathValue. ServeHTTP calls mux.ServeHTTP once; elton routes markHandled so mux-internal 404/405 can be replaced without a second match.
- Constructor pattern:
New<Name>(Config) elton.Handler;NewDefault<Name>()means zero-config,NewFS.../other prefixes mean a specific variant. Config structs embed aSkipper elton.Skipper; usegetSkipperfrommiddleware/helper.go. - Middleware config validation errors panic at construction time (routes are registered before serving); request-time failures return errors.
- Errors use
github.com/vicanso/hesv1.0.0+. Treat*hes.Erroras immutable: never mutate fields afterhes.Wrap(err)(without opts it returns the original pointer from the chain); use options (hes.WithStatus/WithCategory/WithException/WithCause) orwrapAsHesError(err, category)fromhelper.go.hes.Wrapdefaults non-hes errors to 500 +Exception=true; expected errors (e.g. auth failure) should usehes.Newinstead. - Naming trap:
Context.GetHeader/SetHeaderoperate on the response; useGetRequestHeader/SetRequestHeaderfor the request (opposite of gin).
middleware/cache.go encodes cached responses as [status(1)][createdAt(4)][statusCode(2)][headerLen(4)][headers][compression(1)][body] with a fetch/hit/hit-for-pass state machine; middleware/http_header.go compact-encodes headers using a short-header index dictionary. Decoders must tolerate corrupted/truncated input without panicking — extend middleware/fuzz_test.go when touching them.
Table-driven with testify/assert, using httptest. Several tests assert exact hes.Error string formats (statusCode=..., category=..., message=..., exception=true), so error-construction changes usually require test updates.
Any breaking API change must be recorded in docs/migration-v2.md (rename tables, behavior changes, v1 defects fixed). docs/introduction.md quotes middleware source code — keep those snippets in sync when changing responder.go/error.go.