Repository: grpc/grpc-go
File: internal/transport/http2_client.go (Go)

Code before:
				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
			}
			v, err := decodeMetadataHeader(hf.Name, hf.Value)
			if err != nil {
				headerError = fmt.Sprintf("transport: malformed %s: %v", hf.Name, err)
				logger.Warningf("Failed to decode metadata header (%q, %q): %v", hf.Name, hf.Value, err)
				break
			}
			mdata[hf.Name] = append(mdata[hf.Name], v)
		}
	}

	//if response is not grpc or endstream doesn't receive a grpc status, fall back to http error code
	if !isGRPC {
		var code = codes.Internal // when header does not include HTTP status, return INTERNAL

		if httpStatusCode != nil {
			var ok bool
			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, strings.Join(errs, "; "))
		t.closeStream(s, se.Err(), true, http2.ErrCodeProtocol, se, nil, endStream)
		return
	}

	if headerError != "" {
		se := status.New(codes.Internal, headerError)

Review comment (bug):
At line#1500 above, the code is attempting to parse the status code as an integer and returning early if it fails.
```go
         case ":status":
			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
			}
```

Based on the issue description, I think we should completely ignore the `:status` pseudo header if the content type is gRPC. 

I think the performant way to do this would be delaying the `t.closeStream` call till we've seen all the header fields. 
The alternate would be to do 2 passes over the header list, one to check if the content type is gRPC, this may have a noticeable impact on unary RPC performance since it is executed for every RPC.  We can run a benchmark to confirm if we go with this approach.
```
go run benchmark/benchmain/main.go -benchtime=60s -workloads=unary \
   -compression=off -maxConcurrentCalls=120 -trace=off \
   -reqSizeBytes=1024 -respSizeBytes=1024 -networkMode=Local -resultFile="${RUN_NAME}"   -recvBufferPool=simple
```

Code after:
		case "grpc-message":
			grpcMessage = decodeGrpcMessage(hf.Value)
		case ":status":
			httpStatus = hf.Value
		default:
			if isReservedHeader(hf.Name) && !isWhitelistedHeader(hf.Name) {
				break
			}
			v, err := decodeMetadataHeader(hf.Name, hf.Value)
			if err != nil {
				headerError = fmt.Sprintf("transport: malformed %s: %v", hf.Name, err)
				logger.Warningf("Failed to decode metadata header (%q, %q): %v", hf.Name, hf.Value, err)
				break
			}
			mdata[hf.Name] = append(mdata[hf.Name], v)
		}
	}

	// If a non-gRPC response is received, then evaluate the HTTP status to
	// process the response and close the stream.
	// In case http status doesn't provide any error information (status : 200),
	// then evalute response code to be Unknown.
	if !isGRPC {
		var grpcErrorCode = codes.Internal
		if httpStatus == "" {
			httpStatusErr = "malformed header: missing HTTP status"
		} else {
			// Parse the status codes (e.g. "200", 404").
			statusCode, err := strconv.Atoi(httpStatus)
			if err != nil {
				se := status.New(grpcErrorCode, fmt.Sprintf("transport: malformed http-status: %v", err))
				t.closeStream(s, se.Err(), true, http2.ErrCodeProtocol, se, nil, endStream)
				return
			}
			if statusCode >= 100 && statusCode < 200 {
				if endStream {
					se := status.New(codes.Internal, fmt.Sprintf(
						"protocol error: informational header with status code %d must not have END_STREAM set", statusCode))
					t.closeStream(s, se.Err(), true, http2.ErrCodeProtocol, se, nil, endStream)
				}
				// In case of informational headers, return.
				return
			}
			httpStatusErr = fmt.Sprintf(
				"unexpected HTTP status code received from server: %d (%s)",
				statusCode,
				http.StatusText(statusCode),
			)
			var ok bool
			grpcErrorCode, ok = HTTPStatusConvTab[statusCode]
			if !ok {