@@ -55,24 +55,14 @@ const (
55
55
processStartTimeHeader = "Process-Start-Time-Unix"
56
56
)
57
57
58
- type Compression int
58
+ type Compression string
59
59
60
60
const (
61
- Identity Compression = iota
62
- Gzip
63
- Zstd
61
+ Identity Compression = "identity"
62
+ Gzip Compression = "gzip"
63
+ Zstd Compression = "zstd"
64
64
)
65
65
66
- var compressions = [... ]string {
67
- "identity" ,
68
- "gzip" ,
69
- "zstd" ,
70
- }
71
-
72
- func (c Compression ) String () string {
73
- return compressions [c ]
74
- }
75
-
76
66
var defaultCompressionFormats = []Compression {Identity , Gzip , Zstd }
77
67
78
68
var gzipPool = sync.Pool {
@@ -143,6 +133,18 @@ func HandlerForTransactional(reg prometheus.TransactionalGatherer, opts HandlerO
143
133
}
144
134
}
145
135
136
+ // Select all supported compression formats
137
+ var compressions []string
138
+ if ! opts .DisableCompression {
139
+ offers := defaultCompressionFormats
140
+ if len (opts .OfferedCompressions ) > 0 {
141
+ offers = opts .OfferedCompressions
142
+ }
143
+ for _ , comp := range offers {
144
+ compressions = append (compressions , string (comp ))
145
+ }
146
+ }
147
+
146
148
h := http .HandlerFunc (func (rsp http.ResponseWriter , req * http.Request ) {
147
149
if ! opts .ProcessStartTime .IsZero () {
148
150
rsp .Header ().Set (processStartTimeHeader , strconv .FormatInt (opts .ProcessStartTime .Unix (), 10 ))
@@ -188,13 +190,17 @@ func HandlerForTransactional(reg prometheus.TransactionalGatherer, opts HandlerO
188
190
}
189
191
rsp .Header ().Set (contentTypeHeader , string (contentType ))
190
192
191
- w , err := GetWriter (req , rsp , opts .DisableCompression , opts . OfferedCompressions )
193
+ w , encodingHeader , err := NegotiateEncodingWriter (req , rsp , opts .DisableCompression , compressions )
192
194
if err != nil {
193
195
if opts .ErrorLog != nil {
194
196
opts .ErrorLog .Println ("error getting writer" , err )
195
197
}
198
+ // Since the writer received from NegotiateEncodingWriter will be nil, in case there's an error, we set it here
199
+ w = io .Writer (rsp )
196
200
}
197
201
202
+ rsp .Header ().Set (contentEncodingHeader , encodingHeader )
203
+
198
204
enc := expfmt .NewEncoder (w , contentType )
199
205
200
206
// handleError handles the error according to opts.ErrorHandling
@@ -419,48 +425,46 @@ func httpError(rsp http.ResponseWriter, err error) {
419
425
)
420
426
}
421
427
422
- func GetWriter (r * http.Request , rsp http.ResponseWriter , disableCompression bool , offeredCompressions []Compression ) (io.Writer , error ) {
423
- w := io .Writer (rsp )
424
- rsp .Header ().Set (contentEncodingHeader , "identity" )
425
- if ! disableCompression {
426
- offers := defaultCompressionFormats
427
- if len (offeredCompressions ) > 0 {
428
- offers = offeredCompressions
429
- }
430
- var compressions []string
431
- for _ , comp := range offers {
432
- compressions = append (compressions , comp .String ())
433
- }
434
- // TODO(mrueg): Replace internal/github.com/gddo once https://github.com/golang/go/issues/19307 is implemented.
435
- compression := httputil .NegotiateContentEncoding (r , compressions )
436
- switch compression {
437
- case "zstd" :
438
- rsp .Header ().Set (contentEncodingHeader , "zstd" )
439
- // TODO(mrueg): Replace klauspost/compress with stdlib implementation once https://github.com/golang/go/issues/62513 is implemented.
440
- z , err := zstd .NewWriter (rsp , zstd .WithEncoderLevel (zstd .SpeedFastest ))
441
- if err != nil {
442
- return nil , err
443
- }
444
428
445
- z .Reset (w )
446
- defer z .Close ()
429
+ // NegotiateEncodingWriter reads the Accept-Encoding header from a request and
430
+ // selects the right compression based on an allow-list of supported
431
+ // compressions. It returns a writer implementing the compression and an the
432
+ // correct value that the caller can set in the response header.
433
+ func NegotiateEncodingWriter (r * http.Request , rw io.Writer , disableCompression bool , compressions []string ) (_ io.Writer , encodingHeaderValue string , _ error ) {
434
+ w := io .Writer (rw )
447
435
448
- w = z
449
- case "gzip" :
450
- rsp .Header ().Set (contentEncodingHeader , "gzip" )
451
- gz := gzipPool .Get ().(* gzip.Writer )
452
- defer gzipPool .Put (gz )
436
+ if disableCompression {
437
+ return w , string (Identity ), nil
438
+ }
453
439
454
- gz . Reset ( w )
455
- defer gz . Close ( )
440
+ // TODO(mrueg): Replace internal/github.com/gddo once https://github.com/golang/go/issues/19307 is implemented.
441
+ compression := httputil . NegotiateContentEncoding ( r , compressions )
456
442
457
- w = gz
458
- case "identity " :
459
- // This means the content is not compressed .
460
- default :
461
- // The content encoding was not implemented yet.
462
- return w , fmt . Errorf ( "content compression format not recognized: %s. Valid formats are: %s " , compression , defaultCompressionFormats )
443
+ switch compression {
444
+ case "zstd " :
445
+ // TODO(mrueg): Replace klauspost/compress with stdlib implementation once https://github.com/golang/go/issues/62513 is implemented .
446
+ z , err := zstd . NewWriter ( rw , zstd . WithEncoderLevel ( zstd . SpeedFastest ))
447
+ if err != nil {
448
+ return nil , " " , err
463
449
}
450
+
451
+ z .Reset (w )
452
+ defer z .Close ()
453
+
454
+ w = z
455
+ case "gzip" :
456
+ gz := gzipPool .Get ().(* gzip.Writer )
457
+ defer gzipPool .Put (gz )
458
+
459
+ gz .Reset (w )
460
+ defer gz .Close ()
461
+
462
+ w = gz
463
+ case "identity" :
464
+ // This means the content is not compressed.
465
+ default :
466
+ // The content encoding was not implemented yet.
467
+ return nil , "" , fmt .Errorf ("content compression format not recognized: %s. Valid formats are: %s" , compression , defaultCompressionFormats )
464
468
}
465
- return w , nil
469
+ return w , compression , nil
466
470
}
0 commit comments