Skip to content

Commit 6244633

Browse files
fredbiclaude
andauthored
docs(compression): deprecate ContentEncoding, add CAFxX recipe (#447)
Mark negotiate.ContentEncoding deprecated — the runtime does not ship compression. Add a runnable example under docs/examples/middleware/compression/ showing composition with github.com/CAFxX/httpcompression at the http.Handler level. The example lives in its own go module (registered in go.work) so CAFxX is not pulled into the root go.mod. * closes #440 Signed-off-by: Frederic BIDON <fredbi@yahoo.com> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
1 parent 5a88ba4 commit 6244633

6 files changed

Lines changed: 329 additions & 0 deletions

File tree

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# Compression middleware example
2+
3+
This example shows how to add transparent HTTP response compression
4+
(gzip, brotli, …) to a `go-openapi/runtime` server by wrapping the
5+
`http.Handler` returned by `middleware.Serve` with a standard
6+
ecosystem compression middleware.
7+
8+
The runtime itself does not ship compression. Composition with an
9+
external middleware is the recommended approach; this example uses
10+
[`github.com/CAFxX/httpcompression`](https://github.com/CAFxX/httpcompression),
11+
which covers gzip + brotli + zstd + deflate with sensible defaults
12+
(content-type allowlist, minimum-size threshold, `Vary` / `ETag` /
13+
`Content-Length` handling).
14+
15+
## Run
16+
17+
```sh
18+
go run .
19+
```
20+
21+
Then in another terminal:
22+
23+
```sh
24+
# Plain (uncompressed) response.
25+
curl -i http://localhost:8080/api/greeting
26+
27+
# Gzip-compressed response.
28+
curl -i -H 'Accept-Encoding: gzip' http://localhost:8080/api/greeting
29+
30+
# Brotli-compressed response (DefaultAdapter enables brotli too).
31+
curl -i -H 'Accept-Encoding: br' --compressed http://localhost:8080/api/greeting
32+
```
33+
34+
The compressed response carries `Content-Encoding: gzip` (or `br`),
35+
`Vary: Accept-Encoding`, and a transformed `Content-Length`. The
36+
`go-openapi/runtime` pipeline is unchanged — the compressor sits
37+
outside the API handler and operates on the final response bytes.
38+
39+
## Layering
40+
41+
The order of middlewares around the api handler matters:
42+
43+
```text
44+
client ─► [ TLS / auth / rate-limit ] ─► [ compress ] ─► [ go-openapi api ] ─► handler
45+
```
46+
47+
The compressor must wrap the api handler so it sees the complete
48+
response body before transport. Transport-level concerns (TLS
49+
termination, auth gating, rate limiting) typically wrap the
50+
compressor in turn.
51+
52+
## Client-side
53+
54+
`net/http`'s default transport auto-decodes `gzip` responses, but not
55+
`br` / `zstd` / `deflate`. Clients that need broader decoding can wrap
56+
their `http.RoundTripper` with a decoder; `github.com/klauspost/compress`
57+
provides primitives suitable for that purpose. The `go-openapi/runtime`
58+
client (`client.Runtime`) accepts a custom transport via its
59+
configuration, so the same pattern applies.
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
module github.com/go-openapi/runtime/docs/examples/middleware/compression
2+
3+
go 1.25.0
4+
5+
require (
6+
github.com/CAFxX/httpcompression v0.0.9
7+
github.com/go-openapi/loads v0.23.3
8+
github.com/go-openapi/runtime v0.30.0
9+
)
10+
11+
require (
12+
github.com/andybalholm/brotli v1.0.5 // indirect
13+
github.com/go-openapi/analysis v0.25.0 // indirect
14+
github.com/go-openapi/errors v0.22.7 // indirect
15+
github.com/go-openapi/jsonpointer v0.22.5 // indirect
16+
github.com/go-openapi/jsonreference v0.21.5 // indirect
17+
github.com/go-openapi/runtime/server-middleware v0.30.0 // indirect
18+
github.com/go-openapi/spec v0.22.4 // indirect
19+
github.com/go-openapi/strfmt v0.26.2 // indirect
20+
github.com/go-openapi/swag/conv v0.26.0 // indirect
21+
github.com/go-openapi/swag/fileutils v0.26.0 // indirect
22+
github.com/go-openapi/swag/jsonname v0.25.5 // indirect
23+
github.com/go-openapi/swag/jsonutils v0.26.0 // indirect
24+
github.com/go-openapi/swag/loading v0.25.5 // indirect
25+
github.com/go-openapi/swag/mangling v0.25.5 // indirect
26+
github.com/go-openapi/swag/stringutils v0.26.0 // indirect
27+
github.com/go-openapi/swag/typeutils v0.26.0 // indirect
28+
github.com/go-openapi/swag/yamlutils v0.25.5 // indirect
29+
github.com/go-openapi/validate v0.25.2 // indirect
30+
github.com/go-viper/mapstructure/v2 v2.5.0 // indirect
31+
github.com/google/uuid v1.6.0 // indirect
32+
github.com/klauspost/compress v1.16.7 // indirect
33+
github.com/oklog/ulid/v2 v2.1.1 // indirect
34+
go.yaml.in/yaml/v3 v3.0.4 // indirect
35+
golang.org/x/net v0.53.0 // indirect
36+
golang.org/x/sync v0.20.0 // indirect
37+
golang.org/x/text v0.36.0 // indirect
38+
)
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
github.com/CAFxX/httpcompression v0.0.9 h1:0ue2X8dOLEpxTm8tt+OdHcgA+gbDge0OqFQWGKSqgrg=
2+
github.com/CAFxX/httpcompression v0.0.9/go.mod h1:XX8oPZA+4IDcfZ0A71Hz0mZsv/YJOgYygkFhizVPilM=
3+
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
4+
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
5+
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
6+
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
7+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
8+
github.com/go-openapi/analysis v0.25.0 h1:EnjAq1yO8wEO9HbPmY8vLPEIkdZuuFhCAKBPvCB7bCs=
9+
github.com/go-openapi/analysis v0.25.0/go.mod h1:5WFTRE43WLkPG9r9OtlMfqkkvUTYLVVCIxLlEpyF8kE=
10+
github.com/go-openapi/errors v0.22.7 h1:JLFBGC0Apwdzw3484MmBqspjPbwa2SHvpDm0u5aGhUA=
11+
github.com/go-openapi/errors v0.22.7/go.mod h1://QW6SD9OsWtH6gHllUCddOXDL0tk0ZGNYHwsw4sW3w=
12+
github.com/go-openapi/jsonpointer v0.22.5 h1:8on/0Yp4uTb9f4XvTrM2+1CPrV05QPZXu+rvu2o9jcA=
13+
github.com/go-openapi/jsonpointer v0.22.5/go.mod h1:gyUR3sCvGSWchA2sUBJGluYMbe1zazrYWIkWPjjMUY0=
14+
github.com/go-openapi/jsonreference v0.21.5 h1:6uCGVXU/aNF13AQNggxfysJ+5ZcU4nEAe+pJyVWRdiE=
15+
github.com/go-openapi/jsonreference v0.21.5/go.mod h1:u25Bw85sX4E2jzFodh1FOKMTZLcfifd1Q+iKKOUxExw=
16+
github.com/go-openapi/loads v0.23.3 h1:g5Xap1JfwKkUnZdn+S0L3SzBDpcTIYzZ5Qaag0YDkKQ=
17+
github.com/go-openapi/loads v0.23.3/go.mod h1:NOH07zLajXo8y55hom0omlHWDVVvCwBM/S+csCK8LqA=
18+
github.com/go-openapi/runtime v0.30.0 h1:1llnyZcqkjm77VhM4FPO1O1rt2SzLZIRPkZAY4wv4mw=
19+
github.com/go-openapi/runtime v0.30.0/go.mod h1:QtJGUq+1S0VWPP+LQldH6o4tcqGsjzTVjn/wIWEn9o4=
20+
github.com/go-openapi/runtime/server-middleware v0.30.0 h1:8rPoJ/xv7JL8BsovaqboKETlpWBArVh8n+0L/GyePog=
21+
github.com/go-openapi/runtime/server-middleware v0.30.0/go.mod h1:OYNT/TxNvB/VK5oe4htM2jDTwlEXuejVJmu0DVZfAMs=
22+
github.com/go-openapi/spec v0.22.4 h1:4pxGjipMKu0FzFiu/DPwN3CTBRlVM2yLf/YTWorYfDQ=
23+
github.com/go-openapi/spec v0.22.4/go.mod h1:WQ6Ai0VPWMZgMT4XySjlRIE6GP1bGQOtEThn3gcWLtQ=
24+
github.com/go-openapi/strfmt v0.26.2 h1:ysjheCh4i1rmFEo2LanhELDNucNzfWTZhUDKgWWPaFM=
25+
github.com/go-openapi/strfmt v0.26.2/go.mod h1:fXh1e449cyUn2NYuz+wb3wARBUdMl7qPEZwX00nqivY=
26+
github.com/go-openapi/swag/conv v0.26.0 h1:5yGGsPYI1ZCva93U0AoKi/iZrNhaJEjr324YVsiD89I=
27+
github.com/go-openapi/swag/conv v0.26.0/go.mod h1:tpAmIL7X58VPnHHiSO4uE3jBeRamGsFsfdDeDtb5ECE=
28+
github.com/go-openapi/swag/fileutils v0.26.0 h1:WJoPRvsA7QRiiWluowkLJa9jaYR7FCuxmDvnCgaRRxU=
29+
github.com/go-openapi/swag/fileutils v0.26.0/go.mod h1:0WDJ7lp67eNjPMO50wAWYlKvhOb6CQ37rzR7wrgI8Tc=
30+
github.com/go-openapi/swag/jsonname v0.25.5 h1:8p150i44rv/Drip4vWI3kGi9+4W9TdI3US3uUYSFhSo=
31+
github.com/go-openapi/swag/jsonname v0.25.5/go.mod h1:jNqqikyiAK56uS7n8sLkdaNY/uq6+D2m2LANat09pKU=
32+
github.com/go-openapi/swag/jsonutils v0.26.0 h1:FawFML2iAXsPqmERscuMPIHmFsoP1tOqWkxBaKNMsnA=
33+
github.com/go-openapi/swag/jsonutils v0.26.0/go.mod h1:2VmA0CJlyFqgawOaPI9psnjFDqzyivIqLYN34t9p91E=
34+
github.com/go-openapi/swag/jsonutils/fixtures_test v0.26.0 h1:apqeINu/ICHouqiRZbyFvuDge5jCmmLTqGQ9V95EaOM=
35+
github.com/go-openapi/swag/jsonutils/fixtures_test v0.26.0/go.mod h1:AyM6QT8uz5IdKxk5akv0y6u4QvcL9GWERt0Jx/F/R8Y=
36+
github.com/go-openapi/swag/loading v0.25.5 h1:odQ/umlIZ1ZVRteI6ckSrvP6e2w9UTF5qgNdemJHjuU=
37+
github.com/go-openapi/swag/loading v0.25.5/go.mod h1:I8A8RaaQ4DApxhPSWLNYWh9NvmX2YKMoB9nwvv6oW6g=
38+
github.com/go-openapi/swag/mangling v0.25.5 h1:hyrnvbQRS7vKePQPHHDso+k6CGn5ZBs5232UqWZmJZw=
39+
github.com/go-openapi/swag/mangling v0.25.5/go.mod h1:6hadXM/o312N/h98RwByLg088U61TPGiltQn71Iw0NY=
40+
github.com/go-openapi/swag/stringutils v0.26.0 h1:qZQngLxs5s7SLijc3N2ZO+fUq2o8LjuWAASSrJuh+xg=
41+
github.com/go-openapi/swag/stringutils v0.26.0/go.mod h1:sWn5uY+QIIspwPhvgnqJsH8xqFT2ZbYcvbcFanRyhFE=
42+
github.com/go-openapi/swag/typeutils v0.26.0 h1:2kdEwdiNWy+JJdOvu5MA2IIg2SylWAFuuyQIKYybfq4=
43+
github.com/go-openapi/swag/typeutils v0.26.0/go.mod h1:oovDuIUvTrEHVMqWilQzKzV4YlSKgyZmFh7AlfABNVE=
44+
github.com/go-openapi/swag/yamlutils v0.25.5 h1:kASCIS+oIeoc55j28T4o8KwlV2S4ZLPT6G0iq2SSbVQ=
45+
github.com/go-openapi/swag/yamlutils v0.25.5/go.mod h1:Gek1/SjjfbYvM+Iq4QGwa/2lEXde9n2j4a3wI3pNuOQ=
46+
github.com/go-openapi/testify/enable/yaml/v2 v2.5.0 h1:3hZD1fwydvCx/cc1R2uYNQirHqf2s6lqpKV3FcNTURA=
47+
github.com/go-openapi/testify/enable/yaml/v2 v2.5.0/go.mod h1:TvDZKBH7ZbMaF3EqH2AwTvNQCmzyZq8K1agRjf1B+Nk=
48+
github.com/go-openapi/testify/v2 v2.5.0 h1:UOCr63aAsMIDydZbZGqo5Ev01D4eydItRbekDuZMJLw=
49+
github.com/go-openapi/testify/v2 v2.5.0/go.mod h1:SgsVHtfooshd0tublTtJ50FPKhujf47YRqauXXOUxfw=
50+
github.com/go-openapi/validate v0.25.2 h1:12NsfLAwGegqbGWr2CnvT65X/Q2USJipmJ9b7xDJZz0=
51+
github.com/go-openapi/validate v0.25.2/go.mod h1:Pgl1LpPPGFnZ+ys4/hTlDiRYQdI1ocKypgE+8Q8BLfY=
52+
github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro=
53+
github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
54+
github.com/google/brotli/go/cbrotli v0.0.0-20230829110029-ed738e842d2f h1:jopqB+UTSdJGEJT8tEqYyE29zN91fi2827oLET8tl7k=
55+
github.com/google/brotli/go/cbrotli v0.0.0-20230829110029-ed738e842d2f/go.mod h1:nOPhAkwVliJdNTkj3gXpljmWhjc4wCaVqbMJcPKWP4s=
56+
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
57+
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
58+
github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I=
59+
github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
60+
github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
61+
github.com/oklog/ulid/v2 v2.1.1 h1:suPZ4ARWLOJLegGFiZZ1dFAkqzhMjL3J1TzI+5wHz8s=
62+
github.com/oklog/ulid/v2 v2.1.1/go.mod h1:rcEKHmBBKfef9DhnvX7y1HZBYxjXb0cP5ExxNsTT1QQ=
63+
github.com/pborman/getopt v0.0.0-20170112200414-7148bc3a4c30/go.mod h1:85jBQOZwpVEaDAr341tbn15RS4fCAsIst0qp7i8ex1o=
64+
github.com/pierrec/lz4/v4 v4.1.18 h1:xaKrnTkyoqfh1YItXl56+6KJNVYWlEEPuAQW9xsplYQ=
65+
github.com/pierrec/lz4/v4 v4.1.18/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
66+
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
67+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
68+
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
69+
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
70+
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
71+
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
72+
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
73+
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
74+
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
75+
github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
76+
github.com/valyala/gozstd v1.20.1 h1:xPnnnvjmaDDitMFfDxmQ4vpx0+3CdTg2o3lALvXTU/g=
77+
github.com/valyala/gozstd v1.20.1/go.mod h1:y5Ew47GLlP37EkTB+B4s7r6A5rdaeB7ftbl9zoYiIPQ=
78+
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
79+
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
80+
golang.org/x/net v0.53.0 h1:d+qAbo5L0orcWAr0a9JweQpjXF19LMXJE8Ey7hwOdUA=
81+
golang.org/x/net v0.53.0/go.mod h1:JvMuJH7rrdiCfbeHoo3fCQU24Lf5JJwT9W3sJFulfgs=
82+
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
83+
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
84+
golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg=
85+
golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164=
86+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
87+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
88+
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
89+
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
90+
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
// Package main shows how to add transparent response compression
5+
// (gzip, brotli, etc.) to a go-openapi server by wrapping the
6+
// http.Handler returned by middleware.Serve with the CAFxX
7+
// httpcompression adapter.
8+
//
9+
// The runtime does not ship compression itself; this example is a
10+
// recipe for composing the existing ecosystem with the go-openapi
11+
// pipeline. See server-middleware/negotiate.ContentEncoding (now
12+
// deprecated) for context.
13+
//
14+
// Run:
15+
//
16+
// go run .
17+
// curl -sH 'Accept-Encoding: gzip' -i http://localhost:8080/api/greeting | head
18+
package main
19+
20+
import (
21+
"encoding/json"
22+
"log"
23+
"net/http"
24+
"time"
25+
26+
httpcompression "github.com/CAFxX/httpcompression"
27+
28+
"github.com/go-openapi/loads"
29+
"github.com/go-openapi/runtime"
30+
"github.com/go-openapi/runtime/middleware"
31+
"github.com/go-openapi/runtime/middleware/untyped"
32+
)
33+
34+
const (
35+
// greetingPayloadKeys keeps the response body comfortably above
36+
// the compression middleware's MinSize threshold so the example
37+
// actually exercises the compressor instead of falling through.
38+
greetingPayloadKeys = 32
39+
40+
listenAddress = ":8080"
41+
readHeaderTimeout = 5 * time.Second
42+
)
43+
44+
const swaggerSpec = `{
45+
"swagger": "2.0",
46+
"info": {"title": "Compression Demo", "version": "1.0"},
47+
"basePath": "/api",
48+
"consumes": ["application/json"],
49+
"produces": ["application/json"],
50+
"paths": {
51+
"/greeting": {
52+
"get": {
53+
"operationId": "greeting",
54+
"responses": {
55+
"200": {
56+
"description": "a greeting payload large enough to be worth compressing",
57+
"schema": {"type": "object"}
58+
}
59+
}
60+
}
61+
}
62+
}
63+
}`
64+
65+
// greeting returns a payload large enough to clear the compression
66+
// middleware's default minimum-size threshold (CAFxX defaults to
67+
// DefaultMinSize = 200 bytes — payloads below that are left
68+
// uncompressed because the wire overhead would outweigh the win).
69+
var greeting = runtime.OperationHandlerFunc(func(_ any) (any, error) {
70+
greetings := make(map[string]string, greetingPayloadKeys)
71+
for i := range greetingPayloadKeys {
72+
greetings[encodeKey(i)] = "Hello from go-openapi! Compression makes this body smaller on the wire."
73+
}
74+
return greetings, nil
75+
})
76+
77+
func encodeKey(i int) string {
78+
return "greeting_" + string(rune('a'+i%26))
79+
}
80+
81+
func newAPI() (http.Handler, error) {
82+
spec, err := loads.Analyzed(json.RawMessage(swaggerSpec), "")
83+
if err != nil {
84+
return nil, err
85+
}
86+
api := untyped.NewAPI(spec)
87+
api.RegisterOperation("get", "/greeting", greeting)
88+
return middleware.Serve(spec, api), nil
89+
}
90+
91+
func main() {
92+
apiHandler, err := newAPI()
93+
if err != nil {
94+
log.Fatalf("build api: %v", err)
95+
}
96+
97+
// DefaultAdapter wires gzip + brotli encoders with sane defaults:
98+
// content-type allowlist, minimum-size threshold, Vary and
99+
// Content-Length handling, ETag suffixing for cacheability.
100+
//
101+
// Use Adapter (without "Default") for explicit codec + threshold
102+
// control:
103+
//
104+
// compress, err := httpcompression.Adapter(
105+
// httpcompression.GzipCompressionLevel(6),
106+
// httpcompression.BrotliCompressionLevel(4),
107+
// httpcompression.MinSize(512),
108+
// httpcompression.ContentTypes([]string{"application/json"}, false),
109+
// )
110+
compress, err := httpcompression.DefaultAdapter()
111+
if err != nil {
112+
log.Fatalf("compression adapter: %v", err)
113+
}
114+
115+
// Wrap the go-openapi handler. The order matters:
116+
// - the compressor must be OUTSIDE the api pipeline so it sees
117+
// the final response bytes;
118+
// - any TLS / auth / rate-limiting middleware typically wraps
119+
// the compressor (i.e. compressor sits between application
120+
// code and transport-level middleware).
121+
mux := http.NewServeMux()
122+
mux.Handle("/", compress(apiHandler))
123+
124+
log.Printf("listening on %s", listenAddress)
125+
srv := &http.Server{
126+
Addr: listenAddress,
127+
Handler: mux,
128+
ReadHeaderTimeout: readHeaderTimeout,
129+
}
130+
log.Fatal(srv.ListenAndServe())
131+
}

go.work

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use (
22
.
33
./client-middleware/opentracing
4+
./docs/examples/middleware/compression
45
./server-middleware
56
)
67

server-middleware/negotiate/negotiate.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,16 @@ func WithMatchSuffix(enable bool) Option {
8989
//
9090
// Encoding tokens have no parameters, so this function is unaffected by
9191
// the v0.30 parameter-honouring change to [ContentType].
92+
//
93+
// Deprecated: ContentEncoding negotiation is not used by the components
94+
// of this project.
95+
//
96+
// This historical addition has never been associated with proper
97+
// compression middleware and is thus half a feature.
98+
// The runtime does not ship compression.
99+
// Use github.com/CAFxX/httpcompression or github.com/klauspost/compress/gzhttp
100+
// at the http.Handler level, or github.com/klauspost/compress/* for client
101+
// transport wrapping. See docs/examples/middleware for a recipe.
92102
func ContentEncoding(r *http.Request, offers []string) string {
93103
bestOffer := "identity"
94104
bestQ := -1.0

0 commit comments

Comments
 (0)