Add gzip support.

Change-Id: I6ea6e0dcbe7234ad143403d262da6cb40e7d3b50
This commit is contained in:
Bjoern Rabenstein 2014-07-04 17:08:57 +02:00
parent 457bc47eac
commit 23e5e5fefd
1 changed files with 32 additions and 4 deletions

View File

@ -28,10 +28,12 @@ import (
"net/http" "net/http"
"net/url" "net/url"
"sort" "sort"
"strings"
"sync" "sync"
dto "github.com/prometheus/client_model/go" dto "github.com/prometheus/client_model/go"
"compress/gzip"
"code.google.com/p/goprotobuf/proto" "code.google.com/p/goprotobuf/proto"
"github.com/prometheus/client_golang/_vendor/goautoneg" "github.com/prometheus/client_golang/_vendor/goautoneg"
@ -76,6 +78,10 @@ const (
contentTypeHeader = "Content-Type" contentTypeHeader = "Content-Type"
contentLengthHeader = "Content-Length" contentLengthHeader = "Content-Length"
contentEncodingHeader = "Content-Encoding"
acceptEncodingHeader = "Accept-Encoding"
acceptHeader = "Accept"
) )
// Handler returns the HTTP handler for the global Prometheus registry. It is // Handler returns the HTTP handler for the global Prometheus registry. It is
@ -370,16 +376,23 @@ func (r *registry) ServeHTTP(w http.ResponseWriter, req *http.Request) {
enc, contentType := chooseEncoder(req) enc, contentType := chooseEncoder(req)
buf := r.getBuf() buf := r.getBuf()
defer r.giveBuf(buf) defer r.giveBuf(buf)
if _, err := r.writePB(buf, enc); err != nil { writer, encoding := decorateWriter(req, buf)
if _, err := r.writePB(writer, enc); err != nil {
if r.panicOnCollectError { if r.panicOnCollectError {
panic(err) panic(err)
} }
http.Error(w, "An error has occurred:\n\n"+err.Error(), http.StatusInternalServerError) http.Error(w, "An error has occurred:\n\n"+err.Error(), http.StatusInternalServerError)
return return
} }
if closer, ok := writer.(io.Closer); ok {
closer.Close()
}
header := w.Header() header := w.Header()
header.Set(contentTypeHeader, contentType) header.Set(contentTypeHeader, contentType)
header.Set(contentLengthHeader, fmt.Sprint(buf.Len())) header.Set(contentLengthHeader, fmt.Sprint(buf.Len()))
if encoding != "" {
header.Set(contentEncodingHeader, encoding)
}
w.Write(buf.Bytes()) w.Write(buf.Bytes())
} }
@ -622,7 +635,7 @@ func newRegistry() *registry {
} }
func chooseEncoder(req *http.Request) (encoder, string) { func chooseEncoder(req *http.Request) (encoder, string) {
accepts := goautoneg.ParseAccept(req.Header.Get("Accept")) accepts := goautoneg.ParseAccept(req.Header.Get(acceptHeader))
for _, accept := range accepts { for _, accept := range accepts {
switch { switch {
case accept.Type == "application" && case accept.Type == "application" &&
@ -649,6 +662,21 @@ func chooseEncoder(req *http.Request) (encoder, string) {
return text.MetricFamilyToText, TextTelemetryContentType return text.MetricFamilyToText, TextTelemetryContentType
} }
// decorateWriter wraps a writer to handle gzip compression if requested. It
// returns the decorated writer and the appropriate "Content-Encoding" header
// (which is empty if no compression is enabled).
func decorateWriter(request *http.Request, writer io.Writer) (io.Writer, string) {
header := request.Header.Get(acceptEncodingHeader)
parts := strings.Split(header, ",")
for _, part := range parts {
part := strings.TrimSpace(part)
if part == "gzip" || strings.HasPrefix(part, "gzip;") {
return gzip.NewWriter(writer), "gzip"
}
}
return writer, ""
}
type metricSorter []*dto.Metric type metricSorter []*dto.Metric
func (s metricSorter) Len() int { func (s metricSorter) Len() int {