mirror of https://github.com/grpc/grpc-java.git
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:
parent
cb61a5e284
commit
41c027c11b
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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}",
|
||||
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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() {}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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 {
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
io.grpc.netty.NettyChannelProvider
|
||||
io.grpc.netty.UdsNettyChannelProvider
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
io.grpc.netty.UdsNameResolverProvider
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue