From fe36e6defde9cf6a73eea02a1f9f2ba3a47d974e Mon Sep 17 00:00:00 2001 From: vimanikag Date: Mon, 15 Sep 2025 06:05:38 +0000 Subject: [PATCH 1/3] 11246 :: Unexpected error when server expands a compressed message to learn it is too large --- .../java/io/grpc/internal/ServerImpl.java | 13 ++- .../java/io/grpc/internal/ServerImplTest.java | 94 +++++++++++++++++++ 2 files changed, 105 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/io/grpc/internal/ServerImpl.java b/core/src/main/java/io/grpc/internal/ServerImpl.java index dc0709e1fb8..75e410845ab 100644 --- a/core/src/main/java/io/grpc/internal/ServerImpl.java +++ b/core/src/main/java/io/grpc/internal/ServerImpl.java @@ -56,6 +56,7 @@ import io.grpc.ServerServiceDefinition; import io.grpc.ServerTransportFilter; import io.grpc.Status; +import io.grpc.StatusRuntimeException; import io.perfmark.Link; import io.perfmark.PerfMark; import io.perfmark.Tag; @@ -808,10 +809,18 @@ void setListener(ServerStreamListener listener) { /** * Like {@link ServerCall#close(Status, Metadata)}, but thread-safe for internal use. */ - private void internalClose(Throwable t) { + private void internalClose(Throwable throwable) { // TODO(ejona86): this is not thread-safe :) String description = "Application error processing RPC"; - stream.close(Status.UNKNOWN.withDescription(description).withCause(t), new Metadata()); + Status statusToPropagate = Status.UNKNOWN.withDescription(description).withCause(throwable); + if (throwable instanceof StatusRuntimeException) { + StatusRuntimeException statusRuntimeException = (StatusRuntimeException) throwable; + Status.Code code = statusRuntimeException.getStatus().getCode(); + if (code == Status.Code.RESOURCE_EXHAUSTED) { + statusToPropagate = statusRuntimeException.getStatus().withCause(throwable); + } + } + stream.close(statusToPropagate, new Metadata()); } @Override diff --git a/core/src/test/java/io/grpc/internal/ServerImplTest.java b/core/src/test/java/io/grpc/internal/ServerImplTest.java index 0f18efe078c..1027fe53681 100644 --- a/core/src/test/java/io/grpc/internal/ServerImplTest.java +++ b/core/src/test/java/io/grpc/internal/ServerImplTest.java @@ -77,6 +77,7 @@ import io.grpc.ServiceDescriptor; import io.grpc.Status; import io.grpc.Status.Code; +import io.grpc.StatusRuntimeException; import io.grpc.StringMarshaller; import io.grpc.internal.ServerImpl.JumpToApplicationThreadServerStreamListener; import io.grpc.internal.ServerImplBuilder.ClientTransportServersBuilder; @@ -1542,6 +1543,99 @@ public void channelz_transport_membershp() throws Exception { assertTrue(after.end); } + @Test + public void testInternalClose_nonProtocolStatusRuntimeExceptionBecomesUnknown() { + JumpToApplicationThreadServerStreamListener listener + = new JumpToApplicationThreadServerStreamListener( + executor.getScheduledExecutorService(), + executor.getScheduledExecutorService(), + stream, + Context.ROOT.withCancellation(), + PerfMark.createTag()); + ServerStreamListener mockListener = mock(ServerStreamListener.class); + listener.setListener(mockListener); + + StatusRuntimeException statusRuntimeException + = new StatusRuntimeException(Status.PERMISSION_DENIED.withDescription("denied")); + doThrow(statusRuntimeException).when(mockListener).onReady(); + listener.onReady(); + try { + executor.runDueTasks(); + fail("Expected exception"); + } catch (RuntimeException t) { + assertSame(statusRuntimeException, t); + ensureServerStateNotLeaked(); + } + verify(stream).close(statusCaptor.capture(), metadataCaptor.capture()); + Status status = statusCaptor.getValue(); + assertEquals(Code.UNKNOWN, status.getCode()); + assertEquals("Application error processing RPC", status.getDescription()); + assertEquals(statusRuntimeException, status.getCause()); + assertTrue(metadataCaptor.getValue().keys().isEmpty()); + } + + @Test + public void testInternalClose_otherExceptionBecomesUnknown() { + JumpToApplicationThreadServerStreamListener listener + = new JumpToApplicationThreadServerStreamListener( + executor.getScheduledExecutorService(), + executor.getScheduledExecutorService(), + stream, + Context.ROOT.withCancellation(), + PerfMark.createTag()); + ServerStreamListener mockListener = mock(ServerStreamListener.class); + listener.setListener(mockListener); + + RuntimeException expectedT = new RuntimeException(); + doThrow(expectedT).when(mockListener) + .messagesAvailable(any(StreamListener.MessageProducer.class)); + listener.messagesAvailable(mock(StreamListener.MessageProducer.class)); + try { + executor.runDueTasks(); + fail("Expected exception"); + } catch (RuntimeException t) { + assertSame(expectedT, t); + ensureServerStateNotLeaked(); + } + verify(stream).close(statusCaptor.capture(), metadataCaptor.capture()); + Status status = statusCaptor.getValue(); + assertEquals(Code.UNKNOWN, status.getCode()); + assertEquals("Application error processing RPC", status.getDescription()); + assertEquals(expectedT, status.getCause()); + assertTrue(metadataCaptor.getValue().keys().isEmpty()); + } + + @Test + public void testInternalClose_propagatesResourceExhausted() { + JumpToApplicationThreadServerStreamListener listener + = new JumpToApplicationThreadServerStreamListener( + executor.getScheduledExecutorService(), + executor.getScheduledExecutorService(), + stream, + Context.ROOT.withCancellation(), + PerfMark.createTag()); + ServerStreamListener mockListener = mock(ServerStreamListener.class); + listener.setListener(mockListener); + + StatusRuntimeException statusRuntimeException + = new StatusRuntimeException(Status.RESOURCE_EXHAUSTED.withDescription("exhausted")); + doThrow(statusRuntimeException).when(mockListener) + .messagesAvailable(any(StreamListener.MessageProducer.class)); + listener.messagesAvailable(mock(StreamListener.MessageProducer.class)); + try { + executor.runDueTasks(); + fail("Expected exception"); + } catch (RuntimeException t) { + assertSame(statusRuntimeException, t); + } + verify(stream).close(statusCaptor.capture(), metadataCaptor.capture()); + Status status = statusCaptor.getValue(); + assertEquals(Status.Code.RESOURCE_EXHAUSTED, status.getCode()); + assertEquals("exhausted", status.getDescription()); + assertEquals(statusRuntimeException, status.getCause()); + assertTrue(metadataCaptor.getValue().keys().isEmpty()); + } + private void createAndStartServer() throws IOException { createServer(); server.start(); From 61fbe27382416b77524824eeca9e5126f26a29a1 Mon Sep 17 00:00:00 2001 From: vimanikag Date: Mon, 15 Sep 2025 06:18:02 +0000 Subject: [PATCH 2/3] 11246 :: Unexpected error when server expands a compressed message to learn it is too large --- core/src/main/java/io/grpc/internal/ServerImpl.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/io/grpc/internal/ServerImpl.java b/core/src/main/java/io/grpc/internal/ServerImpl.java index 75e410845ab..d482ba908d7 100644 --- a/core/src/main/java/io/grpc/internal/ServerImpl.java +++ b/core/src/main/java/io/grpc/internal/ServerImpl.java @@ -809,15 +809,15 @@ void setListener(ServerStreamListener listener) { /** * Like {@link ServerCall#close(Status, Metadata)}, but thread-safe for internal use. */ - private void internalClose(Throwable throwable) { + private void internalClose(Throwable t) { // TODO(ejona86): this is not thread-safe :) String description = "Application error processing RPC"; - Status statusToPropagate = Status.UNKNOWN.withDescription(description).withCause(throwable); - if (throwable instanceof StatusRuntimeException) { - StatusRuntimeException statusRuntimeException = (StatusRuntimeException) throwable; + Status statusToPropagate = Status.UNKNOWN.withDescription(description).withCause(t); + if (t instanceof StatusRuntimeException) { + StatusRuntimeException statusRuntimeException = (StatusRuntimeException) t; Status.Code code = statusRuntimeException.getStatus().getCode(); if (code == Status.Code.RESOURCE_EXHAUSTED) { - statusToPropagate = statusRuntimeException.getStatus().withCause(throwable); + statusToPropagate = statusRuntimeException.getStatus().withCause(t); } } stream.close(statusToPropagate, new Metadata()); From bb929aa37bdd66db0f319603f6bf5b076257c994 Mon Sep 17 00:00:00 2001 From: vimanikag Date: Mon, 15 Sep 2025 10:03:10 +0000 Subject: [PATCH 3/3] 11246 :: Unexpected error when server expands a compressed message to learn it is too large --- core/src/main/java/io/grpc/internal/ServerImpl.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/io/grpc/internal/ServerImpl.java b/core/src/main/java/io/grpc/internal/ServerImpl.java index d482ba908d7..9af2f8a2cf9 100644 --- a/core/src/main/java/io/grpc/internal/ServerImpl.java +++ b/core/src/main/java/io/grpc/internal/ServerImpl.java @@ -814,10 +814,8 @@ private void internalClose(Throwable t) { String description = "Application error processing RPC"; Status statusToPropagate = Status.UNKNOWN.withDescription(description).withCause(t); if (t instanceof StatusRuntimeException) { - StatusRuntimeException statusRuntimeException = (StatusRuntimeException) t; - Status.Code code = statusRuntimeException.getStatus().getCode(); - if (code == Status.Code.RESOURCE_EXHAUSTED) { - statusToPropagate = statusRuntimeException.getStatus().withCause(t); + if (((StatusRuntimeException) t).getStatus().getCode() == Status.Code.RESOURCE_EXHAUSTED) { + statusToPropagate = ((StatusRuntimeException) t).getStatus().withCause(t); } } stream.close(statusToPropagate, new Metadata());