Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -190,4 +193,26 @@ protected void configureServices(T builder, List<ServerServiceDefinition> servic
});
}

protected String hostName() {
return address().split(":")[0].trim();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this will handle all cases as users could pass in arbitrary addresses (e.g. 12::34::....). I think we need to use a regex to cover all cases. Wdyt?

}

protected SocketAddress socketAddress() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer moving this to GrpcUtils like we do for port. Then rather than super.hostname the subs could call GrpcUtils.hostname.

String address = address();
if (address.startsWith("unix:")) {
throw new UnsupportedOperationException("Unix socket addresses not supported");
}
if (address.startsWith("in-process:")) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think in-process: string every makes it to the server side and is soley used on the client/channel side (i.e. its stripped by the time it gets here).

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());
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ protected NettyServerBuilder newServerBuilder() {
.bossEventLoopGroup(new EpollEventLoopGroup(1))
.workerEventLoopGroup(new EpollEventLoopGroup());
}
return super.newServerBuilder();
return NettyServerBuilder.forAddress(socketAddress(), credentials());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this replacement makes sense and is "safe" as the previous code called:

Grpc.newServerBuilderForPort(port(), credentials());

which ultimately called:

ProtocolNegotiators.FromServerCredentialsResult result = ProtocolNegotiators.from(creds);
if (result.error != null) {
    return NewServerBuilderResult.error(result.error);
}
return NewServerBuilderResult.serverBuilder(
        new NettyServerBuilder(new InetSocketAddress(port), result.negotiator));

and the new code NettyServerBuilder.forAddress(socketAddress(), credentials()); does the same effectively but passes in the hostname not just the port.

Previously we were simply disregarding any host info (as noted in your issue).

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -83,4 +88,37 @@ void whenFilterAllowsOneThenOneServiceAdded() {

}

@Nested
class AddressParser {

@TestFactory
List<DynamicTest> 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) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎉 nice use of DynamicTest!

return DynamicTest.dynamicTest("Socket address: " + address, () -> {
var factory = new DefaultGrpcServerFactory<>(address, List.of());
assertThat(factory.socketAddress()).isEqualTo(expected);
});
}

@TestFactory
List<DynamicTest> 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()));
}

}

}
Loading