@@ -79,7 +79,7 @@ public Integer parseAsciiString(byte[] serialized) {
7979 private Status transportError ;
8080 private Metadata transportErrorMetadata ;
8181 private Charset errorCharset = Charsets .UTF_8 ;
82- private boolean contentTypeChecked ;
82+ private boolean headersReceived ;
8383
8484 protected Http2ClientStreamTransportState (int maxMessageSize , StatsTraceContext statsTraceCtx ) {
8585 super (maxMessageSize , statsTraceCtx );
@@ -99,28 +99,37 @@ protected Http2ClientStreamTransportState(int maxMessageSize, StatsTraceContext
9999 protected void transportHeadersReceived (Metadata headers ) {
100100 Preconditions .checkNotNull (headers , "headers" );
101101 if (transportError != null ) {
102- // Already received a transport error so just augment it.
103- transportError = transportError .augmentDescription (headers . toString () );
102+ // Already received a transport error so just augment it. Something is really, really strange.
103+ transportError = transportError .augmentDescription (" headers: " + headers );
104104 return ;
105105 }
106- Status httpStatus = statusFromHttpStatus (headers );
107- if (httpStatus == null ) {
108- transportError = Status .INTERNAL .withDescription (
109- "received non-terminal headers with no :status" );
110- } else if (!httpStatus .isOk ()) {
111- transportError = httpStatus ;
112- } else {
113- transportError = checkContentType (headers );
114- }
115- if (transportError != null ) {
116- // Note we don't immediately report the transport error, instead we wait for more data on the
117- // stream so we can accumulate more detail into the error before reporting it.
118- transportError = transportError .augmentDescription ("\n " + headers );
119- transportErrorMetadata = headers ;
120- errorCharset = extractCharset (headers );
121- } else {
106+ try {
107+ if (headersReceived ) {
108+ transportError = Status .INTERNAL .withDescription ("Received headers twice" );
109+ return ;
110+ }
111+ Integer httpStatus = headers .get (HTTP2_STATUS );
112+ if (httpStatus != null && httpStatus >= 100 && httpStatus < 200 ) {
113+ // Ignore the headers. See RFC 7540 §8.1
114+ return ;
115+ }
116+ headersReceived = true ;
117+
118+ transportError = validateInitialMetadata (headers );
119+ if (transportError != null ) {
120+ return ;
121+ }
122+
122123 stripTransportDetails (headers );
123124 inboundHeadersReceived (headers );
125+ } finally {
126+ if (transportError != null ) {
127+ // Note we don't immediately report the transport error, instead we wait for more data on
128+ // the stream so we can accumulate more detail into the error before reporting it.
129+ transportError = transportError .augmentDescription ("headers: " + headers );
130+ transportErrorMetadata = headers ;
131+ errorCharset = extractCharset (headers );
132+ }
124133 }
125134 }
126135
@@ -159,14 +168,14 @@ protected void transportDataReceived(ReadableBuffer frame, boolean endOfStream)
159168 */
160169 protected void transportTrailersReceived (Metadata trailers ) {
161170 Preconditions .checkNotNull (trailers , "trailers" );
162- if (transportError != null ) {
163- // Already received a transport error so just augment it.
164- transportError = transportError .augmentDescription (trailers .toString ());
165- } else {
166- transportError = checkContentType (trailers );
167- transportErrorMetadata = trailers ;
171+ if (transportError == null && !headersReceived ) {
172+ transportError = validateInitialMetadata (trailers );
173+ if (transportError != null ) {
174+ transportErrorMetadata = trailers ;
175+ }
168176 }
169177 if (transportError != null ) {
178+ transportError = transportError .augmentDescription ("trailers: " + trailers );
170179 http2ProcessingFailed (transportError , transportErrorMetadata );
171180 } else {
172181 Status status = statusFromTrailers (trailers );
@@ -175,50 +184,44 @@ protected void transportTrailersReceived(Metadata trailers) {
175184 }
176185 }
177186
178- private static Status statusFromHttpStatus (Metadata metadata ) {
179- Integer httpStatus = metadata .get (HTTP2_STATUS );
180- if (httpStatus != null ) {
181- Status status = GrpcUtil .httpStatusToGrpcStatus (httpStatus );
182- return status .isOk () ? status
183- : status .augmentDescription ("extracted status from HTTP :status " + httpStatus );
184- }
185- return null ;
186- }
187-
188187 /**
189188 * Extract the response status from trailers.
190189 */
191- private static Status statusFromTrailers (Metadata trailers ) {
190+ private Status statusFromTrailers (Metadata trailers ) {
192191 Status status = trailers .get (Status .CODE_KEY );
193- if (status == null ) {
194- status = statusFromHttpStatus (trailers );
195- if (status == null || status .isOk ()) {
196- status = Status .UNKNOWN .withDescription ("missing GRPC status in response" );
197- } else {
198- status = status .withDescription (
199- "missing GRPC status, inferred error from HTTP status code" );
200- }
192+ if (status != null ) {
193+ return status .withDescription (trailers .get (Status .MESSAGE_KEY ));
194+ }
195+ // No status; something is broken. Try to provide a resonanable error.
196+ if (headersReceived ) {
197+ return Status .UNKNOWN .withDescription ("missing GRPC status in response" );
201198 }
202- String message = trailers .get (Status .MESSAGE_KEY );
203- if (message != null ) {
204- status = status .augmentDescription (message );
199+ Integer httpStatus = trailers .get (HTTP2_STATUS );
200+ if (httpStatus != null ) {
201+ status = GrpcUtil .httpStatusToGrpcStatus (httpStatus );
202+ } else {
203+ status = Status .INTERNAL .withDescription ("missing HTTP status code" );
205204 }
206- return status ;
205+ return status .augmentDescription (
206+ "missing GRPC status, inferred error from HTTP status code" );
207207 }
208208
209209 /**
210- * Inspect the content type field from received headers or trailers and return an error Status if
211- * content type is invalid or not present. Returns null if no error was found.
210+ * Inspect initial headers to make sure they conform to HTTP and gRPC, returning a {@code Status}
211+ * on failure.
212+ *
213+ * @return status with description of failure, or {@code null} when valid
212214 */
213215 @ Nullable
214- private Status checkContentType (Metadata headers ) {
215- if (contentTypeChecked ) {
216- return null ;
216+ private Status validateInitialMetadata (Metadata headers ) {
217+ Integer httpStatus = headers .get (HTTP2_STATUS );
218+ if (httpStatus == null ) {
219+ return Status .INTERNAL .withDescription ("Missing HTTP status code" );
217220 }
218- contentTypeChecked = true ;
219221 String contentType = headers .get (GrpcUtil .CONTENT_TYPE_KEY );
220222 if (!GrpcUtil .isGrpcContentType (contentType )) {
221- return Status .INTERNAL .withDescription ("Invalid content-type: " + contentType );
223+ return GrpcUtil .httpStatusToGrpcStatus (httpStatus )
224+ .augmentDescription ("invalid content-type: " + contentType );
222225 }
223226 return null ;
224227 }
0 commit comments