mirror of
https://github.com/golang/net.git
synced 2026-04-01 02:47:08 +09:00
http2: make Transport handle HEAD responses with DATA frames
The Google GFE replies to HEAD requests with the END_STREAM bit in an
empty DATA frame, not in the response HEADERS. The Go Transport code
mistook this to mean the Content-Length was real.
Worked around in:
645322e7db
Reported internally in b/27820181.
Change-Id: Id31a047e2277d7d90560fca264919e239ec76d74
Reviewed-on: https://go-review.googlesource.com/21061
Reviewed-by: Dave Day <djd@golang.org>
This commit is contained in:
@@ -1263,7 +1263,8 @@ func (rl *clientConnReadLoop) handleResponse(cs *clientStream, f *MetaHeadersFra
|
||||
}
|
||||
|
||||
streamEnded := f.StreamEnded()
|
||||
if !streamEnded || cs.req.Method == "HEAD" {
|
||||
isHead := cs.req.Method == "HEAD"
|
||||
if !streamEnded || isHead {
|
||||
res.ContentLength = -1
|
||||
if clens := res.Header["Content-Length"]; len(clens) == 1 {
|
||||
if clen64, err := strconv.ParseInt(clens[0], 10, 64); err == nil {
|
||||
@@ -1278,7 +1279,7 @@ func (rl *clientConnReadLoop) handleResponse(cs *clientStream, f *MetaHeadersFra
|
||||
}
|
||||
}
|
||||
|
||||
if streamEnded {
|
||||
if streamEnded || isHead {
|
||||
res.Body = noBody
|
||||
return res, nil
|
||||
}
|
||||
|
||||
@@ -1738,3 +1738,60 @@ func TestTransportNewTLSConfig(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The Google GFE responds to HEAD requests with a HEADERS frame
|
||||
// without END_STREAM, followed by a 0-length DATA frame with
|
||||
// END_STREAM. Make sure we don't get confused by that. (We did.)
|
||||
func TestTransportReadHeadResponse(t *testing.T) {
|
||||
ct := newClientTester(t)
|
||||
clientDone := make(chan struct{})
|
||||
ct.client = func() error {
|
||||
defer close(clientDone)
|
||||
req, _ := http.NewRequest("HEAD", "https://dummy.tld/", nil)
|
||||
res, err := ct.tr.RoundTrip(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if res.ContentLength != 123 {
|
||||
return fmt.Errorf("Content-Length = %d; want 123", res.ContentLength)
|
||||
}
|
||||
slurp, err := ioutil.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return fmt.Errorf("ReadAll: %v", err)
|
||||
}
|
||||
if len(slurp) > 0 {
|
||||
return fmt.Errorf("Unexpected non-empty ReadAll body: %q", slurp)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
ct.server = func() error {
|
||||
ct.greet()
|
||||
for {
|
||||
f, err := ct.fr.ReadFrame()
|
||||
if err != nil {
|
||||
t.Logf("ReadFrame: %v", err)
|
||||
return nil
|
||||
}
|
||||
hf, ok := f.(*HeadersFrame)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
enc := hpack.NewEncoder(&buf)
|
||||
enc.WriteField(hpack.HeaderField{Name: ":status", Value: "200"})
|
||||
enc.WriteField(hpack.HeaderField{Name: "content-length", Value: "123"})
|
||||
ct.fr.WriteHeaders(HeadersFrameParam{
|
||||
StreamID: hf.StreamID,
|
||||
EndHeaders: true,
|
||||
EndStream: false, // as the GFE does
|
||||
BlockFragment: buf.Bytes(),
|
||||
})
|
||||
ct.fr.WriteData(hf.StreamID, true, nil)
|
||||
|
||||
<-clientDone
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
ct.run()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user