From 00e54cb372ce92c64fffa368afaf1b8872cc1b50 Mon Sep 17 00:00:00 2001 From: Ivan Lorca Date: Fri, 5 Dec 2025 13:00:57 +0100 Subject: [PATCH] Bind to configured IP address on netty servers Replace call to super on the `NettyGrpcServer` and `ShaddedNettyGrpcServer` `newServerBuilder()` method, with a call to the `NettyServerBuilder.forAddress` that allows defining the IP and port to be bound to using a `SocketAddress`. The glue logic for getting the `SocketAddress` from the configured address string is in the `DefaultGrpcServerFactory` as is common to both inheritor classes. Signed-off-by: Ivan Lorca [resolves #319] --- .../grpc/server/DefaultGrpcServerFactory.java | 25 ++++++++++++ .../grpc/server/NettyGrpcServerFactory.java | 2 +- .../server/ShadedNettyGrpcServerFactory.java | 2 +- .../server/DefaultGrpcServerFactoryTests.java | 38 +++++++++++++++++++ 4 files changed, 65 insertions(+), 2 deletions(-) diff --git a/spring-grpc-core/src/main/java/org/springframework/grpc/server/DefaultGrpcServerFactory.java b/spring-grpc-core/src/main/java/org/springframework/grpc/server/DefaultGrpcServerFactory.java index d932a6c5..21a08a42 100644 --- a/spring-grpc-core/src/main/java/org/springframework/grpc/server/DefaultGrpcServerFactory.java +++ b/spring-grpc-core/src/main/java/org/springframework/grpc/server/DefaultGrpcServerFactory.java @@ -16,6 +16,8 @@ package org.springframework.grpc.server; +import java.net.InetSocketAddress; +import java.net.SocketAddress; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; @@ -31,6 +33,7 @@ import org.springframework.grpc.internal.GrpcUtils; import org.springframework.grpc.server.service.ServerInterceptorFilter; +import org.springframework.util.StringUtils; import io.grpc.Grpc; import io.grpc.InsecureServerCredentials; @@ -190,4 +193,26 @@ protected void configureServices(T builder, List servic }); } + protected String hostName() { + return address().split(":")[0].trim(); + } + + protected SocketAddress socketAddress() { + String address = address(); + if (address.startsWith("unix:")) { + throw new UnsupportedOperationException("Unix socket addresses not supported"); + } + if (address.startsWith("in-process:")) { + throw new UnsupportedOperationException("In-Process addresses not supported"); + } + + var host = hostName(); + if (StringUtils.hasText(host) && !Objects.equals(host, "*")) { + return new InetSocketAddress(host, port()); + } + else { + return new InetSocketAddress(port()); + } + } + } diff --git a/spring-grpc-core/src/main/java/org/springframework/grpc/server/NettyGrpcServerFactory.java b/spring-grpc-core/src/main/java/org/springframework/grpc/server/NettyGrpcServerFactory.java index 7562f60e..7870d41d 100644 --- a/spring-grpc-core/src/main/java/org/springframework/grpc/server/NettyGrpcServerFactory.java +++ b/spring-grpc-core/src/main/java/org/springframework/grpc/server/NettyGrpcServerFactory.java @@ -56,7 +56,7 @@ protected NettyServerBuilder newServerBuilder() { .bossEventLoopGroup(new MultiThreadIoEventLoopGroup(1, EpollIoHandler.newFactory())) .workerEventLoopGroup(new MultiThreadIoEventLoopGroup(EpollIoHandler.newFactory())); } - return super.newServerBuilder(); + return NettyServerBuilder.forAddress(socketAddress(), credentials()); } } diff --git a/spring-grpc-core/src/main/java/org/springframework/grpc/server/ShadedNettyGrpcServerFactory.java b/spring-grpc-core/src/main/java/org/springframework/grpc/server/ShadedNettyGrpcServerFactory.java index 5a493470..3b661b6f 100644 --- a/spring-grpc-core/src/main/java/org/springframework/grpc/server/ShadedNettyGrpcServerFactory.java +++ b/spring-grpc-core/src/main/java/org/springframework/grpc/server/ShadedNettyGrpcServerFactory.java @@ -55,7 +55,7 @@ protected NettyServerBuilder newServerBuilder() { .bossEventLoopGroup(new EpollEventLoopGroup(1)) .workerEventLoopGroup(new EpollEventLoopGroup()); } - return super.newServerBuilder(); + return NettyServerBuilder.forAddress(socketAddress(), credentials()); } } diff --git a/spring-grpc-core/src/test/java/org/springframework/grpc/server/DefaultGrpcServerFactoryTests.java b/spring-grpc-core/src/test/java/org/springframework/grpc/server/DefaultGrpcServerFactoryTests.java index 15f23fdb..2efe6538 100644 --- a/spring-grpc-core/src/test/java/org/springframework/grpc/server/DefaultGrpcServerFactoryTests.java +++ b/spring-grpc-core/src/test/java/org/springframework/grpc/server/DefaultGrpcServerFactoryTests.java @@ -17,13 +17,18 @@ package org.springframework.grpc.server; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.mockito.Mockito.mock; +import java.net.InetSocketAddress; +import java.net.SocketAddress; import java.util.List; import org.assertj.core.api.InstanceOfAssertFactories; +import org.junit.jupiter.api.DynamicTest; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestFactory; import io.grpc.ServerServiceDefinition; @@ -83,4 +88,37 @@ void whenFilterAllowsOneThenOneServiceAdded() { } + @Nested + class AddressParser { + + @TestFactory + List ipAddress() { + return List.of(testIpAddress(":9999", new InetSocketAddress(9999)), + testIpAddress("localhost:9999", new InetSocketAddress("localhost", 9999)), + testIpAddress("localhost", new InetSocketAddress("localhost", 9090)), + testIpAddress("*", new InetSocketAddress(9090)), + testIpAddress("*:8888", new InetSocketAddress(8888)), + testIpAddress("", new InetSocketAddress(9090))); + } + + private DynamicTest testIpAddress(String address, SocketAddress expected) { + return DynamicTest.dynamicTest("Socket address: " + address, () -> { + var factory = new DefaultGrpcServerFactory<>(address, List.of()); + assertThat(factory.socketAddress()).isEqualTo(expected); + }); + } + + @TestFactory + List unsupportedAddress() { + return List.of(testThrows("unix:dummy"), testThrows("in-process:")); + } + + private DynamicTest testThrows(String address) { + return DynamicTest.dynamicTest("Socket address: " + address, + () -> assertThatExceptionOfType(UnsupportedOperationException.class) + .isThrownBy(() -> new DefaultGrpcServerFactory<>(address, List.of()).socketAddress())); + } + + } + }