Move code around so factory gets address not port
This commit is contained in:
@@ -43,7 +43,7 @@ import io.grpc.ManagedChannel;
|
||||
class GrpcServerIntegrationTests {
|
||||
|
||||
@Nested
|
||||
@SpringBootTest(properties = { "spring.grpc.server.address=0.0.0.0", "spring.grpc.server.port=0" })
|
||||
@SpringBootTest(properties = { "spring.grpc.server.host=0.0.0.0", "spring.grpc.server.port=0" })
|
||||
class ServerWithAnyIPv4AddressAndRandomPort {
|
||||
|
||||
@Test
|
||||
@@ -55,7 +55,7 @@ class GrpcServerIntegrationTests {
|
||||
}
|
||||
|
||||
@Nested
|
||||
@SpringBootTest(properties = { "spring.grpc.server.address=::", "spring.grpc.server.port=0" })
|
||||
@SpringBootTest(properties = { "spring.grpc.server.host=::", "spring.grpc.server.port=0" })
|
||||
class ServerWithAnyIPv6AddressAndRandomPort {
|
||||
|
||||
@Test
|
||||
@@ -67,7 +67,7 @@ class GrpcServerIntegrationTests {
|
||||
}
|
||||
|
||||
@Nested
|
||||
@SpringBootTest(properties = { "spring.grpc.server.address=127.0.0.1", "spring.grpc.server.port=0" })
|
||||
@SpringBootTest(properties = { "spring.grpc.server.host=127.0.0.1", "spring.grpc.server.port=0" })
|
||||
class ServerWithLocalhostAndRandomPort {
|
||||
|
||||
@Test
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2024-2024 the original author or 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
|
||||
*
|
||||
* https://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 org.springframework.grpc.internal;
|
||||
|
||||
public class GrpcUtils {
|
||||
|
||||
public static int DEFAULT_PORT = 9090;
|
||||
|
||||
public static int getPort(String address) {
|
||||
String value = address;
|
||||
if (value.contains(":")) {
|
||||
value = value.substring(value.lastIndexOf(":") + 1);
|
||||
}
|
||||
if (value.contains("/")) {
|
||||
value = value.substring(0, value.indexOf("/"));
|
||||
}
|
||||
if (value.matches("[0-9]+")) {
|
||||
return Integer.parseInt(value);
|
||||
}
|
||||
if (address.startsWith("unix:")) {
|
||||
return -1;
|
||||
}
|
||||
return DEFAULT_PORT;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -22,12 +22,18 @@ import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import io.grpc.Grpc;
|
||||
import io.grpc.InsecureServerCredentials;
|
||||
import io.grpc.Server;
|
||||
import io.grpc.ServerBuilder;
|
||||
import io.grpc.ServerCredentials;
|
||||
import io.grpc.ServerProvider;
|
||||
import io.grpc.ServerServiceDefinition;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.grpc.internal.GrpcUtils;
|
||||
|
||||
/**
|
||||
* Default implementation for {@link GrpcServerFactory gRPC service factories}.
|
||||
@@ -48,25 +54,17 @@ public class DefaultGrpcServerFactory<T extends ServerBuilder<T>> implements Grp
|
||||
|
||||
private final String address;
|
||||
|
||||
private final int port;
|
||||
|
||||
private final List<ServerBuilderCustomizer<T>> serverBuilderCustomizers;
|
||||
|
||||
public DefaultGrpcServerFactory(String address, int port,
|
||||
List<ServerBuilderCustomizer<T>> serverBuilderCustomizers) {
|
||||
public DefaultGrpcServerFactory(String address, List<ServerBuilderCustomizer<T>> serverBuilderCustomizers) {
|
||||
this.address = address;
|
||||
this.port = port;
|
||||
this.serverBuilderCustomizers = Objects.requireNonNull(serverBuilderCustomizers, "serverBuilderCustomizers");
|
||||
}
|
||||
|
||||
protected String getAddress() {
|
||||
protected String address() {
|
||||
return this.address;
|
||||
}
|
||||
|
||||
protected int getPort() {
|
||||
return this.port;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Server createServer() {
|
||||
T builder = newServerBuilder();
|
||||
@@ -85,7 +83,23 @@ public class DefaultGrpcServerFactory<T extends ServerBuilder<T>> implements Grp
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
protected T newServerBuilder() {
|
||||
return (T) ServerBuilder.forPort(port);
|
||||
return (T) Grpc.newServerBuilderForPort(port(), credentials());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the port number on which the server should listen. Use 0 to let the system
|
||||
* choose a port. Use -1 to denote that this server does not listen on a socket.
|
||||
* @return the port number
|
||||
*/
|
||||
protected int port() {
|
||||
return GrpcUtils.getPort(address());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return some server credentials (default is insecure)
|
||||
*/
|
||||
protected ServerCredentials credentials() {
|
||||
return InsecureServerCredentials.create();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -16,10 +16,12 @@
|
||||
|
||||
package org.springframework.grpc.server;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.List;
|
||||
|
||||
import com.google.common.net.InetAddresses;
|
||||
import javax.net.ssl.KeyManagerFactory;
|
||||
|
||||
import io.grpc.ServerCredentials;
|
||||
import io.grpc.TlsServerCredentials;
|
||||
import io.grpc.netty.NettyServerBuilder;
|
||||
import io.netty.channel.epoll.EpollEventLoopGroup;
|
||||
import io.netty.channel.epoll.EpollServerDomainSocketChannel;
|
||||
@@ -33,34 +35,33 @@ import io.netty.channel.unix.DomainSocketAddress;
|
||||
*/
|
||||
public class NettyGrpcServerFactory extends DefaultGrpcServerFactory<NettyServerBuilder> {
|
||||
|
||||
private static final String ANY_IP_ADDRESS = "*";
|
||||
private KeyManagerFactory keyManager;
|
||||
|
||||
public NettyGrpcServerFactory(String address, int port,
|
||||
public NettyGrpcServerFactory(String address, KeyManagerFactory keyManager,
|
||||
List<ServerBuilderCustomizer<NettyServerBuilder>> serverBuilderCustomizers) {
|
||||
super(address, port, serverBuilderCustomizers);
|
||||
super(address, serverBuilderCustomizers);
|
||||
this.keyManager = keyManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new server builder.
|
||||
* @return The newly created server builder.
|
||||
*/
|
||||
@Override
|
||||
protected NettyServerBuilder newServerBuilder() {
|
||||
String address = getAddress();
|
||||
int port = getPort();
|
||||
if (address != null) {
|
||||
if (address.startsWith("unix:")) {
|
||||
String path = address.substring(5);
|
||||
return NettyServerBuilder.forAddress(new DomainSocketAddress(path))
|
||||
.channelType(EpollServerDomainSocketChannel.class)
|
||||
.bossEventLoopGroup(new EpollEventLoopGroup(1))
|
||||
.workerEventLoopGroup(new EpollEventLoopGroup());
|
||||
}
|
||||
if (!ANY_IP_ADDRESS.equals(address)) {
|
||||
return NettyServerBuilder.forAddress(new InetSocketAddress(InetAddresses.forString(address), port));
|
||||
}
|
||||
// TODO: Add more support for address resolution
|
||||
String address = address();
|
||||
if (address.startsWith("unix:")) {
|
||||
String path = address.substring(5);
|
||||
return NettyServerBuilder.forAddress(new DomainSocketAddress(path))
|
||||
.channelType(EpollServerDomainSocketChannel.class)
|
||||
.bossEventLoopGroup(new EpollEventLoopGroup(1))
|
||||
.workerEventLoopGroup(new EpollEventLoopGroup());
|
||||
}
|
||||
return super.newServerBuilder();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ServerCredentials credentials() {
|
||||
if (this.keyManager == null || port() == -1) {
|
||||
return super.credentials();
|
||||
}
|
||||
return TlsServerCredentials.newBuilder().keyManager(this.keyManager.getKeyManagers()).build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,10 +16,12 @@
|
||||
|
||||
package org.springframework.grpc.server;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.List;
|
||||
|
||||
import com.google.common.net.InetAddresses;
|
||||
import javax.net.ssl.KeyManagerFactory;
|
||||
|
||||
import io.grpc.ServerCredentials;
|
||||
import io.grpc.TlsServerCredentials;
|
||||
import io.grpc.netty.shaded.io.grpc.netty.NettyServerBuilder;
|
||||
import io.grpc.netty.shaded.io.netty.channel.epoll.EpollEventLoopGroup;
|
||||
import io.grpc.netty.shaded.io.netty.channel.epoll.EpollServerDomainSocketChannel;
|
||||
@@ -33,34 +35,33 @@ import io.grpc.netty.shaded.io.netty.channel.unix.DomainSocketAddress;
|
||||
*/
|
||||
public class ShadedNettyGrpcServerFactory extends DefaultGrpcServerFactory<NettyServerBuilder> {
|
||||
|
||||
private static final String ANY_IP_ADDRESS = "*";
|
||||
private KeyManagerFactory keyManager;
|
||||
|
||||
public ShadedNettyGrpcServerFactory(String address, int port,
|
||||
public ShadedNettyGrpcServerFactory(String address, KeyManagerFactory keyManager,
|
||||
List<ServerBuilderCustomizer<NettyServerBuilder>> serverBuilderCustomizers) {
|
||||
super(address, port, serverBuilderCustomizers);
|
||||
super(address, serverBuilderCustomizers);
|
||||
this.keyManager = keyManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new server builder.
|
||||
* @return The newly created server builder.
|
||||
*/
|
||||
@Override
|
||||
protected NettyServerBuilder newServerBuilder() {
|
||||
String address = getAddress();
|
||||
int port = getPort();
|
||||
if (address != null) {
|
||||
if (address.startsWith("unix:")) {
|
||||
String path = address.substring(5);
|
||||
return NettyServerBuilder.forAddress(new DomainSocketAddress(path))
|
||||
.channelType(EpollServerDomainSocketChannel.class)
|
||||
.bossEventLoopGroup(new EpollEventLoopGroup(1))
|
||||
.workerEventLoopGroup(new EpollEventLoopGroup());
|
||||
}
|
||||
if (!ANY_IP_ADDRESS.equals(address)) {
|
||||
return NettyServerBuilder.forAddress(new InetSocketAddress(InetAddresses.forString(address), port));
|
||||
}
|
||||
// TODO: Add more support for address resolution
|
||||
String address = address();
|
||||
if (address.startsWith("unix:")) {
|
||||
String path = address.substring(5);
|
||||
return NettyServerBuilder.forAddress(new DomainSocketAddress(path))
|
||||
.channelType(EpollServerDomainSocketChannel.class)
|
||||
.bossEventLoopGroup(new EpollEventLoopGroup(1))
|
||||
.workerEventLoopGroup(new EpollEventLoopGroup());
|
||||
}
|
||||
return super.newServerBuilder();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ServerCredentials credentials() {
|
||||
if (this.keyManager == null || port() == -1) {
|
||||
return super.credentials();
|
||||
}
|
||||
return TlsServerCredentials.newBuilder().keyManager(this.keyManager.getKeyManagers()).build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
package org.springframework.grpc.internal;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class GrpcUtilsTests {
|
||||
|
||||
@Test
|
||||
void testGetPortFromAddress() {
|
||||
assertEquals(8080, GrpcUtils.getPort("localhost:8080"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetNoPort() {
|
||||
assertEquals(9090, GrpcUtils.getPort("localhost"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetPortFromAddressWithPath() {
|
||||
String address = "example.com:1234/path";
|
||||
assertEquals(1234, GrpcUtils.getPort(address));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetDomainAddress() {
|
||||
String address = "unix:/some/file/somewhere";
|
||||
assertEquals(-1, GrpcUtils.getPort(address));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetStaticSchema() {
|
||||
String address = "static://localhost";
|
||||
assertEquals(9090, GrpcUtils.getPort(address));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetInvalidAddress() {
|
||||
String address = "invalid:broken";
|
||||
assertEquals(9090, GrpcUtils.getPort(address)); // -1?
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,7 +16,8 @@
|
||||
|spring.grpc.client.default-channel.ssl.bundle | | SSL bundle name.
|
||||
|spring.grpc.client.default-channel.ssl.enabled | | Whether to enable SSL support. Enabled automatically if "bundle" is provided unless specified otherwise.
|
||||
|spring.grpc.client.default-channel.user-agent | |
|
||||
|spring.grpc.server.address | `+++*+++` | Server address to bind to. The default is any IP address ('*').
|
||||
|spring.grpc.server.address | | The address to bind to. could be a host:port combination or a pseudo URL like static://host:port. Can not be set if host or port are set independently.
|
||||
|spring.grpc.server.host | `+++*+++` | Server address to bind to. The default is any IP address ('*').
|
||||
|spring.grpc.server.keep-alive.max-age | | Maximum time a connection may exist before being gracefully terminated (default infinite).
|
||||
|spring.grpc.server.keep-alive.max-age-grace | | Maximum time for graceful connection termination (default infinite).
|
||||
|spring.grpc.server.keep-alive.max-idle | | Maximum time a connection can remain idle before being gracefully terminated (default infinite).
|
||||
@@ -26,7 +27,7 @@
|
||||
|spring.grpc.server.keep-alive.timeout | `+++20s+++` | Maximum time to wait for read activity after sending a keep alive ping. If sender does not receive an acknowledgment within this time, it will close the connection (default 20s).
|
||||
|spring.grpc.server.max-inbound-message-size | `+++4194304B+++` | Maximum message size allowed to be received by the server (default 4MiB).
|
||||
|spring.grpc.server.max-inbound-metadata-size | `+++8192B+++` | Maximum metadata size allowed to be received by the server (default 8KiB).
|
||||
|spring.grpc.server.port | `+++9090+++` | Server port to listen on. When the value is 0, a random available port is selected. The default is 9090.
|
||||
|spring.grpc.server.port | | Server port to listen on. When the value is 0, a random available port is selected. The default is 9090.
|
||||
|spring.grpc.server.shutdown-grace-period | `+++30s+++` | Maximum time to wait for the server to gracefully shutdown. When the value is negative, the server waits forever. When the value is 0, the server will force shutdown immediately. The default is 30 seconds.
|
||||
|spring.grpc.server.ssl.bundle | | SSL bundle name.
|
||||
|spring.grpc.server.ssl.enabled | | Whether to enable SSL support. Enabled automatically if "bundle" is provided unless specified otherwise.
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
*/
|
||||
package org.springframework.grpc.autoconfigure.client;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.List;
|
||||
|
||||
import javax.net.ssl.SSLException;
|
||||
@@ -33,7 +32,6 @@ import org.springframework.grpc.client.NettyGrpcChannelFactory;
|
||||
import org.springframework.grpc.client.ShadedNettyGrpcChannelFactory;
|
||||
import org.springframework.grpc.client.VirtualTargets;
|
||||
|
||||
import io.grpc.ManagedChannelBuilder;
|
||||
import io.grpc.netty.GrpcSslContexts;
|
||||
import io.grpc.netty.NettyChannelBuilder;
|
||||
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
|
||||
@@ -142,21 +140,6 @@ public class GrpcChannelFactoryConfigurations {
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass(ManagedChannelBuilder.class)
|
||||
@ConditionalOnMissingBean(GrpcChannelFactory.class)
|
||||
public static class DefaultChannelFactoryConfiguration {
|
||||
|
||||
@Bean
|
||||
public DefaultGrpcChannelFactory defaultGrpcChannelFactory(final List<GrpcChannelConfigurer> configurers,
|
||||
GrpcClientProperties channels) {
|
||||
DefaultGrpcChannelFactory factory = new DefaultGrpcChannelFactory(configurers);
|
||||
factory.setVirtualTargets(new NamedChannelVirtualTargets(channels));
|
||||
return factory;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class NamedChannelVirtualTargets implements VirtualTargets {
|
||||
|
||||
private final GrpcClientProperties channels;
|
||||
|
||||
@@ -29,8 +29,7 @@ import org.springframework.grpc.client.GrpcChannelConfigurer;
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableConfigurationProperties(GrpcClientProperties.class)
|
||||
@Import({ GrpcChannelFactoryConfigurations.ShadedNettyChannelFactoryConfiguration.class,
|
||||
GrpcChannelFactoryConfigurations.NettyChannelFactoryConfiguration.class,
|
||||
GrpcChannelFactoryConfigurations.DefaultChannelFactoryConfiguration.class })
|
||||
GrpcChannelFactoryConfigurations.NettyChannelFactoryConfiguration.class })
|
||||
public class GrpcClientAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
|
||||
@@ -27,6 +27,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.grpc.server.GrpcServerFactory;
|
||||
@@ -48,8 +49,7 @@ import org.springframework.grpc.server.lifecycle.GrpcServerLifecycle;
|
||||
@ConditionalOnBean(BindableService.class)
|
||||
@EnableConfigurationProperties(GrpcServerProperties.class)
|
||||
@Import({ GrpcServerFactoryConfigurations.ShadedNettyServerFactoryConfiguration.class,
|
||||
GrpcServerFactoryConfigurations.NettyServerFactoryConfiguration.class,
|
||||
GrpcServerFactoryConfigurations.ServiceProviderServerFactoryConfiguration.class })
|
||||
GrpcServerFactoryConfigurations.NettyServerFactoryConfiguration.class })
|
||||
public class GrpcServerAutoConfiguration {
|
||||
|
||||
private final GrpcServerProperties properties;
|
||||
@@ -58,6 +58,7 @@ public class GrpcServerAutoConfiguration {
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
@ConditionalOnBean(GrpcServerFactory.class)
|
||||
@ConditionalOnMissingBean
|
||||
@Bean
|
||||
GrpcServerLifecycle grpcServerLifecycle(GrpcServerFactory factory, ApplicationEventPublisher eventPublisher) {
|
||||
|
||||
@@ -18,7 +18,7 @@ package org.springframework.grpc.autoconfigure.server;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.net.ssl.SSLException;
|
||||
import javax.net.ssl.KeyManagerFactory;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
@@ -28,17 +28,13 @@ import org.springframework.boot.ssl.SslBundle;
|
||||
import org.springframework.boot.ssl.SslBundles;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.grpc.server.DefaultGrpcServerFactory;
|
||||
import org.springframework.grpc.server.GrpcServerFactory;
|
||||
import org.springframework.grpc.server.NettyGrpcServerFactory;
|
||||
import org.springframework.grpc.server.ServerBuilderCustomizer;
|
||||
import org.springframework.grpc.server.ShadedNettyGrpcServerFactory;
|
||||
|
||||
import io.grpc.BindableService;
|
||||
import io.grpc.ServerBuilder;
|
||||
import io.grpc.netty.GrpcSslContexts;
|
||||
import io.grpc.netty.NettyServerBuilder;
|
||||
import io.netty.handler.ssl.SslContextBuilder;
|
||||
|
||||
/**
|
||||
* Configurations for {@link GrpcServerFactory gRPC server factories}.
|
||||
@@ -55,38 +51,20 @@ class GrpcServerFactoryConfigurations {
|
||||
|
||||
@Bean
|
||||
ShadedNettyGrpcServerFactory shadedNettyGrpcServerFactory(GrpcServerProperties properties,
|
||||
ObjectProvider<BindableService> grpcServicesProvider,
|
||||
ServerBuilderCustomizers serverBuilderCustomizers) {
|
||||
ObjectProvider<BindableService> grpcServicesProvider, ServerBuilderCustomizers serverBuilderCustomizers,
|
||||
SslBundles bundles) {
|
||||
ShadedNettyServerFactoryPropertyMapper mapper = new ShadedNettyServerFactoryPropertyMapper(properties);
|
||||
List<ServerBuilderCustomizer<io.grpc.netty.shaded.io.grpc.netty.NettyServerBuilder>> builderCustomizers = List
|
||||
.of(mapper::customizeServerBuilder, serverBuilderCustomizers::customize);
|
||||
ShadedNettyGrpcServerFactory factory = new ShadedNettyGrpcServerFactory(properties.getAddress(),
|
||||
properties.getPort(), builderCustomizers);
|
||||
grpcServicesProvider.orderedStream().map(BindableService::bindService).forEach(factory::addService);
|
||||
return factory;
|
||||
}
|
||||
|
||||
@Bean
|
||||
ServerBuilderCustomizer<io.grpc.netty.shaded.io.grpc.netty.NettyServerBuilder> sslServerCustomizer(
|
||||
GrpcServerProperties properties, SslBundles bundles) {
|
||||
KeyManagerFactory keyManager = null;
|
||||
if (properties.getSsl().isEnabled()) {
|
||||
SslBundle bundle = bundles.getBundle(properties.getSsl().getBundle());
|
||||
return builder -> {
|
||||
try {
|
||||
builder.sslContext(io.grpc.netty.shaded.io.grpc.netty.GrpcSslContexts
|
||||
.configure(io.grpc.netty.shaded.io.netty.handler.ssl.SslContextBuilder
|
||||
.forServer(bundle.getManagers().getKeyManagerFactory()))
|
||||
.build());
|
||||
}
|
||||
catch (SSLException e) {
|
||||
throw new IllegalStateException("Failed to create SSL context", e);
|
||||
}
|
||||
};
|
||||
}
|
||||
else {
|
||||
return builder -> {
|
||||
};
|
||||
keyManager = bundle.getManagers().getKeyManagerFactory();
|
||||
}
|
||||
ShadedNettyGrpcServerFactory factory = new ShadedNettyGrpcServerFactory(properties.getAddress(), keyManager,
|
||||
builderCustomizers);
|
||||
grpcServicesProvider.orderedStream().map(BindableService::bindService).forEach(factory::addService);
|
||||
return factory;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -99,56 +77,18 @@ class GrpcServerFactoryConfigurations {
|
||||
|
||||
@Bean
|
||||
NettyGrpcServerFactory nettyGrpcServerFactory(GrpcServerProperties properties,
|
||||
ObjectProvider<BindableService> grpcServicesProvider,
|
||||
ServerBuilderCustomizers serverBuilderCustomizers) {
|
||||
ObjectProvider<BindableService> grpcServicesProvider, ServerBuilderCustomizers serverBuilderCustomizers,
|
||||
SslBundles bundles) {
|
||||
NettyServerFactoryPropertyMapper mapper = new NettyServerFactoryPropertyMapper(properties);
|
||||
List<ServerBuilderCustomizer<NettyServerBuilder>> builderCustomizers = List
|
||||
.of(mapper::customizeServerBuilder, serverBuilderCustomizers::customize);
|
||||
NettyGrpcServerFactory factory = new NettyGrpcServerFactory(properties.getAddress(), properties.getPort(),
|
||||
builderCustomizers);
|
||||
grpcServicesProvider.orderedStream().map(BindableService::bindService).forEach(factory::addService);
|
||||
return factory;
|
||||
}
|
||||
|
||||
@Bean
|
||||
ServerBuilderCustomizer<NettyServerBuilder> sslServerCustomizer(GrpcServerProperties properties,
|
||||
SslBundles bundles) {
|
||||
KeyManagerFactory keyManager = null;
|
||||
if (properties.getSsl().isEnabled()) {
|
||||
SslBundle bundle = bundles.getBundle(properties.getSsl().getBundle());
|
||||
return builder -> {
|
||||
try {
|
||||
builder.sslContext(GrpcSslContexts
|
||||
.configure(SslContextBuilder.forServer(bundle.getManagers().getKeyManagerFactory()))
|
||||
.build());
|
||||
}
|
||||
catch (SSLException e) {
|
||||
throw new IllegalStateException("Failed to create SSL context", e);
|
||||
}
|
||||
};
|
||||
keyManager = bundle.getManagers().getKeyManagerFactory();
|
||||
}
|
||||
else {
|
||||
return builder -> {
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass(ServerBuilder.class)
|
||||
@ConditionalOnMissingBean(GrpcServerFactory.class)
|
||||
@EnableConfigurationProperties(GrpcServerProperties.class)
|
||||
static class ServiceProviderServerFactoryConfiguration {
|
||||
|
||||
@Bean
|
||||
<T extends ServerBuilder<T>> DefaultGrpcServerFactory<T> serviceProviderGrpcServerFactory(
|
||||
GrpcServerProperties properties, ObjectProvider<BindableService> grpcServicesProvider,
|
||||
ServerBuilderCustomizers serverBuilderCustomizers) {
|
||||
DefaultServerFactoryPropertyMapper<T> mapper = new DefaultServerFactoryPropertyMapper<>(properties);
|
||||
List<ServerBuilderCustomizer<T>> builderCustomizers = List.of(mapper::customizeServerBuilder,
|
||||
serverBuilderCustomizers::customize);
|
||||
DefaultGrpcServerFactory<T> factory = new DefaultGrpcServerFactory<>(properties.getAddress(),
|
||||
properties.getPort(), builderCustomizers);
|
||||
NettyGrpcServerFactory factory = new NettyGrpcServerFactory(properties.getAddress(), keyManager,
|
||||
builderCustomizers);
|
||||
grpcServicesProvider.orderedStream().map(BindableService::bindService).forEach(factory::addService);
|
||||
return factory;
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import java.time.temporal.ChronoUnit;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.convert.DataSizeUnit;
|
||||
import org.springframework.boot.convert.DurationUnit;
|
||||
import org.springframework.grpc.internal.GrpcUtils;
|
||||
import org.springframework.util.unit.DataSize;
|
||||
import org.springframework.util.unit.DataUnit;
|
||||
|
||||
@@ -35,13 +36,13 @@ public class GrpcServerProperties {
|
||||
/**
|
||||
* Server address to bind to. The default is any IP address ('*').
|
||||
*/
|
||||
private String address = ANY_IP_ADDRESS;
|
||||
private String host = ANY_IP_ADDRESS;
|
||||
|
||||
/**
|
||||
* Server port to listen on. When the value is 0, a random available port is selected.
|
||||
* The default is 9090.
|
||||
*/
|
||||
private int port = 9090;
|
||||
private int port = GrpcUtils.DEFAULT_PORT;
|
||||
|
||||
/**
|
||||
* Maximum time to wait for the server to gracefully shutdown. When the value is
|
||||
@@ -65,19 +66,42 @@ public class GrpcServerProperties {
|
||||
|
||||
private final KeepAlive keepAlive = new KeepAlive();
|
||||
|
||||
/**
|
||||
* The address to bind to. could be a host:port combination or a pseudo URL like
|
||||
* static://host:port. Can not be set if host or port are set independently.
|
||||
*/
|
||||
private String address;
|
||||
|
||||
public String getAddress() {
|
||||
return this.address;
|
||||
return this.address == null ? this.host + ":" + this.port : this.address;
|
||||
}
|
||||
|
||||
public void setAddress(String address) {
|
||||
this.address = address;
|
||||
}
|
||||
|
||||
public String getHost() {
|
||||
return host;
|
||||
}
|
||||
|
||||
public void setHost(String host) {
|
||||
if (this.address != null) {
|
||||
throw new IllegalStateException("Cannot set host when address is already set");
|
||||
}
|
||||
this.host = host;
|
||||
}
|
||||
|
||||
public int getPort() {
|
||||
if (this.address != null) {
|
||||
return GrpcUtils.getPort(this.address);
|
||||
}
|
||||
return this.port;
|
||||
}
|
||||
|
||||
public void setPort(int port) {
|
||||
if (this.address != null) {
|
||||
throw new IllegalStateException("Cannot set port when address is already set");
|
||||
}
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,22 +16,23 @@
|
||||
|
||||
package org.springframework.grpc.autoconfigure.server;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.Mockito.inOrder;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import io.grpc.BindableService;
|
||||
import io.grpc.ServerBuilder;
|
||||
import io.grpc.ServerServiceDefinition;
|
||||
import io.grpc.ServiceDescriptor;
|
||||
import io.grpc.netty.NettyServerBuilder;
|
||||
import org.assertj.core.api.InstanceOfAssertFactories;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.InOrder;
|
||||
import org.mockito.MockedStatic;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.stubbing.Answer;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.autoconfigure.ssl.SslAutoConfiguration;
|
||||
import org.springframework.boot.test.context.FilteredClassLoader;
|
||||
@@ -39,18 +40,18 @@ import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.grpc.server.DefaultGrpcServerFactory;
|
||||
import org.springframework.grpc.server.GrpcServerFactory;
|
||||
import org.springframework.grpc.server.NettyGrpcServerFactory;
|
||||
import org.springframework.grpc.server.ServerBuilderCustomizer;
|
||||
import org.springframework.grpc.server.ShadedNettyGrpcServerFactory;
|
||||
import org.springframework.grpc.server.lifecycle.GrpcServerLifecycle;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.Mockito.inOrder;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
import io.grpc.BindableService;
|
||||
import io.grpc.Grpc;
|
||||
import io.grpc.ServerBuilder;
|
||||
import io.grpc.ServerServiceDefinition;
|
||||
import io.grpc.ServiceDescriptor;
|
||||
import io.grpc.netty.NettyServerBuilder;
|
||||
|
||||
/**
|
||||
* Tests for {@link GrpcServerAutoConfiguration}.
|
||||
@@ -149,15 +150,6 @@ class GrpcServerAutoConfigurationTests {
|
||||
.isInstanceOf(NettyGrpcServerFactory.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenNeitherShadedNorNonShadedNettyOnClasspathBaseServerFactoryIsAutoConfigured() {
|
||||
this.contextRunner()
|
||||
.withClassLoader(new FilteredClassLoader(NettyServerBuilder.class,
|
||||
io.grpc.netty.shaded.io.grpc.netty.NettyServerBuilder.class))
|
||||
.run((context) -> assertThat(context).getBean(GrpcServerFactory.class)
|
||||
.isInstanceOf(DefaultGrpcServerFactory.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shadedNettyServerFactoryAutoConfiguredAsExpected() {
|
||||
serverFactoryAutoConfiguredAsExpected(this.contextRunner(), ShadedNettyGrpcServerFactory.class);
|
||||
@@ -171,21 +163,19 @@ class GrpcServerAutoConfigurationTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void baseServerFactoryAutoConfiguredAsExpected() {
|
||||
serverFactoryAutoConfiguredAsExpected(
|
||||
this.contextRunner()
|
||||
.withClassLoader(new FilteredClassLoader(NettyServerBuilder.class,
|
||||
io.grpc.netty.shaded.io.grpc.netty.NettyServerBuilder.class)),
|
||||
DefaultGrpcServerFactory.class);
|
||||
void noServerFactoryAutoConfiguredAsExpected() {
|
||||
this.contextRunner()
|
||||
.withClassLoader(new FilteredClassLoader(NettyServerBuilder.class,
|
||||
io.grpc.netty.shaded.io.grpc.netty.NettyServerBuilder.class))
|
||||
.run((context) -> assertThat(context).doesNotHaveBean(GrpcServerFactory.class));
|
||||
}
|
||||
|
||||
private void serverFactoryAutoConfiguredAsExpected(ApplicationContextRunner contextRunner,
|
||||
Class<?> expectedServerFactoryType) {
|
||||
contextRunner.withPropertyValues("spring.grpc.server.address=myhost", "spring.grpc.server.port=6160")
|
||||
contextRunner.withPropertyValues("spring.grpc.server.host=myhost", "spring.grpc.server.port=6160")
|
||||
.run((context) -> assertThat(context).getBean(GrpcServerFactory.class)
|
||||
.isInstanceOf(expectedServerFactoryType)
|
||||
.hasFieldOrPropertyWithValue("address", "myhost")
|
||||
.hasFieldOrPropertyWithValue("port", 6160)
|
||||
.hasFieldOrPropertyWithValue("address", "myhost:6160")
|
||||
.extracting("serviceList", InstanceOfAssertFactories.list(ServerServiceDefinition.class))
|
||||
.singleElement()
|
||||
.extracting(ServerServiceDefinition::getServiceDescriptor)
|
||||
@@ -200,19 +190,16 @@ class GrpcServerAutoConfigurationTests {
|
||||
ShadedNettyGrpcServerFactory.class);
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
@Test
|
||||
void nettyServerFactoryAutoConfiguredWithCustomizers() {
|
||||
// FilteredClassLoader hides the class from the auto-configuration but not from
|
||||
// the Java SPI
|
||||
// used by ServerBuilder.forPort(int) which by default returns shaded Netty. This
|
||||
// results in
|
||||
// class cast exception when NettyGrpcServerFactory is expecting a non-shaded
|
||||
// server builder.
|
||||
// We static mock the builder to return non-shaded Netty - which would happen in
|
||||
// the Java SPI used by ServerBuilder.forPort(int) which by default returns
|
||||
// shaded Netty. This results in class cast exception when
|
||||
// NettyGrpcServerFactory is expecting a non-shaded server builder. We static
|
||||
// mock the builder to return non-shaded Netty - which would happen in
|
||||
// real world.
|
||||
try (MockedStatic<ServerBuilder> serverBuilderForPort = Mockito.mockStatic(ServerBuilder.class)) {
|
||||
serverBuilderForPort.when(() -> ServerBuilder.forPort(anyInt()))
|
||||
try (MockedStatic<Grpc> serverBuilderForPort = Mockito.mockStatic(Grpc.class)) {
|
||||
serverBuilderForPort.when(() -> Grpc.newServerBuilderForPort(anyInt(), any()))
|
||||
.thenAnswer((Answer<NettyServerBuilder>) invocation -> NettyServerBuilder
|
||||
.forPort(invocation.getArgument(0)));
|
||||
NettyServerBuilder builder = mock();
|
||||
@@ -222,16 +209,6 @@ class GrpcServerAutoConfigurationTests {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
<T extends ServerBuilder<T>> void baseServerFactoryAutoConfiguredWithCustomizers() {
|
||||
ServerBuilder<T> builder = mock();
|
||||
serverFactoryAutoConfiguredWithCustomizers(
|
||||
this.contextRunnerWithLifecyle()
|
||||
.withClassLoader(new FilteredClassLoader(NettyServerBuilder.class,
|
||||
io.grpc.netty.shaded.io.grpc.netty.NettyServerBuilder.class)),
|
||||
builder, DefaultGrpcServerFactory.class);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T extends ServerBuilder<T>> void serverFactoryAutoConfiguredWithCustomizers(
|
||||
ApplicationContextRunner contextRunner, ServerBuilder<T> mockServerBuilder,
|
||||
@@ -259,7 +236,9 @@ class GrpcServerAutoConfigurationTests {
|
||||
serverFactoryAutoConfiguredAsExpected(
|
||||
this.contextRunner()
|
||||
.withPropertyValues("spring.grpc.server.ssl.bundle=ssltest",
|
||||
"spring.ssl.bundle.jks.ssltest.keystore.location=classpath:test.jks")
|
||||
"spring.ssl.bundle.jks.ssltest.keystore.location=classpath:test.jks",
|
||||
"spring.ssl.bundle.jks.ssltest.keystore.password=secret",
|
||||
"spring.ssl.bundle.jks.ssltest.key.password=password")
|
||||
.withClassLoader(
|
||||
new FilteredClassLoader(io.grpc.netty.shaded.io.grpc.netty.NettyServerBuilder.class)),
|
||||
NettyGrpcServerFactory.class);
|
||||
|
||||
@@ -22,12 +22,13 @@ import java.util.Map;
|
||||
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.context.properties.bind.BindException;
|
||||
import org.springframework.boot.context.properties.bind.Binder;
|
||||
import org.springframework.boot.context.properties.source.MapConfigurationPropertySource;
|
||||
import org.springframework.util.unit.DataSize;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
|
||||
/**
|
||||
* Tests for {@link GrpcServerProperties}.
|
||||
@@ -48,11 +49,11 @@ class GrpcServerPropertiesTests {
|
||||
@Test
|
||||
void bind() {
|
||||
Map<String, String> map = new HashMap<>();
|
||||
map.put("spring.grpc.server.address", "my-server-ip");
|
||||
map.put("spring.grpc.server.host", "my-server-ip");
|
||||
map.put("spring.grpc.server.port", "3130");
|
||||
map.put("spring.grpc.server.shutdown-grace-period", "15");
|
||||
GrpcServerProperties properties = bindProperties(map);
|
||||
assertThat(properties.getAddress()).isEqualTo("my-server-ip");
|
||||
assertThat(properties.getAddress()).isEqualTo("my-server-ip:3130");
|
||||
assertThat(properties.getPort()).isEqualTo(3130);
|
||||
assertThat(properties.getShutdownGracePeriod()).isEqualTo(Duration.ofSeconds(15));
|
||||
}
|
||||
@@ -127,4 +128,26 @@ class GrpcServerPropertiesTests {
|
||||
|
||||
}
|
||||
|
||||
@Nested
|
||||
class AddressProperties {
|
||||
|
||||
@Test
|
||||
void bind() {
|
||||
Map<String, String> map = new HashMap<>();
|
||||
map.put("spring.grpc.server.address", "my-server-ip:3130");
|
||||
GrpcServerProperties properties = bindProperties(map);
|
||||
assertThat(properties.getAddress()).isEqualTo("my-server-ip:3130");
|
||||
assertThat(properties.getPort()).isEqualTo(3130);
|
||||
}
|
||||
|
||||
@Test
|
||||
void illegalBecauseAddressAndPortSpecified() {
|
||||
Map<String, String> map = new HashMap<>();
|
||||
map.put("spring.grpc.server.address", "my-server-ip:3130");
|
||||
map.put("spring.grpc.server.port", "10000");
|
||||
assertThrows(BindException.class, () -> bindProperties(map));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user