Skip to content

Commit 5780703

Browse files
client: handle 1xx HTTP status HEADERS (#8518)
Fixes: #8485 RELEASE NOTES: * client: Ignore http headers with status 1xx and `END_STREAM` flag unset. * client: Fail RPCs with status `INTERNAL` instead of `UNKNOWN` on receiving http headers with status 1xx and `END_STREAM` flag set.
1 parent 6524c7b commit 5780703

File tree

2 files changed

+91
-7
lines changed

2 files changed

+91
-7
lines changed

internal/transport/http2_client.go

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1499,21 +1499,26 @@ func (t *http2Client) operateHeaders(frame *http2.MetaHeadersFrame) {
14991499
case "grpc-message":
15001500
grpcMessage = decodeGrpcMessage(hf.Value)
15011501
case ":status":
1502-
if hf.Value == "200" {
1503-
httpStatusErr = ""
1504-
statusCode := 200
1505-
httpStatusCode = &statusCode
1506-
break
1507-
}
1508-
15091502
c, err := strconv.ParseInt(hf.Value, 10, 32)
15101503
if err != nil {
15111504
se := status.New(codes.Internal, fmt.Sprintf("transport: malformed http-status: %v", err))
15121505
t.closeStream(s, se.Err(), true, http2.ErrCodeProtocol, se, nil, endStream)
15131506
return
15141507
}
15151508
statusCode := int(c)
1509+
if statusCode >= 100 && statusCode < 200 {
1510+
if endStream {
1511+
se := status.New(codes.Internal, fmt.Sprintf(
1512+
"protocol error: informational header with status code %d must not have END_STREAM set", statusCode))
1513+
t.closeStream(s, se.Err(), true, http2.ErrCodeProtocol, se, nil, endStream)
1514+
}
1515+
return
1516+
}
15161517
httpStatusCode = &statusCode
1518+
if statusCode == 200 {
1519+
httpStatusErr = ""
1520+
break
1521+
}
15171522

15181523
httpStatusErr = fmt.Sprintf(
15191524
"unexpected HTTP status code received from server: %d (%s)",

internal/transport/transport_test.go

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3125,3 +3125,82 @@ func (s) TestServerSendsRSTAfterDeadlineToMisbehavedClient(t *testing.T) {
31253125
t.Fatalf("RST frame received earlier than expected by duration: %v", want-got)
31263126
}
31273127
}
3128+
3129+
// TestClientTransport_Handle1xxHeaders validates that 1xx HTTP status headers
3130+
// are ignored and treated as a protocol error if END_STREAM is set.
3131+
func (s) TestClientTransport_Handle1xxHeaders(t *testing.T) {
3132+
testStream := func() *ClientStream {
3133+
return &ClientStream{
3134+
Stream: &Stream{
3135+
buf: &recvBuffer{
3136+
c: make(chan recvMsg),
3137+
mu: sync.Mutex{},
3138+
},
3139+
},
3140+
done: make(chan struct{}),
3141+
headerChan: make(chan struct{}),
3142+
}
3143+
}
3144+
3145+
testClient := func(ts *ClientStream) *http2Client {
3146+
return &http2Client{
3147+
mu: sync.Mutex{},
3148+
activeStreams: map[uint32]*ClientStream{
3149+
0: ts,
3150+
},
3151+
controlBuf: newControlBuffer(make(<-chan struct{})),
3152+
}
3153+
}
3154+
3155+
for _, test := range []struct {
3156+
name string
3157+
metaHeaderFrame *http2.MetaHeadersFrame
3158+
httpFlags http2.Flags
3159+
wantStatus *status.Status
3160+
}{
3161+
{
3162+
name: "1xx with END_STREAM is error",
3163+
metaHeaderFrame: &http2.MetaHeadersFrame{
3164+
Fields: []hpack.HeaderField{
3165+
{Name: ":status", Value: "100"},
3166+
},
3167+
},
3168+
httpFlags: http2.FlagHeadersEndStream,
3169+
wantStatus: status.New(
3170+
codes.Internal,
3171+
"protocol error: informational header with status code 100 must not have END_STREAM set",
3172+
),
3173+
},
3174+
{
3175+
name: "1xx without END_STREAM is ignored",
3176+
metaHeaderFrame: &http2.MetaHeadersFrame{
3177+
Fields: []hpack.HeaderField{
3178+
{Name: ":status", Value: "100"},
3179+
},
3180+
},
3181+
httpFlags: 0,
3182+
wantStatus: nil,
3183+
},
3184+
} {
3185+
t.Run(test.name, func(t *testing.T) {
3186+
ts := testStream()
3187+
s := testClient(ts)
3188+
3189+
test.metaHeaderFrame.HeadersFrame = &http2.HeadersFrame{
3190+
FrameHeader: http2.FrameHeader{
3191+
StreamID: 0,
3192+
Flags: test.httpFlags,
3193+
},
3194+
}
3195+
3196+
s.operateHeaders(test.metaHeaderFrame)
3197+
3198+
got := ts.status
3199+
want := test.wantStatus
3200+
3201+
if got.Code() != want.Code() || got.Message() != want.Message() {
3202+
t.Fatalf("operateHeaders(%v); status = %v, want %v", test.metaHeaderFrame, got, want)
3203+
}
3204+
})
3205+
}
3206+
}

0 commit comments

Comments
 (0)