Commit 0944837f authored by Alexey Borzenkov's avatar Alexey Borzenkov Committed by Brad Fitzpatrick

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
parent 5539ef02
...@@ -812,13 +812,7 @@ func (pc *persistConn) readLoop() { ...@@ -812,13 +812,7 @@ func (pc *persistConn) readLoop() {
resp.Header.Del("Content-Encoding") resp.Header.Del("Content-Encoding")
resp.Header.Del("Content-Length") resp.Header.Del("Content-Length")
resp.ContentLength = -1 resp.ContentLength = -1
gzReader, zerr := gzip.NewReader(resp.Body) resp.Body = &gzipReader{body: resp.Body}
if zerr != nil {
pc.close()
err = zerr
} else {
resp.Body = &readerAndCloser{gzReader, resp.Body}
}
} }
resp.Body = &bodyEOFSignal{body: resp.Body} resp.Body = &bodyEOFSignal{body: resp.Body}
} }
...@@ -1156,6 +1150,27 @@ func (es *bodyEOFSignal) condfn(err error) { ...@@ -1156,6 +1150,27 @@ func (es *bodyEOFSignal) condfn(err error) {
es.fn = nil 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 { type readerAndCloser struct {
io.Reader io.Reader
io.Closer io.Closer
......
...@@ -803,6 +803,33 @@ func TestTransportGzipRecursive(t *testing.T) { ...@@ -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. // tests that persistent goroutine connections shut down when no longer desired.
func TestTransportPersistConnLeak(t *testing.T) { func TestTransportPersistConnLeak(t *testing.T) {
if runtime.GOOS == "plan9" { if runtime.GOOS == "plan9" {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment