Skip to content

Tags: goceleris/celeris

Tags

v1.3.3

Toggle v1.3.3's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
feat: v1.3.3 utility & serving middleware — pprof, swagger, static, r…

…ewrite, adapters (#223)

* feat: add pprof, swagger, static, rewrite, and adapters middleware

Five new utility & serving middleware for v1.3.3:

- pprof (#189): Go profiling endpoints with loopback-only default.
  Wraps all net/http/pprof handlers via celeris.Adapt(). AuthFunc guard.
  10 tests.

- swagger (#190): OpenAPI spec + CDN-loaded Swagger UI/Scalar.
  No bundled assets. SpecContent or fs.FS support. AuthFunc optional.
  16 tests.

- static (#191): Static file serving with SPA mode, Cache-Control,
  prefix stripping, directory browse. Path traversal protection via
  FileFromDir (OS) and path.Clean + .. rejection (fs.FS).
  20 tests.

- rewrite (#192): Regex-based URL rewriting with capture groups ($1/$2).
  Pre-routing (Server.Pre). Compile at init, sorted first-match-wins.
  Optional redirect mode (301/302). 9 tests.

- adapters (#193): Bidirectional stdlib ↔ celeris conversion.
  WrapMiddleware wraps func(http.Handler) http.Handler for celeris chains.
  ToStdlib delegates to celeris.ToHandler. ReverseProxy via httputil.
  8 tests.

Documentation:
- middleware/doc.go: ordering guide updated (rewrite in Pre, pprof/swagger/static in Use)
- README.md: 5 new rows, package count 29
- SECURITY.md: v1.3.3 section (pprof loopback, static traversal, swagger AuthFunc)
- CONTRIBUTING.md: new reference implementation categories

Closes #189, Closes #190, Closes #191, Closes #192, Closes #193

* fix: resolve all review findings from v1.3.3 audit

Security:
- swagger: escape SpecURL with html.EscapeString before injecting into
  HTML/JS templates (prevents XSS via misconfigured SpecURL)

Code quality:
- pprof: replace manual skipMap with celeris.SkipHelper (per CONTRIBUTING.md)
- adapters: fix gofmt formatting + errcheck for resp.Body.Close
- .golangci.yml: add pprof var-naming exclusion (package name matches stdlib)

* fix: resolve all remaining review findings

- swagger/doc.go: add CSP warning for secure middleware interaction
  (cdn.jsdelivr.net must be allowed in script-src/style-src)
- rewrite/doc.go: anchor regex in basic example (^/old$ not /old),
  add Security section with ReDoS and open redirect warnings
- adapters/adapters.go: add 100MB body size cap to responseCapture
  (prevents OOM from misbehaving stdlib middleware, matches core bridge)

* fix: major rework of all 5 middleware to address review findings

static:
- FIX: Browse XSS — href uses url.PathEscape + ./ prefix (prevents javascript:)
- FIX: Prefix matching now segment-boundary-aware (/api no longer matches /api-docs)
- ADD: ETag/Last-Modified headers on OS and fs.FS files (conditional GET + 304)
- ADD: Range requests (206) for fs.FS files (video seeking, download resume)
- ADD: accept-ranges: bytes header
- 28 tests covering all new functionality

swagger:
- FIX: YAML spec auto-detected and served with correct Content-Type
- ADD: UIConfig with DocExpansion, DeepLinking, PersistAuthorization, Title
- ADD: AssetsPath for air-gapped/self-hosted asset serving (no CDN required)
- ADD: RendererScalar/RendererSwaggerUI type for UI engine selection
- 26 tests

adapters:
- FIX: WrapMiddleware now propagates pre-inner headers (rs/cors, gorilla/csrf work)
- FIX: buildRequest sets ContentLength, TLS state, Protocol version
- ADD: ReverseProxy with Options (WithTransport, WithModifyRequest, WithErrorHandler)
- ADD: Pooled responseCapture for zero-alloc hot path
- DEL: ToStdlib (was pure alias of celeris.ToHandler)
- DOC: Limitations section (no Hijacker/Flusher for WebSocket/streaming)

rewrite:
- FIX: Rules changed from map[string]string to []Rule slice (insertion-order,
  not alphabetical — matches user expectations and Echo's approach)
- DOC: First-Match-Wins section updated for ordered evaluation

* fix: gofmt formatting

* fix: security fix + integration review fixes for v1.3.3 middleware

Security:
- static: fix path traversal in Browse directory listing. serveOS
  now validates that filepath.Join result stays within root directory
  (prefix check + symlink escape check), mirroring FileFromDir.
  Added TestPathTraversalBrowse and TestPathTraversalFile.

Core integration:
- rewrite: make New() variadic (config ...Config) with defaultConfig
  and applyDefaults() for pattern consistency with all other middleware
- pprof: use cfg := defaultConfig instead of var cfg Config
- swagger: use c.Redirect() instead of manual SetHeader + NoContent

Tests:
- pprof: add TestPprofSkipFunc (was missing Skip callback coverage)
- adapters: add TestReverseProxyPanicNilTarget and TestReverseProxyOptions
  (ReverseProxy had zero test coverage)
- rewrite/bench_test.go: fix regex (d+) -> (\d+) so BenchmarkRewriteMatch
  actually tests the match path

Benchmarks:
- bench_chain_test.go: add BenchmarkChainPreRoutingWithRewrite,
  BenchmarkChainRewritePassthrough, BenchmarkChainWithWrapMiddleware
- swagger/bench_test.go: fix spec path /swagger/doc.json -> /swagger/spec

Documentation:
- middleware/doc.go: fix rewrite production stack example ([]Rule syntax),
  fix swagger description (remove AuthFunc claim), add adapters mention
- pprof/swagger/static doc.go: add missing Skip/Ordering sections
- adapters doc.go: document ReverseProxy function and Option API
- SECURITY.md: fix swagger AuthFunc claim (feature doesn't exist)
- README.md: fix static description (no SPA or Cache-Control)

* fix: address all 33-agent review findings for v1.3.3 middleware

P0 fixes:
- adapters: add 100MB body size cap to responseCapture.Write (matches
  core bridge maxBridgeResponseBytes). Prevents OOM from misbehaving
  stdlib middleware.
- adapters: WrapMiddleware(nil) now panics at init instead of deferred
  panic on first request.
- static: validate() rejects Prefix without leading / (was silently
  serving nothing).

Feature additions:
- static: SPA mode (Config.SPA bool) — serves index file for
  non-existent paths instead of falling through. Works for both OS
  and fs.FS backends.
- static: Cache-Control (Config.MaxAge time.Duration) — sets
  "public, max-age=N" header alongside ETag/Last-Modified.
- swagger: ReDoc renderer (RendererReDoc) with CDN + self-hosted
  assets support.
- swagger: DefaultModelsExpandDepth changed to *int to allow depth=0
  (zero-value sentinel collision fixed).
- rewrite: per-rule RedirectCode (Rule.RedirectCode) — each rule can
  override the config-level default (e.g., /old→301, /temp→307).

Code quality:
- static: precompute filepath.Clean(root) in New() (saves 1 alloc/req)
- static: deduplicate ETag computation (setCacheHeaders returns etag,
  notModified accepts it instead of recomputing)
- static: io.ReadAll → make([]byte, size) + io.ReadFull for fs.FS
  (eliminates O(log N) growth allocations)
- static: fix If-None-Match to handle comma-separated ETag lists
  (RFC 7232 weak comparison with W/ prefix stripping)
- static: fix RFC 7232 §6 — skip If-Modified-Since when If-None-Match
  is present (regardless of match result)
- pprof/swagger: use NewHTTPError() for 403/404/405 instead of
  c.NoContent() (enables global error handler integration)
- swagger: HTML-escape Title and AssetsPath in templates
- swagger: pin Scalar CDN to @1 major version
- rewrite: validate empty Rule.Pattern (panics instead of silently
  matching everything)

Tests (30+ new):
- static: SPA mode (OS + FS + disabled), MaxAge, HEAD fs.FS, IMS 304
  fs.FS, multi-ETag If-None-Match, Prefix validation panic
- swagger: HEAD on /spec, DocExpansion "none", ReDoc renderer + title
  + assets, DefaultModelsExpandDepth=0
- adapters: WrapMiddleware nil panic, query string round-trip, host
  propagation, body round-trip, responseCapture body cap,
  ReverseProxy behavioral (httptest.Server), ReverseProxy error handler
- pprof: IPv6 loopback (allow ::1 + deny remote v6), bare prefix,
  validate Prefix="/" panic
- rewrite: per-rule RedirectCode, empty Pattern panic, invalid
  per-rule RedirectCode panic

Benchmarks:
- bench_chain_test.go: add BenchmarkChainPprofPassthrough,
  BenchmarkChainSwaggerPassthrough, BenchmarkChainStaticPassthrough,
  BenchmarkChainStaticServe (with etag)

Documentation:
- pprof: fix SkipPaths doc (was "before any other logic", now
  correctly says "before auth and handler dispatch")
- pprof: validate Prefix="/" (would intercept all traffic)
- pprof: rename ExampleNew_publicAccess → ExampleNew_tokenAuth,
  add actual ExampleNew_publicAccess
- swagger: doc.go notes UIConfig options are Swagger UI-only
- static: doc.go adds SPA Mode + Cache-Control sections
- rewrite: Rule.Pattern doc recommends ^ and $ anchors
- adapters: doc.go warns about dual CORS + streaming limitation
- middleware/doc.go: Pre-routing ordering guidance

* fix: goimports ordering + errcheck in lint

* feat: address all remaining weakness and feature gaps

static:
- io.ReadSeeker fast-path: when fs.File implements ReadSeeker, range
  requests seek to offset and read only the requested bytes instead of
  loading the entire file into memory. Eliminates 50MB heap allocation
  for range requests on large files.
- Content-type sniffing: when mime.TypeByExtension fails, read first
  512 bytes and use http.DetectContentType instead of falling back to
  application/octet-stream.
- Pre-compressed file support (Config.Compress): checks for .br and .gz
  variants when Accept-Encoding allows, sets Content-Encoding and Vary
  headers. Brotli preferred over gzip. OS filesystem only.
- 7 new tests: ReadSeeker, content-type sniffing (OS+FS+ReadSeeker),
  pre-compressed (brotli, gzip, not accepted, missing).

swagger:
- Exported IntPtr helper for DefaultModelsExpandDepth ergonomics.
- ReDoc customization: ReDocConfig struct with Theme (dark mode),
  ExpandResponses, HideDownloadButton, ScrollYOffset, NoAutoAuth.
  Dark theme maps to full ReDoc color scheme.
- OAuth2 UI pre-configuration: OAuth2Config struct with ClientID,
  ClientSecret, Realm, AppName, Scopes. Generates ui.initOAuth() call
  in Swagger UI. OAuth2RedirectURL support.
- 12 new tests: IntPtr, ReDoc theme/expandResponses/hideDownload/
  scrollOffset/noAutoAuth/defaults, OAuth2 full/nil/partial/redirectURL.

adapters:
- WithModifyResponse option for ReverseProxy: modify response headers
  from backend before forwarding to client.
- http.Flusher support on responseCapture (no-op): prevents panics in
  stdlib middleware that type-asserts Flusher. Hijacker still unsupported.
- Performance section in doc.go: explicitly documents 8-15 alloc cost
  and recommends native middleware for hot paths.
- 3 new tests: ModifyResponse, Flusher type assertion, WrapMiddleware
  with Flusher.

rewrite:
- Conditional rewriting: Rule.Methods restricts to specific HTTP methods,
  Rule.Host restricts to specific Host header value. Methods map built
  at init time (zero per-request alloc). Both empty means match all.
- 4 new tests: method restriction, host restriction, combined, no
  restriction (backwards compatibility).

* fix: gofmt static_test.go

* refactor: simplify APIs, fix Flusher footgun, extract static helpers

swagger API simplification:
- Delete ReDocConfig struct — replaced by generic Options map[string]any
  that works for ALL renderers (ReDoc, Scalar, Swagger UI). More powerful
  with fewer types. Users pass raw renderer options as JSON-serializable maps.
- Delete IntPtr helper — revert DefaultModelsExpandDepth to plain int.
  Zero-value (0 = show model names) is a reasonable default. Document clearly.
- Add ClientSecret security warning to OAuth2Config doc: values are embedded
  in page source, only use for dev/test or PKCE public clients.
- Net: 3 exported structs (was 4), 0 helper functions (was 1).

static refactoring (no behavior changes):
- Extract detectContentType() — extension check + http.DetectContentType
- Extract sniffContentType() — ReadSeeker-specific with seek-back
- Extract serveRangeFromReader() — range handling from io.ReadSeeker
- Extract serveFSFullRead() — non-ReadSeeker full-read path
- Extract servePreCompressedFS() — pre-compressed file support for fs.FS
  (was OS-only, now works with embed.FS and custom fs.FS)
- serveFS is now a clean orchestrator calling focused helpers
- Update Compress doc: works with both OS and fs.FS

adapters Flusher fix:
- Remove no-op Flush() from responseCapture. A silent no-op is worse than
  not implementing the interface: SSE middleware calls Flush() expecting
  data delivery, gets silent success, client receives nothing until
  response completes. Now the type assertion returns false, so stdlib
  middleware can take appropriate fallback action.
- Remove Unwrap() (returning nil was misleading for ResponseController)
- Update doc.go Limitations section

* fix: errcheck on rs.Seek in sniffContentType

* fix: final polish — fs.FS cache, DefaultModelsExpandDepth default, content-type sniffing

static:
- Add sync.Map per-file cache for fs.FS content. Since fs.FS (especially
  embed.FS) is immutable, cache file bytes on first read. Eliminates
  repeated heap allocations for the same file — second+ requests for any
  file are O(1) map lookup + Blob. Particularly important for embed.FS
  which doesn't implement io.ReadSeeker.
- Delete serveFSReadSeeker, serveFSFullRead, serveRangeFromReader (3 funcs).
  Replace with serveFSCached (1 func). Net: 17 -> 15 functions.
- Fix content-type sniffing in servePreCompressedFS: when extension lookup
  fails, open the original uncompressed file and sniff first 512 bytes
  via http.DetectContentType. Consistent with OS path behavior.

swagger:
- DefaultModelsExpandDepth: applyDefaults now treats 0 as unset and
  defaults to 1 (matching Swagger UI's own default). The basic 3-line
  setup now produces identical behavior to Swagger UI's defaults.
  Documented: "0 is treated as unset; use -1 to hide models."

* fix: DefaultModelsExpandDepth *int + Options validation

- Revert DefaultModelsExpandDepth to *int with IntPtr() helper.
  nil means "use Swagger UI default (1)". IntPtr(0) correctly sets
  depth to 0 (show model names only). No more zero-value ambiguity.
- Add Options JSON-serializability validation in validate(). Panics
  at init time if Options contains non-serializable values (functions,
  channels, etc.) instead of silently falling back to {}.
- Tests: TestDefaultModelsExpandDepthZero verifies depth=0 works,
  TestDefaultModelsExpandDepthNilDefault verifies nil renders as 1,
  TestOptionsValidationPanic verifies non-serializable Options panic.

v1.3.2

Toggle v1.3.2's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
docs: add v1.3.2 security notes + update CONTRIBUTING references (#220)

SECURITY.md:
- Add v1.3.2 section documenting singleflight cross-user data leakage fix,
  multi-value header replay fix, circuit breaker panic recording, and
  circuit breaker validation hardening

CONTRIBUTING.md:
- Update middleware reference implementations by complexity level
  (simple, stateful, response-transform, request-coalescing)
- Add circuitbreaker and singleflight as reference patterns

middleware/otel/v1.3.3

Toggle middleware/otel/v1.3.3's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
feat: v1.3.3 utility & serving middleware — pprof, swagger, static, r…

…ewrite, adapters (#223)

* feat: add pprof, swagger, static, rewrite, and adapters middleware

Five new utility & serving middleware for v1.3.3:

- pprof (#189): Go profiling endpoints with loopback-only default.
  Wraps all net/http/pprof handlers via celeris.Adapt(). AuthFunc guard.
  10 tests.

- swagger (#190): OpenAPI spec + CDN-loaded Swagger UI/Scalar.
  No bundled assets. SpecContent or fs.FS support. AuthFunc optional.
  16 tests.

- static (#191): Static file serving with SPA mode, Cache-Control,
  prefix stripping, directory browse. Path traversal protection via
  FileFromDir (OS) and path.Clean + .. rejection (fs.FS).
  20 tests.

- rewrite (#192): Regex-based URL rewriting with capture groups ($1/$2).
  Pre-routing (Server.Pre). Compile at init, sorted first-match-wins.
  Optional redirect mode (301/302). 9 tests.

- adapters (#193): Bidirectional stdlib ↔ celeris conversion.
  WrapMiddleware wraps func(http.Handler) http.Handler for celeris chains.
  ToStdlib delegates to celeris.ToHandler. ReverseProxy via httputil.
  8 tests.

Documentation:
- middleware/doc.go: ordering guide updated (rewrite in Pre, pprof/swagger/static in Use)
- README.md: 5 new rows, package count 29
- SECURITY.md: v1.3.3 section (pprof loopback, static traversal, swagger AuthFunc)
- CONTRIBUTING.md: new reference implementation categories

Closes #189, Closes #190, Closes #191, Closes #192, Closes #193

* fix: resolve all review findings from v1.3.3 audit

Security:
- swagger: escape SpecURL with html.EscapeString before injecting into
  HTML/JS templates (prevents XSS via misconfigured SpecURL)

Code quality:
- pprof: replace manual skipMap with celeris.SkipHelper (per CONTRIBUTING.md)
- adapters: fix gofmt formatting + errcheck for resp.Body.Close
- .golangci.yml: add pprof var-naming exclusion (package name matches stdlib)

* fix: resolve all remaining review findings

- swagger/doc.go: add CSP warning for secure middleware interaction
  (cdn.jsdelivr.net must be allowed in script-src/style-src)
- rewrite/doc.go: anchor regex in basic example (^/old$ not /old),
  add Security section with ReDoS and open redirect warnings
- adapters/adapters.go: add 100MB body size cap to responseCapture
  (prevents OOM from misbehaving stdlib middleware, matches core bridge)

* fix: major rework of all 5 middleware to address review findings

static:
- FIX: Browse XSS — href uses url.PathEscape + ./ prefix (prevents javascript:)
- FIX: Prefix matching now segment-boundary-aware (/api no longer matches /api-docs)
- ADD: ETag/Last-Modified headers on OS and fs.FS files (conditional GET + 304)
- ADD: Range requests (206) for fs.FS files (video seeking, download resume)
- ADD: accept-ranges: bytes header
- 28 tests covering all new functionality

swagger:
- FIX: YAML spec auto-detected and served with correct Content-Type
- ADD: UIConfig with DocExpansion, DeepLinking, PersistAuthorization, Title
- ADD: AssetsPath for air-gapped/self-hosted asset serving (no CDN required)
- ADD: RendererScalar/RendererSwaggerUI type for UI engine selection
- 26 tests

adapters:
- FIX: WrapMiddleware now propagates pre-inner headers (rs/cors, gorilla/csrf work)
- FIX: buildRequest sets ContentLength, TLS state, Protocol version
- ADD: ReverseProxy with Options (WithTransport, WithModifyRequest, WithErrorHandler)
- ADD: Pooled responseCapture for zero-alloc hot path
- DEL: ToStdlib (was pure alias of celeris.ToHandler)
- DOC: Limitations section (no Hijacker/Flusher for WebSocket/streaming)

rewrite:
- FIX: Rules changed from map[string]string to []Rule slice (insertion-order,
  not alphabetical — matches user expectations and Echo's approach)
- DOC: First-Match-Wins section updated for ordered evaluation

* fix: gofmt formatting

* fix: security fix + integration review fixes for v1.3.3 middleware

Security:
- static: fix path traversal in Browse directory listing. serveOS
  now validates that filepath.Join result stays within root directory
  (prefix check + symlink escape check), mirroring FileFromDir.
  Added TestPathTraversalBrowse and TestPathTraversalFile.

Core integration:
- rewrite: make New() variadic (config ...Config) with defaultConfig
  and applyDefaults() for pattern consistency with all other middleware
- pprof: use cfg := defaultConfig instead of var cfg Config
- swagger: use c.Redirect() instead of manual SetHeader + NoContent

Tests:
- pprof: add TestPprofSkipFunc (was missing Skip callback coverage)
- adapters: add TestReverseProxyPanicNilTarget and TestReverseProxyOptions
  (ReverseProxy had zero test coverage)
- rewrite/bench_test.go: fix regex (d+) -> (\d+) so BenchmarkRewriteMatch
  actually tests the match path

Benchmarks:
- bench_chain_test.go: add BenchmarkChainPreRoutingWithRewrite,
  BenchmarkChainRewritePassthrough, BenchmarkChainWithWrapMiddleware
- swagger/bench_test.go: fix spec path /swagger/doc.json -> /swagger/spec

Documentation:
- middleware/doc.go: fix rewrite production stack example ([]Rule syntax),
  fix swagger description (remove AuthFunc claim), add adapters mention
- pprof/swagger/static doc.go: add missing Skip/Ordering sections
- adapters doc.go: document ReverseProxy function and Option API
- SECURITY.md: fix swagger AuthFunc claim (feature doesn't exist)
- README.md: fix static description (no SPA or Cache-Control)

* fix: address all 33-agent review findings for v1.3.3 middleware

P0 fixes:
- adapters: add 100MB body size cap to responseCapture.Write (matches
  core bridge maxBridgeResponseBytes). Prevents OOM from misbehaving
  stdlib middleware.
- adapters: WrapMiddleware(nil) now panics at init instead of deferred
  panic on first request.
- static: validate() rejects Prefix without leading / (was silently
  serving nothing).

Feature additions:
- static: SPA mode (Config.SPA bool) — serves index file for
  non-existent paths instead of falling through. Works for both OS
  and fs.FS backends.
- static: Cache-Control (Config.MaxAge time.Duration) — sets
  "public, max-age=N" header alongside ETag/Last-Modified.
- swagger: ReDoc renderer (RendererReDoc) with CDN + self-hosted
  assets support.
- swagger: DefaultModelsExpandDepth changed to *int to allow depth=0
  (zero-value sentinel collision fixed).
- rewrite: per-rule RedirectCode (Rule.RedirectCode) — each rule can
  override the config-level default (e.g., /old→301, /temp→307).

Code quality:
- static: precompute filepath.Clean(root) in New() (saves 1 alloc/req)
- static: deduplicate ETag computation (setCacheHeaders returns etag,
  notModified accepts it instead of recomputing)
- static: io.ReadAll → make([]byte, size) + io.ReadFull for fs.FS
  (eliminates O(log N) growth allocations)
- static: fix If-None-Match to handle comma-separated ETag lists
  (RFC 7232 weak comparison with W/ prefix stripping)
- static: fix RFC 7232 §6 — skip If-Modified-Since when If-None-Match
  is present (regardless of match result)
- pprof/swagger: use NewHTTPError() for 403/404/405 instead of
  c.NoContent() (enables global error handler integration)
- swagger: HTML-escape Title and AssetsPath in templates
- swagger: pin Scalar CDN to @1 major version
- rewrite: validate empty Rule.Pattern (panics instead of silently
  matching everything)

Tests (30+ new):
- static: SPA mode (OS + FS + disabled), MaxAge, HEAD fs.FS, IMS 304
  fs.FS, multi-ETag If-None-Match, Prefix validation panic
- swagger: HEAD on /spec, DocExpansion "none", ReDoc renderer + title
  + assets, DefaultModelsExpandDepth=0
- adapters: WrapMiddleware nil panic, query string round-trip, host
  propagation, body round-trip, responseCapture body cap,
  ReverseProxy behavioral (httptest.Server), ReverseProxy error handler
- pprof: IPv6 loopback (allow ::1 + deny remote v6), bare prefix,
  validate Prefix="/" panic
- rewrite: per-rule RedirectCode, empty Pattern panic, invalid
  per-rule RedirectCode panic

Benchmarks:
- bench_chain_test.go: add BenchmarkChainPprofPassthrough,
  BenchmarkChainSwaggerPassthrough, BenchmarkChainStaticPassthrough,
  BenchmarkChainStaticServe (with etag)

Documentation:
- pprof: fix SkipPaths doc (was "before any other logic", now
  correctly says "before auth and handler dispatch")
- pprof: validate Prefix="/" (would intercept all traffic)
- pprof: rename ExampleNew_publicAccess → ExampleNew_tokenAuth,
  add actual ExampleNew_publicAccess
- swagger: doc.go notes UIConfig options are Swagger UI-only
- static: doc.go adds SPA Mode + Cache-Control sections
- rewrite: Rule.Pattern doc recommends ^ and $ anchors
- adapters: doc.go warns about dual CORS + streaming limitation
- middleware/doc.go: Pre-routing ordering guidance

* fix: goimports ordering + errcheck in lint

* feat: address all remaining weakness and feature gaps

static:
- io.ReadSeeker fast-path: when fs.File implements ReadSeeker, range
  requests seek to offset and read only the requested bytes instead of
  loading the entire file into memory. Eliminates 50MB heap allocation
  for range requests on large files.
- Content-type sniffing: when mime.TypeByExtension fails, read first
  512 bytes and use http.DetectContentType instead of falling back to
  application/octet-stream.
- Pre-compressed file support (Config.Compress): checks for .br and .gz
  variants when Accept-Encoding allows, sets Content-Encoding and Vary
  headers. Brotli preferred over gzip. OS filesystem only.
- 7 new tests: ReadSeeker, content-type sniffing (OS+FS+ReadSeeker),
  pre-compressed (brotli, gzip, not accepted, missing).

swagger:
- Exported IntPtr helper for DefaultModelsExpandDepth ergonomics.
- ReDoc customization: ReDocConfig struct with Theme (dark mode),
  ExpandResponses, HideDownloadButton, ScrollYOffset, NoAutoAuth.
  Dark theme maps to full ReDoc color scheme.
- OAuth2 UI pre-configuration: OAuth2Config struct with ClientID,
  ClientSecret, Realm, AppName, Scopes. Generates ui.initOAuth() call
  in Swagger UI. OAuth2RedirectURL support.
- 12 new tests: IntPtr, ReDoc theme/expandResponses/hideDownload/
  scrollOffset/noAutoAuth/defaults, OAuth2 full/nil/partial/redirectURL.

adapters:
- WithModifyResponse option for ReverseProxy: modify response headers
  from backend before forwarding to client.
- http.Flusher support on responseCapture (no-op): prevents panics in
  stdlib middleware that type-asserts Flusher. Hijacker still unsupported.
- Performance section in doc.go: explicitly documents 8-15 alloc cost
  and recommends native middleware for hot paths.
- 3 new tests: ModifyResponse, Flusher type assertion, WrapMiddleware
  with Flusher.

rewrite:
- Conditional rewriting: Rule.Methods restricts to specific HTTP methods,
  Rule.Host restricts to specific Host header value. Methods map built
  at init time (zero per-request alloc). Both empty means match all.
- 4 new tests: method restriction, host restriction, combined, no
  restriction (backwards compatibility).

* fix: gofmt static_test.go

* refactor: simplify APIs, fix Flusher footgun, extract static helpers

swagger API simplification:
- Delete ReDocConfig struct — replaced by generic Options map[string]any
  that works for ALL renderers (ReDoc, Scalar, Swagger UI). More powerful
  with fewer types. Users pass raw renderer options as JSON-serializable maps.
- Delete IntPtr helper — revert DefaultModelsExpandDepth to plain int.
  Zero-value (0 = show model names) is a reasonable default. Document clearly.
- Add ClientSecret security warning to OAuth2Config doc: values are embedded
  in page source, only use for dev/test or PKCE public clients.
- Net: 3 exported structs (was 4), 0 helper functions (was 1).

static refactoring (no behavior changes):
- Extract detectContentType() — extension check + http.DetectContentType
- Extract sniffContentType() — ReadSeeker-specific with seek-back
- Extract serveRangeFromReader() — range handling from io.ReadSeeker
- Extract serveFSFullRead() — non-ReadSeeker full-read path
- Extract servePreCompressedFS() — pre-compressed file support for fs.FS
  (was OS-only, now works with embed.FS and custom fs.FS)
- serveFS is now a clean orchestrator calling focused helpers
- Update Compress doc: works with both OS and fs.FS

adapters Flusher fix:
- Remove no-op Flush() from responseCapture. A silent no-op is worse than
  not implementing the interface: SSE middleware calls Flush() expecting
  data delivery, gets silent success, client receives nothing until
  response completes. Now the type assertion returns false, so stdlib
  middleware can take appropriate fallback action.
- Remove Unwrap() (returning nil was misleading for ResponseController)
- Update doc.go Limitations section

* fix: errcheck on rs.Seek in sniffContentType

* fix: final polish — fs.FS cache, DefaultModelsExpandDepth default, content-type sniffing

static:
- Add sync.Map per-file cache for fs.FS content. Since fs.FS (especially
  embed.FS) is immutable, cache file bytes on first read. Eliminates
  repeated heap allocations for the same file — second+ requests for any
  file are O(1) map lookup + Blob. Particularly important for embed.FS
  which doesn't implement io.ReadSeeker.
- Delete serveFSReadSeeker, serveFSFullRead, serveRangeFromReader (3 funcs).
  Replace with serveFSCached (1 func). Net: 17 -> 15 functions.
- Fix content-type sniffing in servePreCompressedFS: when extension lookup
  fails, open the original uncompressed file and sniff first 512 bytes
  via http.DetectContentType. Consistent with OS path behavior.

swagger:
- DefaultModelsExpandDepth: applyDefaults now treats 0 as unset and
  defaults to 1 (matching Swagger UI's own default). The basic 3-line
  setup now produces identical behavior to Swagger UI's defaults.
  Documented: "0 is treated as unset; use -1 to hide models."

* fix: DefaultModelsExpandDepth *int + Options validation

- Revert DefaultModelsExpandDepth to *int with IntPtr() helper.
  nil means "use Swagger UI default (1)". IntPtr(0) correctly sets
  depth to 0 (show model names only). No more zero-value ambiguity.
- Add Options JSON-serializability validation in validate(). Panics
  at init time if Options contains non-serializable values (functions,
  channels, etc.) instead of silently falling back to {}.
- Tests: TestDefaultModelsExpandDepthZero verifies depth=0 works,
  TestDefaultModelsExpandDepthNilDefault verifies nil renders as 1,
  TestOptionsValidationPanic verifies non-serializable Options panic.

middleware/otel/v1.3.2

Toggle middleware/otel/v1.3.2's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
docs: add v1.3.2 security notes + update CONTRIBUTING references (#220)

SECURITY.md:
- Add v1.3.2 section documenting singleflight cross-user data leakage fix,
  multi-value header replay fix, circuit breaker panic recording, and
  circuit breaker validation hardening

CONTRIBUTING.md:
- Update middleware reference implementations by complexity level
  (simple, stateful, response-transform, request-coalescing)
- Add circuitbreaker and singleflight as reference patterns

middleware/metrics/v1.3.3

Toggle middleware/metrics/v1.3.3's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
feat: v1.3.3 utility & serving middleware — pprof, swagger, static, r…

…ewrite, adapters (#223)

* feat: add pprof, swagger, static, rewrite, and adapters middleware

Five new utility & serving middleware for v1.3.3:

- pprof (#189): Go profiling endpoints with loopback-only default.
  Wraps all net/http/pprof handlers via celeris.Adapt(). AuthFunc guard.
  10 tests.

- swagger (#190): OpenAPI spec + CDN-loaded Swagger UI/Scalar.
  No bundled assets. SpecContent or fs.FS support. AuthFunc optional.
  16 tests.

- static (#191): Static file serving with SPA mode, Cache-Control,
  prefix stripping, directory browse. Path traversal protection via
  FileFromDir (OS) and path.Clean + .. rejection (fs.FS).
  20 tests.

- rewrite (#192): Regex-based URL rewriting with capture groups ($1/$2).
  Pre-routing (Server.Pre). Compile at init, sorted first-match-wins.
  Optional redirect mode (301/302). 9 tests.

- adapters (#193): Bidirectional stdlib ↔ celeris conversion.
  WrapMiddleware wraps func(http.Handler) http.Handler for celeris chains.
  ToStdlib delegates to celeris.ToHandler. ReverseProxy via httputil.
  8 tests.

Documentation:
- middleware/doc.go: ordering guide updated (rewrite in Pre, pprof/swagger/static in Use)
- README.md: 5 new rows, package count 29
- SECURITY.md: v1.3.3 section (pprof loopback, static traversal, swagger AuthFunc)
- CONTRIBUTING.md: new reference implementation categories

Closes #189, Closes #190, Closes #191, Closes #192, Closes #193

* fix: resolve all review findings from v1.3.3 audit

Security:
- swagger: escape SpecURL with html.EscapeString before injecting into
  HTML/JS templates (prevents XSS via misconfigured SpecURL)

Code quality:
- pprof: replace manual skipMap with celeris.SkipHelper (per CONTRIBUTING.md)
- adapters: fix gofmt formatting + errcheck for resp.Body.Close
- .golangci.yml: add pprof var-naming exclusion (package name matches stdlib)

* fix: resolve all remaining review findings

- swagger/doc.go: add CSP warning for secure middleware interaction
  (cdn.jsdelivr.net must be allowed in script-src/style-src)
- rewrite/doc.go: anchor regex in basic example (^/old$ not /old),
  add Security section with ReDoS and open redirect warnings
- adapters/adapters.go: add 100MB body size cap to responseCapture
  (prevents OOM from misbehaving stdlib middleware, matches core bridge)

* fix: major rework of all 5 middleware to address review findings

static:
- FIX: Browse XSS — href uses url.PathEscape + ./ prefix (prevents javascript:)
- FIX: Prefix matching now segment-boundary-aware (/api no longer matches /api-docs)
- ADD: ETag/Last-Modified headers on OS and fs.FS files (conditional GET + 304)
- ADD: Range requests (206) for fs.FS files (video seeking, download resume)
- ADD: accept-ranges: bytes header
- 28 tests covering all new functionality

swagger:
- FIX: YAML spec auto-detected and served with correct Content-Type
- ADD: UIConfig with DocExpansion, DeepLinking, PersistAuthorization, Title
- ADD: AssetsPath for air-gapped/self-hosted asset serving (no CDN required)
- ADD: RendererScalar/RendererSwaggerUI type for UI engine selection
- 26 tests

adapters:
- FIX: WrapMiddleware now propagates pre-inner headers (rs/cors, gorilla/csrf work)
- FIX: buildRequest sets ContentLength, TLS state, Protocol version
- ADD: ReverseProxy with Options (WithTransport, WithModifyRequest, WithErrorHandler)
- ADD: Pooled responseCapture for zero-alloc hot path
- DEL: ToStdlib (was pure alias of celeris.ToHandler)
- DOC: Limitations section (no Hijacker/Flusher for WebSocket/streaming)

rewrite:
- FIX: Rules changed from map[string]string to []Rule slice (insertion-order,
  not alphabetical — matches user expectations and Echo's approach)
- DOC: First-Match-Wins section updated for ordered evaluation

* fix: gofmt formatting

* fix: security fix + integration review fixes for v1.3.3 middleware

Security:
- static: fix path traversal in Browse directory listing. serveOS
  now validates that filepath.Join result stays within root directory
  (prefix check + symlink escape check), mirroring FileFromDir.
  Added TestPathTraversalBrowse and TestPathTraversalFile.

Core integration:
- rewrite: make New() variadic (config ...Config) with defaultConfig
  and applyDefaults() for pattern consistency with all other middleware
- pprof: use cfg := defaultConfig instead of var cfg Config
- swagger: use c.Redirect() instead of manual SetHeader + NoContent

Tests:
- pprof: add TestPprofSkipFunc (was missing Skip callback coverage)
- adapters: add TestReverseProxyPanicNilTarget and TestReverseProxyOptions
  (ReverseProxy had zero test coverage)
- rewrite/bench_test.go: fix regex (d+) -> (\d+) so BenchmarkRewriteMatch
  actually tests the match path

Benchmarks:
- bench_chain_test.go: add BenchmarkChainPreRoutingWithRewrite,
  BenchmarkChainRewritePassthrough, BenchmarkChainWithWrapMiddleware
- swagger/bench_test.go: fix spec path /swagger/doc.json -> /swagger/spec

Documentation:
- middleware/doc.go: fix rewrite production stack example ([]Rule syntax),
  fix swagger description (remove AuthFunc claim), add adapters mention
- pprof/swagger/static doc.go: add missing Skip/Ordering sections
- adapters doc.go: document ReverseProxy function and Option API
- SECURITY.md: fix swagger AuthFunc claim (feature doesn't exist)
- README.md: fix static description (no SPA or Cache-Control)

* fix: address all 33-agent review findings for v1.3.3 middleware

P0 fixes:
- adapters: add 100MB body size cap to responseCapture.Write (matches
  core bridge maxBridgeResponseBytes). Prevents OOM from misbehaving
  stdlib middleware.
- adapters: WrapMiddleware(nil) now panics at init instead of deferred
  panic on first request.
- static: validate() rejects Prefix without leading / (was silently
  serving nothing).

Feature additions:
- static: SPA mode (Config.SPA bool) — serves index file for
  non-existent paths instead of falling through. Works for both OS
  and fs.FS backends.
- static: Cache-Control (Config.MaxAge time.Duration) — sets
  "public, max-age=N" header alongside ETag/Last-Modified.
- swagger: ReDoc renderer (RendererReDoc) with CDN + self-hosted
  assets support.
- swagger: DefaultModelsExpandDepth changed to *int to allow depth=0
  (zero-value sentinel collision fixed).
- rewrite: per-rule RedirectCode (Rule.RedirectCode) — each rule can
  override the config-level default (e.g., /old→301, /temp→307).

Code quality:
- static: precompute filepath.Clean(root) in New() (saves 1 alloc/req)
- static: deduplicate ETag computation (setCacheHeaders returns etag,
  notModified accepts it instead of recomputing)
- static: io.ReadAll → make([]byte, size) + io.ReadFull for fs.FS
  (eliminates O(log N) growth allocations)
- static: fix If-None-Match to handle comma-separated ETag lists
  (RFC 7232 weak comparison with W/ prefix stripping)
- static: fix RFC 7232 §6 — skip If-Modified-Since when If-None-Match
  is present (regardless of match result)
- pprof/swagger: use NewHTTPError() for 403/404/405 instead of
  c.NoContent() (enables global error handler integration)
- swagger: HTML-escape Title and AssetsPath in templates
- swagger: pin Scalar CDN to @1 major version
- rewrite: validate empty Rule.Pattern (panics instead of silently
  matching everything)

Tests (30+ new):
- static: SPA mode (OS + FS + disabled), MaxAge, HEAD fs.FS, IMS 304
  fs.FS, multi-ETag If-None-Match, Prefix validation panic
- swagger: HEAD on /spec, DocExpansion "none", ReDoc renderer + title
  + assets, DefaultModelsExpandDepth=0
- adapters: WrapMiddleware nil panic, query string round-trip, host
  propagation, body round-trip, responseCapture body cap,
  ReverseProxy behavioral (httptest.Server), ReverseProxy error handler
- pprof: IPv6 loopback (allow ::1 + deny remote v6), bare prefix,
  validate Prefix="/" panic
- rewrite: per-rule RedirectCode, empty Pattern panic, invalid
  per-rule RedirectCode panic

Benchmarks:
- bench_chain_test.go: add BenchmarkChainPprofPassthrough,
  BenchmarkChainSwaggerPassthrough, BenchmarkChainStaticPassthrough,
  BenchmarkChainStaticServe (with etag)

Documentation:
- pprof: fix SkipPaths doc (was "before any other logic", now
  correctly says "before auth and handler dispatch")
- pprof: validate Prefix="/" (would intercept all traffic)
- pprof: rename ExampleNew_publicAccess → ExampleNew_tokenAuth,
  add actual ExampleNew_publicAccess
- swagger: doc.go notes UIConfig options are Swagger UI-only
- static: doc.go adds SPA Mode + Cache-Control sections
- rewrite: Rule.Pattern doc recommends ^ and $ anchors
- adapters: doc.go warns about dual CORS + streaming limitation
- middleware/doc.go: Pre-routing ordering guidance

* fix: goimports ordering + errcheck in lint

* feat: address all remaining weakness and feature gaps

static:
- io.ReadSeeker fast-path: when fs.File implements ReadSeeker, range
  requests seek to offset and read only the requested bytes instead of
  loading the entire file into memory. Eliminates 50MB heap allocation
  for range requests on large files.
- Content-type sniffing: when mime.TypeByExtension fails, read first
  512 bytes and use http.DetectContentType instead of falling back to
  application/octet-stream.
- Pre-compressed file support (Config.Compress): checks for .br and .gz
  variants when Accept-Encoding allows, sets Content-Encoding and Vary
  headers. Brotli preferred over gzip. OS filesystem only.
- 7 new tests: ReadSeeker, content-type sniffing (OS+FS+ReadSeeker),
  pre-compressed (brotli, gzip, not accepted, missing).

swagger:
- Exported IntPtr helper for DefaultModelsExpandDepth ergonomics.
- ReDoc customization: ReDocConfig struct with Theme (dark mode),
  ExpandResponses, HideDownloadButton, ScrollYOffset, NoAutoAuth.
  Dark theme maps to full ReDoc color scheme.
- OAuth2 UI pre-configuration: OAuth2Config struct with ClientID,
  ClientSecret, Realm, AppName, Scopes. Generates ui.initOAuth() call
  in Swagger UI. OAuth2RedirectURL support.
- 12 new tests: IntPtr, ReDoc theme/expandResponses/hideDownload/
  scrollOffset/noAutoAuth/defaults, OAuth2 full/nil/partial/redirectURL.

adapters:
- WithModifyResponse option for ReverseProxy: modify response headers
  from backend before forwarding to client.
- http.Flusher support on responseCapture (no-op): prevents panics in
  stdlib middleware that type-asserts Flusher. Hijacker still unsupported.
- Performance section in doc.go: explicitly documents 8-15 alloc cost
  and recommends native middleware for hot paths.
- 3 new tests: ModifyResponse, Flusher type assertion, WrapMiddleware
  with Flusher.

rewrite:
- Conditional rewriting: Rule.Methods restricts to specific HTTP methods,
  Rule.Host restricts to specific Host header value. Methods map built
  at init time (zero per-request alloc). Both empty means match all.
- 4 new tests: method restriction, host restriction, combined, no
  restriction (backwards compatibility).

* fix: gofmt static_test.go

* refactor: simplify APIs, fix Flusher footgun, extract static helpers

swagger API simplification:
- Delete ReDocConfig struct — replaced by generic Options map[string]any
  that works for ALL renderers (ReDoc, Scalar, Swagger UI). More powerful
  with fewer types. Users pass raw renderer options as JSON-serializable maps.
- Delete IntPtr helper — revert DefaultModelsExpandDepth to plain int.
  Zero-value (0 = show model names) is a reasonable default. Document clearly.
- Add ClientSecret security warning to OAuth2Config doc: values are embedded
  in page source, only use for dev/test or PKCE public clients.
- Net: 3 exported structs (was 4), 0 helper functions (was 1).

static refactoring (no behavior changes):
- Extract detectContentType() — extension check + http.DetectContentType
- Extract sniffContentType() — ReadSeeker-specific with seek-back
- Extract serveRangeFromReader() — range handling from io.ReadSeeker
- Extract serveFSFullRead() — non-ReadSeeker full-read path
- Extract servePreCompressedFS() — pre-compressed file support for fs.FS
  (was OS-only, now works with embed.FS and custom fs.FS)
- serveFS is now a clean orchestrator calling focused helpers
- Update Compress doc: works with both OS and fs.FS

adapters Flusher fix:
- Remove no-op Flush() from responseCapture. A silent no-op is worse than
  not implementing the interface: SSE middleware calls Flush() expecting
  data delivery, gets silent success, client receives nothing until
  response completes. Now the type assertion returns false, so stdlib
  middleware can take appropriate fallback action.
- Remove Unwrap() (returning nil was misleading for ResponseController)
- Update doc.go Limitations section

* fix: errcheck on rs.Seek in sniffContentType

* fix: final polish — fs.FS cache, DefaultModelsExpandDepth default, content-type sniffing

static:
- Add sync.Map per-file cache for fs.FS content. Since fs.FS (especially
  embed.FS) is immutable, cache file bytes on first read. Eliminates
  repeated heap allocations for the same file — second+ requests for any
  file are O(1) map lookup + Blob. Particularly important for embed.FS
  which doesn't implement io.ReadSeeker.
- Delete serveFSReadSeeker, serveFSFullRead, serveRangeFromReader (3 funcs).
  Replace with serveFSCached (1 func). Net: 17 -> 15 functions.
- Fix content-type sniffing in servePreCompressedFS: when extension lookup
  fails, open the original uncompressed file and sniff first 512 bytes
  via http.DetectContentType. Consistent with OS path behavior.

swagger:
- DefaultModelsExpandDepth: applyDefaults now treats 0 as unset and
  defaults to 1 (matching Swagger UI's own default). The basic 3-line
  setup now produces identical behavior to Swagger UI's defaults.
  Documented: "0 is treated as unset; use -1 to hide models."

* fix: DefaultModelsExpandDepth *int + Options validation

- Revert DefaultModelsExpandDepth to *int with IntPtr() helper.
  nil means "use Swagger UI default (1)". IntPtr(0) correctly sets
  depth to 0 (show model names only). No more zero-value ambiguity.
- Add Options JSON-serializability validation in validate(). Panics
  at init time if Options contains non-serializable values (functions,
  channels, etc.) instead of silently falling back to {}.
- Tests: TestDefaultModelsExpandDepthZero verifies depth=0 works,
  TestDefaultModelsExpandDepthNilDefault verifies nil renders as 1,
  TestOptionsValidationPanic verifies non-serializable Options panic.

middleware/metrics/v1.3.2

Toggle middleware/metrics/v1.3.2's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
docs: add v1.3.2 security notes + update CONTRIBUTING references (#220)

SECURITY.md:
- Add v1.3.2 section documenting singleflight cross-user data leakage fix,
  multi-value header replay fix, circuit breaker panic recording, and
  circuit breaker validation hardening

CONTRIBUTING.md:
- Update middleware reference implementations by complexity level
  (simple, stateful, response-transform, request-coalescing)
- Add circuitbreaker and singleflight as reference patterns

middleware/compress/v1.3.3

Toggle middleware/compress/v1.3.3's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
feat: v1.3.3 utility & serving middleware — pprof, swagger, static, r…

…ewrite, adapters (#223)

* feat: add pprof, swagger, static, rewrite, and adapters middleware

Five new utility & serving middleware for v1.3.3:

- pprof (#189): Go profiling endpoints with loopback-only default.
  Wraps all net/http/pprof handlers via celeris.Adapt(). AuthFunc guard.
  10 tests.

- swagger (#190): OpenAPI spec + CDN-loaded Swagger UI/Scalar.
  No bundled assets. SpecContent or fs.FS support. AuthFunc optional.
  16 tests.

- static (#191): Static file serving with SPA mode, Cache-Control,
  prefix stripping, directory browse. Path traversal protection via
  FileFromDir (OS) and path.Clean + .. rejection (fs.FS).
  20 tests.

- rewrite (#192): Regex-based URL rewriting with capture groups ($1/$2).
  Pre-routing (Server.Pre). Compile at init, sorted first-match-wins.
  Optional redirect mode (301/302). 9 tests.

- adapters (#193): Bidirectional stdlib ↔ celeris conversion.
  WrapMiddleware wraps func(http.Handler) http.Handler for celeris chains.
  ToStdlib delegates to celeris.ToHandler. ReverseProxy via httputil.
  8 tests.

Documentation:
- middleware/doc.go: ordering guide updated (rewrite in Pre, pprof/swagger/static in Use)
- README.md: 5 new rows, package count 29
- SECURITY.md: v1.3.3 section (pprof loopback, static traversal, swagger AuthFunc)
- CONTRIBUTING.md: new reference implementation categories

Closes #189, Closes #190, Closes #191, Closes #192, Closes #193

* fix: resolve all review findings from v1.3.3 audit

Security:
- swagger: escape SpecURL with html.EscapeString before injecting into
  HTML/JS templates (prevents XSS via misconfigured SpecURL)

Code quality:
- pprof: replace manual skipMap with celeris.SkipHelper (per CONTRIBUTING.md)
- adapters: fix gofmt formatting + errcheck for resp.Body.Close
- .golangci.yml: add pprof var-naming exclusion (package name matches stdlib)

* fix: resolve all remaining review findings

- swagger/doc.go: add CSP warning for secure middleware interaction
  (cdn.jsdelivr.net must be allowed in script-src/style-src)
- rewrite/doc.go: anchor regex in basic example (^/old$ not /old),
  add Security section with ReDoS and open redirect warnings
- adapters/adapters.go: add 100MB body size cap to responseCapture
  (prevents OOM from misbehaving stdlib middleware, matches core bridge)

* fix: major rework of all 5 middleware to address review findings

static:
- FIX: Browse XSS — href uses url.PathEscape + ./ prefix (prevents javascript:)
- FIX: Prefix matching now segment-boundary-aware (/api no longer matches /api-docs)
- ADD: ETag/Last-Modified headers on OS and fs.FS files (conditional GET + 304)
- ADD: Range requests (206) for fs.FS files (video seeking, download resume)
- ADD: accept-ranges: bytes header
- 28 tests covering all new functionality

swagger:
- FIX: YAML spec auto-detected and served with correct Content-Type
- ADD: UIConfig with DocExpansion, DeepLinking, PersistAuthorization, Title
- ADD: AssetsPath for air-gapped/self-hosted asset serving (no CDN required)
- ADD: RendererScalar/RendererSwaggerUI type for UI engine selection
- 26 tests

adapters:
- FIX: WrapMiddleware now propagates pre-inner headers (rs/cors, gorilla/csrf work)
- FIX: buildRequest sets ContentLength, TLS state, Protocol version
- ADD: ReverseProxy with Options (WithTransport, WithModifyRequest, WithErrorHandler)
- ADD: Pooled responseCapture for zero-alloc hot path
- DEL: ToStdlib (was pure alias of celeris.ToHandler)
- DOC: Limitations section (no Hijacker/Flusher for WebSocket/streaming)

rewrite:
- FIX: Rules changed from map[string]string to []Rule slice (insertion-order,
  not alphabetical — matches user expectations and Echo's approach)
- DOC: First-Match-Wins section updated for ordered evaluation

* fix: gofmt formatting

* fix: security fix + integration review fixes for v1.3.3 middleware

Security:
- static: fix path traversal in Browse directory listing. serveOS
  now validates that filepath.Join result stays within root directory
  (prefix check + symlink escape check), mirroring FileFromDir.
  Added TestPathTraversalBrowse and TestPathTraversalFile.

Core integration:
- rewrite: make New() variadic (config ...Config) with defaultConfig
  and applyDefaults() for pattern consistency with all other middleware
- pprof: use cfg := defaultConfig instead of var cfg Config
- swagger: use c.Redirect() instead of manual SetHeader + NoContent

Tests:
- pprof: add TestPprofSkipFunc (was missing Skip callback coverage)
- adapters: add TestReverseProxyPanicNilTarget and TestReverseProxyOptions
  (ReverseProxy had zero test coverage)
- rewrite/bench_test.go: fix regex (d+) -> (\d+) so BenchmarkRewriteMatch
  actually tests the match path

Benchmarks:
- bench_chain_test.go: add BenchmarkChainPreRoutingWithRewrite,
  BenchmarkChainRewritePassthrough, BenchmarkChainWithWrapMiddleware
- swagger/bench_test.go: fix spec path /swagger/doc.json -> /swagger/spec

Documentation:
- middleware/doc.go: fix rewrite production stack example ([]Rule syntax),
  fix swagger description (remove AuthFunc claim), add adapters mention
- pprof/swagger/static doc.go: add missing Skip/Ordering sections
- adapters doc.go: document ReverseProxy function and Option API
- SECURITY.md: fix swagger AuthFunc claim (feature doesn't exist)
- README.md: fix static description (no SPA or Cache-Control)

* fix: address all 33-agent review findings for v1.3.3 middleware

P0 fixes:
- adapters: add 100MB body size cap to responseCapture.Write (matches
  core bridge maxBridgeResponseBytes). Prevents OOM from misbehaving
  stdlib middleware.
- adapters: WrapMiddleware(nil) now panics at init instead of deferred
  panic on first request.
- static: validate() rejects Prefix without leading / (was silently
  serving nothing).

Feature additions:
- static: SPA mode (Config.SPA bool) — serves index file for
  non-existent paths instead of falling through. Works for both OS
  and fs.FS backends.
- static: Cache-Control (Config.MaxAge time.Duration) — sets
  "public, max-age=N" header alongside ETag/Last-Modified.
- swagger: ReDoc renderer (RendererReDoc) with CDN + self-hosted
  assets support.
- swagger: DefaultModelsExpandDepth changed to *int to allow depth=0
  (zero-value sentinel collision fixed).
- rewrite: per-rule RedirectCode (Rule.RedirectCode) — each rule can
  override the config-level default (e.g., /old→301, /temp→307).

Code quality:
- static: precompute filepath.Clean(root) in New() (saves 1 alloc/req)
- static: deduplicate ETag computation (setCacheHeaders returns etag,
  notModified accepts it instead of recomputing)
- static: io.ReadAll → make([]byte, size) + io.ReadFull for fs.FS
  (eliminates O(log N) growth allocations)
- static: fix If-None-Match to handle comma-separated ETag lists
  (RFC 7232 weak comparison with W/ prefix stripping)
- static: fix RFC 7232 §6 — skip If-Modified-Since when If-None-Match
  is present (regardless of match result)
- pprof/swagger: use NewHTTPError() for 403/404/405 instead of
  c.NoContent() (enables global error handler integration)
- swagger: HTML-escape Title and AssetsPath in templates
- swagger: pin Scalar CDN to @1 major version
- rewrite: validate empty Rule.Pattern (panics instead of silently
  matching everything)

Tests (30+ new):
- static: SPA mode (OS + FS + disabled), MaxAge, HEAD fs.FS, IMS 304
  fs.FS, multi-ETag If-None-Match, Prefix validation panic
- swagger: HEAD on /spec, DocExpansion "none", ReDoc renderer + title
  + assets, DefaultModelsExpandDepth=0
- adapters: WrapMiddleware nil panic, query string round-trip, host
  propagation, body round-trip, responseCapture body cap,
  ReverseProxy behavioral (httptest.Server), ReverseProxy error handler
- pprof: IPv6 loopback (allow ::1 + deny remote v6), bare prefix,
  validate Prefix="/" panic
- rewrite: per-rule RedirectCode, empty Pattern panic, invalid
  per-rule RedirectCode panic

Benchmarks:
- bench_chain_test.go: add BenchmarkChainPprofPassthrough,
  BenchmarkChainSwaggerPassthrough, BenchmarkChainStaticPassthrough,
  BenchmarkChainStaticServe (with etag)

Documentation:
- pprof: fix SkipPaths doc (was "before any other logic", now
  correctly says "before auth and handler dispatch")
- pprof: validate Prefix="/" (would intercept all traffic)
- pprof: rename ExampleNew_publicAccess → ExampleNew_tokenAuth,
  add actual ExampleNew_publicAccess
- swagger: doc.go notes UIConfig options are Swagger UI-only
- static: doc.go adds SPA Mode + Cache-Control sections
- rewrite: Rule.Pattern doc recommends ^ and $ anchors
- adapters: doc.go warns about dual CORS + streaming limitation
- middleware/doc.go: Pre-routing ordering guidance

* fix: goimports ordering + errcheck in lint

* feat: address all remaining weakness and feature gaps

static:
- io.ReadSeeker fast-path: when fs.File implements ReadSeeker, range
  requests seek to offset and read only the requested bytes instead of
  loading the entire file into memory. Eliminates 50MB heap allocation
  for range requests on large files.
- Content-type sniffing: when mime.TypeByExtension fails, read first
  512 bytes and use http.DetectContentType instead of falling back to
  application/octet-stream.
- Pre-compressed file support (Config.Compress): checks for .br and .gz
  variants when Accept-Encoding allows, sets Content-Encoding and Vary
  headers. Brotli preferred over gzip. OS filesystem only.
- 7 new tests: ReadSeeker, content-type sniffing (OS+FS+ReadSeeker),
  pre-compressed (brotli, gzip, not accepted, missing).

swagger:
- Exported IntPtr helper for DefaultModelsExpandDepth ergonomics.
- ReDoc customization: ReDocConfig struct with Theme (dark mode),
  ExpandResponses, HideDownloadButton, ScrollYOffset, NoAutoAuth.
  Dark theme maps to full ReDoc color scheme.
- OAuth2 UI pre-configuration: OAuth2Config struct with ClientID,
  ClientSecret, Realm, AppName, Scopes. Generates ui.initOAuth() call
  in Swagger UI. OAuth2RedirectURL support.
- 12 new tests: IntPtr, ReDoc theme/expandResponses/hideDownload/
  scrollOffset/noAutoAuth/defaults, OAuth2 full/nil/partial/redirectURL.

adapters:
- WithModifyResponse option for ReverseProxy: modify response headers
  from backend before forwarding to client.
- http.Flusher support on responseCapture (no-op): prevents panics in
  stdlib middleware that type-asserts Flusher. Hijacker still unsupported.
- Performance section in doc.go: explicitly documents 8-15 alloc cost
  and recommends native middleware for hot paths.
- 3 new tests: ModifyResponse, Flusher type assertion, WrapMiddleware
  with Flusher.

rewrite:
- Conditional rewriting: Rule.Methods restricts to specific HTTP methods,
  Rule.Host restricts to specific Host header value. Methods map built
  at init time (zero per-request alloc). Both empty means match all.
- 4 new tests: method restriction, host restriction, combined, no
  restriction (backwards compatibility).

* fix: gofmt static_test.go

* refactor: simplify APIs, fix Flusher footgun, extract static helpers

swagger API simplification:
- Delete ReDocConfig struct — replaced by generic Options map[string]any
  that works for ALL renderers (ReDoc, Scalar, Swagger UI). More powerful
  with fewer types. Users pass raw renderer options as JSON-serializable maps.
- Delete IntPtr helper — revert DefaultModelsExpandDepth to plain int.
  Zero-value (0 = show model names) is a reasonable default. Document clearly.
- Add ClientSecret security warning to OAuth2Config doc: values are embedded
  in page source, only use for dev/test or PKCE public clients.
- Net: 3 exported structs (was 4), 0 helper functions (was 1).

static refactoring (no behavior changes):
- Extract detectContentType() — extension check + http.DetectContentType
- Extract sniffContentType() — ReadSeeker-specific with seek-back
- Extract serveRangeFromReader() — range handling from io.ReadSeeker
- Extract serveFSFullRead() — non-ReadSeeker full-read path
- Extract servePreCompressedFS() — pre-compressed file support for fs.FS
  (was OS-only, now works with embed.FS and custom fs.FS)
- serveFS is now a clean orchestrator calling focused helpers
- Update Compress doc: works with both OS and fs.FS

adapters Flusher fix:
- Remove no-op Flush() from responseCapture. A silent no-op is worse than
  not implementing the interface: SSE middleware calls Flush() expecting
  data delivery, gets silent success, client receives nothing until
  response completes. Now the type assertion returns false, so stdlib
  middleware can take appropriate fallback action.
- Remove Unwrap() (returning nil was misleading for ResponseController)
- Update doc.go Limitations section

* fix: errcheck on rs.Seek in sniffContentType

* fix: final polish — fs.FS cache, DefaultModelsExpandDepth default, content-type sniffing

static:
- Add sync.Map per-file cache for fs.FS content. Since fs.FS (especially
  embed.FS) is immutable, cache file bytes on first read. Eliminates
  repeated heap allocations for the same file — second+ requests for any
  file are O(1) map lookup + Blob. Particularly important for embed.FS
  which doesn't implement io.ReadSeeker.
- Delete serveFSReadSeeker, serveFSFullRead, serveRangeFromReader (3 funcs).
  Replace with serveFSCached (1 func). Net: 17 -> 15 functions.
- Fix content-type sniffing in servePreCompressedFS: when extension lookup
  fails, open the original uncompressed file and sniff first 512 bytes
  via http.DetectContentType. Consistent with OS path behavior.

swagger:
- DefaultModelsExpandDepth: applyDefaults now treats 0 as unset and
  defaults to 1 (matching Swagger UI's own default). The basic 3-line
  setup now produces identical behavior to Swagger UI's defaults.
  Documented: "0 is treated as unset; use -1 to hide models."

* fix: DefaultModelsExpandDepth *int + Options validation

- Revert DefaultModelsExpandDepth to *int with IntPtr() helper.
  nil means "use Swagger UI default (1)". IntPtr(0) correctly sets
  depth to 0 (show model names only). No more zero-value ambiguity.
- Add Options JSON-serializability validation in validate(). Panics
  at init time if Options contains non-serializable values (functions,
  channels, etc.) instead of silently falling back to {}.
- Tests: TestDefaultModelsExpandDepthZero verifies depth=0 works,
  TestDefaultModelsExpandDepthNilDefault verifies nil renders as 1,
  TestOptionsValidationPanic verifies non-serializable Options panic.

middleware/compress/v1.3.2

Toggle middleware/compress/v1.3.2's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
docs: add v1.3.2 security notes + update CONTRIBUTING references (#220)

SECURITY.md:
- Add v1.3.2 section documenting singleflight cross-user data leakage fix,
  multi-value header replay fix, circuit breaker panic recording, and
  circuit breaker validation hardening

CONTRIBUTING.md:
- Update middleware reference implementations by complexity level
  (simple, stateful, response-transform, request-coalescing)
- Add circuitbreaker and singleflight as reference patterns

v1.3.1

Toggle v1.3.1's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
docs: add v1.3.1 security notes to SECURITY.md (#211)

- Document Scheme() trust model hardening (XFP no longer trusted from
  untrusted clients)
- Document proxy host/IP validation, method override target restriction,
  negotiate q=0 exclusion, redirect code validation, BREACH warning
- Add middleware/compress to sub-module scope list

v1.3.0

Toggle v1.3.0's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
arch: v1.3.0 — middleware in-tree, review-driven hardening, API addit…

…ions (#174)

* arch: respHdrBuf[16] + eliminate ctxkit indirection

- Increase respHdrBuf from [8] to [16] entries, eliminating the heap
  allocation when secure middleware sets 10+ response headers. Adds 512
  bytes to pooled Context struct. Closes #167

- Replace ctxkit function-pointer indirection with direct exported test
  helpers: AcquireTestContext, ReleaseTestContext, SetTestStartTime,
  SetTestFullPath, SetTestTrustedNets, TestStream, AddTestParam,
  SetTestHandlers. Removes 10 type-unsafe `any` hooks. ctxkit reduced
  to single ReleaseContext hook needed by internal/conn. Closes #173

* arch: move middleware in-tree under middleware/

Move all 17 middleware packages from github.com/goceleris/middlewares
into github.com/goceleris/celeris/middleware/. This eliminates:
- Version coordination between separate repos
- Symbol duplication (RequestIDKey, etc.)
- Import restrictions across module boundaries
- ctxkit hacks for cross-module type sharing

Zero-dep packages: basicauth, bodylimit, cors, csrf, debug,
healthcheck, jwt, keyauth, logger, ratelimit, recovery, requestid,
secure, session, timeout + internal/{extract,fnv1a,randutil,testutil}

Heavy-dep submodules (separate go.mod):
- middleware/metrics (Prometheus client)
- middleware/otel (OpenTelemetry SDK)

All 19 test suites pass with -race. Import paths rewritten from
github.com/goceleris/middlewares/X to github.com/goceleris/celeris/middleware/X.

Closes #164

* perf: profiling-driven optimizations — 6 issues closed

- SetCookie: stack [256]byte buffer replaces strings.Builder, inline
  appendCookieSafe strips unsafe chars during append. Closes #168
- Session MemoryStore: Get returns stored map directly (caller-owned),
  Save still copies. Deferred make(map) to !loaded branch. Closes #169
- JWT: pool Token and MapClaims via sync.Pool + OnRelease return.
  ReleaseToken/AcquireMapClaims exported from jwtparse. Closes #170
- HexToken: stack [64]byte buffer for n<=32, single alloc (string
  conversion only). Closes #171
- requestid: EnableStdContext opt-in for context.WithValue. Default
  false saves 1 alloc/req for 90% of users. Closes #172
- SkipHelper: exported type in celeris package, used by cors, ratelimit,
  recovery. Reduces 5-line skip boilerplate to 1-line call. Closes #166
- ResponseWriter split (#165): deferred — 10+ implementors, needs
  dedicated follow-up PR

* fix: review findings — JWT pool leak, session race, cookie null bytes, stale docs

- JWT: release pooled Token+MapClaims on validation failure path
  (prevents pool leak on every rejected JWT request)
- Session MemoryStore.Get: restore defensive copy — caller-owned
  semantics caused data race on concurrent same-session requests
- appendCookieSafe: strip null bytes (c == 0) matching stripCRLF
- Root doc.go, README.md, observe/doc.go, SECURITY.md: update all
  stale goceleris/middlewares references to in-tree middleware/ paths
- middleware/doc.go: fix jwt.SigningKey compile error in auth example,
  add healthcheck/metrics/otel/debug to ordering
- CSRF CookieHTTPOnly: fix godoc to match security invariant
- SkipHelper, ReleaseTestContext: improved godoc

* arch: split ResponseWriter into ResponseWriter + H2Controller (#165)

ResponseWriter now contains only WriteResponse — the minimal interface
all engines, test recorders, and stdlib bridges implement.

H2Controller contains the 5 HTTP/2 connection-level operations
(SendGoAway, MarkStreamClosed, IsStreamClosed, WriteRSTStreamPriority,
CloseConn) that only H2-capable engines implement.

Processor uses a private h2Conn composite interface internally.
Removed H2 no-op stubs from h1ResponseAdapter, celeristest
recorderWriter, stdResponseWriter, toStdlibResponseWriter, and
the context_test mock. Removed unused http2 imports from 4 files.

* perf: optimize Logger, Recovery, and middleware chains

Three targeted optimizations driven by profiling:

1. FastHandler.HandleDirect: new method bypasses slog.Record entirely
   when the logger middleware detects FastHandler. Avoids closure escape
   in Record.Attrs and eliminates 2 allocations per request.
   Logger: 549 → 427 ns/op, 6 → 4 allocs, 481 → 80 B/op.

2. FastHandler: remove copy-before-write in Handle and groupHandler.
   io.Writer.Write must not retain data per contract, so the defensive
   append([]byte(nil), buf...) copy was unnecessary.

3. Context.reset: only clear used entries in respHdrBuf and paramBuf.
   Skipping memclrHasPointers on 16 unused [2]string slots saves ~30 ns.
   Recovery: 136 → 106 ns/op. Auth chain: 849 → 644 ns/op.

* test: add in-tree middleware benchcmp suite with mage targets

Comprehensive middleware benchmark comparison across 5 frameworks
(Celeris, Fiber v3, Echo v4, Chi v5, net/http stdlib). Separate Go
module at test/benchcmp/ with framework dependencies isolated from
the main module.

Benchmarks:
- 14 individual middleware benchmarks per framework
- 4 chain benchmarks (API, Auth, Security, FullStack) × 5 frameworks
- Mage targets: MiddlewareBenchmark (runs + formats report),
  MiddlewareProfile (CPU profiles + flamegraphs per middleware)

* fix: resolve CI lint failures in middleware test files

Add golangci-lint exclusions for revive unused-parameter in test files
and var-naming in middleware/debug/ (stdlib name collision is intentional).
Rename unused lambda params in ratelimit_test.go config literals.

* fix: remove stale blank lines from import blocks after http2 removal

* fix: gofmt alignment and trailing whitespace

* security: harden CORS MirrorRequestHeaders and credential+wildcard validation

MirrorRequestHeaders now validates each echoed header name against RFC
7230 Section 3.2.6 token charset, caps at 20 headers and 128-byte names.

AllowCredentials + wildcard subdomain origins (e.g. https://*.example.com)
now panics at init. Added UnsafeAllowCredentialsWithWildcard escape hatch.

* fix: JWT error differentiation + JWKS eager preload

Added ErrJWTExpired and ErrJWTMalformed sentinels — errors.Is can now
distinguish expired tokens from malformed/unverifiable ones. Generic
ErrTokenInvalid remains as fallback for other validation failures.

JWKS fetchers now preload keys eagerly in New() by default, eliminating
the first-request blocking fetch. Config.JWKSPreload (*bool, default
true) controls this; preload failures log a warning and fall back to
lazy loading.

* feat: Context API additions — SetMethod, StatusJSON, SetResponseHeaders, FileFromFS

SetMethod(m) — overrides HTTP method for pre-routing middleware.
BasicAuth() — now case-insensitive per RFC 7617 (accepts basic/BASIC/Basic).
StatusJSON/StatusXML/StatusString/StatusBlob — use status from Status().
SetResponseHeaders — bulk header replacement for cache/replay middleware.
FileFromFS — serves files from fs.FS (embed.FS support).
Fixed respHdrBuf tmp buffer: [6] → [14] to match [16] respHdrBuf capacity.

* feat: Server.Pre(), Any() → []*Route, eager Collector

Server.Pre() — pre-routing middleware hook for method override and URL
rewrite. Runs before router.find(), can modify method/path via
SetMethod/SetPath.

Any() now returns []*Route (was *Server/*RouteGroup), enabling .Name()
on individual method routes.

Collector created eagerly in New() instead of lazily in doPrepare(),
so middleware can access it at registration time.

* fix: middleware review findings — 8 packages improved

keyauth: add Vary header on 401 for header-based key lookups.
recovery: standardize error prefixes, add sentinel errors
  (ErrPanic, ErrPanicContextCancelled, ErrPanicResponseCommitted,
  ErrBrokenPipe) for programmatic error matching.
healthcheck/requestid/logger/secure/timeout: adopt SkipHelper for
  consistency with cors/recovery/ratelimit pattern.
secure: doc warning about COEP + CORS interaction.
timeout: doc warning about preemptive mode + StreamWriter incompatibility.
ratelimit: add ValidateConfig() for pre-New() config validation.

* fix: lowercase ratelimit ValidateConfig error strings for staticcheck

* test: add CORS security edge-case tests + fix doc accuracy

7 new tests: MirrorRequestHeaders validation (>128 byte, >20 headers,
forbidden chars, empty segments), credential+wildcard subdomain panic,
UnsafeAllowCredentialsWithWildcard escape hatch suppression and request.

Updated doc.go: MirrorRequestHeaders section now accurately states
headers are validated against RFC 7230 token charset with size/count
limits (was "without validation"). Added UnsafeAllowCredentialsWithWildcard
documentation section.

* chore: remove 29 redundant tests, fix 4 doc gaps

Removed coverage-padding constant checks, true duplicate tests, and
tests subsumed by broader table-driven suites across 12 middleware
packages. Renamed misleading TestLimitStringEmptyPanics (tests no-panic).

Doc fixes: recovery sentinel errors documented, csrf CookieHTTPOnly
enforcement noted, debug Skip/config clarified, healthcheck validate()
reference rephrased.

952 tests → 923 tests. Net -198 lines.

* ci: release workflow publishes middleware sub-modules

Added tag-submodules job that creates middleware/metrics/vX.Y.Z and
middleware/otel/vX.Y.Z tags on release. Notify-proxy job now pings
the Go proxy for all three modules (root + metrics + otel).

Warns if sub-module go.mod pins a stale celeris version.

* ci: test, lint, build, and vulncheck middleware sub-modules

middleware/metrics and middleware/otel have separate go.mod files and
were invisible to the root `go list ./...`. Added dedicated steps for:
- Test: race-enabled tests for both sub-modules
- Lint: golangci-lint with working-directory for each sub-module
- Build: go build for each sub-module
- Vulncheck: govulncheck for each sub-module

* fix: goimports formatting + var-naming exclusion for metrics sub-module

* fix: goimports formatting for otel sub-module

* docs: update README, CONTRIBUTING, and SECURITY for v1.3.0

README: added middleware table with all 17 packages, Server.Pre() docs,
StatusJSON example, FileFromFS feature, benchcmp reference, updated
project structure to show middleware/.

CONTRIBUTING: added sub-module testing instructions, middleware dev
guidelines (SkipHelper pattern), middlewareBenchmark/middlewareProfile
mage targets, redundant test policy.

SECURITY: minimum supported version raised to v1.3.0. Documented all
security improvements from the 24-agent review (CORS hardening, JWT
error differentiation, BasicAuth RFC fix, KeyAuth Vary, Recovery
sentinels). Added Out of Scope section for deprecated middlewares repo.