From 624615223696c97d9ee93a723fc7ec829b01fbd2 Mon Sep 17 00:00:00 2001 From: Andrew Pritchard Date: Fri, 24 Jul 2015 07:39:22 -0700 Subject: [PATCH 1/2] Honor END_STREAM flags in HTTP/2 Data frames. Without this, "broken deployments" that do not send trailers after their response will cause Go clients to hang indefinitely (until a client-side timeout or server-side slowloris protection kills the connection). See the diagram at https://httpwg.github.io/specs/rfc7540.html#StreamStates and the Data frame specification at https://httpwg.github.io/specs/rfc7540.html#DATA for the relevant HTTP/2 specification. See https://github.com/grpc/grpc-common/blob/master/PROTOCOL-HTTP2.md#responses for the specification that clients must be prepared for servers to omit the trailers. --- transport/http2_client.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/transport/http2_client.go b/transport/http2_client.go index 59f4d5c3..33fc6e01 100644 --- a/transport/http2_client.go +++ b/transport/http2_client.go @@ -542,6 +542,17 @@ func (t *http2Client) handleData(f *http2.DataFrame) { data := make([]byte, size) copy(data, f.Data()) s.write(recvMsg{data: data}) + + if f.FrameHeader.Flags.Has(http2.FlagDataEndStream) { + s.mu.Lock() + if (s.state == streamWriteDone) { + s.state = streamDone + } else { + s.state = streamReadDone + } + s.mu.Unlock() + s.write(recvMsg{err: io.EOF}) + } } func (t *http2Client) handleRSTStream(f *http2.RSTStreamFrame) { From 34745e1d3970556ba859cadf9606c419263554a5 Mon Sep 17 00:00:00 2001 From: Andrew Pritchard Date: Fri, 24 Jul 2015 15:47:35 -0700 Subject: [PATCH 2/2] Treat missing trailers as an internal error rather than success. --- transport/http2_client.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/transport/http2_client.go b/transport/http2_client.go index 33fc6e01..c16f9d70 100644 --- a/transport/http2_client.go +++ b/transport/http2_client.go @@ -543,6 +543,8 @@ func (t *http2Client) handleData(f *http2.DataFrame) { copy(data, f.Data()) s.write(recvMsg{data: data}) + // The server has closed the stream without sending trailers. Record that + // the read direction is closed, and set the status appropriately. if f.FrameHeader.Flags.Has(http2.FlagDataEndStream) { s.mu.Lock() if (s.state == streamWriteDone) { @@ -550,6 +552,8 @@ func (t *http2Client) handleData(f *http2.DataFrame) { } else { s.state = streamReadDone } + s.statusCode = codes.Internal + s.statusDesc = "server closed the stream without sending trailers" s.mu.Unlock() s.write(recvMsg{err: io.EOF}) }