Skip to content

Commit fb0f7fe

Browse files
author
beorn7
committed
Pull up HTTP changes into deprecated functions
So that also users of those can benefit. Obviously, we will end updating deprecated functions one day (at latest once v0.10 is out). Signed-off-by: beorn7 <[email protected]>
1 parent 62361fc commit fb0f7fe

File tree

1 file changed

+52
-53
lines changed

1 file changed

+52
-53
lines changed

prometheus/http.go

Lines changed: 52 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,7 @@ package prometheus
1515

1616
import (
1717
"bufio"
18-
"bytes"
1918
"compress/gzip"
20-
"fmt"
2119
"io"
2220
"net"
2321
"net/http"
@@ -41,19 +39,10 @@ const (
4139
acceptEncodingHeader = "Accept-Encoding"
4240
)
4341

44-
var bufPool sync.Pool
45-
46-
func getBuf() *bytes.Buffer {
47-
buf := bufPool.Get()
48-
if buf == nil {
49-
return &bytes.Buffer{}
50-
}
51-
return buf.(*bytes.Buffer)
52-
}
53-
54-
func giveBuf(buf *bytes.Buffer) {
55-
buf.Reset()
56-
bufPool.Put(buf)
42+
var gzipPool = sync.Pool{
43+
New: func() interface{} {
44+
return gzip.NewWriter(nil)
45+
},
5746
}
5847

5948
// Handler returns an HTTP handler for the DefaultGatherer. It is
@@ -71,58 +60,40 @@ func Handler() http.Handler {
7160
// Deprecated: Use promhttp.HandlerFor(DefaultGatherer, promhttp.HandlerOpts{})
7261
// instead. See there for further documentation.
7362
func UninstrumentedHandler() http.Handler {
74-
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
63+
return http.HandlerFunc(func(rsp http.ResponseWriter, req *http.Request) {
7564
mfs, err := DefaultGatherer.Gather()
7665
if err != nil {
77-
http.Error(w, "An error has occurred during metrics collection:\n\n"+err.Error(), http.StatusInternalServerError)
66+
httpError(rsp, err)
7867
return
7968
}
8069

8170
contentType := expfmt.Negotiate(req.Header)
82-
buf := getBuf()
83-
defer giveBuf(buf)
84-
writer, encoding := decorateWriter(req, buf)
85-
enc := expfmt.NewEncoder(writer, contentType)
86-
var lastErr error
71+
header := rsp.Header()
72+
header.Set(contentTypeHeader, string(contentType))
73+
74+
w := io.Writer(rsp)
75+
if gzipAccepted(req.Header) {
76+
header.Set(contentEncodingHeader, "gzip")
77+
gz := gzipPool.Get().(*gzip.Writer)
78+
defer gzipPool.Put(gz)
79+
80+
gz.Reset(w)
81+
defer gz.Close()
82+
83+
w = gz
84+
}
85+
86+
enc := expfmt.NewEncoder(w, contentType)
87+
8788
for _, mf := range mfs {
8889
if err := enc.Encode(mf); err != nil {
89-
lastErr = err
90-
http.Error(w, "An error has occurred during metrics encoding:\n\n"+err.Error(), http.StatusInternalServerError)
90+
httpError(rsp, err)
9191
return
9292
}
9393
}
94-
if closer, ok := writer.(io.Closer); ok {
95-
closer.Close()
96-
}
97-
if lastErr != nil && buf.Len() == 0 {
98-
http.Error(w, "No metrics encoded, last error:\n\n"+lastErr.Error(), http.StatusInternalServerError)
99-
return
100-
}
101-
header := w.Header()
102-
header.Set(contentTypeHeader, string(contentType))
103-
header.Set(contentLengthHeader, fmt.Sprint(buf.Len()))
104-
if encoding != "" {
105-
header.Set(contentEncodingHeader, encoding)
106-
}
107-
w.Write(buf.Bytes())
10894
})
10995
}
11096

111-
// decorateWriter wraps a writer to handle gzip compression if requested. It
112-
// returns the decorated writer and the appropriate "Content-Encoding" header
113-
// (which is empty if no compression is enabled).
114-
func decorateWriter(request *http.Request, writer io.Writer) (io.Writer, string) {
115-
header := request.Header.Get(acceptEncodingHeader)
116-
parts := strings.Split(header, ",")
117-
for _, part := range parts {
118-
part = strings.TrimSpace(part)
119-
if part == "gzip" || strings.HasPrefix(part, "gzip;") {
120-
return gzip.NewWriter(writer), "gzip"
121-
}
122-
}
123-
return writer, ""
124-
}
125-
12697
var instLabels = []string{"method", "code"}
12798

12899
type nower interface {
@@ -503,3 +474,31 @@ func sanitizeCode(s int) string {
503474
return strconv.Itoa(s)
504475
}
505476
}
477+
478+
// gzipAccepted returns whether the client will accept gzip-encoded content.
479+
func gzipAccepted(header http.Header) bool {
480+
a := header.Get(acceptEncodingHeader)
481+
parts := strings.Split(a, ",")
482+
for _, part := range parts {
483+
part = strings.TrimSpace(part)
484+
if part == "gzip" || strings.HasPrefix(part, "gzip;") {
485+
return true
486+
}
487+
}
488+
return false
489+
}
490+
491+
// httpError removes any content-encoding header and then calls http.Error with
492+
// the provided error and http.StatusInternalServerErrer. Error contents is
493+
// supposed to be uncompressed plain text. However, same as with a plain
494+
// http.Error, any header settings will be void if the header has already been
495+
// sent. The error message will still be written to the writer, but it will
496+
// probably be of limited use.
497+
func httpError(rsp http.ResponseWriter, err error) {
498+
rsp.Header().Del(contentEncodingHeader)
499+
http.Error(
500+
rsp,
501+
"An error has occurred while serving metrics:\n\n"+err.Error(),
502+
http.StatusInternalServerError,
503+
)
504+
}

0 commit comments

Comments
 (0)