Skip to content

Commit

Permalink
[grid] Accommodate ability to specify sub-paths (SeleniumHQ#11271)
Browse files Browse the repository at this point in the history
* [grid] Accommodate ability to specify sub-paths

Fixes: SeleniumHQ#10847

Sub-paths can now be specified for the below 
Components:
1. Hub
2. Standalone
3. Router

It can be specified via “--sub-path <actualPathGoesHere>”

Or via the Toml configuration file by specifying

[network]
sub-path = “<actualPathGoesHere>”

* Fixing review comments

* Fixing build dependencies

* Simplify route building logic

* Renaming the flag to “network”
  • Loading branch information
krmahadevan authored Dec 15, 2022
1 parent 58122b2 commit 2917a66
Show file tree
Hide file tree
Showing 10 changed files with 385 additions and 27 deletions.
42 changes: 42 additions & 0 deletions java/src/org/openqa/selenium/grid/TemplateGridServerCommand.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@

package org.openqa.selenium.grid;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.function.Function;
import java.util.function.Supplier;
import org.openqa.selenium.grid.config.CompoundConfig;
import org.openqa.selenium.grid.config.Config;
import org.openqa.selenium.grid.config.MemoizedConfig;
Expand All @@ -30,6 +35,8 @@
import java.util.Optional;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import org.openqa.selenium.remote.http.Routable;
import org.openqa.selenium.remote.http.Route;

public abstract class TemplateGridServerCommand extends TemplateGridCommand {

Expand All @@ -46,6 +53,41 @@ public Server<?> asServer(Config initialConfig) {
handler.websocketHandler);
}

private static final String GRAPHQL = "/graphql";

protected static Routable graphqlRoute(String prefix, Supplier<HttpHandler> handler) {
Routable optionsRoute = buildRoute(GRAPHQL,
prefix,
path -> Route.options(path).to(handler)
);
Routable postRoute = buildRoute(GRAPHQL,
prefix,
path -> Route.post(path).to(handler)
);
return Route.combine(optionsRoute, postRoute);
}

protected static Routable hubRoute(String prefix, Route route) {
return buildRoute("/wd/hub",
prefix,
path -> Route.prefix(path).to(route)
);
}

private static Routable buildRoute(String url, String prefix, Function<String, Route> mapper) {
List<String> subPaths = prefix.isEmpty()
? Collections.singletonList(url)
: Arrays.asList(prefix + url, url);
return subPaths.stream()
.map(mapper)
.reduce(Route::combine)
.get();
}

protected static Routable baseRoute(String prefix, Route route) {
return Route.prefix(prefix).to(route);
}

protected abstract Handlers createHandlers(Config config);

public static class Handlers {
Expand Down
1 change: 1 addition & 0 deletions java/src/org/openqa/selenium/grid/commands/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ java_library(
"//java/src/org/openqa/selenium/grid/node/config",
"//java/src/org/openqa/selenium/grid/node/local",
"//java/src/org/openqa/selenium/grid/router",
"//java/src/org/openqa/selenium/grid/router/httpd",
"//java/src/org/openqa/selenium/grid/security",
"//java/src/org/openqa/selenium/grid/server",
"//java/src/org/openqa/selenium/grid/sessionmap",
Expand Down
22 changes: 15 additions & 7 deletions java/src/org/openqa/selenium/grid/commands/Hub.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import com.google.auto.service.AutoService;
import com.google.common.collect.ImmutableSet;

import java.util.stream.Stream;
import org.openqa.selenium.BuildInfo;
import org.openqa.selenium.UsernameAndPassword;
import org.openqa.selenium.cli.CliCommand;
Expand All @@ -40,6 +41,7 @@
import org.openqa.selenium.grid.server.BaseServerOptions;
import org.openqa.selenium.grid.server.EventBusOptions;
import org.openqa.selenium.grid.server.NetworkOptions;
import org.openqa.selenium.grid.router.httpd.RouterOptions;
import org.openqa.selenium.grid.server.Server;
import org.openqa.selenium.grid.sessionmap.SessionMap;
import org.openqa.selenium.grid.sessionmap.local.LocalSessionMap;
Expand Down Expand Up @@ -184,15 +186,21 @@ protected Handlers createHandlers(Config config) {
.setContent(Contents.utf8String("Router is " + ready));
};

Routable ui = new GridUiRoute();
Routable routerWithSpecChecks = router.with(networkOptions.getSpecComplianceChecks());

Routable httpHandler = combine(
ui,
routerWithSpecChecks,
Route.prefix("/wd/hub").to(combine(routerWithSpecChecks)),
Route.options("/graphql").to(() -> graphqlHandler),
Route.post("/graphql").to(() -> graphqlHandler));
String subPath = new RouterOptions(config).subPath();
Routable ui = new GridUiRoute(subPath);

Routable appendRoute = Stream.of(
routerWithSpecChecks,
hubRoute(subPath, combine(routerWithSpecChecks)),
graphqlRoute(subPath, () -> graphqlHandler)
).reduce(Route::combine)
.get();
if (!subPath.isEmpty()) {
appendRoute = Route.combine(appendRoute, baseRoute(subPath, combine(routerWithSpecChecks)));
}
Routable httpHandler = combine(ui, appendRoute);

UsernameAndPassword uap = secretOptions.getServerAuthentication();
if (uap != null) {
Expand Down
23 changes: 16 additions & 7 deletions java/src/org/openqa/selenium/grid/commands/Standalone.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import com.google.auto.service.AutoService;
import com.google.common.collect.ImmutableSet;

import java.util.stream.Stream;
import org.openqa.selenium.BuildInfo;
import org.openqa.selenium.UsernameAndPassword;
import org.openqa.selenium.cli.CliCommand;
Expand All @@ -43,6 +44,7 @@
import org.openqa.selenium.grid.server.BaseServerOptions;
import org.openqa.selenium.grid.server.EventBusOptions;
import org.openqa.selenium.grid.server.NetworkOptions;
import org.openqa.selenium.grid.router.httpd.RouterOptions;
import org.openqa.selenium.grid.server.Server;
import org.openqa.selenium.grid.sessionmap.SessionMap;
import org.openqa.selenium.grid.sessionmap.local.LocalSessionMap;
Expand Down Expand Up @@ -185,14 +187,21 @@ protected Handlers createHandlers(Config config) {
serverOptions.getExternalUri(),
getFormattedVersion());

Routable ui = new GridUiRoute();
String subPath = new RouterOptions(config).subPath();
Routable ui = new GridUiRoute(subPath);

Routable httpHandler = combine(
ui,
router,
Route.prefix("/wd/hub").to(combine(router)),
Route.options("/graphql").to(() -> graphqlHandler),
Route.post("/graphql").to(() -> graphqlHandler));
Routable appendRoute = Stream.of(
router,
hubRoute(subPath, combine(router)),
graphqlRoute(subPath, () -> graphqlHandler)
).reduce(Route::combine)
.get();

if (!subPath.isEmpty()) {
appendRoute = Route.combine(appendRoute, baseRoute(subPath, combine(router)));
}

Routable httpHandler = combine(ui, appendRoute);

UsernameAndPassword uap = secretOptions.getServerAuthentication();
if (uap != null) {
Expand Down
1 change: 1 addition & 0 deletions java/src/org/openqa/selenium/grid/router/httpd/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ java_library(
srcs = glob(["*.java"]),
visibility = [
"//java/src/org/openqa/selenium/grid:__pkg__",
"//java/src/org/openqa/selenium/grid/commands:__pkg__",
"//java/test/org/openqa/selenium/grid/router:__pkg__",
"//java/test/org/openqa/selenium/grid/router/httpd:__pkg__",
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
package org.openqa.selenium.grid.router.httpd;

import static org.openqa.selenium.grid.config.StandardGridRoles.ROUTER_ROLE;
import static org.openqa.selenium.grid.router.httpd.RouterOptions.NETWORK;

import com.google.auto.service.AutoService;

Expand Down Expand Up @@ -56,6 +57,13 @@ public class RouterFlags implements HasRoles {
@ConfigValue(section = "router", name = "password", example = "hunter2")
private String password;

@Parameter(
names = {"--sub-path"},
arity = 1,
description = "A sub-path that should be considered for all user facing routes on the Hub/Router/Standalone")
@ConfigValue(section = NETWORK, name = "sub-path", example = "my_company/selenium_grid")
public String subPath;

@Override
public Set<Role> getRoles() {
return Collections.singleton(ROUTER_ROLE);
Expand Down
44 changes: 44 additions & 0 deletions java/src/org/openqa/selenium/grid/router/httpd/RouterOptions.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Licensed to the Software Freedom Conservancy (SFC) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The SFC licenses this file
// to you 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
//
// http://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.openqa.selenium.grid.router.httpd;

import org.openqa.selenium.grid.config.Config;

public class RouterOptions {

static final String NETWORK = "network";

private final Config config;

public RouterOptions(Config config) {
this.config = config;
}

public String subPath() {
return config.get(NETWORK, "sub-path").map(prefix -> {
prefix = prefix.trim();
if (!prefix.startsWith("/")) {
prefix = "/" + prefix; //Prefix with a '/' if absent.
}
if (prefix.endsWith("/")) {
prefix = prefix.substring(0, prefix.length() -1); //Remove the trailing '/' if present.
}
return prefix;
}).orElse("");
}
}
21 changes: 14 additions & 7 deletions java/src/org/openqa/selenium/grid/router/httpd/RouterServer.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;

import java.util.stream.Stream;
import org.openqa.selenium.BuildInfo;
import org.openqa.selenium.UsernameAndPassword;
import org.openqa.selenium.cli.CliCommand;
Expand Down Expand Up @@ -157,16 +158,22 @@ protected Handlers createHandlers(Config config) {
serverOptions.getExternalUri(),
getServerVersion());

Routable ui = new GridUiRoute();
String subPath = new RouterOptions(config).subPath();
Routable ui = new GridUiRoute(subPath);
Router router = new Router(tracer, clientFactory, sessions, queue, distributor);
Routable routerWithSpecChecks = router.with(networkOptions.getSpecComplianceChecks());

Routable route = Route.combine(
ui,
routerWithSpecChecks,
Route.prefix("/wd/hub").to(combine(routerWithSpecChecks)),
Route.options("/graphql").to(() -> graphqlHandler),
Route.post("/graphql").to(() -> graphqlHandler));
Routable appendRoute = Stream.of(
routerWithSpecChecks,
hubRoute(subPath, combine(routerWithSpecChecks)),
graphqlRoute(subPath, () -> graphqlHandler)
)
.reduce(Route::combine)
.get();
if (!subPath.isEmpty()) {
appendRoute = Route.combine(appendRoute, baseRoute(subPath, combine(routerWithSpecChecks)));
}
Routable route = Route.combine(ui, appendRoute);

UsernameAndPassword uap = secretOptions.getServerAuthentication();
if (uap != null) {
Expand Down
59 changes: 53 additions & 6 deletions java/src/org/openqa/selenium/grid/web/GridUiRoute.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,14 @@

package org.openqa.selenium.grid.web;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.function.Function;
import java.util.function.Supplier;
import org.openqa.selenium.internal.Require;
import org.openqa.selenium.json.Json;
import org.openqa.selenium.remote.http.HttpHandler;
import org.openqa.selenium.remote.http.HttpRequest;
import org.openqa.selenium.remote.http.HttpResponse;
import org.openqa.selenium.remote.http.Routable;
Expand All @@ -38,17 +45,26 @@ public class GridUiRoute implements Routable {

private final Route routes;

public GridUiRoute() {
public GridUiRoute(String prefix) {
Require.nonNull(prefix, "Prefix cannot be null");
URL uiRoot = GridUiRoute.class.getResource(GRID_RESOURCE_WITH_PREFIX);
if (uiRoot != null) {
ResourceHandler uiHandler = new ResourceHandler(new ClassPathResource(uiRoot, GRID_RESOURCE));
HttpResponse uiRedirect = new HttpResponse()
.setStatus(HTTP_MOVED_TEMP)
.addHeader("Location", "/ui");
routes = Route.combine(
get("/").to(() -> req -> uiRedirect),
get("/grid/console").to(() -> req -> uiRedirect),
Route.prefix("/ui").to(Route.matching(req -> true).to(() -> uiHandler)));
.addHeader("Location", prefix.concat("/ui"));

Supplier<HttpHandler> redirectHandler = () -> req -> uiRedirect;

Routable appendRoute = Route.combine(
consoleRoute(prefix, redirectHandler),
uiRoute(prefix, () -> uiHandler)
);
if (!prefix.isEmpty()) {
appendRoute = Route.combine(appendRoute, redirectRoute(prefix, redirectHandler));
}

routes = Route.combine(get("/").to(redirectHandler), appendRoute);
} else {
LOG.warning("It was not possible to load the Grid UI.");
Json json = new Json();
Expand All @@ -65,4 +81,35 @@ public boolean matches(HttpRequest req) {
public HttpResponse execute(HttpRequest req) {
return routes.execute(req);
}

private static Routable uiRoute(String prefix, Supplier<HttpHandler> handler) {
return buildRoute(
"/ui",
prefix,
path -> Route.prefix(path).to(Route.matching(req -> true).to(handler))
);
}

private static Routable consoleRoute(String prefix, Supplier<HttpHandler> handler) {
return buildRoute(
"/grid/console",
prefix,
path -> get(path).to(handler)
);
}

private static Routable buildRoute(String url, String prefix, Function<String, Route> mapper) {
List<String> subPaths = prefix.isEmpty()
? Collections.singletonList(url)
: Arrays.asList(prefix + url, url);
return subPaths.stream()
.map(mapper)
.reduce(Route::combine)
.get();
}

private static Routable redirectRoute(String prefix, Supplier<HttpHandler> handler ) {
prefix = prefix.endsWith("/") ? prefix.substring(0, prefix.length() -1) : prefix;
return get(prefix).to(handler);
}
}
Loading

0 comments on commit 2917a66

Please sign in to comment.