diff --git a/core/src/main/java/io/grpc/internal/ManagedChannelImpl.java b/core/src/main/java/io/grpc/internal/ManagedChannelImpl.java index ac9c78ad2f4..4b3d5109b35 100644 --- a/core/src/main/java/io/grpc/internal/ManagedChannelImpl.java +++ b/core/src/main/java/io/grpc/internal/ManagedChannelImpl.java @@ -1179,6 +1179,7 @@ protected ClientCall delegate() { return delegate; } + @SuppressWarnings("unchecked") @Override public void start(Listener observer, Metadata headers) { PickSubchannelArgs args = new PickSubchannelArgsImpl(method, headers, callOptions); @@ -1186,6 +1187,7 @@ public void start(Listener observer, Metadata headers) { Status status = result.getStatus(); if (!status.isOk()) { executeCloseObserverInContext(observer, status); + delegate = (ClientCall) NOOP_CALL; return; } ClientInterceptor interceptor = result.getInterceptor(); @@ -1226,6 +1228,29 @@ public void cancel(@Nullable String message, @Nullable Throwable cause) { } } + private static final ClientCall NOOP_CALL = new ClientCall() { + @Override + public void start(Listener responseListener, Metadata headers) {} + + @Override + public void request(int numMessages) {} + + @Override + public void cancel(String message, Throwable cause) {} + + @Override + public void halfClose() {} + + @Override + public void sendMessage(Object message) {} + + // Always returns {@code false}, since this is only used when the startup of the call fails. + @Override + public boolean isReady() { + return false; + } + }; + /** * Terminate the channel if termination conditions are met. */ diff --git a/core/src/test/java/io/grpc/internal/ConfigSelectingClientCallTest.java b/core/src/test/java/io/grpc/internal/ConfigSelectingClientCallTest.java index 33f2e014e62..9b3f8ad3b23 100644 --- a/core/src/test/java/io/grpc/internal/ConfigSelectingClientCallTest.java +++ b/core/src/test/java/io/grpc/internal/ConfigSelectingClientCallTest.java @@ -135,6 +135,9 @@ public Result selectConfig(PickSubchannelArgs args) { ArgumentCaptor statusCaptor = ArgumentCaptor.forClass(null); verify(callListener).onClose(statusCaptor.capture(), any(Metadata.class)); assertThat(statusCaptor.getValue().getCode()).isEqualTo(Status.Code.FAILED_PRECONDITION); + + // The call should not delegate to null and fail methods with NPE. + configSelectingClientCall.request(1); } private final class TestChannel extends Channel {