Solve "The Proxy Problem" of InstrumentHandler

InstrumentHandler provides proxy object over given http.ResponseWriter
- responseWriterDelegator that only implements the bare minimum
required of an http.ResponseWriter and doesn’t implement any interface
upgrades (http.Flusher, http.Hijacker, etc.). This commit fixes it by
providing fancyResponseWriterDelegator with all the fancy bells and
whistles if standard library http.ResponseWriter is detected.

Heavily inspired by Goji's middleware.

https://avtok.com/2014/11/05/interface-upgrades.html#the-proxy-problem
This commit is contained in:
Tomasz Elendt 2015-06-04 23:24:54 +02:00
parent 288762e79c
commit dd4dd69878
1 changed files with 40 additions and 1 deletions

View File

@ -14,6 +14,9 @@
package prometheus package prometheus
import ( import (
"bufio"
"io"
"net"
"net/http" "net/http"
"strconv" "strconv"
"strings" "strings"
@ -141,7 +144,18 @@ func InstrumentHandlerFuncWithOpts(opts SummaryOpts, handlerFunc func(http.Respo
urlLen = len(r.URL.String()) urlLen = len(r.URL.String())
} }
go computeApproximateRequestSize(r, out, urlLen) go computeApproximateRequestSize(r, out, urlLen)
handlerFunc(delegate, r)
_, cn := w.(http.CloseNotifier)
_, fl := w.(http.Flusher)
_, hj := w.(http.Hijacker)
_, rf := w.(io.ReaderFrom)
var rw http.ResponseWriter
if cn && fl && hj && rf {
rw = &fancyResponseWriterDelegator{delegate}
} else {
rw = delegate
}
handlerFunc(rw, r)
elapsed := float64(time.Since(now)) / float64(time.Microsecond) elapsed := float64(time.Since(now)) / float64(time.Microsecond)
@ -197,6 +211,31 @@ func (r *responseWriterDelegator) Write(b []byte) (int, error) {
return n, err return n, err
} }
type fancyResponseWriterDelegator struct {
*responseWriterDelegator
}
func (f *fancyResponseWriterDelegator) CloseNotify() <-chan bool {
return f.ResponseWriter.(http.CloseNotifier).CloseNotify()
}
func (f *fancyResponseWriterDelegator) Flush() {
f.ResponseWriter.(http.Flusher).Flush()
}
func (f *fancyResponseWriterDelegator) Hijack() (net.Conn, *bufio.ReadWriter, error) {
return f.ResponseWriter.(http.Hijacker).Hijack()
}
func (f *fancyResponseWriterDelegator) ReadFrom(r io.Reader) (int64, error) {
if !f.wroteHeader {
f.WriteHeader(http.StatusOK)
}
n, err := f.ResponseWriter.(io.ReaderFrom).ReadFrom(r)
f.written += n
return n, err
}
func sanitizeMethod(m string) string { func sanitizeMethod(m string) string {
switch m { switch m {
case "GET", "get": case "GET", "get":