Skip to content

Commit 86b66ac

Browse files
committed
Make the prefixed routes more robust
This allows redirects to be set properly. *phew*
1 parent 6aac5f3 commit 86b66ac

File tree

4 files changed

+239
-5
lines changed

4 files changed

+239
-5
lines changed

java/client/src/org/openqa/selenium/remote/http/Route.java

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,10 @@
1818
package org.openqa.selenium.remote.http;
1919

2020
import com.google.common.base.Preconditions;
21-
import com.google.common.collect.ImmutableList;
2221
import com.google.common.collect.ImmutableMap;
2322
import org.openqa.selenium.json.Json;
2423

24+
import java.util.LinkedList;
2525
import java.util.List;
2626
import java.util.Map;
2727
import java.util.Objects;
@@ -32,12 +32,14 @@
3232
import java.util.stream.StreamSupport;
3333

3434
import static com.google.common.base.Preconditions.checkArgument;
35+
import static com.google.common.collect.ImmutableList.toImmutableList;
3536
import static java.net.HttpURLConnection.HTTP_INTERNAL_ERROR;
3637
import static java.net.HttpURLConnection.HTTP_NOT_FOUND;
3738
import static org.openqa.selenium.remote.http.Contents.utf8String;
3839
import static org.openqa.selenium.remote.http.HttpMethod.DELETE;
3940
import static org.openqa.selenium.remote.http.HttpMethod.GET;
4041
import static org.openqa.selenium.remote.http.HttpMethod.POST;
42+
import static org.openqa.selenium.remote.http.UrlPath.ROUTE_PREFIX_KEY;
4143

