@@ -55,7 +55,25 @@ const (
55
55
processStartTimeHeader = "Process-Start-Time-Unix"
56
56
)
57
57
58
- var defaultEncodingOffers = []string {"gzip" , "zstd" }
58
+ type Compression int
59
+
60
+ const (
61
+ Identity Compression = iota
62
+ Gzip
63
+ Zstd
64
+ )
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
+ var defaultCompressionFormats = []Compression {Identity , Gzip , Zstd }
59
77
60
78
var gzipPool = sync.Pool {
61
79
New : func () interface {} {
@@ -168,46 +186,14 @@ func HandlerForTransactional(reg prometheus.TransactionalGatherer, opts HandlerO
168
186
} else {
169
187
contentType = expfmt .Negotiate (req .Header )
170
188
}
171
- header := rsp .Header ()
172
- header .Set (contentTypeHeader , string (contentType ))
173
-
174
- w := io .Writer (rsp )
175
- if ! opts .DisableCompression {
176
- offers := defaultEncodingOffers
177
- if len (opts .EncodingOffers ) > 0 {
178
- offers = opts .EncodingOffers
179
- }
180
- // TODO(mrueg): Replace internal/github.com/gddo once https://github.com/golang/go/issues/19307 is implemented.
181
- compression := httputil .NegotiateContentEncoding (req , offers )
182
- switch compression {
183
- case "zstd" :
184
- header .Set (contentEncodingHeader , "zstd" )
185
- // TODO(mrueg): Replace klauspost/compress with stdlib implementation once https://github.com/golang/go/issues/62513 is implemented.
186
- z , err := zstd .NewWriter (rsp , zstd .WithEncoderLevel (zstd .SpeedFastest ))
187
- if err != nil {
188
- return
189
- }
190
-
191
- z .Reset (w )
192
- defer z .Close ()
189
+ rsp .Header ().Set (contentTypeHeader , string (contentType ))
193
190
194
- w = z
195
- case "gzip" :
196
- header .Set (contentEncodingHeader , "gzip" )
197
- gz := gzipPool .Get ().(* gzip.Writer )
198
- defer gzipPool .Put (gz )
191
+ w , err := GetWriter (req , rsp , opts .DisableCompression , opts .OfferedCompressions )
199
192
200
- gz .Reset (w )
201
- defer gz .Close ()
202
-
203
- w = gz
204
- case "identity" :
205
- // This means the content is not encoded.
206
- default :
207
- // The content encoding was not implemented yet.
208
- return
193
+ if err != nil {
194
+ if opts .ErrorLog != nil {
195
+ opts .ErrorLog .Println ("error setting getting writer" , err )
209
196
}
210
-
211
197
}
212
198
213
199
enc := expfmt .NewEncoder (w , contentType )
@@ -373,12 +359,19 @@ type HandlerOpts struct {
373
359
// no effect on the HTTP status code because ErrorHandling is set to
374
360
// ContinueOnError.
375
361
Registry prometheus.Registerer
376
- // If DisableCompression is true, the handler will never compress the
377
- // response, even if requested by the client.
362
+ // DisableCompression disables the response encoding (compression) and
363
+ // encoding negotiation. If true, the handler will
364
+ // never compress the response, even if requested
365
+ // by the client and the OfferedCompressions field is ignored.
378
366
DisableCompression bool
379
- // If DisableCompression is false, this option will allow to define the
380
- // set of offered encoding algorithms.
381
- EncodingOffers []string
367
+ // OfferedCompressions is a set of encodings (compressions) handler will
368
+ // try to offer when negotiating with the client. This defaults to zstd,
369
+ // gzip and identity.
370
+ // NOTE: If handler can't agree on the encodings with the client or
371
+ // caller using unsupported or empty encodings in OfferedCompressions,
372
+ // handler always fallbacks to no compression (identity), for
373
+ // compatibility reasons. In such cases ErrorLog will be used if set.
374
+ OfferedCompressions []Compression
382
375
// The number of concurrent HTTP requests is limited to
383
376
// MaxRequestsInFlight. Additional requests are responded to with 503
384
377
// Service Unavailable and a suitable message in the body. If
@@ -426,3 +419,48 @@ func httpError(rsp http.ResponseWriter, err error) {
426
419
http .StatusInternalServerError ,
427
420
)
428
421
}
422
+
423
+ func GetWriter (r * http.Request , rsp http.ResponseWriter , disableCompression bool , offeredCompressions []Compression ) (io.Writer , error ) {
424
+ w := io .Writer (rsp )
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
+
445
+ z .Reset (w )
446
+ defer z .Close ()
447
+
448
+ w = z
449
+ case "gzip" :
450
+ rsp .Header ().Set (contentEncodingHeader , "gzip" )
451
+ gz := gzipPool .Get ().(* gzip.Writer )
452
+ defer gzipPool .Put (gz )
453
+
454
+ gz .Reset (w )
455
+ defer gz .Close ()
456
+
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 )
463
+ }
464
+ }
465
+ return w , nil
466
+ }
0 commit comments