Skip to content

Commit 3e3eee5

Browse files
committed
[grid] Routing VNC websockets through the Grid
Also, changing class names to express better what they intent to do, which is, routing websockets for VNC and CDP through the Grid, but websockets in general.
1 parent 4733eea commit 3e3eee5

File tree

8 files changed

+76
-49
lines changed

8 files changed

+76
-49
lines changed

java/server/src/org/openqa/selenium/grid/commands/Hub.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
import com.google.auto.service.AutoService;
2121
import com.google.common.collect.ImmutableSet;
22+
2223
import org.openqa.selenium.BuildInfo;
2324
import org.openqa.selenium.UsernameAndPassword;
2425
import org.openqa.selenium.cli.CliCommand;
@@ -31,7 +32,7 @@
3132
import org.openqa.selenium.grid.distributor.local.LocalDistributor;
3233
import org.openqa.selenium.grid.graphql.GraphqlHandler;
3334
import org.openqa.selenium.grid.log.LoggingOptions;
34-
import org.openqa.selenium.grid.router.ProxyCdpIntoGrid;
35+
import org.openqa.selenium.grid.router.ProxyWebsocketsIntoGrid;
3536
import org.openqa.selenium.grid.router.Router;
3637
import org.openqa.selenium.grid.security.BasicAuthenticationFilter;
3738
import org.openqa.selenium.grid.security.Secret;
@@ -199,7 +200,7 @@ protected Handlers createHandlers(Config config) {
199200
// Allow the liveness endpoint to be reached, since k8s doesn't make it easy to authenticate these checks
200201
httpHandler = combine(httpHandler, Route.get("/readyz").to(() -> readinessCheck));
201202

202-
return new Handlers(httpHandler, new ProxyCdpIntoGrid(clientFactory, sessions));
203+
return new Handlers(httpHandler, new ProxyWebsocketsIntoGrid(clientFactory, sessions));
203204
}
204205

205206
@Override

java/server/src/org/openqa/selenium/grid/commands/Standalone.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
import com.google.auto.service.AutoService;
2121
import com.google.common.collect.ImmutableSet;
22+
2223
import org.openqa.selenium.BuildInfo;
2324
import org.openqa.selenium.UsernameAndPassword;
2425
import org.openqa.selenium.cli.CliCommand;
@@ -32,7 +33,7 @@
3233
import org.openqa.selenium.grid.graphql.GraphqlHandler;
3334
import org.openqa.selenium.grid.log.LoggingOptions;
3435
import org.openqa.selenium.grid.node.Node;
35-
import org.openqa.selenium.grid.node.ProxyNodeCdp;
36+
import org.openqa.selenium.grid.node.ProxyNodeWebsockets;
3637
import org.openqa.selenium.grid.node.config.NodeOptions;
3738
import org.openqa.selenium.grid.router.Router;
3839
import org.openqa.selenium.grid.security.BasicAuthenticationFilter;
@@ -203,7 +204,7 @@ protected Handlers createHandlers(Config config) {
203204
combinedHandler.addHandler(node);
204205
distributor.add(node);
205206

206-
return new Handlers(httpHandler, new ProxyNodeCdp(clientFactory, node));
207+
return new Handlers(httpHandler, new ProxyNodeWebsockets(clientFactory, node));
207208
}
208209

209210
@Override

java/server/src/org/openqa/selenium/grid/node/ProxyNodeCdp.java renamed to java/server/src/org/openqa/selenium/grid/node/ProxyNodeWebsockets.java

Lines changed: 55 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717

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

20+
import com.google.common.collect.ImmutableSet;
21+
2022
import org.openqa.selenium.Capabilities;
2123
import org.openqa.selenium.devtools.CdpEndpointFinder;
2224
import org.openqa.selenium.grid.data.Session;
@@ -32,6 +34,7 @@
3234
import org.openqa.selenium.remote.http.WebSocket;
3335

3436
import java.net.URI;
37+
import java.net.URISyntaxException;
3538
import java.util.Objects;
3639
import java.util.Optional;
3740
import java.util.function.BiFunction;
@@ -42,28 +45,40 @@
4245
import static org.openqa.selenium.internal.Debug.getDebugLogLevel;
4346
import static org.openqa.selenium.remote.http.HttpMethod.GET;
4447