4244
public abstract class Route implements HttpHandler, Routable {
4345

@@ -234,17 +236,32 @@ public Route to(Route route) {
234236

235237
private static class NestedRoute extends Route {
236238

239+
private final String[] prefixPaths;
237240
private final String prefix;
238241
private final Route route;
239242

240243
private NestedRoute(String prefix, Route route) {
241-
this.prefix = Objects.requireNonNull(prefix, "Prefix must be set.");
244+
this.prefixPaths = Objects.requireNonNull(prefix, "Prefix must be set.").split("/");
245+
this.prefix = prefix;
242246
this.route = Objects.requireNonNull(route, "Target for requests must be set.");
243247
}
244248

245249
@Override
246250
public boolean matches(HttpRequest request) {
247-
return request.getUri().startsWith(prefix) && route.matches(transform(request));
251+
return hasPrefix(request) && route.matches(transform(request));
252+
}
253+
254+
private boolean hasPrefix(HttpRequest request) {
255+
String[] parts = request.getUri().split("/");
256+
if (parts.length < prefixPaths.length) {
257+
return false;
258+
}
259+
for (int i = 0; i < prefixPaths.length; i++) {
260+
if (!prefixPaths[i].equals(parts[i])) {
261+
return false;
262+
}
263+
}
264+
return true;
248265
}
249266

250267
@Override
@@ -254,7 +271,7 @@ protected HttpResponse handle(HttpRequest req) {
254271

255272
private HttpRequest transform(HttpRequest request) {
256273
// Strip the prefix from the existing request and forward it.
257-
String unprefixed = request.getUri().startsWith(prefix) ?
274+
String unprefixed = hasPrefix(request) ?
258275
request.getUri().substring(prefix.length()) :
259276
request.getUri();
260277

@@ -270,6 +287,16 @@ private HttpRequest transform(HttpRequest request) {
270287
request.getAttributeNames().forEach(
271288
attr -> toForward.setAttribute(attr, request.getAttribute(attr)));
272289

290+
// Don't forget to register our prefix
291+
Object rawPrefixes = request.getAttribute(ROUTE_PREFIX_KEY);
292+
if (!(rawPrefixes instanceof List)) {
293+
rawPrefixes = new LinkedList<>();
294+
}
295+
List<String> prefixes = Stream.concat(((List<?>) rawPrefixes).stream(), Stream.of(prefix))
296+
.map(String::valueOf)
297+
.collect(toImmutableList());
298+
toForward.setAttribute(ROUTE_PREFIX_KEY, prefixes);
299+
273300
request.getQueryParameterNames().forEach(name -> {
274301
request.getQueryParameters(name).forEach(value -> toForward.addQueryParameter(name, value));
275302
});
@@ -287,7 +314,7 @@ private static class CombinedRoute extends Route {
287314
private CombinedRoute(Stream<Routable> routes) {
288315
// We want later routes to have a greater chance of being called so that we can override
289316
// routes as necessary.
290-
allRoutes = routes.collect(ImmutableList.toImmutableList()).reverse();
317+
allRoutes = routes.collect(toImmutableList()).reverse();
291318
Preconditions.checkArgument(!allRoutes.isEmpty(), "At least one route must be specified.");
292319
}
293320

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// Licensed to the Software Freedom Conservancy (SFC) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The SFC licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
package org.openqa.selenium.remote.http;
19+
20+
import java.util.List;
21+
import java.util.Objects;
22+
import java.util.stream.Collectors;
23+
24+
public class UrlPath {
25+
26+
static final String ROUTE_PREFIX_KEY = "selenium.route";
27+
28+
private UrlPath() {
29+
// Utility class
30+
}
31+
32+
public static String relativeToServer(HttpRequest req, String location) {
33+
Objects.requireNonNull(location, "Location must be set");
34+
35+
return location;
36+
}
37+
38+
public static String relativeToContext(HttpRequest req, String location) {
39+
Objects.requireNonNull(location, "Location to redirect to must be set");
40+
41+
Object rawPrefix = req.getAttribute(ROUTE_PREFIX_KEY);
42+
String prefix;
43+
if (rawPrefix instanceof List) {
44+
prefix = ((List<?>) rawPrefix).stream().map(String::valueOf).collect(Collectors.joining());
45+
} else {
46+
prefix = "";
47+
}
48+
49+
return prefix + location;
50+
}
51+
}
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
// Licensed to the Software Freedom Conservancy (SFC) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The SFC licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
package org.openqa.selenium.remote.http;
19+
20+
import org.junit.Test;
21+
22+
import java.util.List;
23+
import java.util.concurrent.atomic.AtomicReference;
24+
25+
import static org.assertj.core.api.Assertions.assertThat;
26+
import static org.assertj.core.api.Assertions.fail;
27+
import static org.openqa.selenium.remote.http.HttpMethod.GET;
28+
29+
public class PrefixedRouteTest {
30+
31+
@Test
32+
public void pathWithoutPrefixIsNotMatched() {
33+
Route route = Route.prefix("/cheese").to(Route.matching(req -> true).to(() -> req -> new HttpResponse()));
34+
35+
assertThat(route.matches(new HttpRequest(GET, "/cake"))).isFalse();
36+
}
37+
38+
@Test
39+
public void pathWithPrefixIsMatched() {
40+
Route route = Route.prefix("/cheese").to(Route.matching(req -> true).to(() -> req -> new HttpResponse()));
41+
42+
assertThat(route.matches(new HttpRequest(GET, "/cheese/cake"))).isTrue();
43+
}
44+
45+
@Test
46+
public void pathWhichCoincidentallyStartsWithThePrefixIsNotMacthed() {
47+
Route route = Route.prefix("/cheese").to(Route.matching(req -> true).to(() -> req -> new HttpResponse()));
48+
49+
assertThat(route.matches(new HttpRequest(GET, "/cheeseandpeas"))).isFalse();
50+
}
51+
52+
@Test
53+
public void pathWhichIsJustThePrefixMatches() {
54+
Route route = Route.prefix("/cheese").to(Route.matching(req -> true).to(() -> req -> new HttpResponse()));
55+
56+
assertThat(route.matches(new HttpRequest(GET, "/cheese"))).isTrue();
57+
}
58+
59+
@Test
60+
public void pathWhichIsJustThePrefixAndATrailingSlashMatches() {
61+
Route route = Route.prefix("/cheese").to(Route.matching(req -> true).to(() -> req -> new HttpResponse()));
62+
63+
assertThat(route.matches(new HttpRequest(GET, "/cheese/"))).isTrue();
64+
}
65+
66+
@Test
67+
public void pathWhichDoesMatchHasPrefixAsAttributeWhenHandling() {
68+
AtomicReference<String> path = new AtomicReference<>();
69+
AtomicReference<List<?>> parts = new AtomicReference<>();
70+
71+
Route route = Route.prefix("/cheese").to(Route.matching(req -> true)
72+
.to(() -> req -> {
73+
path.set(req.getUri());
74+
parts.set((List<?>) req.getAttribute(UrlPath.ROUTE_PREFIX_KEY));
75+
return new HttpResponse();
76+
}));
77+
78+
route.execute(new HttpRequest(GET, "/cheese/and/peas"));
79+
80+
assertThat(path.get()).isEqualTo("/and/peas");
81+
assertThat(parts.get()).isEqualTo(List.of("/cheese"));
82+
}
83+
84+
@Test
85+
public void nestingPrefixesAlsoCausesPathStoredInAttributeToBeExtended() {
86+
AtomicReference<String> path = new AtomicReference<>();
87+
AtomicReference<List<?>> parts = new AtomicReference<>();
88+
89+
Route route = Route.prefix("/cheese").to(
90+
Route.prefix("/and").to(
91+
Route.matching(req -> true)
92+
.to(() -> req -> {
93+
path.set(req.getUri());
94+
parts.set((List<?>) req.getAttribute(UrlPath.ROUTE_PREFIX_KEY));
95+
return new HttpResponse();
96+
})));
97+
98+
route.execute(new HttpRequest(GET, "/cheese/and/peas"));
99+
100+
assertThat(path.get()).isEqualTo("/peas");
101+
assertThat(parts.get()).isEqualTo(List.of("/cheese", "/and"));
102+
}
103+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// Licensed to the Software Freedom Conservancy (SFC) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The SFC licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
package org.openqa.selenium.remote.http;
19+
20+
import org.junit.Test;
21+
22+
import java.util.List;
23+
24+
import static org.assertj.core.api.Assertions.assertThat;
25+
import static org.openqa.selenium.remote.http.HttpMethod.GET;
26+
import static org.openqa.selenium.remote.http.UrlPath.ROUTE_PREFIX_KEY;
27+
28+
public class UrlPathTest {
29+
30+
@Test
31+
public void shouldAssumeARegularHttpRequestHasNoPrefix() {
32+
HttpRequest req = new HttpRequest(GET, "/cheese");
33+
34+
String absolute = UrlPath.relativeToServer(req, "/cake");
35+
assertThat(absolute).isEqualTo("/cake");
36+
37+
String relative = UrlPath.relativeToContext(req, "/cake");
38+
assertThat(relative).isEqualTo("/cake");
39+
}
40+
41+
@Test
42+
public void shouldRedirectARequestWithAPrefixAttribute() {
43+
HttpRequest req = new HttpRequest(GET, "/cake");
44+
req.setAttribute(ROUTE_PREFIX_KEY, List.of("/cheese"));
45+
46+
String absolute = UrlPath.relativeToServer(req, "/cake");
47+
assertThat(absolute).isEqualTo("/cake");
48+
49+
String relative = UrlPath.relativeToContext(req, "/cake");
50+
assertThat(relative).isEqualTo("/cheese/cake");
51+
}
52+
53+
}

0 commit comments

Comments
 (0)