netty: implement UdsNameResolver and UdsNettyChannelProvider (#9113)

* netty: implement UdsNameResolver and UdsNettyChannelProvider
When the scheme is "unix:" we get the UdsNettyChannelProvider to
create a NettyChannelBuilder with DomainSocketAddress type and
other related params needed for UDS sockets
This commit is contained in:
sanjaypujare 2022-05-02 16:41:50 -07:00 committed by GitHub
parent cb61a5e284
commit 41c027c11b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 662 additions and 2 deletions

View File

@ -145,6 +145,11 @@ public final class ManagedChannelRegistry {
} catch (ClassNotFoundException e) {
logger.log(Level.FINE, "Unable to find NettyChannelProvider", e);
}
try {
list.add(Class.forName("io.grpc.netty.UdsNettyChannelProvider"));
} catch (ClassNotFoundException e) {
logger.log(Level.FINE, "Unable to find UdsNettyChannelProvider", e);
}
return Collections.unmodifiableList(list);
}

View File

@ -176,6 +176,8 @@ subprojects {
netty: "io.netty:netty-codec-http2:[${nettyVersion}]",
netty_epoll: "io.netty:netty-transport-native-epoll:${nettyVersion}:linux-x86_64",
netty_epoll_common: "io.netty:netty-transport-native-epoll:${nettyVersion}",
netty_unix_common: "io.netty:netty-transport-native-unix-common:${nettyVersion}",
netty_epoll_arm64: "io.netty:netty-transport-native-epoll:${nettyVersion}:linux-aarch_64",
netty_proxy_handler: "io.netty:netty-handler-proxy:${nettyVersion}",

View File

@ -20,6 +20,7 @@ java_library(
"@io_netty_netty_codec_http2//jar",
"@io_netty_netty_codec_socks//jar",
"@io_netty_netty_common//jar",
"@io_netty_netty_transport_native_unix_common//jar",
"@io_netty_netty_handler//jar",
"@io_netty_netty_handler_proxy//jar",
"@io_netty_netty_resolver//jar",

View File

@ -21,13 +21,15 @@ dependencies {
implementation libraries.netty_proxy_handler,
libraries.guava,
libraries.errorprone,
libraries.perfmark
libraries.perfmark,
libraries.netty_unix_common
// Tests depend on base class defined by core module.
testImplementation project(':grpc-core').sourceSets.test.output,
project(':grpc-api').sourceSets.test.output,
project(':grpc-testing'),
project(':grpc-testing-proto')
project(':grpc-testing-proto'),
libraries.netty_epoll_common
testRuntimeOnly libraries.netty_tcnative,
libraries.conscrypt,
libraries.netty_epoll

View File

@ -0,0 +1,66 @@
/*
* Copyright 2022 The gRPC Authors
*
* 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.grpc.netty;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.base.Preconditions;
import io.grpc.EquivalentAddressGroup;
import io.grpc.NameResolver;
import io.netty.channel.unix.DomainSocketAddress;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
final class UdsNameResolver extends NameResolver {
private NameResolver.Listener2 listener;
private final String authority;
UdsNameResolver(String authority, String targetPath) {
checkArgument(authority == null, "non-null authority not supported");
this.authority = targetPath;
}
@Override
public String getServiceAuthority() {
return this.authority;
}
@Override
public void start(Listener2 listener) {
Preconditions.checkState(this.listener == null, "already started");
this.listener = checkNotNull(listener, "listener");
resolve();
}
@Override
public void refresh() {
resolve();
}
private void resolve() {
ResolutionResult.Builder resolutionResultBuilder = ResolutionResult.newBuilder();
List<EquivalentAddressGroup> servers = new ArrayList<>(1);
servers.add(new EquivalentAddressGroup(new DomainSocketAddress(authority)));
resolutionResultBuilder.setAddresses(Collections.unmodifiableList(servers));
listener.onResult(resolutionResultBuilder.build());
}
@Override
public void shutdown() {}
}

View File

@ -0,0 +1,71 @@
/*
* Copyright 2022 The gRPC Authors
*
* 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.grpc.netty;
import com.google.common.base.Preconditions;
import io.grpc.Internal;
import io.grpc.NameResolver;
import io.grpc.NameResolverProvider;
import io.netty.channel.unix.DomainSocketAddress;
import java.net.SocketAddress;
import java.net.URI;
import java.util.Collection;
import java.util.Collections;
@Internal
public final class UdsNameResolverProvider extends NameResolverProvider {
private static final String SCHEME = "unix";
@Override
public UdsNameResolver newNameResolver(URI targetUri, NameResolver.Args args) {
if (SCHEME.equals(targetUri.getScheme())) {
return new UdsNameResolver(targetUri.getAuthority(), getTargetPathFromUri(targetUri));
} else {
return null;
}
}
static String getTargetPathFromUri(URI targetUri) {
Preconditions.checkArgument(SCHEME.equals(targetUri.getScheme()), "scheme must be " + SCHEME);
String targetPath = targetUri.getPath();
if (targetPath == null) {
targetPath = Preconditions.checkNotNull(targetUri.getSchemeSpecificPart(), "targetPath");
}
return targetPath;
}
@Override
public String getDefaultScheme() {
return SCHEME;
}
@Override
protected boolean isAvailable() {
return true;
}
@Override
protected int priority() {
return 3;
}
@Override
protected Collection<Class<? extends SocketAddress>> getProducedSocketAddressTypes() {
return Collections.singleton(DomainSocketAddress.class);
}
}

View File

@ -0,0 +1,94 @@
/*
* Copyright 2015 The gRPC Authors
*
* 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.grpc.netty;
import io.grpc.CallCredentials;
import io.grpc.ChannelCredentials;
import io.grpc.InsecureChannelCredentials;
import io.grpc.Internal;
import io.grpc.ManagedChannelProvider;
import io.grpc.internal.SharedResourcePool;
import io.netty.channel.unix.DomainSocketAddress;
import java.net.SocketAddress;
import java.net.URI;
import java.util.Collection;
import java.util.Collections;
/** Provider for {@link NettyChannelBuilder} instances for UDS channels. */
@Internal
public final class UdsNettyChannelProvider extends ManagedChannelProvider {
@Override
public boolean isAvailable() {
return (Utils.EPOLL_DOMAIN_CLIENT_CHANNEL_TYPE != null);
}
@Override
public int priority() {
return 3;
}
@Override
public NettyChannelBuilder builderForAddress(String name, int port) {
throw new UnsupportedOperationException("host:port not supported");
}
@Override
public NettyChannelBuilder builderForTarget(String target) {
ChannelCredentials creds = InsecureChannelCredentials.create();
ProtocolNegotiators.FromChannelCredentialsResult result = ProtocolNegotiators.from(creds);
if (result.error != null) {
throw new RuntimeException(result.error);
}
return getNettyChannelBuilder(target, creds, null, result.negotiator);
}
@Override
public NewChannelBuilderResult newChannelBuilder(String target, ChannelCredentials creds) {
ProtocolNegotiators.FromChannelCredentialsResult result = ProtocolNegotiators.from(creds);
if (result.error != null) {
return NewChannelBuilderResult.error(result.error);
}
return NewChannelBuilderResult.channelBuilder(
getNettyChannelBuilder(target, creds, result.callCredentials, result.negotiator));
}
private static NettyChannelBuilder getNettyChannelBuilder(
String target,
ChannelCredentials creds,
CallCredentials callCredentials,
ProtocolNegotiator.ClientFactory negotiator) {
if (Utils.EPOLL_DOMAIN_CLIENT_CHANNEL_TYPE == null) {
throw new IllegalStateException("Epoll is not available");
}
String targetPath = UdsNameResolverProvider.getTargetPathFromUri(URI.create(target));
NettyChannelBuilder builder =
new NettyChannelBuilder(
new DomainSocketAddress(targetPath), creds, callCredentials, negotiator);
builder =
builder
.eventLoopGroupPool(
SharedResourcePool.forResource(Utils.DEFAULT_WORKER_EVENT_LOOP_GROUP))
.channelType(Utils.EPOLL_DOMAIN_CLIENT_CHANNEL_TYPE);
return builder;
}
@Override
protected Collection<Class<? extends SocketAddress>> getSupportedSocketAddressTypes() {
return Collections.singleton(DomainSocketAddress.class);
}
}

View File

@ -89,6 +89,7 @@ class Utils {
= new DefaultEventLoopGroupResource(1, "grpc-nio-boss-ELG", EventLoopGroupType.NIO);
public static final Resource<EventLoopGroup> NIO_WORKER_EVENT_LOOP_GROUP
= new DefaultEventLoopGroupResource(0, "grpc-nio-worker-ELG", EventLoopGroupType.NIO);
public static final Resource<EventLoopGroup> DEFAULT_BOSS_EVENT_LOOP_GROUP;
public static final Resource<EventLoopGroup> DEFAULT_WORKER_EVENT_LOOP_GROUP;
@ -104,6 +105,7 @@ class Utils {
public static final ChannelFactory<? extends ServerChannel> DEFAULT_SERVER_CHANNEL_FACTORY;
public static final Class<? extends Channel> DEFAULT_CLIENT_CHANNEL_TYPE;
public static final Class<? extends Channel> EPOLL_DOMAIN_CLIENT_CHANNEL_TYPE;
@Nullable
private static final Constructor<? extends EventLoopGroup> EPOLL_EVENT_LOOP_GROUP_CONSTRUCTOR;
@ -112,6 +114,7 @@ class Utils {
// Decide default channel types and EventLoopGroup based on Epoll availability
if (isEpollAvailable()) {
DEFAULT_CLIENT_CHANNEL_TYPE = epollChannelType();
EPOLL_DOMAIN_CLIENT_CHANNEL_TYPE = epollDomainSocketChannelType();
DEFAULT_SERVER_CHANNEL_FACTORY = new ReflectiveChannelFactory<>(epollServerChannelType());
EPOLL_EVENT_LOOP_GROUP_CONSTRUCTOR = epollEventLoopGroupConstructor();
DEFAULT_BOSS_EVENT_LOOP_GROUP
@ -122,6 +125,7 @@ class Utils {
logger.log(Level.FINE, "Epoll is not available, using Nio.", getEpollUnavailabilityCause());
DEFAULT_SERVER_CHANNEL_FACTORY = nioServerChannelFactory();
DEFAULT_CLIENT_CHANNEL_TYPE = NioSocketChannel.class;
EPOLL_DOMAIN_CLIENT_CHANNEL_TYPE = null;
DEFAULT_BOSS_EVENT_LOOP_GROUP = NIO_BOSS_EVENT_LOOP_GROUP;
DEFAULT_WORKER_EVENT_LOOP_GROUP = NIO_WORKER_EVENT_LOOP_GROUP;
EPOLL_EVENT_LOOP_GROUP_CONSTRUCTOR = null;
@ -326,6 +330,17 @@ class Utils {
}
}
// Must call when epoll is available
private static Class<? extends Channel> epollDomainSocketChannelType() {
try {
Class<? extends Channel> channelType = Class
.forName("io.netty.channel.epoll.EpollDomainSocketChannel").asSubclass(Channel.class);
return channelType;
} catch (ClassNotFoundException e) {
throw new RuntimeException("Cannot load EpollDomainSocketChannel", e);
}
}
// Must call when epoll is available
private static Constructor<? extends EventLoopGroup> epollEventLoopGroupConstructor() {
try {

View File

@ -1 +1,2 @@
io.grpc.netty.NettyChannelProvider
io.grpc.netty.UdsNettyChannelProvider

View File

@ -0,0 +1 @@
io.grpc.netty.UdsNameResolverProvider

View File

@ -0,0 +1,124 @@
/*
* Copyright 2015 The gRPC Authors
*
* 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.grpc.netty;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.verify;
import io.grpc.EquivalentAddressGroup;
import io.grpc.NameResolver;
import io.netty.channel.unix.DomainSocketAddress;
import java.net.SocketAddress;
import java.net.URI;
import java.util.List;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
/** Unit tests for {@link UdsNameResolverProvider}. */
@RunWith(JUnit4.class)
public class UdsNameResolverProviderTest {
@Rule
public final MockitoRule mocks = MockitoJUnit.rule();
@Mock
private NameResolver.Listener2 mockListener;
@Captor
private ArgumentCaptor<NameResolver.ResolutionResult> resultCaptor;
UdsNameResolverProvider udsNameResolverProvider = new UdsNameResolverProvider();
@Test
public void testUnixRelativePath() {
UdsNameResolver udsNameResolver =
udsNameResolverProvider.newNameResolver(URI.create("unix:sock.sock"), null);
assertThat(udsNameResolver).isNotNull();
udsNameResolver.start(mockListener);
verify(mockListener).onResult(resultCaptor.capture());
NameResolver.ResolutionResult result = resultCaptor.getValue();
List<EquivalentAddressGroup> list = result.getAddresses();
assertThat(list).isNotNull();
assertThat(list).hasSize(1);
EquivalentAddressGroup eag = list.get(0);
assertThat(eag).isNotNull();
List<SocketAddress> addresses = eag.getAddresses();
assertThat(addresses).hasSize(1);
assertThat(addresses.get(0)).isInstanceOf(DomainSocketAddress.class);
DomainSocketAddress domainSocketAddress = (DomainSocketAddress) addresses.get(0);
assertThat(domainSocketAddress.path()).isEqualTo("sock.sock");
}
@Test
public void testUnixAbsolutePath() {
UdsNameResolver udsNameResolver =
udsNameResolverProvider.newNameResolver(URI.create("unix:/sock.sock"), null);
assertThat(udsNameResolver).isNotNull();
udsNameResolver.start(mockListener);
verify(mockListener).onResult(resultCaptor.capture());
NameResolver.ResolutionResult result = resultCaptor.getValue();
List<EquivalentAddressGroup> list = result.getAddresses();
assertThat(list).isNotNull();
assertThat(list).hasSize(1);
EquivalentAddressGroup eag = list.get(0);
assertThat(eag).isNotNull();
List<SocketAddress> addresses = eag.getAddresses();
assertThat(addresses).hasSize(1);
assertThat(addresses.get(0)).isInstanceOf(DomainSocketAddress.class);
DomainSocketAddress domainSocketAddress = (DomainSocketAddress) addresses.get(0);
assertThat(domainSocketAddress.path()).isEqualTo("/sock.sock");
}
@Test
public void testUnixAbsoluteAlternatePath() {
UdsNameResolver udsNameResolver =
udsNameResolverProvider.newNameResolver(URI.create("unix:///sock.sock"), null);
assertThat(udsNameResolver).isNotNull();
udsNameResolver.start(mockListener);
verify(mockListener).onResult(resultCaptor.capture());
NameResolver.ResolutionResult result = resultCaptor.getValue();
List<EquivalentAddressGroup> list = result.getAddresses();
assertThat(list).isNotNull();
assertThat(list).hasSize(1);
EquivalentAddressGroup eag = list.get(0);
assertThat(eag).isNotNull();
List<SocketAddress> addresses = eag.getAddresses();
assertThat(addresses).hasSize(1);
assertThat(addresses.get(0)).isInstanceOf(DomainSocketAddress.class);
DomainSocketAddress domainSocketAddress = (DomainSocketAddress) addresses.get(0);
assertThat(domainSocketAddress.path()).isEqualTo("/sock.sock");
}
@Test
public void testUnixPathWithAuthority() {
try {
udsNameResolverProvider.newNameResolver(URI.create("unix://localhost/sock.sock"), null);
fail("exception expected");
} catch (IllegalArgumentException e) {
assertThat(e).hasMessageThat().isEqualTo("non-null authority not supported");
}
}
}

View File

@ -0,0 +1,81 @@
/*
* Copyright 2015 The gRPC Authors
*
* 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.grpc.netty;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.verify;
import io.grpc.EquivalentAddressGroup;
import io.grpc.NameResolver;
import io.netty.channel.unix.DomainSocketAddress;
import java.net.SocketAddress;
import java.util.List;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
/** Unit tests for {@link UdsNameResolver}. */
@RunWith(JUnit4.class)
public class UdsNameResolverTest {
@Rule
public final MockitoRule mocks = MockitoJUnit.rule();
@Mock
private NameResolver.Listener2 mockListener;
@Captor
private ArgumentCaptor<NameResolver.ResolutionResult> resultCaptor;
private UdsNameResolver udsNameResolver;
@Test
public void testValidTargetPath() {
udsNameResolver = new UdsNameResolver(null, "sock.sock");
udsNameResolver.start(mockListener);
verify(mockListener).onResult(resultCaptor.capture());
NameResolver.ResolutionResult result = resultCaptor.getValue();
List<EquivalentAddressGroup> list = result.getAddresses();
assertThat(list).isNotNull();
assertThat(list).hasSize(1);
EquivalentAddressGroup eag = list.get(0);
assertThat(eag).isNotNull();
List<SocketAddress> addresses = eag.getAddresses();
assertThat(addresses).hasSize(1);
assertThat(addresses.get(0)).isInstanceOf(DomainSocketAddress.class);
DomainSocketAddress domainSocketAddress = (DomainSocketAddress) addresses.get(0);
assertThat(domainSocketAddress.path()).isEqualTo("sock.sock");
assertThat(udsNameResolver.getServiceAuthority()).isEqualTo("sock.sock");
}
@Test
public void testNonNullAuthority() {
try {
udsNameResolver = new UdsNameResolver("authority", "sock.sock");
fail("exception expected");
} catch (IllegalArgumentException e) {
assertThat(e).hasMessageThat().isEqualTo("non-null authority not supported");
}
}
}

View File

@ -0,0 +1,197 @@
/*
* Copyright 2015 The gRPC Authors
*
* 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.grpc.netty;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import io.grpc.Grpc;
import io.grpc.InsecureChannelCredentials;
import io.grpc.InternalServiceProviders;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import io.grpc.ManagedChannelProvider;
import io.grpc.ManagedChannelProvider.NewChannelBuilderResult;
import io.grpc.ManagedChannelRegistryAccessor;
import io.grpc.TlsChannelCredentials;
import io.grpc.stub.StreamObserver;
import io.grpc.testing.GrpcCleanupRule;
import io.grpc.testing.protobuf.SimpleRequest;
import io.grpc.testing.protobuf.SimpleResponse;
import io.grpc.testing.protobuf.SimpleServiceGrpc;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.epoll.EpollEventLoopGroup;
import io.netty.channel.epoll.EpollServerDomainSocketChannel;
import io.netty.channel.unix.DomainSocketAddress;
import java.io.IOException;
import org.junit.After;
import org.junit.Assume;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Unit tests for {@link UdsNettyChannelProvider}. */
@RunWith(JUnit4.class)
public class UdsNettyChannelProviderTest {
@Rule public TemporaryFolder tempFolder = new TemporaryFolder();
@Rule
public final GrpcCleanupRule cleanupRule = new GrpcCleanupRule();
private UdsNettyChannelProvider provider = new UdsNettyChannelProvider();
private EventLoopGroup elg;
private EventLoopGroup boss;
@After
public void tearDown() {
if (elg != null) {
elg.shutdownGracefully();
}
if (boss != null) {
boss.shutdownGracefully();
}
}
@Test
public void provided() {
for (ManagedChannelProvider current
: InternalServiceProviders.getCandidatesViaServiceLoader(
ManagedChannelProvider.class, getClass().getClassLoader())) {
if (current instanceof UdsNettyChannelProvider) {
return;
}
}
fail("ServiceLoader unable to load UdsNettyChannelProvider");
}
@Test
public void providedHardCoded() {
for (Class<?> current : ManagedChannelRegistryAccessor.getHardCodedClasses()) {
if (current == UdsNettyChannelProvider.class) {
return;
}
}
fail("Hard coded unable to load UdsNettyChannelProvider");
}
@Test
public void basicMethods() {
Assume.assumeTrue(provider.isAvailable());
assertEquals(3, provider.priority());
}
@Test
public void builderForTarget() {
Assume.assumeTrue(Utils.isEpollAvailable());
assertThat(provider.builderForTarget("unix:sock.sock")).isInstanceOf(NettyChannelBuilder.class);
}
@Test
public void builderForTarget_badScheme() {
Assume.assumeTrue(Utils.isEpollAvailable());
try {
provider.builderForTarget("dns:sock.sock");
fail("exception expected");
} catch (IllegalArgumentException e) {
assertThat(e).hasMessageThat().isEqualTo("scheme must be unix");
}
}
@Test
public void newChannelBuilder_success() {
Assume.assumeTrue(Utils.isEpollAvailable());
NewChannelBuilderResult result =
provider.newChannelBuilder("unix:sock.sock", TlsChannelCredentials.create());
assertThat(result.getChannelBuilder()).isInstanceOf(NettyChannelBuilder.class);
}
@Test
public void newChannelBuilder_badScheme() {
Assume.assumeTrue(Utils.isEpollAvailable());
try {
provider.newChannelBuilder("dns:sock.sock", InsecureChannelCredentials.create());
fail("exception expected");
} catch (IllegalArgumentException e) {
assertThat(e).hasMessageThat().isEqualTo("scheme must be unix");
}
}
@Test
public void managedChannelRegistry_newChannelBuilder() {
Assume.assumeTrue(Utils.isEpollAvailable());
ManagedChannelBuilder<?> managedChannelBuilder
= Grpc.newChannelBuilder("unix:///sock.sock", InsecureChannelCredentials.create());
assertThat(managedChannelBuilder).isNotNull();
ManagedChannel channel = managedChannelBuilder.build();
assertThat(channel).isNotNull();
assertThat(channel.authority()).isEqualTo("/sock.sock");
channel.shutdownNow();
}
@Test
public void udsClientServerTestUsingProvider() throws IOException {
Assume.assumeTrue(Utils.isEpollAvailable());
String socketPath = tempFolder.getRoot().getAbsolutePath() + "/test.socket";
createUdsServer(socketPath);
ManagedChannelBuilder<?> channelBuilder =
Grpc.newChannelBuilder("unix://" + socketPath, InsecureChannelCredentials.create());
SimpleServiceGrpc.SimpleServiceBlockingStub stub =
SimpleServiceGrpc.newBlockingStub(cleanupRule.register(channelBuilder.build()));
assertThat(unaryRpc("buddy", stub)).isEqualTo("Hello buddy");
}
/** Say hello to server. */
private static String unaryRpc(
String requestMessage, SimpleServiceGrpc.SimpleServiceBlockingStub blockingStub) {
SimpleRequest request = SimpleRequest.newBuilder().setRequestMessage(requestMessage).build();
SimpleResponse response = blockingStub.unaryRpc(request);
return response.getResponseMessage();
}
private void createUdsServer(String name) throws IOException {
elg = new EpollEventLoopGroup();
boss = new EpollEventLoopGroup(1);
cleanupRule.register(
NettyServerBuilder.forAddress(new DomainSocketAddress(name))
.bossEventLoopGroup(boss)
.workerEventLoopGroup(elg)
.channelType(EpollServerDomainSocketChannel.class)
.addService(new SimpleServiceImpl())
.directExecutor()
.build()
.start());
}
private static class SimpleServiceImpl extends SimpleServiceGrpc.SimpleServiceImplBase {
@Override
public void unaryRpc(SimpleRequest req, StreamObserver<SimpleResponse> responseObserver) {
SimpleResponse response =
SimpleResponse.newBuilder()
.setResponseMessage("Hello " + req.getRequestMessage())
.build();
responseObserver.onNext(response);
responseObserver.onCompleted();
}
}
}