Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

core: fix NPE in ConfigSelectingClientCall #8087

Merged
merged 1 commit into from Apr 15, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
25 changes: 25 additions & 0 deletions core/src/main/java/io/grpc/internal/ManagedChannelImpl.java
Expand Up @@ -1179,13 +1179,15 @@ protected ClientCall<ReqT, RespT> delegate() {
return delegate;
}

@SuppressWarnings("unchecked")
@Override
public void start(Listener<RespT> observer, Metadata headers) {
PickSubchannelArgs args = new PickSubchannelArgsImpl(method, headers, callOptions);
InternalConfigSelector.Result result = configSelector.selectConfig(args);
Status status = result.getStatus();
if (!status.isOk()) {
executeCloseObserverInContext(observer, status);
delegate = (ClientCall<ReqT, RespT>) NOOP_CALL;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would have been nice to avoid the cast and I think it is possible to achieve that but I understand this pattern was copied from ClientInterceptors.NOOP_CALL and DelayedClientCall.NOOP_CALL. If possible the refactoring can combine these into one and also avoid the cast?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If possible the refactoring can combine these into one and also avoid the cast?

I think so.

return;
}
ClientInterceptor interceptor = result.getInterceptor();
Expand Down Expand Up @@ -1226,6 +1228,29 @@ public void cancel(@Nullable String message, @Nullable Throwable cause) {
}
}

private static final ClientCall<Object, Object> NOOP_CALL = new ClientCall<Object, Object>() {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that this is copied from ClientInterceptors.NOOP_CALL and DelayedClientCall.NOOP_CALL. Not refactoring and reusing the code, because this makes minimum change to backport.

@Override
public void start(Listener<Object> 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.
*/
Expand Down
Expand Up @@ -135,6 +135,9 @@ public Result selectConfig(PickSubchannelArgs args) {
ArgumentCaptor<Status> 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 {
Expand Down