-
Notifications
You must be signed in to change notification settings - Fork 4.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
transport: validate http 200 status for responses #4474
Changes from 22 commits
fe87cc5
154e687
e437a72
9cb2546
17d7508
71ad202
29c60a1
0e104a3
17758ef
6de802f
399b76e
253fd40
1446920
fa0bff4
efaf7ab
c0cda22
6230a62
43990b6
8b0e761
472d502
6642a86
adfc275
9207e93
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -24,6 +24,7 @@ import ( | |
"io" | ||
"math" | ||
"net" | ||
"net/http" | ||
"strconv" | ||
"strings" | ||
"sync" | ||
|
@@ -1280,29 +1281,41 @@ func (t *http2Client) operateHeaders(frame *http2.MetaHeadersFrame) { | |
// that the peer is speaking gRPC and we are in gRPC mode. | ||
isGRPC = !initialHeader | ||
mdata = make(map[string][]string) | ||
contentTypeErr string | ||
contentTypeErr = "malformed header: missing HTTP content-type" | ||
grpcMessage string | ||
statusGen *status.Status | ||
|
||
httpStatus string | ||
rawStatus string | ||
httpStatusCode *int | ||
httpStatusErr string | ||
rawStatusCode *codes.Code | ||
// headerError is set if an error is encountered while parsing the headers | ||
headerError string | ||
) | ||
|
||
if initialHeader { | ||
httpStatusErr = "malformed header: missing HTTP status" | ||
} | ||
|
||
for _, hf := range frame.Fields { | ||
switch hf.Name { | ||
case "content-type": | ||
if _, validContentType := grpcutil.ContentSubtype(hf.Value); !validContentType { | ||
contentTypeErr = fmt.Sprintf("transport: received the unexpected content-type %q", hf.Value) | ||
contentTypeErr = fmt.Sprintf("transport: received unexpected content-type %q", hf.Value) | ||
break | ||
} | ||
contentTypeErr = "" | ||
mdata[hf.Name] = append(mdata[hf.Name], hf.Value) | ||
isGRPC = true | ||
case "grpc-encoding": | ||
s.recvCompress = hf.Value | ||
case "grpc-status": | ||
rawStatus = hf.Value | ||
code, err := strconv.ParseInt(hf.Value, 10, 32) | ||
if err != nil { | ||
se := status.New(codes.Internal, fmt.Sprintf("transport: malformed grpc-status: %v", err)) | ||
t.closeStream(s, se.Err(), true, http2.ErrCodeProtocol, se, nil, endStream) | ||
return | ||
} | ||
c := codes.Code(uint32(code)) | ||
rawStatusCode = &c | ||
case "grpc-message": | ||
grpcMessage = decodeGrpcMessage(hf.Value) | ||
case "grpc-status-details-bin": | ||
|
@@ -1312,7 +1325,27 @@ func (t *http2Client) operateHeaders(frame *http2.MetaHeadersFrame) { | |
headerError = fmt.Sprintf("transport: malformed grpc-status-details-bin: %v", err) | ||
} | ||
case ":status": | ||
httpStatus = hf.Value | ||
if hf.Value == "200" { | ||
httpStatusErr = "" | ||
statusCode := 200 | ||
httpStatusCode = &statusCode | ||
break | ||
} | ||
|
||
c, err := strconv.ParseInt(hf.Value, 10, 32) | ||
if err != nil { | ||
se := status.New(codes.Internal, fmt.Sprintf("transport: malformed http-status: %v", err)) | ||
t.closeStream(s, se.Err(), true, http2.ErrCodeProtocol, se, nil, endStream) | ||
return | ||
} | ||
statusCode := int(c) | ||
httpStatusCode = &statusCode | ||
|
||
httpStatusErr = fmt.Sprintf( | ||
"unexpected HTTP status code received from server: %d (%s)", | ||
statusCode, | ||
http.StatusText(statusCode), | ||
) | ||
default: | ||
if isReservedHeader(hf.Name) && !isWhitelistedHeader(hf.Name) { | ||
break | ||
|
@@ -1327,30 +1360,25 @@ func (t *http2Client) operateHeaders(frame *http2.MetaHeadersFrame) { | |
} | ||
} | ||
|
||
if !isGRPC { | ||
var ( | ||
code = codes.Internal // when header does not include HTTP status, return INTERNAL | ||
httpStatusCode int | ||
) | ||
|
||
if httpStatus != "" { | ||
c, err := strconv.ParseInt(httpStatus, 10, 32) | ||
if err != nil { | ||
se := status.New(codes.Internal, fmt.Sprintf("transport: malformed http-status: %v", err)) | ||
t.closeStream(s, se.Err(), true, http2.ErrCodeProtocol, se, nil, endStream) | ||
return | ||
} | ||
httpStatusCode = int(c) | ||
if !isGRPC || httpStatusErr != "" { | ||
var code = codes.Internal // when header does not include HTTP status, return INTERNAL | ||
|
||
if httpStatusCode != nil { | ||
var ok bool | ||
code, ok = HTTPStatusConvTab[httpStatusCode] | ||
code, ok = HTTPStatusConvTab[*httpStatusCode] | ||
if !ok { | ||
code = codes.Unknown | ||
} | ||
} | ||
|
||
var errs []string | ||
if httpStatusErr != "" { | ||
errs = append(errs, httpStatusErr) | ||
} | ||
if contentTypeErr != "" { | ||
errs = append(errs, contentTypeErr) | ||
} | ||
// Verify the HTTP response is a 200. | ||
se := status.New(code, constructHTTPErrMsg(&httpStatusCode, contentTypeErr)) | ||
se := status.New(code, strings.Join(errs, "; ")) | ||
t.closeStream(s, se.Err(), true, http2.ErrCodeProtocol, se, nil, endStream) | ||
return | ||
} | ||
|
@@ -1407,17 +1435,11 @@ func (t *http2Client) operateHeaders(frame *http2.MetaHeadersFrame) { | |
} | ||
|
||
if statusGen == nil { | ||
rawStatusCode := codes.Unknown | ||
if rawStatus != "" { | ||
code, err := strconv.ParseInt(rawStatus, 10, 32) | ||
if err != nil { | ||
se := status.New(codes.Internal, fmt.Sprintf("transport: malformed grpc-status: %v", err)) | ||
t.closeStream(s, se.Err(), true, http2.ErrCodeProtocol, se, nil, endStream) | ||
return | ||
} | ||
rawStatusCode = codes.Code(uint32(code)) | ||
rsc := codes.Unknown | ||
if rawStatusCode != nil { | ||
rsc = *rawStatusCode | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why not make There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've initialized rawStatusCode as |
||
} | ||
statusGen = status.New(rawStatusCode, grpcMessage) | ||
statusGen = status.New(rsc, grpcMessage) | ||
} | ||
|
||
// if client received END_STREAM from server while stream was still active, send RST_STREAM | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting. I just was pointed to this:
https://github.com/grpc/grpc/blob/master/doc/http-grpc-status-mapping.md
So, I think this part should only be happening if there is no grpc-status (rawStatusCode), but using rawStatusCode otherwise. Do you want to take a shot at this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've put up a PR for if here