From 288762e79c5f99a72778c7e8ce79d6531f229923 Mon Sep 17 00:00:00 2001 From: Tomasz Elendt Date: Thu, 4 Jun 2015 23:14:37 +0200 Subject: [PATCH 1/2] Change responseWriterDelegator.written to int64 Change responseWriterDelegator.written to int64 so that responses bigger than 4GB could be properly observed on 32 bit systems. --- prometheus/http.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/prometheus/http.go b/prometheus/http.go index dac92fd..e391af4 100644 --- a/prometheus/http.go +++ b/prometheus/http.go @@ -178,7 +178,7 @@ type responseWriterDelegator struct { handler, method string status int - written int + written int64 wroteHeader bool } @@ -193,7 +193,7 @@ func (r *responseWriterDelegator) Write(b []byte) (int, error) { r.WriteHeader(http.StatusOK) } n, err := r.ResponseWriter.Write(b) - r.written += n + r.written += int64(n) return n, err } From dd4dd698786aafa0e40d0757607263f9cc766299 Mon Sep 17 00:00:00 2001 From: Tomasz Elendt Date: Thu, 4 Jun 2015 23:24:54 +0200 Subject: [PATCH 2/2] Solve "The Proxy Problem" of InstrumentHandler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- prometheus/http.go | 41 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/prometheus/http.go b/prometheus/http.go index e391af4..eabe602 100644 --- a/prometheus/http.go +++ b/prometheus/http.go @@ -14,6 +14,9 @@ package prometheus import ( + "bufio" + "io" + "net" "net/http" "strconv" "strings" @@ -141,7 +144,18 @@ func InstrumentHandlerFuncWithOpts(opts SummaryOpts, handlerFunc func(http.Respo urlLen = len(r.URL.String()) } 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) @@ -197,6 +211,31 @@ func (r *responseWriterDelegator) Write(b []byte) (int, error) { 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 { switch m { case "GET", "get":