Skip to content

Commit 40a2977

Browse files
committed
[grid] Adding a "bind-host" flag
#9981 fixed a regression where the Grid server was binding to all interfaces, even if a host was being configured. However, this was not ideal for environments like Docker, where the host value can be the reachable IP of the machine where the containers are running, but we cannot bind to that IP inside the container. This adds a flag where the user can decide if the server should bind to the host or not, true by default to keep the current behaviour, but it will be configured to false by default in the docker-selenium project. This is a partial fix for SeleniumHQ/docker-selenium#1453
1 parent f7a4c21 commit 40a2977

5 files changed

Lines changed: 62 additions & 14 deletions

File tree

java/src/org/openqa/selenium/grid/server/BaseServerFlags.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,16 @@ public class BaseServerFlags implements HasRoles {
4242
@ConfigValue(section = SERVER_SECTION, name = "host", example = "\"localhost\"")
4343
private String host;
4444

45+
@Parameter(
46+
names = "--bind-host",
47+
description = "Whether the server should bind to the host address/name, or only use it to" +
48+
" report its reachable url. Helpful in complex network topologies where the" +
49+
" server cannot report itself with the current IP/hostname but rather an" +
50+
" external IP or hostname (e.g. inside a Docker container).",
51+
arity = 1)
52+
@ConfigValue(section = SERVER_SECTION, name = "bind-host", example = "true")
53+
private Boolean bindHost = true;
54+
4555
@Parameter(
4656
description = "Port to listen on. There is no default as this parameter is used by "
4757
+ "different components, for example Router/Hub/Standalone will use 4444 and "

java/src/org/openqa/selenium/grid/server/BaseServerOptions.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,10 @@ public boolean getAllowCORS() {
115115
return config.getBool(SERVER_SECTION, "allow-cors").orElse(false);
116116
}
117117

118+
public boolean getBindHost() {
119+
return config.getBool(SERVER_SECTION, "bind-host").orElse(true);
120+
}
121+
118122
public boolean isSecure() {
119123
return config.get(SERVER_SECTION, "https-private-key").isPresent()
120124
&& config.get(SERVER_SECTION, "https-certificate").isPresent();

java/src/org/openqa/selenium/netty/server/NettyServer.java

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,14 @@
1717

1818
package org.openqa.selenium.netty.server;
1919

20-
import java.net.InetSocketAddress;
20+
import org.openqa.selenium.grid.server.BaseServerOptions;
21+
import org.openqa.selenium.grid.server.Server;
22+
import org.openqa.selenium.internal.Require;
23+
import org.openqa.selenium.remote.AddWebDriverSpecHeaders;
24+
import org.openqa.selenium.remote.ErrorFilter;
25+
import org.openqa.selenium.remote.http.HttpHandler;
26+
import org.openqa.selenium.remote.http.Message;
27+
2128
import io.netty.bootstrap.ServerBootstrap;
2229
import io.netty.channel.Channel;
2330
import io.netty.channel.EventLoopGroup;
@@ -30,31 +37,27 @@
3037
import io.netty.handler.ssl.util.SelfSignedCertificate;
3138
import io.netty.util.internal.logging.InternalLoggerFactory;
3239
import io.netty.util.internal.logging.JdkLoggerFactory;
33-
import org.openqa.selenium.remote.AddWebDriverSpecHeaders;
34-
import org.openqa.selenium.grid.server.BaseServerOptions;
35-
import org.openqa.selenium.grid.server.Server;
36-
import org.openqa.selenium.remote.ErrorFilter;
37-
import org.openqa.selenium.internal.Require;
38-
import org.openqa.selenium.remote.http.HttpHandler;
39-
import org.openqa.selenium.remote.http.Message;
4040

41-
import javax.net.ssl.SSLException;
4241
import java.io.IOException;
4342
import java.io.UncheckedIOException;
4443
import java.net.BindException;
44+
import java.net.InetSocketAddress;
4545
import java.net.MalformedURLException;
4646
import java.net.URL;
4747
import java.security.cert.CertificateException;
4848
import java.util.Optional;
4949
import java.util.function.BiFunction;
5050
import java.util.function.Consumer;
5151

52+
import javax.net.ssl.SSLException;
53+
5254
public class NettyServer implements Server<NettyServer> {
5355

5456
private final EventLoopGroup bossGroup;
5557
private final EventLoopGroup workerGroup;
5658
private final int port;
5759
private final String host;
60+
private final boolean bindHost;
5861
private final URL externalUrl;
5962
private final HttpHandler handler;
6063
private final BiFunction<String, Consumer<Message>, Optional<Consumer<Message>>> websocketHandler;
@@ -106,6 +109,7 @@ public NettyServer(
106109

107110
port = options.getPort();
108111
host = options.getHostname().orElse("0.0.0.0");
112+
bindHost = options.getBindHost();
109113
allowCors = options.getAllowCORS();
110114

111115
try {
@@ -150,7 +154,11 @@ public NettyServer start() {
150154
.childHandler(new SeleniumHttpInitializer(sslCtx, handler, websocketHandler, allowCors));
151155

152156
try {
153-
channel = b.bind(new InetSocketAddress(host, port)).sync().channel();
157+
// Using a flag to avoid binding to the host, useful in environments like Docker,
158+
// where the "host" value can be the IP of the Docker host machine, which cannot
159+
// be bind inside the container.
160+
channel = bindHost ? b.bind(new InetSocketAddress(host, port)).sync().channel() :
161+
b.bind(port).sync().channel();
154162
} catch (InterruptedException e) {
155163
Thread.currentThread().interrupt();
156164
throw new UncheckedIOException(new IOException("Start up interrupted", e));

java/test/org/openqa/selenium/grid/server/BaseServerOptionsTest.java

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,23 +17,33 @@
1717

1818
package org.openqa.selenium.grid.server;
1919

20+
import static org.assertj.core.api.Assertions.assertThat;
21+
2022
import org.junit.Test;
2123
import org.openqa.selenium.grid.config.MapConfig;
2224

2325
import java.util.Map;
2426

25-
import static org.assertj.core.api.Assertions.assertThat;
26-
2727
public class BaseServerOptionsTest {
2828

2929
@Test
3030
public void readingThePortTwiceShouldGiveTheSameResult() {
31-
BaseServerOptions options = new BaseServerOptions(new MapConfig(Map.of("server", Map.of("port", -1))));
31+
BaseServerOptions options = new BaseServerOptions(
32+
new MapConfig(Map.of("server", Map.of("port", -1))));
3233

3334
int first = options.getPort();
3435
int second = options.getPort();
3536

3637
assertThat(first).isEqualTo(second);
3738
}
3839

40+
@Test
41+
public void serverConfigBindsToHostByDefault() {
42+
BaseServerOptions options = new BaseServerOptions(
43+
new MapConfig(Map.of("server", Map.of("port", 4444))));
44+
45+
assertThat(options.getBindHost()).isEqualTo(true);
46+
}
47+
48+
3949
}

java/test/org/openqa/selenium/netty/server/NettyServerTest.java

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
import static org.assertj.core.api.Assertions.assertThat;
2121
import static org.junit.Assert.assertEquals;
22+
import static org.junit.Assert.assertFalse;
2223
import static org.junit.Assert.assertNull;
2324
import static org.junit.Assert.assertTrue;
2425
import static org.openqa.selenium.remote.http.Contents.utf8String;
@@ -107,7 +108,6 @@ public void shouldAllowCORS() {
107108
BaseServerOptions options = new BaseServerOptions(cfg);
108109
assertTrue("Allow CORS should be enabled", options.getAllowCORS());
109110

110-
// TODO: Server setup
111111
Server<?> server = new NettyServer(
112112
options,
113113
req -> new HttpResponse()
@@ -126,6 +126,22 @@ public void shouldAllowCORS() {
126126
response.getHeader("Access-Control-Allow-Origin"));
127127
}
128128

129+
@Test
130+
public void shouldNotBindToHost() {
131+
Config cfg = new CompoundConfig(
132+
new MapConfig(ImmutableMap.of("server", ImmutableMap.of(
133+
"bind-host", "false", "host", "anyRandomHost"))));
134+
BaseServerOptions options = new BaseServerOptions(cfg);
135+
assertFalse("Bind to host should be disabled", options.getBindHost());
136+
137+
Server<?> server = new NettyServer(
138+
options,
139+
req -> new HttpResponse()
140+
).start();
141+
142+
assertEquals("anyRandomHost", server.getUrl().getHost());
143+
}
144+
129145
private void outputHeaders(HttpResponse res) {
130146
res.getHeaderNames()
131147
.forEach(name ->

0 commit comments

Comments
 (0)