Skip to content

Commit 4739e9c

Browse files
authored
Merge pull request #726 from prometheus/beorn7/promhttp
Fix promhttp error handling
2 parents 346356f + 586178b commit 4739e9c

File tree

2 files changed

+20
-17
lines changed

2 files changed

+20
-17
lines changed

prometheus/promhttp/delegator.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,12 +53,16 @@ func (r *responseWriterDelegator) Written() int64 {
5353
}
5454

5555
func (r *responseWriterDelegator) WriteHeader(code int) {
56+
if r.observeWriteHeader != nil && !r.wroteHeader {
57+
// Only call observeWriteHeader for the 1st time. It's a bug if
58+
// WriteHeader is called more than once, but we want to protect
59+
// against it here. Note that we still delegate the WriteHeader
60+
// to the original ResponseWriter to not mask the bug from it.
61+
r.observeWriteHeader(code)
62+
}
5663
r.status = code
5764
r.wroteHeader = true
5865
r.ResponseWriter.WriteHeader(code)
59-
if r.observeWriteHeader != nil {
60-
r.observeWriteHeader(code)
61-
}
6266
}
6367

6468
func (r *responseWriterDelegator) Write(b []byte) (int, error) {

prometheus/promhttp/http.go

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -167,15 +167,12 @@ func HandlerFor(reg prometheus.Gatherer, opts HandlerOpts) http.Handler {
167167

168168
enc := expfmt.NewEncoder(w, contentType)
169169

170-
var lastErr error
171-
172170
// handleError handles the error according to opts.ErrorHandling
173171
// and returns true if we have to abort after the handling.
174172
handleError := func(err error) bool {
175173
if err == nil {
176174
return false
177175
}
178-
lastErr = err
179176
if opts.ErrorLog != nil {
180177
opts.ErrorLog.Println("error encoding and sending metric family:", err)
181178
}
@@ -184,7 +181,10 @@ func HandlerFor(reg prometheus.Gatherer, opts HandlerOpts) http.Handler {
184181
case PanicOnError:
185182
panic(err)
186183
case HTTPErrorOnError:
187-
httpError(rsp, err)
184+
// We cannot really send an HTTP error at this
185+
// point because we most likely have written
186+
// something to rsp already. But at least we can
187+
// stop sending.
188188
return true
189189
}
190190
// Do nothing in all other cases, including ContinueOnError.
@@ -202,10 +202,6 @@ func HandlerFor(reg prometheus.Gatherer, opts HandlerOpts) http.Handler {
202202
return
203203
}
204204
}
205-
206-
if lastErr != nil {
207-
httpError(rsp, lastErr)
208-
}
209205
})
210206

211207
if opts.Timeout <= 0 {
@@ -276,7 +272,12 @@ type HandlerErrorHandling int
276272
// errors are encountered.
277273
const (
278274
// Serve an HTTP status code 500 upon the first error
279-
// encountered. Report the error message in the body.
275+
// encountered. Report the error message in the body. Note that HTTP
276+
// errors cannot be served anymore once the beginning of a regular
277+
// payload has been sent. Thus, in the (unlikely) case that encoding the
278+
// payload into the negotiated wire format fails, serving the response
279+
// will simply be aborted. Set an ErrorLog in HandlerOpts to detect
280+
// those errors.
280281
HTTPErrorOnError HandlerErrorHandling = iota
281282
// Ignore errors and try to serve as many metrics as possible. However,
282283
// if no metrics can be served, serve an HTTP status code 500 and the
@@ -365,11 +366,9 @@ func gzipAccepted(header http.Header) bool {
365366
}
366367

367368
// httpError removes any content-encoding header and then calls http.Error with
368-
// the provided error and http.StatusInternalServerErrer. Error contents is
369-
// supposed to be uncompressed plain text. However, same as with a plain
370-
// http.Error, any header settings will be void if the header has already been
371-
// sent. The error message will still be written to the writer, but it will
372-
// probably be of limited use.
369+
// the provided error and http.StatusInternalServerError. Error contents is
370+
// supposed to be uncompressed plain text. Same as with a plain http.Error, this
371+
// must not be called if the header or any payload has already been sent.
373372
func httpError(rsp http.ResponseWriter, err error) {
374373
rsp.Header().Del(contentEncodingHeader)
375374
http.Error(

0 commit comments

Comments
 (0)