Skip to content

Commit

Permalink
binder: Helper class to allow in process servers to use peer uids in …
Browse files Browse the repository at this point in the history
…test (#11014)
  • Loading branch information
abtom committed Mar 22, 2024
1 parent 10cb4a3 commit 537dbe8
Show file tree
Hide file tree
Showing 2 changed files with 186 additions and 0 deletions.
123 changes: 123 additions & 0 deletions binder/src/test/java/io/grpc/binder/PeerUidTestHelperTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
package io.grpc.binder;

import static com.google.common.truth.Truth.assertThat;
import static java.nio.charset.StandardCharsets.UTF_8;

import com.google.common.collect.ImmutableList;
import io.grpc.CallOptions;
import io.grpc.Channel;
import io.grpc.ClientInterceptors;
import io.grpc.ManagedChannel;
import io.grpc.Metadata;
import io.grpc.MethodDescriptor;
import io.grpc.ServerCallHandler;
import io.grpc.ServerInterceptor;
import io.grpc.ServerInterceptors;
import io.grpc.ServerServiceDefinition;
import io.grpc.inprocess.InProcessChannelBuilder;
import io.grpc.inprocess.InProcessServerBuilder;
import io.grpc.stub.ClientCalls;
import io.grpc.stub.MetadataUtils;
import io.grpc.stub.ServerCalls;
import io.grpc.testing.GrpcCleanupRule;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.atomic.AtomicReference;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;

@RunWith(JUnit4.class)
public final class PeerUidTestHelperTest {

@Rule
public final GrpcCleanupRule grpcCleanup = new GrpcCleanupRule();

private static final int FAKE_UID = 12345;

private final AtomicReference<PeerUid> clientUidCapture = new AtomicReference<>();

@Test
public void keyPopulatedWithInterceptorAndHeader() throws Exception {
makeServiceCall(/* includeInterceptor= */ true, /* includeUidInHeader= */ true, FAKE_UID);
assertThat(clientUidCapture.get()).isEqualTo(new PeerUid(FAKE_UID));
}

@Test
public void keyNotPopulatedWithInterceptorAndNoHeader() throws Exception {
makeServiceCall(/* includeInterceptor= */ true, /* includeUidInHeader= */ false, /* uid= */ -1);
assertThat(clientUidCapture.get()).isNull();
}

@Test
public void keyNotPopulatedWithoutInterceptorAndWithHeader() throws Exception {
makeServiceCall(
/* includeInterceptor= */ false, /* includeUidInHeader= */ true, /* uid= */ FAKE_UID);
assertThat(clientUidCapture.get()).isNull();
}

private final MethodDescriptor<String, String> method =
MethodDescriptor.newBuilder(StringMarshaller.INSTANCE, StringMarshaller.INSTANCE)
.setFullMethodName("test/method")
.setType(MethodDescriptor.MethodType.UNARY)
.build();

private void makeServiceCall(boolean includeInterceptor, boolean includeUidInHeader, int uid)
throws Exception {
ServerCallHandler<String, String> callHandler =
ServerCalls.asyncUnaryCall(
(req, respObserver) -> {
clientUidCapture.set(PeerUids.REMOTE_PEER.get());
respObserver.onNext(req);
respObserver.onCompleted();
});
ImmutableList<ServerInterceptor> interceptors;
if (includeInterceptor) {
interceptors = ImmutableList.of(PeerUidTestHelper.newTestPeerIdentifyingServerInterceptor());
} else {
interceptors = ImmutableList.of();
}
ServerServiceDefinition serviceDef =
ServerInterceptors.intercept(
ServerServiceDefinition.builder("test").addMethod(method, callHandler).build(),
interceptors);

InProcessServerBuilder server =
InProcessServerBuilder.forName("test").directExecutor().addService(serviceDef);

grpcCleanup.register(server.build().start());

Channel channel = InProcessChannelBuilder.forName("test").directExecutor().build();
grpcCleanup.register((ManagedChannel) channel);

if (includeUidInHeader) {
Metadata header = new Metadata();
header.put(PeerUidTestHelper.UID_KEY, uid);
channel =
ClientInterceptors.intercept(channel, MetadataUtils.newAttachHeadersInterceptor(header));
}

ClientCalls.blockingUnaryCall(channel, method, CallOptions.DEFAULT, "hello");
}

private static class StringMarshaller implements MethodDescriptor.Marshaller<String> {

public static final StringMarshaller INSTANCE = new StringMarshaller();

@Override
public InputStream stream(String value) {
return new ByteArrayInputStream(value.getBytes(UTF_8));
}

@Override
public String parse(InputStream stream) {
try {
return new String(stream.readAllBytes(), UTF_8);
} catch (IOException ex) {
throw new RuntimeException(ex);
}
}
}
}
63 changes: 63 additions & 0 deletions binder/src/testFixtures/java/io/grpc/binder/PeerUidTestHelper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package io.grpc.binder;

import io.grpc.Context;
import io.grpc.Contexts;
import io.grpc.Metadata;
import io.grpc.ServerCall;
import io.grpc.ServerCallHandler;
import io.grpc.ServerInterceptor;

/**
* Class which helps set up {@link PeerUids} to be used in tests.
*/
public final class PeerUidTestHelper {

/**
* The UID of the calling package is set with the value of this key.
*/
public static final Metadata.Key<Integer> UID_KEY =
Metadata.Key.of("binder-remote-uid-for-unit-testing", PeerUidTestMarshaller.INSTANCE);

/**
* Creates an interceptor that associates the {@link PeerUids#REMOTE_PEER} key in the request
* {@link Context} with a UID provided by the client in the {@link #UID_KEY} request header, if
* present.
*
* <p>The returned interceptor works with any gRPC transport but is meant for in-process unit
* testing of gRPC/binder services that depend on {@link PeerUids}.
*/
public static ServerInterceptor newTestPeerIdentifyingServerInterceptor() {
return new ServerInterceptor() {
@Override
public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(
ServerCall<ReqT, RespT> call, Metadata headers, ServerCallHandler<ReqT, RespT> next) {
if (headers.containsKey(UID_KEY)) {
Context context =
Context.current().withValue(PeerUids.REMOTE_PEER, new PeerUid(headers.get(UID_KEY)));
return Contexts.interceptCall(context, call, headers, next);
}
return next.startCall(call, headers);
}
};
}

private PeerUidTestHelper() {
}

private static class PeerUidTestMarshaller implements Metadata.AsciiMarshaller<Integer> {

public static final PeerUidTestMarshaller INSTANCE = new PeerUidTestMarshaller();

@Override
public String toAsciiString(Integer value) {
return value.toString();
}

@Override
public Integer parseAsciiString(String serialized) {
return Integer.parseInt(serialized);
}
}

;
}

0 comments on commit 537dbe8

Please sign in to comment.