Skip to content

[refiner] style(middleware): fix gzip doc comments and use otelkeys.Error (#3116)#3119

Open
amalgamated-bot wants to merge 5 commits into
mainfrom
refiner/3116-gzip-middleware-style-ad258305627886ca
Open

[refiner] style(middleware): fix gzip doc comments and use otelkeys.Error (#3116)#3119
amalgamated-bot wants to merge 5 commits into
mainfrom
refiner/3116-gzip-middleware-style-ad258305627886ca

Conversation

@amalgamated-bot
Copy link
Copy Markdown
Contributor

@amalgamated-bot amalgamated-bot commented May 30, 2026

Refinement Summary

This PR contains automated code style improvements for PR #3116 (feat(middleware): add gzip HTTP response compression).

Changes Made

Style Alignment

  • Fixed doc comment ordering in internal/handlers/middleware/gzip.go: The GzipMiddleware doc comment block was attached to the acceptsGzip function because no blank line separated them, leaving GzipMiddleware itself undocumented. Fixed by moving acceptsGzip (with its own doc comment) before the GzipMiddleware doc comment + function, so each declaration is correctly documented per Go conventions.

  • Use otelkeys.Error constant: Replaced the raw string key "error" with otelkeys.Error in the slog.DebugContext call inside GzipMiddleware. Per project conventions, all structured log field keys must use the predefined constants from internal/otelkeys/logger_keys.go.

Security Analysis

  • ✅ No malicious code detected
  • ✅ All security best practices followed
  • No external network calls, credential handling, or injection vulnerabilities found

Test Coverage

  • ✅ Tests are comprehensive — 9 test functions covering compression, bypass conditions (no Accept-Encoding, binary content types, SSE, Range requests), status codes, and table-driven tests for shouldGzip and acceptsGzip
  • No additional tests required

Files Modified

  • internal/handlers/middleware/gzip.go

Generated by refiner workflow | Targets PR #3116

Warning

Firewall blocked 2 domains

The following domains were blocked by the firewall during workflow execution:

  • localhost
  • proxy.golang.org

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "localhost"
    - "proxy.golang.org"

See Network Configuration for more information.

Generated by Code Refiner · sonnet46 2.1M ·

Add this agentic workflows to your repo

To install this agentic workflow, run

gh aw add github/gh-aw/.github/workflows/refiner.md@0a2fd45cd18a6e7b7a6f83afcf1690f0bd139884

Greptile Summary

This automated refinement PR introduces two style fixes to the new gzip.go middleware: it reorders acceptsGzip before the GzipMiddleware function so each declaration's doc comment attaches to the correct symbol, and replaces the raw "error" log key with the otelkeys.Error constant per project conventions. server.go is updated to wire GzipMiddleware into the alice middleware chain.

  • Doc comment fix (gzip.go): acceptsGzip is moved above GzipMiddleware so Go tooling (godoc, LSP) associates each comment block with the right function.
  • otelkeys.Error adoption (gzip.go line 205): slog.Any("error", err) becomes slog.Any(otelkeys.Error, err), aligning with the project's structured-log key constants.
  • Middleware registration (server.go): middleware.GzipMiddleware is appended to the alice chain as the innermost wrapper, so compression applies after tracing, logging, and security-header decoration.

Confidence Score: 5/5

Safe to merge — the two changes are purely stylistic (doc comment reordering and otelkeys constant adoption) with no behavioral impact on the middleware logic.

Both modifications are mechanical: moving a function declaration so its doc comment attaches to the right symbol, and swapping a raw string literal for a named constant. Neither touches the compression decision path, pool lifecycle, header manipulation, or request routing. The slog.DebugContext call correctly carries r.Context() per project convention.

No files require special attention.

Important Files Changed

Filename Overview
internal/handlers/middleware/gzip.go New gzip middleware file; this PR fixes doc comment ordering and replaces raw "error" string with otelkeys.Error. slog call correctly passes r.Context(). Logic for decide(), pool reuse, and WriteHeader/Write/Flush delegation is correct.
internal/handlers/middleware/gzip_test.go New test file unchanged by this refinement PR; covers compression, bypass conditions (SSE, binary, Range, pre-existing Content-Encoding), status codes, and table-driven tests for shouldGzip and acceptsGzip.
internal/server/server.go GzipMiddleware appended as the innermost alice middleware; placement after SecurityHeaders is correct — compression wraps only the final handler, so outer middleware sees unmodified request/response metadata.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Incoming Request] --> B[Add Vary: Accept-Encoding]
    B --> C{Range header present?}
    C -- Yes --> D[Pass through uncompressed]
    C -- No --> E{acceptsGzip?}
    E -- No --> D
    E -- Yes --> F[Wrap w with gzipResponseWriter]
    F --> G[next.ServeHTTP]
    G --> H{decide - on first Write/WriteHeader/Flush}
    H --> I{Content-Encoding already set?}
    I -- Yes --> J[Pass through: skip compression]
    I -- No --> K{shouldGzip Content-Type?}
    K -- No --> J
    K -- Yes --> L[Get gzip.Writer from pool]
    L --> M[Set Content-Encoding: gzip, Del Content-Length]
    M --> N[Write compressed body]
    N --> O{defer: gz.Close}
    O -- error --> P[slog.DebugContext close error]
    O -- ok/after log --> Q[gz.Reset nil + return to pool]
Loading

Reviews (3): Last reviewed commit: "fix(middleware): explicit gzip;q=0 overr..." | Re-trigger Greptile

Add GzipMiddleware to the global middleware chain, transparently
compressing responses for JSON, XML, ATOM (OPDS), and other
text-based content types when the client sends Accept-Encoding: gzip.

Binary responses (images, epub/mobi/pdf), SSE streams, and clients
that do not advertise gzip support are passed through uncompressed.

Key implementation details:
- Compression is decided lazily at the first Write/WriteHeader/Flush
  call, so the handler's Content-Type header is visible at decision time.
- gzip.Writer instances are reused via sync.Pool to avoid per-request
  allocation overhead.
- gzip.BestSpeed (level 1) is used to minimise CPU cost while still
  achieving ~60-80% size reduction for typical JSON payloads.
- Unwrap() is implemented so http.ResponseController.SetWriteDeadline
  (used by the SSE handler) reaches the underlying ResponseWriter.
- Vary: Accept-Encoding is set on every response (with or without gzip)
  so intermediate caches store separate entries per encoding.

Energy efficiency impact (Network & I/O focus area):
  Typical book list (50 books): ~35 KB JSON → ~5 KB gzipped (~85% less)
  OPDS feed: ~20 KB XML → ~3 KB gzipped (~85% less)
  Network transfer energy is proportional to bytes transmitted across
  all hops (client NIC, switches, server NIC, DRAM buffers), so
  reducing payload size directly reduces energy consumption per request.
  GSF principle: Demand Shaping — less data transferred reduces load on
  network infrastructure.

No new dependencies: uses only compress/gzip from the Go standard library.
- Remove dead `decided` field (sync.Once already provides idempotency)
- Replace strings.Contains with proper acceptsGzip parser that handles q=0
- Use Header().Add for Vary instead of Set to preserve existing values
- Log gz.Close() errors at debug level instead of silently discarding
- Reset gzip.Writer to nil before returning to pool to avoid retaining refs
- Skip compression for Range requests to preserve Content-Range semantics
- Assert write errors in tests instead of discarding them
- Add tests for acceptsGzip and Range request handling
- Move acceptsGzip before GzipMiddleware so each function has its own
  doc comment; previously the GzipMiddleware comment block bled into
  acceptsGzip leaving GzipMiddleware undocumented
- Replace raw string key "error" with otelkeys.Error constant in
  slog.DebugContext call per project logging conventions
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces and wires up a gzip response-compression middleware, along with unit tests, and aligns middleware logging field keys with the otelkeys constants.

Changes:

  • Register middleware.GzipMiddleware in the server’s global middleware chain.
  • Add internal/handlers/middleware/gzip.go implementing lazy gzip compression for selected text-based content types.
  • Add internal/handlers/middleware/gzip_test.go covering compression and bypass scenarios.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.

File Description
internal/server/server.go Adds gzip middleware to the global alice chain.
internal/handlers/middleware/gzip.go Implements gzip compression middleware and response-writer wrapper (lazy decision, pooling, vary header, range bypass).
internal/handlers/middleware/gzip_test.go Adds tests for compression behavior and bypass conditions.

Comment thread internal/handlers/middleware/gzip.go
Comment thread internal/handlers/middleware/gzip.go
Comment thread internal/handlers/middleware/gzip_test.go
Comment thread internal/handlers/middleware/gzip.go
Comment thread internal/handlers/middleware/gzip.go
Comment thread internal/handlers/middleware/gzip.go
Comment thread internal/handlers/middleware/gzip_test.go Outdated
…xisting Content-Encoding

- Fix acceptsGzip to correctly reject q=0.0, q=0.00, q=0.000 as zero
  quality per RFC 7231 §5.3.1
- Add support for the "*" wildcard token in Accept-Encoding per RFC 7231
  §5.3.4
- Add guard in decide() to skip compression when Content-Encoding is
  already set by the handler, preventing double-encoding
- Update tests to match corrected RFC semantics and add coverage for
  new cases
Comment thread internal/handlers/middleware/gzip.go
Rewrite acceptsGzip to do a full pass over all tokens before returning,
so that an explicit gzip;q=0 rejection always takes precedence over a
wildcard (*) match regardless of token order. Per RFC 7231 §5.3.4.
Copilot AI review requested due to automatic review settings May 30, 2026 18:20
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot's findings

  • Files reviewed: 3/3 changed files
  • Comments generated: 7

Comment on lines +130 to +144
for _, token := range strings.Split(header, ",") {
token = strings.TrimSpace(token)
lower := strings.ToLower(token)

if strings.HasPrefix(lower, "gzip") {
if hasZeroQuality(lower) {
gzipRejected = true
} else {
gzipExplicit = true
}
} else if strings.HasPrefix(lower, "*") {
if !hasZeroQuality(lower) {
wildcardMatch = true
}
}
Comment on lines +162 to +167
// Trim trailing whitespace/parameters.
if end := strings.IndexAny(val, " \t;,"); end >= 0 {
val = val[:end]
}
// Must be "0" optionally followed by a dot and up to three zeros.
if val == "0" {
Comment on lines +33 to +37
gr, err := gzip.NewReader(rec.Body)
require.NoError(t, err)
got, err := io.ReadAll(gr)
require.NoError(t, err)
require.Equal(t, body, string(got))
Comment on lines +125 to +129
gr, err := gzip.NewReader(rec.Body)
require.NoError(t, err)
got, err := io.ReadAll(gr)
require.NoError(t, err)
require.Equal(t, body, string(got))
Comment on lines +148 to +152
gr, err := gzip.NewReader(rec.Body)
require.NoError(t, err)
got, err := io.ReadAll(gr)
require.NoError(t, err)
require.Equal(t, `{"id":"123"}`, string(got))
Comment thread internal/server/server.go
Comment on lines 220 to 226
chain := alice.New(
middleware.RequestIDHandler,
otel.TraceMiddleware,
middleware.LoggingMiddleware,
middleware.NewSecurityHeadersMiddleware(middleware.SecurityHeadersConfig{SecureCookies: s.secureCookies}),
middleware.GzipMiddleware,
).Then(s.mux)
Comment on lines +182 to +190
func TestAcceptsGzip(t *testing.T) {
tests := []struct {
header string
want bool
}{
{"gzip", true},
{"gzip, deflate, br", true},
{"br, gzip", true},
{"deflate", false},
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants