Skip to content

Commit d25ebaf

Browse files
authored
core: fix NPE in ConfigSelectingClientCall
Fix the following bug: ManagedChannelImpl.ConfigSelectingClientCall may return early in start() leaving delegate null, and fails request() method after start(). Currently the bug can only be triggered when using xDS.
1 parent d4fa0ec commit d25ebaf

File tree

2 files changed

+28
-0
lines changed

2 files changed

+28
-0
lines changed

core/src/main/java/io/grpc/internal/ManagedChannelImpl.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1179,13 +1179,15 @@ protected ClientCall<ReqT, RespT> delegate() {
11791179
return delegate;
11801180
}
11811181

1182+
@SuppressWarnings("unchecked")
11821183
@Override
11831184
public void start(Listener<RespT> observer, Metadata headers) {
11841185
PickSubchannelArgs args = new PickSubchannelArgsImpl(method, headers, callOptions);
11851186
InternalConfigSelector.Result result = configSelector.selectConfig(args);
11861187
Status status = result.getStatus();
11871188
if (!status.isOk()) {
11881189
executeCloseObserverInContext(observer, status);
1190+
delegate = (ClientCall<ReqT, RespT>) NOOP_CALL;
11891191
return;
11901192
}
11911193
ClientInterceptor interceptor = result.getInterceptor();
@@ -1226,6 +1228,29 @@ public void cancel(@Nullable String message, @Nullable Throwable cause) {
12261228
}
12271229
}
12281230

1231+
private static final ClientCall<Object, Object> NOOP_CALL = new ClientCall<Object, Object>() {
1232+
@Override
1233+
public void start(Listener<Object> responseListener, Metadata headers) {}
1234+
1235+
@Override
1236+
public void request(int numMessages) {}
1237+
1238+
@Override
1239+
public void cancel(String message, Throwable cause) {}
1240+
1241+
@Override
1242+
public void halfClose() {}
1243+
1244+
@Override
1245+
public void sendMessage(Object message) {}
1246+
1247+
// Always returns {@code false}, since this is only used when the startup of the call fails.
1248+
@Override
1249+
public boolean isReady() {
1250+
return false;
1251+
}
1252+
};
1253+
12291254
/**
12301255
* Terminate the channel if termination conditions are met.
12311256
*/

core/src/test/java/io/grpc/internal/ConfigSelectingClientCallTest.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,9 @@ public Result selectConfig(PickSubchannelArgs args) {
135135
ArgumentCaptor<Status> statusCaptor = ArgumentCaptor.forClass(null);
136136
verify(callListener).onClose(statusCaptor.capture(), any(Metadata.class));
137137
assertThat(statusCaptor.getValue().getCode()).isEqualTo(Status.Code.FAILED_PRECONDITION);
138+
139+
// The call should not delegate to null and fail methods with NPE.
140+
configSelectingClientCall.request(1);
138141
}
139142

140143
private final class TestChannel extends Channel {

0 commit comments

Comments
 (0)