45-
public class ProxyNodeCdp implements BiFunction<String, Consumer<Message>, Optional<Consumer<Message>>> {
48+
public class ProxyNodeWebsockets implements BiFunction<String, Consumer<Message>,
49+
Optional<Consumer<Message>>> {
4650

4751
private static final UrlTemplate CDP_TEMPLATE = new UrlTemplate("/session/{sessionId}/se/cdp");
48-
private static final Logger LOG = Logger.getLogger(ProxyNodeCdp.class.getName());
52+
private static final UrlTemplate VNC_TEMPLATE = new UrlTemplate("/session/{sessionId}/se/vnc");
53+
private static final Logger LOG = Logger.getLogger(ProxyNodeWebsockets.class.getName());
54+
private static final ImmutableSet<String> CDP_ENDPOINT_CAPS =
55+
ImmutableSet.of("goog:chromeOptions",
56+
"moz:debuggerAddress",
57+
"ms:edgeOptions");
4958
private final HttpClient.Factory clientFactory;
5059
private final Node node;
5160

52-
public ProxyNodeCdp(HttpClient.Factory clientFactory, Node node) {
61+
62+
public ProxyNodeWebsockets(HttpClient.Factory clientFactory, Node node) {
5363
this.clientFactory = Objects.requireNonNull(clientFactory);
5464
this.node = Objects.requireNonNull(node);
5565
}
5666

5767
@Override
5868
public Optional<Consumer<Message>> apply(String uri, Consumer<Message> downstream) {
59-
UrlTemplate.Match match = CDP_TEMPLATE.match(uri);
60-
if (match == null) {
69+
UrlTemplate.Match cdpMatch = CDP_TEMPLATE.match(uri);
70+
UrlTemplate.Match vncMatch = VNC_TEMPLATE.match(uri);
71+
72+
if (cdpMatch == null && vncMatch == null) {
6173
return Optional.empty();
6274
}
6375

64-
LOG.fine("Matching CDP session for " + match.getParameters().get("sessionId"));
76+
String sessionId = cdpMatch != null ?
77+
cdpMatch.getParameters().get("sessionId") :
78+
vncMatch.getParameters().get("sessionId");
6579

66-
SessionId id = new SessionId(match.getParameters().get("sessionId"));
80+
LOG.fine("Matching websockets for session id: " + sessionId);
81+
SessionId id = new SessionId(sessionId);
6782

6883
if (!node.isSessionOwner(id)) {
6984
LOG.info("Not owner of " + id);
@@ -72,40 +87,50 @@ public Optional<Consumer<Message>> apply(String uri, Consumer<Message> downstrea
7287

7388
Session session = node.getSession(id);
7489
Capabilities caps = session.getCapabilities();
90+
LOG.fine("Scanning for endpoint: " + caps);
7591

76-
LOG.fine("Scanning for CDP endpoint: " + caps);
77-
78-
// Using strings here to avoid Node depending upon specific drivers.
79-
Optional<URI> cdpUri = CdpEndpointFinder.getReportedUri("goog:chromeOptions", caps)
80-
.flatMap(reported -> CdpEndpointFinder.getCdpEndPoint(clientFactory, reported));
81-
if (cdpUri.isPresent()) {
82-
LOG.log(getDebugLogLevel(), "Chrome endpoint found");
83-
return cdpUri.map(cdp -> createCdpEndPoint(cdp, downstream));
92+
if (cdpMatch != null) {
93+
return findCdpEndpoint(downstream, caps);
8494
}
95+
return findVncEndpoint(downstream, caps);
96+
}
8597

86-
cdpUri = CdpEndpointFinder.getReportedUri("moz:debuggerAddress", caps)
87-
.flatMap(reported -> CdpEndpointFinder.getCdpEndPoint(clientFactory, reported));
88-
if (cdpUri.isPresent()) {
89-
LOG.log(getDebugLogLevel(), "Firefox endpoint found");
90-
return cdpUri.map(cdp -> createCdpEndPoint(cdp, downstream));
98+
private Optional<Consumer<Message>> findCdpEndpoint(Consumer<Message> downstream,
99+
Capabilities caps) {
100+
// Using strings here to avoid Node depending upon specific drivers.
101+
for (String cdpEndpointCap : CDP_ENDPOINT_CAPS) {
102+
Optional<URI> cdpUri = CdpEndpointFinder.getReportedUri(cdpEndpointCap, caps)
103+
.flatMap(reported -> CdpEndpointFinder.getCdpEndPoint(clientFactory, reported));
104+
if (cdpUri.isPresent()) {
105+
LOG.log(getDebugLogLevel(), String.format("Endpoint found in %s", cdpEndpointCap));
106+
return cdpUri.map(cdp -> createWsEndPoint(cdp, downstream));
107+
}
91108
}
109+
return Optional.empty();
110+
}
92111

93-
LOG.fine("Searching for edge options");
94-
cdpUri = CdpEndpointFinder.getReportedUri("ms:edgeOptions", caps)
95-
.flatMap(reported -> CdpEndpointFinder.getCdpEndPoint(clientFactory, reported));
96-
if (cdpUri.isPresent()) {
97-
LOG.log(getDebugLogLevel(), "Edge endpoint found");
112+
private Optional<Consumer<Message>> findVncEndpoint(Consumer<Message> downstream,
113+
Capabilities caps) {
114+
String vncLocalAddress = (String) caps.getCapability("se:vncLocalAddress");
115+
Optional<URI> vncUri;
116+
try {
117+
vncUri = Optional.of(new URI(vncLocalAddress));
118+
} catch (URISyntaxException e) {
119+
LOG.warning("Invalid URI for endpoint " + vncLocalAddress);
120+
return Optional.empty();
98121
}
99-
return cdpUri.map(cdp -> createCdpEndPoint(cdp, downstream));
122+
LOG.log(getDebugLogLevel(), String.format("Endpoint found in %s", "se:vncLocalAddress"));
123+
return vncUri.map(vnc -> createWsEndPoint(vnc, downstream));
100124
}
101125

102-
private Consumer<Message> createCdpEndPoint(URI uri, Consumer<Message> downstream) {
126+
private Consumer<Message> createWsEndPoint(URI uri, Consumer<Message> downstream) {
103127
Objects.requireNonNull(uri);
104128

105-
LOG.info("Establishing CDP connection to " + uri);
129+
LOG.info("Establishing connection to " + uri);
106130

107131
HttpClient client = clientFactory.createClient(ClientConfig.defaultConfig().baseUri(uri));
108-
WebSocket upstream = client.openSocket(new HttpRequest(GET, uri.toString()), new ForwardingListener(downstream));
132+
WebSocket upstream = client.openSocket(
133+
new HttpRequest(GET, uri.toString()), new ForwardingListener(downstream));
109134
return upstream::send;
110135
}
111136

@@ -133,7 +158,7 @@ public void onText(CharSequence data) {
133158

134159
@Override
135160
public void onError(Throwable cause) {
136-
LOG.log(Level.WARNING, "Error proxying CDP command", cause);
161+
LOG.log(Level.WARNING, "Error proxying websocket command", cause);
137162
}
138163
}
139164
}

java/server/src/org/openqa/selenium/grid/node/httpd/NodeServer.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@
3030
import org.openqa.selenium.grid.TemplateGridServerCommand;
3131
import org.openqa.selenium.grid.config.CompoundConfig;
3232
import org.openqa.selenium.grid.config.Config;
33-
import org.openqa.selenium.grid.config.ConfigFlags;
3433
import org.openqa.selenium.grid.config.MemoizedConfig;
3534
import org.openqa.selenium.grid.config.Role;
3635
import org.openqa.selenium.grid.data.NodeAddedEvent;
@@ -39,7 +38,7 @@
3938
import org.openqa.selenium.grid.log.LoggingOptions;
4039
import org.openqa.selenium.grid.node.HealthCheck;
4140
import org.openqa.selenium.grid.node.Node;
42-
import org.openqa.selenium.grid.node.ProxyNodeCdp;
41+
import org.openqa.selenium.grid.node.ProxyNodeWebsockets;
4342
import org.openqa.selenium.grid.node.config.NodeOptions;
4443
import org.openqa.selenium.grid.server.BaseServerOptions;
4544
import org.openqa.selenium.grid.server.EventBusOptions;
@@ -168,7 +167,7 @@ protected Handlers createHandlers(Config config) {
168167
node,
169168
get("/readyz").to(() -> readinessCheck));
170169

171-
return new Handlers(httpHandler, new ProxyNodeCdp(clientFactory, node));
170+
return new Handlers(httpHandler, new ProxyNodeWebsockets(clientFactory, node));
172171
}
173172

174173
@Override

java/server/src/org/openqa/selenium/grid/router/ProxyCdpIntoGrid.java renamed to java/server/src/org/openqa/selenium/grid/router/ProxyWebsocketsIntoGrid.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,13 +40,14 @@
4040

4141
import static org.openqa.selenium.remote.http.HttpMethod.GET;
4242

43-
public class ProxyCdpIntoGrid implements BiFunction<String, Consumer<Message>, Optional<Consumer<Message>>> {
43+
public class ProxyWebsocketsIntoGrid implements BiFunction<String, Consumer<Message>,
44+
Optional<Consumer<Message>>> {
4445

45-
private static final Logger LOG = Logger.getLogger(ProxyCdpIntoGrid.class.getName());
46+
private static final Logger LOG = Logger.getLogger(ProxyWebsocketsIntoGrid.class.getName());
4647
private final HttpClient.Factory clientFactory;
4748
private final SessionMap sessions;
4849

49-
public ProxyCdpIntoGrid(HttpClient.Factory clientFactory, SessionMap sessions) {
50+
public ProxyWebsocketsIntoGrid(HttpClient.Factory clientFactory, SessionMap sessions) {
5051
this.clientFactory = Objects.requireNonNull(clientFactory);
5152
this.sessions = Objects.requireNonNull(sessions);
5253
}
@@ -99,7 +100,7 @@ public void onText(CharSequence data) {
99100

100101
@Override
101102
public void onError(Throwable cause) {
102-
LOG.log(Level.WARNING, "Error proxying CDP command", cause);
103+
LOG.log(Level.WARNING, "Error proxying websocket command", cause);
103104
}
104105
}
105106
}

java/server/src/org/openqa/selenium/grid/router/httpd/RouterServer.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
import org.openqa.selenium.grid.distributor.remote.RemoteDistributor;
3434
import org.openqa.selenium.grid.graphql.GraphqlHandler;
3535
import org.openqa.selenium.grid.log.LoggingOptions;
36-
import org.openqa.selenium.grid.router.ProxyCdpIntoGrid;
36+
import org.openqa.selenium.grid.router.ProxyWebsocketsIntoGrid;
3737
import org.openqa.selenium.grid.router.Router;
3838
import org.openqa.selenium.grid.security.BasicAuthenticationFilter;
3939
import org.openqa.selenium.grid.security.Secret;
@@ -168,7 +168,7 @@ protected Handlers createHandlers(Config config) {
168168
route,
169169
get("/readyz").to(() -> req -> new HttpResponse().setStatus(HTTP_NO_CONTENT)));
170170

171-
return new Handlers(routeWithLiveness, new ProxyCdpIntoGrid(clientFactory, sessions));
171+
return new Handlers(routeWithLiveness, new ProxyWebsocketsIntoGrid(clientFactory, sessions));
172172
}
173173

174174
@Override

java/server/test/org/openqa/selenium/grid/router/NewSessionCreationTest.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import com.google.common.collect.ImmutableList;
2121
import com.google.common.collect.ImmutableMap;
2222
import com.google.common.collect.ImmutableSet;
23+
2324
import org.junit.After;
2425
import org.junit.Before;
2526
import org.junit.Test;
@@ -119,7 +120,7 @@ public void ensureJsCannotCreateANewSession() throws URISyntaxException {
119120
server = new NettyServer(
120121
new BaseServerOptions(new MapConfig(ImmutableMap.of())),
121122
router,
122-
new ProxyCdpIntoGrid(clientFactory, sessions))
123+
new ProxyWebsocketsIntoGrid(clientFactory, sessions))
123124
.start();
124125

125126
URI uri = server.getUrl().toURI();

java/server/test/org/openqa/selenium/grid/router/ProxyCdpTest.java

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717

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

20+
import com.google.common.collect.ImmutableMap;
21+
2022
import org.junit.After;
2123
import org.junit.Before;
2224
import org.junit.Test;
@@ -44,7 +46,6 @@
4446
import java.net.URISyntaxException;
4547
import java.time.Instant;
4648
import java.util.Collections;
47-
import java.util.Map;
4849
import java.util.Optional;
4950
import java.util.UUID;
5051
import java.util.concurrent.CountDownLatch;
@@ -54,8 +55,6 @@
5455
import static org.assertj.core.api.Assertions.assertThat;
5556
import static org.openqa.selenium.remote.http.HttpMethod.GET;
5657

57-
import com.google.common.collect.ImmutableMap;
58-
5958
public class ProxyCdpTest {
6059

6160
private final HttpHandler nullHandler = req -> new HttpResponse();
@@ -73,7 +72,7 @@ public void setUp() {
7372
// Set up the proxy we'll be using
7473
HttpClient.Factory clientFactory = HttpClient.Factory.createDefault();
7574

76-
ProxyCdpIntoGrid proxy = new ProxyCdpIntoGrid(clientFactory, sessions);
75+
ProxyWebsocketsIntoGrid proxy = new ProxyWebsocketsIntoGrid(clientFactory, sessions);
7776
proxyServer = new NettyServer(new BaseServerOptions(emptyConfig), nullHandler, proxy).start();
7877
}
7978

@@ -144,7 +143,7 @@ public void shouldBeAbleToSendMessagesOverSecureWebSocket()
144143
"https-self-signed", true)));
145144

146145
HttpClient.Factory clientFactory = HttpClient.Factory.createDefault();
147-
ProxyCdpIntoGrid proxy = new ProxyCdpIntoGrid(clientFactory, sessions);
146+
ProxyWebsocketsIntoGrid proxy = new ProxyWebsocketsIntoGrid(clientFactory, sessions);
148147

149148
Server<?> backend = createBackendServer(new CountDownLatch(1), new AtomicReference<>(), "Cheddar", emptyConfig);
150149

0 commit comments

Comments
 (0)