From b78b7f4f3b80da957acd715649c18bd33a3398b6 Mon Sep 17 00:00:00 2001 From: jlaportebot Date: Sun, 9 Aug 2026 10:41:00 -0400 Subject: [PATCH 1/7] fix: validate grpc-accept-encoding header when client sends gzip request - Add validation in Http2ClientStreamTransportState to check if server response includes grpc-accept-encoding: gzip when client sent gzip-encoded request - Log warning at FINE level when server misbehavior detected (missing or invalid header) - Add setMessageCompression(boolean, String) method to ClientStream interface and implementations to track when client sends gzip-compressed requests - Add unit tests for grpc-accept-encoding validation Fixes grpc/grpc-java#1804 --- .../internal/MultiMessageClientStream.java | 5 + .../internal/SingleMessageClientStream.java | 5 + .../grpc/internal/AbstractClientStream.java | 12 + .../java/io/grpc/internal/ClientCallImpl.java | 3 +- .../java/io/grpc/internal/ClientStream.java | 10 + .../java/io/grpc/internal/DelayedStream.java | 15 ++ .../grpc/internal/ForwardingClientStream.java | 5 + .../Http2ClientStreamTransportState.java | 72 +++++- .../io/grpc/internal/NoopClientStream.java | 5 + .../io/grpc/internal/RetriableStream.java | 12 + ...mTransportStateGrpcAcceptEncodingTest.java | 242 ++++++++++++++++++ .../io/grpc/inprocess/InProcessTransport.java | 8 + 12 files changed, 392 insertions(+), 2 deletions(-) create mode 100644 core/src/test/java/io/grpc/internal/Http2ClientStreamTransportStateGrpcAcceptEncodingTest.java diff --git a/binder/src/main/java/io/grpc/binder/internal/MultiMessageClientStream.java b/binder/src/main/java/io/grpc/binder/internal/MultiMessageClientStream.java index 9873adcb447..c9d9d5f16e2 100644 --- a/binder/src/main/java/io/grpc/binder/internal/MultiMessageClientStream.java +++ b/binder/src/main/java/io/grpc/binder/internal/MultiMessageClientStream.java @@ -159,6 +159,11 @@ public final void setMessageCompression(boolean enable) { // Ignore. } + @Override + public void setMessageCompression(boolean enabled, String compressorName) { + // Ignore. + } + @Override public void setAuthority(String authority) { // Ignore. diff --git a/binder/src/main/java/io/grpc/binder/internal/SingleMessageClientStream.java b/binder/src/main/java/io/grpc/binder/internal/SingleMessageClientStream.java index 8a899d621a2..2b1f5ddf163 100644 --- a/binder/src/main/java/io/grpc/binder/internal/SingleMessageClientStream.java +++ b/binder/src/main/java/io/grpc/binder/internal/SingleMessageClientStream.java @@ -151,6 +151,11 @@ public final void setMessageCompression(boolean enable) { // Ignore. } + @Override + public void setMessageCompression(boolean enabled, String compressorName) { + // Ignore. + } + @Override public void setAuthority(String authority) { // Ignore. diff --git a/core/src/main/java/io/grpc/internal/AbstractClientStream.java b/core/src/main/java/io/grpc/internal/AbstractClientStream.java index bce1820b482..42fa24f6f30 100644 --- a/core/src/main/java/io/grpc/internal/AbstractClientStream.java +++ b/core/src/main/java/io/grpc/internal/AbstractClientStream.java @@ -261,6 +261,18 @@ private void setDecompressorRegistry(DecompressorRegistry decompressorRegistry) checkNotNull(decompressorRegistry, "decompressorRegistry"); } + /** + * Sets whether the client is sending a gzip-compressed request. This is called by + * {@link ClientCallImpl#setMessageCompression(boolean)} when the compressor is gzip. + * This information is used to validate the server's {@code grpc-accept-encoding} response header. + * + * @param enabled whether message compression is enabled + * @param compressorName the name of the compressor being used (e.g., "gzip") + */ + public void setMessageCompression(boolean enabled, String compressorName) { + // Default implementation does nothing. Override in Http2ClientStreamTransportState. + } + @VisibleForTesting public final void setListener(ClientStreamListener listener) { checkState(this.listener == null, "Already called setListener"); diff --git a/core/src/main/java/io/grpc/internal/ClientCallImpl.java b/core/src/main/java/io/grpc/internal/ClientCallImpl.java index 4b24b1eae3d..3833deaa8db 100644 --- a/core/src/main/java/io/grpc/internal/ClientCallImpl.java +++ b/core/src/main/java/io/grpc/internal/ClientCallImpl.java @@ -542,7 +542,8 @@ private void sendMessageInternal(ReqT message) { @Override public void setMessageCompression(boolean enabled) { checkState(stream != null, "Not started"); - stream.setMessageCompression(enabled); + String compressorName = callOptions.getCompressor(); + stream.setMessageCompression(enabled, compressorName); } @Override diff --git a/core/src/main/java/io/grpc/internal/ClientStream.java b/core/src/main/java/io/grpc/internal/ClientStream.java index 7dba4d70871..fb7383a8780 100644 --- a/core/src/main/java/io/grpc/internal/ClientStream.java +++ b/core/src/main/java/io/grpc/internal/ClientStream.java @@ -88,6 +88,16 @@ public interface ClientStream extends Stream { */ void setMaxOutboundMessageSize(int maxSize); + /** + * Sets whether the client is sending a gzip-compressed request. This is called by + * {@link ClientCallImpl#setMessageCompression(boolean)} when the compressor is gzip. + * This information is used to validate the server's {@code grpc-accept-encoding} response header. + * + * @param enabled whether message compression is enabled + * @param compressorName the name of the compressor being used (e.g., "gzip") + */ + void setMessageCompression(boolean enabled, String compressorName); + /** * Sets the effective deadline of the RPC. */ diff --git a/core/src/main/java/io/grpc/internal/DelayedStream.java b/core/src/main/java/io/grpc/internal/DelayedStream.java index a2b1e963ac5..1980458b113 100644 --- a/core/src/main/java/io/grpc/internal/DelayedStream.java +++ b/core/src/main/java/io/grpc/internal/DelayedStream.java @@ -447,6 +447,21 @@ public void run() { } } + @Override + public void setMessageCompression(boolean enabled, String compressorName) { + checkState(listener != null, "May only be called after start"); + if (passThrough) { + realStream.setMessageCompression(enabled, compressorName); + } else { + delayOrExecute(new Runnable() { + @Override + public void run() { + realStream.setMessageCompression(enabled, compressorName); + } + }); + } + } + @VisibleForTesting ClientStream getRealStream() { return realStream; diff --git a/core/src/main/java/io/grpc/internal/ForwardingClientStream.java b/core/src/main/java/io/grpc/internal/ForwardingClientStream.java index 22719c9b341..da7428751f5 100644 --- a/core/src/main/java/io/grpc/internal/ForwardingClientStream.java +++ b/core/src/main/java/io/grpc/internal/ForwardingClientStream.java @@ -62,6 +62,11 @@ public void setMessageCompression(boolean enable) { delegate().setMessageCompression(enable); } + @Override + public void setMessageCompression(boolean enabled, String compressorName) { + delegate().setMessageCompression(enabled, compressorName); + } + @Override public void cancel(Status reason) { delegate().cancel(reason); diff --git a/core/src/main/java/io/grpc/internal/Http2ClientStreamTransportState.java b/core/src/main/java/io/grpc/internal/Http2ClientStreamTransportState.java index 7124f2fc88a..999117c4a17 100644 --- a/core/src/main/java/io/grpc/internal/Http2ClientStreamTransportState.java +++ b/core/src/main/java/io/grpc/internal/Http2ClientStreamTransportState.java @@ -24,6 +24,8 @@ import io.grpc.Status; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; +import java.util.logging.Level; +import java.util.logging.Logger; import javax.annotation.Nullable; /** @@ -65,6 +67,14 @@ public Integer parseAsciiString(byte[] serialized) { private Charset errorCharset = StandardCharsets.UTF_8; private boolean headersReceived; + /** + * Tracks whether the client sent a gzip-encoded request. This is set by {@link + * #setMessageCompression(boolean, String)} when the compressor is gzip. + */ + private boolean clientSentGzipRequest = false; + + private static final Logger log = Logger.getLogger(Http2ClientStreamTransportState.class.getName()); + protected Http2ClientStreamTransportState( int maxMessageSize, StatsTraceContext statsTraceCtx, @@ -73,6 +83,20 @@ protected Http2ClientStreamTransportState( super(maxMessageSize, statsTraceCtx, transportTracer, options); } + /** + * Sets whether the client is sending a gzip-compressed request. This is called by + * {@link ClientCallImpl#setMessageCompression(boolean)} when the compressor is gzip. + * This information is used to validate the server's {@code grpc-accept-encoding} response header. + * + * @param enabled whether message compression is enabled + * @param compressorName the name of the compressor being used (e.g., "gzip") + */ + public final void setMessageCompression(boolean enabled, String compressorName) { + if (enabled && "gzip".equals(compressorName)) { + clientSentGzipRequest = true; + } + } + /** * Called to process a failure in HTTP/2 processing. It should notify the transport to cancel the * stream and call {@code transportReportStatus()}. @@ -109,6 +133,9 @@ protected void transportHeadersReceived(Metadata headers) { return; } + // Validate grpc-accept-encoding header if client sent gzip request + validateGrpcAcceptEncoding(headers); + stripTransportDetails(headers); inboundHeadersReceived(headers); } finally { @@ -257,4 +284,47 @@ private static void stripTransportDetails(Metadata metadata) { metadata.discardAll(InternalStatus.CODE_KEY); metadata.discardAll(InternalStatus.MESSAGE_KEY); } -} + + /** + * Validates that the server's response includes a {@code grpc-accept-encoding} header that + * includes {@code gzip} when the client sent a gzip-encoded request. + * + *

