Skip to content

Commit a7cad41

Browse files
fraenkelbradfitz
authored andcommitted
net/http/httputil: log err encountered during reverseproxy body copying
Fixes #16659 Change-Id: I13dd797e93e0b572eaf8726f1be594870d40183b Reviewed-on: https://go-review.googlesource.com/30692 Run-TryBot: Brad Fitzpatrick <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Brad Fitzpatrick <[email protected]>
1 parent 95f3e47 commit a7cad41

File tree

2 files changed

+71
-1
lines changed

2 files changed

+71
-1
lines changed

src/net/http/httputil/reverseproxy.go

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -260,12 +260,40 @@ func (p *ReverseProxy) copyResponse(dst io.Writer, src io.Reader) {
260260
if p.BufferPool != nil {
261261
buf = p.BufferPool.Get()
262262
}
263-
io.CopyBuffer(dst, src, buf)
263+
p.copyBuffer(dst, src, buf)
264264
if p.BufferPool != nil {
265265
p.BufferPool.Put(buf)
266266
}
267267
}
268268

269+
func (p *ReverseProxy) copyBuffer(dst io.Writer, src io.Reader, buf []byte) (int64, error) {
270+
if len(buf) == 0 {
271+
buf = make([]byte, 32*1024)
272+
}
273+
var written int64
274+
for {
275+
nr, rerr := src.Read(buf)
276+
if rerr != nil && rerr != io.EOF {
277+
p.logf("httputil: ReverseProxy read error during body copy: %v", rerr)
278+
}
279+
if nr > 0 {
280+
nw, werr := dst.Write(buf[:nr])
281+
if nw > 0 {
282+
written += int64(nw)
283+
}
284+
if werr != nil {
285+
return written, werr
286+
}
287+
if nr != nw {
288+
return written, io.ErrShortWrite
289+
}
290+
}
291+
if rerr != nil {
292+
return written, rerr
293+
}
294+
}
295+
}
296+
269297
func (p *ReverseProxy) logf(format string, args ...interface{}) {
270298
if p.ErrorLog != nil {
271299
p.ErrorLog.Printf(format, args...)

src/net/http/httputil/reverseproxy_test.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"bufio"
1111
"bytes"
1212
"errors"
13+
"fmt"
1314
"io"
1415
"io/ioutil"
1516
"log"
@@ -581,3 +582,44 @@ func TestReverseProxy_NilBody(t *testing.T) {
581582
t.Errorf("status code = %v; want 502 (Gateway Error)", res.Status)
582583
}
583584
}
585+
586+
// Issue 16659: log errors from short read
587+
func TestReverseProxy_CopyBuffer(t *testing.T) {
588+
backendServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
589+
out := "this call was relayed by the reverse proxy"
590+
// Coerce a wrong content length to induce io.UnexpectedEOF
591+
w.Header().Set("Content-Length", fmt.Sprintf("%d", len(out)*2))
592+
fmt.Fprintln(w, out)
593+
}))
594+
defer backendServer.Close()
595+
596+
rpURL, err := url.Parse(backendServer.URL)
597+
if err != nil {
598+
t.Fatal(err)
599+
}
600+
601+
var proxyLog bytes.Buffer
602+
rproxy := NewSingleHostReverseProxy(rpURL)
603+
rproxy.ErrorLog = log.New(&proxyLog, "", log.Lshortfile)
604+
frontendProxy := httptest.NewServer(rproxy)
605+
defer frontendProxy.Close()
606+
607+
resp, err := http.Get(frontendProxy.URL)
608+
if err != nil {
609+
t.Fatalf("failed to reach proxy: %v", err)
610+
}
611+
defer resp.Body.Close()
612+
613+
if _, err := ioutil.ReadAll(resp.Body); err == nil {
614+
t.Fatalf("want non-nil error")
615+
}
616+
expected := []string{
617+
"EOF",
618+
"read",
619+
}
620+
for _, phrase := range expected {
621+
if !bytes.Contains(proxyLog.Bytes(), []byte(phrase)) {
622+
t.Errorf("expected log to contain phrase %q", phrase)
623+
}
624+
}
625+
}

0 commit comments

Comments
 (0)