Skip to content

Commit

Permalink
Basic support for client interceptors with service stubs
Browse files Browse the repository at this point in the history
  • Loading branch information
spericas committed Mar 21, 2024
1 parent 1973419 commit 6559f96
Show file tree
Hide file tree
Showing 7 changed files with 155 additions and 44 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,15 @@

package io.helidon.webclient.grpc;

import java.util.Collection;
import java.util.function.Consumer;

import io.helidon.builder.api.RuntimeType;
import io.helidon.webclient.api.WebClient;
import io.helidon.webclient.spi.Protocol;

import io.grpc.Channel;
import io.grpc.ClientInterceptor;

/**
* gRPC client.
Expand Down Expand Up @@ -94,4 +96,22 @@ static GrpcClient create() {
* @return a new gRPC channel
*/
Channel channel();

/**
* Create a gRPC channel for this client that can be used to create stubs.
*
* @param interceptors the array of client interceptors
* @return a new gRPC channel
*/
Channel channel(ClientInterceptor... interceptors);

/**
* Create a gRPC channel for this client that can be used to create stubs.
*
* @param interceptors the list of client interceptors
* @return a new gRPC channel
*/
default Channel channel(Collection<ClientInterceptor> interceptors) {
return channel(interceptors.toArray(new ClientInterceptor[] {}));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
import io.helidon.webclient.http2.Http2Client;

import io.grpc.Channel;
import io.grpc.ClientInterceptor;
import io.grpc.ClientInterceptors;

class GrpcClientImpl implements GrpcClient {
private final WebClient webClient;
Expand Down Expand Up @@ -54,4 +56,9 @@ public GrpcServiceClient serviceClient(GrpcServiceDescriptor descriptor) {
public Channel channel() {
return new GrpcChannel(this);
}

@Override
public Channel channel(ClientInterceptor... interceptors) {
return ClientInterceptors.intercept(channel(), interceptors);
}
}
2 changes: 1 addition & 1 deletion webclient/tests/grpc/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@
</execution>
</executions>
<configuration>
<protocArtifact>com.google.protobuf:protoc:3.5.1-1:exe:${os.detected.classifier}</protocArtifact>
<protocArtifact>com.google.protobuf:protoc:3.17.3:exe:${os.detected.classifier}</protocArtifact>
<pluginId>grpc-java</pluginId>
<pluginArtifact>io.grpc:protoc-gen-grpc-java:${version.lib.grpc}:exe:${os.detected.classifier}
</pluginArtifact>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,30 @@
import java.util.List;
import java.util.Locale;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CopyOnWriteArrayList;

import io.grpc.CallOptions;
import io.grpc.Channel;
import io.grpc.ClientCall;
import io.grpc.ClientInterceptor;
import io.grpc.MethodDescriptor;
import io.grpc.stub.StreamObserver;
import io.helidon.common.Weight;
import io.helidon.common.configurable.Resource;
import io.helidon.webserver.WebServerConfig;
import io.helidon.webserver.grpc.GrpcRouting;
import io.helidon.webserver.testing.junit5.SetUpRoute;
import io.helidon.webserver.testing.junit5.SetUpServer;

import io.grpc.stub.StreamObserver;
import org.junit.jupiter.api.BeforeEach;

class GrpcBaseTest {

private final List<Class<?>> calledInterceptors = new CopyOnWriteArrayList<>();

protected List<Class<?>> calledInterceptors() {
return calledInterceptors;
}

@SetUpServer
public static void setup(WebServerConfig.Builder builder) {
builder.tls(tls -> tls.privateKey(key -> key
Expand Down Expand Up @@ -64,6 +77,11 @@ static void setUpRoute(GrpcRouting.Builder routing) {
GrpcStubTest::echo);
}

@BeforeEach
void setUpTest() {
calledInterceptors.clear();
}

static void upper(Strings.StringMessage req,
StreamObserver<Strings.StringMessage> streamObserver) {
Strings.StringMessage msg = Strings.StringMessage.newBuilder()
Expand Down Expand Up @@ -178,4 +196,42 @@ public void onCompleted() {
}
};
}

class BaseInterceptor implements ClientInterceptor {
@Override
public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(MethodDescriptor<ReqT, RespT> method,
CallOptions callOptions,
Channel next) {
calledInterceptors.add(getClass());
return next.newCall(method, callOptions);
}
}

@Weight(10.0)
class Weight10Interceptor extends BaseInterceptor {
}

@Weight(50.0)
class Weight50Interceptor extends BaseInterceptor {
}

@Weight(100.0)
class Weight100Interceptor extends BaseInterceptor {
}

@Weight(500.0)
class Weight500Interceptor extends BaseInterceptor {
}

@Weight(1000.0)
class Weight1000Interceptor extends BaseInterceptor {
}

List<ClientInterceptor> allInterceptors() {
return List.of(new Weight10Interceptor(),
new Weight50Interceptor(),
new Weight100Interceptor(),
new Weight500Interceptor(),
new Weight1000Interceptor());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* Copyright (c) 2024 Oracle and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.helidon.webclient.grpc.tests;

import io.grpc.Channel;
import io.helidon.common.configurable.Resource;
import io.helidon.common.tls.Tls;
import io.helidon.webclient.api.WebClient;
import io.helidon.webclient.grpc.GrpcClient;
import io.helidon.webserver.WebServer;
import io.helidon.webserver.testing.junit5.ServerTest;
import org.junit.jupiter.api.Test;

import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.contains;

/**
* Tests gRPC client using stubs and TLS.
*/
@ServerTest
class GrpcInterceptorStubTest extends GrpcBaseTest {

private final WebClient webClient;

private GrpcInterceptorStubTest(WebServer server) {
Tls clientTls = Tls.builder()
.trust(trust -> trust
.keystore(store -> store
.passphrase("password")
.trustStore(true)
.keystore(Resource.create("client.p12"))))
.build();
this.webClient = WebClient.builder()
.tls(clientTls)
.baseUri("https://localhost:" + server.port())
.build();
}

@Test
void testUnaryUpper() {
GrpcClient grpcClient = webClient.client(GrpcClient.PROTOCOL);
Channel channel = grpcClient.channel(allInterceptors()); // channel with all interceptors
StringServiceGrpc.StringServiceBlockingStub service = StringServiceGrpc.newBlockingStub(channel);
Strings.StringMessage res = service.upper(newStringMessage("hello"));
assertThat(res.getText(), is("HELLO"));
assertThat(calledInterceptors(), contains(Weight1000Interceptor.class,
Weight500Interceptor.class,
Weight100Interceptor.class,
Weight50Interceptor.class,
Weight10Interceptor.class));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,6 @@

package io.helidon.webclient.grpc.tests;

import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

import io.grpc.CallOptions;
import io.grpc.Channel;
import io.grpc.ClientCall;
import io.grpc.ClientInterceptor;
import io.grpc.MethodDescriptor;
import io.helidon.common.Weight;
import io.helidon.common.configurable.Resource;
import io.helidon.common.tls.Tls;
import io.helidon.webclient.grpc.GrpcClient;
Expand All @@ -46,7 +37,6 @@ class GrpcInterceptorTest extends GrpcBaseTest {

private final GrpcClient grpcClient;
private final GrpcServiceDescriptor serviceDescriptor;
private final List<Class<?>> calledInterceptors = new CopyOnWriteArrayList<>();

private GrpcInterceptorTest(WebServer server) {
Tls clientTls = Tls.builder()
Expand Down Expand Up @@ -80,40 +70,10 @@ void testUnaryUpper() {
Strings.StringMessage res = grpcClient.serviceClient(serviceDescriptor)
.unary("Upper", newStringMessage("hello"));
assertThat(res.getText(), is("HELLO"));
assertThat(calledInterceptors, contains(Weight1000Interceptor.class,
assertThat(calledInterceptors(), contains(Weight1000Interceptor.class,
Weight500Interceptor.class,
Weight100Interceptor.class,
Weight50Interceptor.class,
Weight10Interceptor.class));
}

class BaseInterceptor implements ClientInterceptor {
@Override
public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(MethodDescriptor<ReqT, RespT> method,
CallOptions callOptions,
Channel next) {
calledInterceptors.add(getClass());
return next.newCall(method, callOptions);
}
}

@Weight(10.0)
class Weight10Interceptor extends BaseInterceptor {
}

@Weight(50.0)
class Weight50Interceptor extends BaseInterceptor {
}

@Weight(100.0)
class Weight100Interceptor extends BaseInterceptor {
}

@Weight(500.0)
class Weight500Interceptor extends BaseInterceptor {
}

@Weight(1000.0)
class Weight1000Interceptor extends BaseInterceptor {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ class GrpcTest extends GrpcBaseTest {
private static final long TIMEOUT_SECONDS = 10;

private final GrpcClient grpcClient;

private final GrpcServiceDescriptor serviceDescriptor;

private GrpcTest(WebServer server) {
Expand Down

0 comments on commit 6559f96

Please sign in to comment.