2424import io .grpc .Status ;
2525import java .nio .charset .Charset ;
2626import java .nio .charset .StandardCharsets ;
27+ import java .util .logging .Level ;
28+ import java .util .logging .Logger ;
2729import javax .annotation .Nullable ;
2830
2931/**
@@ -65,6 +67,14 @@ public Integer parseAsciiString(byte[] serialized) {
6567 private Charset errorCharset = StandardCharsets .UTF_8 ;
6668 private boolean headersReceived ;
6769
70+ /**
71+ * Tracks whether the client sent a gzip-encoded request. This is set by {@link
72+ * #setMessageCompression(boolean, String)} when the compressor is gzip.
73+ */
74+ private boolean clientSentGzipRequest = false ;
75+
76+ private static final Logger log = Logger .getLogger (Http2ClientStreamTransportState .class .getName ());
77+
6878 protected Http2ClientStreamTransportState (
6979 int maxMessageSize ,
7080 StatsTraceContext statsTraceCtx ,
@@ -73,6 +83,20 @@ protected Http2ClientStreamTransportState(
7383 super (maxMessageSize , statsTraceCtx , transportTracer , options );
7484 }
7585
86+ /**
87+ * Sets whether the client is sending a gzip-compressed request. This is called by
88+ * {@link ClientCallImpl#setMessageCompression(boolean)} when the compressor is gzip.
89+ * This information is used to validate the server's {@code grpc-accept-encoding} response header.
90+ *
91+ * @param enabled whether message compression is enabled
92+ * @param compressorName the name of the compressor being used (e.g., "gzip")
93+ */
94+ public final void setMessageCompression (boolean enabled , String compressorName ) {
95+ if (enabled && "gzip" .equals (compressorName )) {
96+ clientSentGzipRequest = true ;
97+ }
98+ }
99+
76100 /**
77101 * Called to process a failure in HTTP/2 processing. It should notify the transport to cancel the
78102 * stream and call {@code transportReportStatus()}.
@@ -109,6 +133,9 @@ protected void transportHeadersReceived(Metadata headers) {
109133 return ;
110134 }
111135
136+ // Validate grpc-accept-encoding header if client sent gzip request
137+ validateGrpcAcceptEncoding (headers );
138+
112139 stripTransportDetails (headers );
113140 inboundHeadersReceived (headers );
114141 } finally {
@@ -257,4 +284,47 @@ private static void stripTransportDetails(Metadata metadata) {
257284 metadata .discardAll (InternalStatus .CODE_KEY );
258285 metadata .discardAll (InternalStatus .MESSAGE_KEY );
259286 }
260- }
287+
288+ /**
289+ * Validates that the server's response includes a {@code grpc-accept-encoding} header that
290+ * includes {@code gzip} when the client sent a gzip-encoded request.
291+ *
292+ * <p>According to the gRPC spec, when a client sends a gzip-encoded request, the server must
293+ * respond with {@code grpc-accept-encoding: gzip} in the response headers to indicate it can
294+ * accept gzip-encoded responses. If this header is missing or doesn't include gzip, it's a
295+ * server misbehavior that we log at FINE level.
296+ *
297+ * @param headers the response headers from the server
298+ */
299+ private void validateGrpcAcceptEncoding (Metadata headers ) {
300+ if (!clientSentGzipRequest ) {
301+ // No validation needed if client didn't send gzip
302+ return ;
303+ }
304+
305+ byte [] acceptEncodingBytes = headers .get (MESSAGE_ACCEPT_ENCODING_KEY );
306+ if (acceptEncodingBytes == null ) {
307+ log .log (Level .FINE ,
308+ "Server sent gzip-encoded request but response missing grpc-accept-encoding header. "
309+ + "This is server misbehavior." );
310+ return ;
311+ }
312+
313+ String acceptEncoding = new String (acceptEncodingBytes , StandardCharsets .US_ASCII );
314+ // Check if gzip is in the accepted encodings (comma-separated list)
315+ String [] encodings = GrpcUtil .ACCEPT_ENCODING_SPLITTER .split (acceptEncoding );
316+ boolean gzipAccepted = false ;
317+ for (String encoding : encodings ) {
318+ if ("gzip" .equalsIgnoreCase (encoding .trim ())) {
319+ gzipAccepted = true ;
320+ break ;
321+ }
322+ }
323+
324+ if (!gzipAccepted ) {
325+ log .log (Level .FINE ,
326+ "Server sent gzip-encoded request but grpc-accept-encoding ({0}) does not include gzip. "
327+ + "This is server misbehavior." , acceptEncoding );
328+ }
329+ }
330+ }
0 commit comments