According to the gRPC spec, when a client sends a gzip-encoded request, the server must + * respond with {@code grpc-accept-encoding: gzip} in the response headers to indicate it can + * accept gzip-encoded responses. If this header is missing or doesn't include gzip, it's a + * server misbehavior that we log at FINE level. + * + * @param headers the response headers from the server + */ + private void validateGrpcAcceptEncoding(Metadata headers) { + if (!clientSentGzipRequest) { + // No validation needed if client didn't send gzip + return; + } + + byte[] acceptEncodingBytes = headers.get(MESSAGE_ACCEPT_ENCODING_KEY); + if (acceptEncodingBytes == null) { + log.log(Level.FINE, + "Server sent gzip-encoded request but response missing grpc-accept-encoding header. " + + "This is server misbehavior."); + return; + } + + String acceptEncoding = new String(acceptEncodingBytes, StandardCharsets.US_ASCII); + // Check if gzip is in the accepted encodings (comma-separated list) + String[] encodings = GrpcUtil.ACCEPT_ENCODING_SPLITTER.split(acceptEncoding); + boolean gzipAccepted = false; + for (String encoding : encodings) { + if ("gzip".equalsIgnoreCase(encoding.trim())) { + gzipAccepted = true; + break; + } + } + + if (!gzipAccepted) { + log.log(Level.FINE, + "Server sent gzip-encoded request but grpc-accept-encoding ({0}) does not include gzip. " + + "This is server misbehavior.", acceptEncoding); + } + } +} \ No newline at end of file diff --git a/core/src/main/java/io/grpc/internal/NoopClientStream.java b/core/src/main/java/io/grpc/internal/NoopClientStream.java index d77d72a5412..823d3e2f343 100644 --- a/core/src/main/java/io/grpc/internal/NoopClientStream.java +++ b/core/src/main/java/io/grpc/internal/NoopClientStream.java @@ -68,6 +68,11 @@ public void setMessageCompression(boolean enable) { // noop } + @Override + public void setMessageCompression(boolean enabled, String compressorName) { + // noop + } + @Override public void optimizeForDirectExecutor() {} diff --git a/core/src/main/java/io/grpc/internal/RetriableStream.java b/core/src/main/java/io/grpc/internal/RetriableStream.java index 0c37a0beaca..d422282df26 100644 --- a/core/src/main/java/io/grpc/internal/RetriableStream.java +++ b/core/src/main/java/io/grpc/internal/RetriableStream.java @@ -689,6 +689,18 @@ public void runWith(Substream substream) { delayOrExecute(new MessageCompressionEntry()); } + @Override + public final void setMessageCompression(boolean enabled, String compressorName) { + class MessageCompressionEntry implements BufferEntry { + @Override + public void runWith(Substream substream) { + substream.stream.setMessageCompression(enabled, compressorName); + } + } + + delayOrExecute(new MessageCompressionEntry()); + } + @Override public final void halfClose() { class HalfCloseEntry implements BufferEntry { diff --git a/core/src/test/java/io/grpc/internal/Http2ClientStreamTransportStateGrpcAcceptEncodingTest.java b/core/src/test/java/io/grpc/internal/Http2ClientStreamTransportStateGrpcAcceptEncodingTest.java new file mode 100644 index 00000000000..4601d347cc0 --- /dev/null +++ b/core/src/test/java/io/grpc/internal/Http2ClientStreamTransportStateGrpcAcceptEncodingTest.java @@ -0,0 +1,242 @@ +/* + * Copyright 2026 The gRPC Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.grpc.internal; + +import static io.grpc.internal.ClientStreamListener.RpcProgress.PROCESSED; +import static io.grpc.internal.GrpcUtil.DEFAULT_MAX_MESSAGE_SIZE; +import static io.grpc.internal.GrpcUtil.MESSAGE_ACCEPT_ENCODING_KEY; +import static io.grpc.internal.GrpcUtil.MESSAGE_ENCODING_KEY; +import static java.nio.charset.StandardCharsets.US_ASCII; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.same; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; + +import io.grpc.CallOptions; +import io.grpc.InternalMetadata; +import io.grpc.Metadata; +import io.grpc.Status; +import io.grpc.Status.Code; +import io.grpc.internal.Http2ClientStreamTransportState; +import io.grpc.internal.TransportTracer; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.mockito.ArgumentCaptor; +import org.mockito.ArgumentMatchers; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; +import org.mockito.stubbing.Answer; + +/** Unit tests for grpc-accept-encoding validation in {@link Http2ClientStreamTransportState}. */ +@RunWith(JUnit4.class) +public class Http2ClientStreamTransportStateGrpcAcceptEncodingTest { + + @Rule + public final MockitoRule mocks = MockitoJUnit.rule(); + + private final Metadata.Key testStatusMashaller = + InternalMetadata.keyOf(":status", Metadata.ASCII_STRING_MARSHALLER); + + private TransportTracer transportTracer; + @Mock private ClientStreamListener mockListener; + @Captor private ArgumentCaptor statusCaptor; + + @Before + public void setUp() { + transportTracer = new TransportTracer(); + + doAnswer(new Answer() { + @Override + public Void answer(InvocationOnMock invocation) throws Throwable { + StreamListener.MessageProducer producer = + (StreamListener.MessageProducer) invocation.getArguments()[0]; + while (producer.next() != null) {} + return null; + } + }).when(mockListener).messagesAvailable(ArgumentMatchers.any()); + } + + @Test + public void transportHeadersReceived_validGrpcAcceptEncoding_gzip() { + BaseTransportState state = new BaseTransportState(transportTracer); + state.setListener(mockListener); + // Client sent gzip-encoded request + state.setMessageCompression(true, "gzip"); + + Metadata headers = new Metadata(); + headers.put(testStatusMashaller, "200"); + headers.put(Metadata.Key.of("content-type", Metadata.ASCII_STRING_MARSHALLER), + "application/grpc"); + headers.put(MESSAGE_ACCEPT_ENCODING_KEY, "gzip".getBytes(US_ASCII)); + state.transportHeadersReceived(headers); + + verify(mockListener, never()).closed(any(Status.class), same(PROCESSED), any(Metadata.class)); + verify(mockListener).headersRead(headers); + } + + @Test + public void transportHeadersReceived_missingGrpcAcceptEncoding_whenGzipSent_logsWarning() { + BaseTransportState state = new BaseTransportState(transportTracer); + state.setListener(mockListener); + // Client sent gzip-encoded request + state.setMessageCompression(true, "gzip"); + + Metadata headers = new Metadata(); + headers.put(testStatusMashaller, "200"); + headers.put(Metadata.Key.of("content-type", Metadata.ASCII_STRING_MARSHALLER), + "application/grpc"); + // No grpc-accept-encoding header when client sent gzip + state.transportHeadersReceived(headers); + + // Should still notify listener but log warning + verify(mockListener).headersRead(headers); + } + + @Test + public void transportHeadersReceived_grpcAcceptEncodingIdentity_whenGzipSent_logsWarning() { + BaseTransportState state = new BaseTransportState(transportTracer); + state.setListener(mockListener); + // Client sent gzip-encoded request + state.setMessageCompression(true, "gzip"); + + Metadata headers = new Metadata(); + headers.put(testStatusMashaller, "200"); + headers.put(Metadata.Key.of("content-type", Metadata.ASCII_STRING_MARSHALLER), + "application/grpc"); + // Server only accepts identity when client sent gzip + headers.put(MESSAGE_ACCEPT_ENCODING_KEY, "identity".getBytes(US_ASCII)); + state.transportHeadersReceived(headers); + + // Should still notify listener but log warning + verify(mockListener).headersRead(headers); + } + + @Test + public void transportHeadersReceived_grpcAcceptEncodingGzipAndDeflate_whenGzipSent_ok() { + BaseTransportState state = new BaseTransportState(transportTracer); + state.setListener(mockListener); + // Client sent gzip-encoded request + state.setMessageCompression(true, "gzip"); + + Metadata headers = new Metadata(); + headers.put(testStatusMashaller, "200"); + headers.put(Metadata.Key.of("content-type", Metadata.ASCII_STRING_MARSHALLER), + "application/grpc"); + // Server accepts gzip and deflate + headers.put(MESSAGE_ACCEPT_ENCODING_KEY, "gzip,deflate".getBytes(US_ASCII)); + state.transportHeadersReceived(headers); + + verify(mockListener).headersRead(headers); + } + + @Test + public void transportHeadersReceived_noClientCompression_noWarning() { + BaseTransportState state = new BaseTransportState(transportTracer); + state.setListener(mockListener); + // Client did NOT send compressed request (no setMessageCompression call) + + Metadata headers = new Metadata(); + headers.put(testStatusMashaller, "200"); + headers.put(Metadata.Key.of("content-type", Metadata.ASCII_STRING_MARSHALLER), + "application/grpc"); + // Client didn't send compressed request, so no validation needed + state.transportHeadersReceived(headers); + + verify(mockListener).headersRead(headers); + } + + @Test + public void transportHeadersReceived_grpcAcceptEncodingMissingOnTrailers() { + BaseTransportState state = new BaseTransportState(transportTracer); + state.setListener(mockListener); + // Client sent gzip-encoded request + state.setMessageCompression(true, "gzip"); + + Metadata headers = new Metadata(); + headers.put(testStatusMashaller, "200"); + headers.put(Metadata.Key.of("content-type", Metadata.ASCII_STRING_MARSHALLER), + "application/grpc"); + state.transportHeadersReceived(headers); + + Metadata trailers = new Metadata(); + trailers.put(Metadata.Key.of("grpc-status", Metadata.ASCII_STRING_MARSHALLER), "0"); + state.transportTrailersReceived(trailers); + + verify(mockListener).closed(Status.OK, PROCESSED, trailers); + } + + @Test + public void transportHeadersReceived_grpcAcceptEncodingCaseInsensitive() { + BaseTransportState state = new BaseTransportState(transportTracer); + state.setListener(mockListener); + // Client sent gzip-encoded request + state.setMessageCompression(true, "gzip"); + + Metadata headers = new Metadata(); + headers.put(testStatusMashaller, "200"); + headers.put(Metadata.Key.of("content-type", Metadata.ASCII_STRING_MARSHALLER), + "application/grpc"); + // Server accepts GZIP (uppercase) + headers.put(MESSAGE_ACCEPT_ENCODING_KEY, "GZIP".getBytes(US_ASCII)); + state.transportHeadersReceived(headers); + + verify(mockListener).headersRead(headers); + } + + private static class BaseTransportState extends Http2ClientStreamTransportState { + private int onReadyThreshold; + + public BaseTransportState(TransportTracer transportTracer, CallOptions options) { + super(DEFAULT_MAX_MESSAGE_SIZE, StatsTraceContext.NOOP, transportTracer, options); + } + + public BaseTransportState(TransportTracer transportTracer) { + this(transportTracer, CallOptions.DEFAULT); + } + + @Override + protected void http2ProcessingFailed(Status status, boolean stopDelivery, Metadata trailers) { + transportReportStatus(status, stopDelivery, trailers); + } + + @Override + public void deframeFailed(Throwable cause) {} + + @Override + public void bytesRead(int processedBytes) {} + + @Override + public void runOnTransportThread(Runnable r) { + r.run(); + } + + @Override + void setOnReadyThreshold(int numBytes) { + onReadyThreshold = numBytes; + super.setOnReadyThreshold(numBytes); + } + } +} \ No newline at end of file diff --git a/inprocess/src/main/java/io/grpc/inprocess/InProcessTransport.java b/inprocess/src/main/java/io/grpc/inprocess/InProcessTransport.java index a92f10fd5c5..f3a24e1c343 100644 --- a/inprocess/src/main/java/io/grpc/inprocess/InProcessTransport.java +++ b/inprocess/src/main/java/io/grpc/inprocess/InProcessTransport.java @@ -668,6 +668,11 @@ public void setMessageCompression(boolean enable) { // noop } + @Override + public void setMessageCompression(boolean enabled, String compressorName) { + // noop + } + @Override public void optimizeForDirectExecutor() {} @@ -894,6 +899,9 @@ public void halfClose() { @Override public void setMessageCompression(boolean enable) {} + @Override + public void setMessageCompression(boolean enabled, String compressorName) {} + @Override public void setAuthority(String string) { InProcessStream.this.authority = string; From ec956d5c004d1dad52b1d8f3c247eb01c818ae1a Mon Sep 17 00:00:00 2001 From: jlaportebot Date: Sun, 9 Aug 2026 10:48:09 -0400 Subject: [PATCH 2/7] fix: fix compilation errors - add static imports and fix Splitter return type --- .../io/grpc/internal/Http2ClientStreamTransportState.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/io/grpc/internal/Http2ClientStreamTransportState.java b/core/src/main/java/io/grpc/internal/Http2ClientStreamTransportState.java index 999117c4a17..b1ee7370f1a 100644 --- a/core/src/main/java/io/grpc/internal/Http2ClientStreamTransportState.java +++ b/core/src/main/java/io/grpc/internal/Http2ClientStreamTransportState.java @@ -16,6 +16,9 @@ package io.grpc.internal; +import static io.grpc.internal.GrpcUtil.ACCEPT_ENCODING_SPLITTER; +import static io.grpc.internal.GrpcUtil.MESSAGE_ACCEPT_ENCODING_KEY; + import com.google.common.base.Preconditions; import io.grpc.CallOptions; import io.grpc.InternalMetadata; @@ -312,9 +315,9 @@ private void validateGrpcAcceptEncoding(Metadata headers) { String acceptEncoding = new String(acceptEncodingBytes, StandardCharsets.US_ASCII); // Check if gzip is in the accepted encodings (comma-separated list) - String[] encodings = GrpcUtil.ACCEPT_ENCODING_SPLITTER.split(acceptEncoding); + Iterable encodingsIterable = GrpcUtil.ACCEPT_ENCODING_SPLITTER.split(acceptEncoding); boolean gzipAccepted = false; - for (String encoding : encodings) { + for (String encoding : encodingsIterable) { if ("gzip".equalsIgnoreCase(encoding.trim())) { gzipAccepted = true; break; From 12404c6db5c9443e8543fbdbfe7f79fe228c1ba6 Mon Sep 17 00:00:00 2001 From: jlaportebot Date: Sun, 9 Aug 2026 10:52:17 -0400 Subject: [PATCH 3/7] fix: add @Override annotation to fix MissingOverride error --- .../java/io/grpc/internal/Http2ClientStreamTransportState.java | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/main/java/io/grpc/internal/Http2ClientStreamTransportState.java b/core/src/main/java/io/grpc/internal/Http2ClientStreamTransportState.java index b1ee7370f1a..b9b4c79c814 100644 --- a/core/src/main/java/io/grpc/internal/Http2ClientStreamTransportState.java +++ b/core/src/main/java/io/grpc/internal/Http2ClientStreamTransportState.java @@ -94,6 +94,7 @@ protected Http2ClientStreamTransportState( * @param enabled whether message compression is enabled * @param compressorName the name of the compressor being used (e.g., "gzip") */ + @Override public final void setMessageCompression(boolean enabled, String compressorName) { if (enabled && "gzip".equals(compressorName)) { clientSentGzipRequest = true; From 1c3900513ab8bcfe25110976445e4a7b04d3f0be Mon Sep 17 00:00:00 2001 From: jlaportebot Date: Sun, 9 Aug 2026 11:02:39 -0400 Subject: [PATCH 4/7] fix: remove duplicate setMessageCompression in InProcessTransport to fix Bazel build --- .../java/io/grpc/inprocess/InProcessTransport.java | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/inprocess/src/main/java/io/grpc/inprocess/InProcessTransport.java b/inprocess/src/main/java/io/grpc/inprocess/InProcessTransport.java index f3a24e1c343..37244d8b6b6 100644 --- a/inprocess/src/main/java/io/grpc/inprocess/InProcessTransport.java +++ b/inprocess/src/main/java/io/grpc/inprocess/InProcessTransport.java @@ -668,11 +668,6 @@ public void setMessageCompression(boolean enable) { // noop } - @Override - public void setMessageCompression(boolean enabled, String compressorName) { - // noop - } - @Override public void optimizeForDirectExecutor() {} @@ -896,12 +891,6 @@ public void halfClose() { syncContext.drain(); } - @Override - public void setMessageCompression(boolean enable) {} - - @Override - public void setMessageCompression(boolean enabled, String compressorName) {} - @Override public void setAuthority(String string) { InProcessStream.this.authority = string; From a91ccc4611a3b0271ebb45ebcd4a6e8c97bebec0 Mon Sep 17 00:00:00 2001 From: jlaportebot Date: Sun, 9 Aug 2026 21:23:57 -0400 Subject: [PATCH 5/7] fix: add setMessageCompression override to AbstractClientStream and InProcessClientStream NettyClientStream extends AbstractClientStream but compilation failed because AbstractClientStream did not override the new ClientStream.setMessageCompression(boolean, String) method added to the interface. The method was only on the inner TransportState class. Add the override to AbstractClientStream that delegates to transportState(), matching the pattern of setDecompressorRegistry. Also add the override to InProcessClientStream (noop, matching existing setMessageCompression(boolean)). Signed-off-by: jlaportebot --- .../java/io/grpc/internal/AbstractClientStream.java | 11 +++++++++++ .../java/io/grpc/inprocess/InProcessTransport.java | 5 +++++ 2 files changed, 16 insertions(+) diff --git a/core/src/main/java/io/grpc/internal/AbstractClientStream.java b/core/src/main/java/io/grpc/internal/AbstractClientStream.java index 42fa24f6f30..512fc59d57b 100644 --- a/core/src/main/java/io/grpc/internal/AbstractClientStream.java +++ b/core/src/main/java/io/grpc/internal/AbstractClientStream.java @@ -147,6 +147,17 @@ public final void setDecompressorRegistry(DecompressorRegistry decompressorRegis transportState().setDecompressorRegistry(decompressorRegistry); } + /** + * {@inheritDoc} + * + *

Delegates to the transport state so that subclasses (e.g., NettyClientStream, + * InProcessClientStream) inherit the new implementation via their TransportState. + */ + @Override + public void setMessageCompression(boolean enabled, String compressorName) { + transportState().setMessageCompression(enabled, compressorName); + } + /** {@inheritDoc} */ @Override protected abstract TransportState transportState(); diff --git a/inprocess/src/main/java/io/grpc/inprocess/InProcessTransport.java b/inprocess/src/main/java/io/grpc/inprocess/InProcessTransport.java index 37244d8b6b6..9bda1b2dfaf 100644 --- a/inprocess/src/main/java/io/grpc/inprocess/InProcessTransport.java +++ b/inprocess/src/main/java/io/grpc/inprocess/InProcessTransport.java @@ -668,6 +668,11 @@ public void setMessageCompression(boolean enable) { // noop } + @Override + public void setMessageCompression(boolean enabled, String compressorName) { + // noop + } + @Override public void optimizeForDirectExecutor() {} From 4236cab8bb33a5eba94521fc36e13dce6951539a Mon Sep 17 00:00:00 2001 From: jlaportebot Date: Sun, 9 Aug 2026 22:06:53 -0400 Subject: [PATCH 6/7] fix: add setMessageCompression override to InProcessClientStream NettyClientStream extends AbstractClientStream but compilation failed because AbstractClientStream did not override the new ClientStream.setMessageCompression(boolean, String) method added to the interface. The method was only on the inner TransportState class. Add the override to InProcessClientStream (noop, matching existing setMessageCompression(boolean)). Also add the override to AbstractClientStream that delegates to transportState(), matching the pattern of setDecompressorRegistry. Signed-off-by: jlaportebot --- .../src/main/java/io/grpc/inprocess/InProcessTransport.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/inprocess/src/main/java/io/grpc/inprocess/InProcessTransport.java b/inprocess/src/main/java/io/grpc/inprocess/InProcessTransport.java index 9bda1b2dfaf..2294b60a93d 100644 --- a/inprocess/src/main/java/io/grpc/inprocess/InProcessTransport.java +++ b/inprocess/src/main/java/io/grpc/inprocess/InProcessTransport.java @@ -947,6 +947,11 @@ public void setDeadline(Deadline deadline) { @Override public void appendTimeoutInsight(InsightBuilder insight) { } + + @Override + public void setMessageCompression(boolean enabled, String compressorName) { + // noop + } } } From f19593a66f34cb9b2ca89c99aa87d1b4d9bb8ca9 Mon Sep 17 00:00:00 2001 From: jlaportebot Date: Sun, 9 Aug 2026 23:06:47 -0400 Subject: [PATCH 7/7] fix: add setMessageCompression(boolean) override to InProcessServerStream and InProcessClientStream Both classes implement Stream interface which requires setMessageCompression(boolean). Previously only setMessageCompression(boolean, String) was implemented. Fixes compilation errors in grpc-inprocess module. --- .../src/main/java/io/grpc/inprocess/InProcessTransport.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/inprocess/src/main/java/io/grpc/inprocess/InProcessTransport.java b/inprocess/src/main/java/io/grpc/inprocess/InProcessTransport.java index 2294b60a93d..9d8ade99b1a 100644 --- a/inprocess/src/main/java/io/grpc/inprocess/InProcessTransport.java +++ b/inprocess/src/main/java/io/grpc/inprocess/InProcessTransport.java @@ -948,6 +948,11 @@ public void setDeadline(Deadline deadline) { public void appendTimeoutInsight(InsightBuilder insight) { } + @Override + public void setMessageCompression(boolean enable) { + // noop + } + @Override public void setMessageCompression(boolean enabled, String compressorName) { // noop