From 0944837f7900d18f9be7e6f77673e994399b7ea7 Mon Sep 17 00:00:00 2001 From: Alexey Borzenkov Date: Thu, 10 Apr 2014 14:12:36 -0700 Subject: [PATCH] net/http: fix requests failing on short gzip body Fixes #7750. LGTM=bradfitz R=golang-codereviews, ibilicc, bradfitz CC=golang-codereviews https://golang.org/cl/84850043 --- src/pkg/net/http/transport.go | 29 ++++++++++++++++++++++------- src/pkg/net/http/transport_test.go | 27 +++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 7 deletions(-) diff --git a/src/pkg/net/http/transport.go b/src/pkg/net/http/transport.go index de0ff9ce57..1d776c2680 100644 --- a/src/pkg/net/http/transport.go +++ b/src/pkg/net/http/transport.go @@ -812,13 +812,7 @@ func (pc *persistConn) readLoop() { resp.Header.Del("Content-Encoding") resp.Header.Del("Content-Length") resp.ContentLength = -1 - gzReader, zerr := gzip.NewReader(resp.Body) - if zerr != nil { - pc.close() - err = zerr - } else { - resp.Body = &readerAndCloser{gzReader, resp.Body} - } + resp.Body = &gzipReader{body: resp.Body} } resp.Body = &bodyEOFSignal{body: resp.Body} } @@ -1156,6 +1150,27 @@ func (es *bodyEOFSignal) condfn(err error) { es.fn = nil } +// gzipReader wraps a response body so it can lazily +// call gzip.NewReader on the first call to Read +type gzipReader struct { + body io.ReadCloser // underlying Response.Body + zr io.Reader // lazily-initialized gzip reader +} + +func (gz *gzipReader) Read(p []byte) (n int, err error) { + if gz.zr == nil { + gz.zr, err = gzip.NewReader(gz.body) + if err != nil { + return 0, err + } + } + return gz.zr.Read(p) +} + +func (gz *gzipReader) Close() error { + return gz.body.Close() +} + type readerAndCloser struct { io.Reader io.Closer diff --git a/src/pkg/net/http/transport_test.go b/src/pkg/net/http/transport_test.go index 24466e5369..6c97119312 100644 --- a/src/pkg/net/http/transport_test.go +++ b/src/pkg/net/http/transport_test.go @@ -803,6 +803,33 @@ func TestTransportGzipRecursive(t *testing.T) { } } +// golang.org/issue/7750: request fails when server replies with +// a short gzip body +func TestTransportGzipShort(t *testing.T) { + defer afterTest(t) + ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { + w.Header().Set("Content-Encoding", "gzip") + w.Write([]byte{0x1f, 0x8b}) + })) + defer ts.Close() + + tr := &Transport{} + defer tr.CloseIdleConnections() + c := &Client{Transport: tr} + res, err := c.Get(ts.URL) + if err != nil { + t.Fatal(err) + } + defer res.Body.Close() + _, err = ioutil.ReadAll(res.Body) + if err == nil { + t.Fatal("Expect an error from reading a body.") + } + if err != io.ErrUnexpectedEOF { + t.Errorf("ReadAll error = %v; want io.ErrUnexpectedEOF", err) + } +} + // tests that persistent goroutine connections shut down when no longer desired. func TestTransportPersistConnLeak(t *testing.T) { if runtime.GOOS == "plan9" { -- 2.50.0