feat(rest): compress the docs surface via the existing gzip middleware#227
Merged
Conversation
The docs handler served the embedded Scalar bundle (~3.6 MB) and the OpenAPI spec uncompressed, so they shipped full size on first paint even though /api responses already negotiated gzip. This wraps the docs handler in the existing GzipMiddleware, so the spec and the static assets compress when a client sends Accept-Encoding: gzip and serve verbatim when it does not. The handler owns its own compression, which keeps the public-listener split a plain path dispatch and leaves the /api chain alone. rest/docs_gzip_test.go covers both modes: negotiated (Content-Encoding: gzip, decodes back to the verbatim bytes, spec version stamp intact) and not negotiated (served raw). The existing docs tests send no Accept-Encoding and keep passing unchanged. > *This was generated by AI*
Add a real-wire test over httptest.NewServer that fetches the Scalar bundle with Accept-Encoding: gzip set by hand, so the transport leaves the raw gzip stream alone. It proves the stale uncompressed Content-Length that http.FileServer sets never reaches a length-enforcing transport, which is acceptance criterion 4. The recorder-based tests can't observe this, since a recorder enforces no Content-Length, so also pin an empty Content-Length on those as a cheap regression catch. Fix the docs.go wrap comment, which claimed this surface produces 304 conditional GETs. The embedded FS has no modtime or ETag, so http.FileServer sends no validators and never answers a 304 here. Replace the body-length guard in the verbatim test so a short or empty body fails loudly instead of skipping the magic-byte check. Reword two test comments that described the change instead of the behavior. > *This was generated by AI*
The package-level doc still said a ResponseRecorder was enough for the whole file and that the stale-Content-Length wire behavior lived in gzip_test.go. Neither holds anymore: this file now has a httptest.NewServer test that pins that behavior on a length-enforcing transport, which a recorder can't observe. Rewrote the doc to say what each test actually covers. Also reworded the wire test's comment and one error message so the direct Content-Length header assertion reads as the primary guard for criterion 4, with the gunzip round-trip framed as byte-for-byte integrity verification rather than the main truncation catch. Comments and strings only, no behavioral change. > *This was generated by AI*
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #218.
The docs handler served the embedded Scalar bundle (~3.6 MB) and the OpenAPI spec uncompressed. It sat outside the gzip negotiation the
/apistack already runs, so the bundle shipped at full size on first paint.This wraps the handler that
DocsHandlerreturns in the existingGzipMiddleware. Docs responses now compress when the client sendsAccept-Encoding: gzip, and a client that does not advertise gzip gets the bytes verbatim, exactly as before. The docs handler owns its own compression, so the public-listener split inservers/server.gostays a plain path dispatch and the/apichain is untouched (no double-wrap).What changed
rest/docs.go: wrap the spec and static-asset handler inGzipMiddleware.rest/docs_gzip_test.go(new): covers both modes. WithAccept-Encoding: gzipthe bundle and spec come back withContent-Encoding: gzipand decode byte-for-byte to the source; without it they stay verbatim. Also confirms theinfo.versionstamping survives compression.No changes to
contract/oropenapi/openapi.yaml. The docs surface is not part of the golden corpus.