Skip to content

Commit fd5149c

Browse files
committed
Amend ResourceHandler to properly handle static content
1 parent 86b66ac commit fd5149c

File tree

3 files changed

+148
-12
lines changed

3 files changed

+148
-12
lines changed

java/server/src/org/openqa/selenium/grid/web/PathResource.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,11 @@ public String name() {
4242

4343
@Override
4444
public Optional<Resource> get(String path) {
45+
// Paths are expected to start with a leading slash. Strip it, if present
46+
if (path.startsWith("/")) {
47+
path = path.length() == 1 ? "" : path.substring(1);
48+
}
49+
4550
Path normalized = base.resolve(path).normalize();
4651
if (!normalized.startsWith(base)) {
4752
throw new RuntimeException("Attempt to navigate away from the parent directory");

java/server/src/org/openqa/selenium/grid/web/ResourceHandler.java

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import org.openqa.selenium.remote.http.HttpRequest;
2323
import org.openqa.selenium.remote.http.HttpResponse;
2424
import org.openqa.selenium.remote.http.Routable;
25+
import org.openqa.selenium.remote.http.UrlPath;
2526

2627
import java.io.UncheckedIOException;
2728
import java.util.Objects;
@@ -40,6 +41,7 @@
4041
import static com.google.common.net.MediaType.WOFF;
4142
import static com.google.common.net.MediaType.XHTML_UTF_8;
4243
import static com.google.common.net.MediaType.XML_UTF_8;
44+
import static java.net.HttpURLConnection.HTTP_MOVED_TEMP;
4345
import static java.net.HttpURLConnection.HTTP_NOT_FOUND;
4446
import static java.nio.charset.StandardCharsets.UTF_8;
4547
import static org.openqa.selenium.remote.http.HttpMethod.GET;
@@ -70,29 +72,46 @@ public HttpResponse execute(HttpRequest req) throws UncheckedIOException {
7072
Resource resource = result.get();
7173

7274
if (resource.isDirectory()) {
73-
String links = resource.list().stream()
74-
.map(res -> String.format("<a href=\"%s\">%s</a>", res.name(), res.name()))
75-
.sorted()
76-
.collect(Collectors.joining("\n"));
77-
78-
String html = String.format(
79-
"<html><title>Listing of %s</title><body><h1>%s</h1>%s",
80-
resource.name(),
81-
resource.name(),
82-
resource.name());
75+
return readDirectory(req, resource);
76+
}
77+
return readFile(req, resource);
78+
}
8379

80+
private HttpResponse readDirectory(HttpRequest req, Resource resource) {
81+
if (!req.getUri().endsWith("/")) {
82+
String dest = UrlPath.relativeToContext(req, req.getUri() + "/");
8483
return new HttpResponse()
85-
.addHeader("Content-Type", HTML_UTF_8.toString())
86-
.setContent(Contents.string(html, UTF_8));
84+
.setStatus(HTTP_MOVED_TEMP)
85+
.addHeader("Location", dest);
8786
}
8887

88+
String links = resource.list().stream()
89+
.map(res -> String.format("<p><a href=\"%s\">%s</a>", res.name(), res.name()))
90+
.sorted()
91+
.collect(Collectors.joining("\n"));
92+
93+
String html = String.format(
94+
"<html><title>Listing of %s</title><body><h1>%s></h1>%s",
95+
resource.name(),
96+
resource.name(),
97+
links);
98+
99+
return new HttpResponse()
100+
.addHeader("Content-Type", HTML_UTF_8.toString())
101+
.setContent(Contents.string(html, UTF_8));
102+
}
103+
104+
private HttpResponse readFile(HttpRequest req, Resource resource) {
89105
Optional<byte[]> bytes = resource.read();
90106
if (bytes.isPresent()) {
91107
return new HttpResponse()
92108
.addHeader("Content-Type", mediaType(req.getUri()))
93109
.setContent(Contents.bytes(bytes.get()));
94110
}
111+
return get404(req);
112+
}
95113

114+
private HttpResponse get404(HttpRequest req) {
96115
return new HttpResponse()
97116
.setStatus(HTTP_NOT_FOUND)
98117
.setContent(Contents.string("Unable to read " + req.getUri(), UTF_8));
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
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.grid.web;
19+
20+
import org.junit.Before;
21+
import org.junit.Rule;
22+
import org.junit.Test;
23+
import org.junit.rules.TemporaryFolder;
24+
import org.openqa.selenium.remote.http.Contents;
25+
import org.openqa.selenium.remote.http.HttpHandler;
26+
import org.openqa.selenium.remote.http.HttpRequest;
27+
import org.openqa.selenium.remote.http.HttpResponse;
28+
import org.openqa.selenium.remote.http.Route;
29+
30+
import java.io.File;
31+
import java.io.IOException;
32+
import java.nio.file.Files;
33+
import java.nio.file.Path;
34+
35+
import static java.net.HttpURLConnection.HTTP_MOVED_TEMP;
36+
import static java.net.HttpURLConnection.HTTP_OK;
37+
import static java.nio.charset.StandardCharsets.UTF_8;
38+
import static org.assertj.core.api.Assertions.assertThat;
39+
import static org.openqa.selenium.remote.http.HttpMethod.GET;
40+
41+
public class ResourceHandlerTest {
42+
43+
@Rule
44+
public TemporaryFolder temp = new TemporaryFolder();
45+
private Path base;
46+
47+
@Before
48+
public void getPath() throws IOException {
49+
File folder = temp.newFolder();
50+
this.base = folder.toPath();
51+
}
52+
53+
@Test
54+
public void shouldLoadContent() throws IOException {
55+
Files.write(base.resolve("content.txt"), "I like cheese".getBytes(UTF_8));
56+
57+
HttpHandler handler = new ResourceHandler(new PathResource(base));
58+
HttpResponse res = handler.execute(new HttpRequest(GET, "/content.txt"));
59+
60+
assertThat(Contents.string(res)).isEqualTo("I like cheese");
61+
}
62+
63+
@Test
64+
public void shouldRedirectIfDirectoryButPathDoesNotEndInASlash() throws IOException {
65+
Path dir = base.resolve("cheese");
66+
67+
Files.createDirectories(dir);
68+
69+
HttpHandler handler = new ResourceHandler(new PathResource(base));
70+
HttpResponse res = handler.execute(new HttpRequest(GET, "/cheese"));
71+
72+
assertThat(res.getStatus()).isEqualTo(HTTP_MOVED_TEMP);
73+
assertThat(res.getHeader("Location")).endsWith("/cheese/");
74+
}
75+
76+
@Test
77+
public void shouldLoadAnIndexPage() throws IOException {
78+
Path subdir = base.resolve("subdir");
79+
Files.createDirectories(subdir);
80+
81+
Files.write(subdir.resolve("1.txt"), new byte[0]);
82+
Files.write(subdir.resolve("2.txt"), new byte[0]);
83+
84+
HttpHandler handler = new ResourceHandler(new PathResource(base));
85+
HttpResponse res = handler.execute(new HttpRequest(GET, "/subdir/"));
86+
87+
String text = Contents.string(res);
88+
assertThat(text).contains("1.txt");
89+
assertThat(text).contains("2.txt");
90+
}
91+
92+
@Test
93+
public void canBeNestedWithinARoute() throws IOException {
94+
Path contents = base.resolve("cheese").resolve("cake.txt");
95+
96+
Files.createDirectories(contents.getParent());
97+
Files.write(contents, "delicious".getBytes(UTF_8));
98+
99+
HttpHandler handler = Route.prefix("/peas").to(Route.combine(new ResourceHandler(new PathResource(base))));
100+
101+
// Check redirect works as expected
102+
HttpResponse res = handler.execute(new HttpRequest(GET, "/peas/cheese"));
103+
assertThat(res.getStatus()).isEqualTo(HTTP_MOVED_TEMP);
104+
assertThat(res.getHeader("Location")).isEqualTo("/peas/cheese/");
105+
106+
// And now that content can be read
107+
res = handler.execute(new HttpRequest(GET, "/peas/cheese/cake.txt"));
108+
assertThat(res.getStatus()).isEqualTo(HTTP_OK);
109+
assertThat(Contents.string(res)).isEqualTo("delicious");
110+
}
111+
112+
}

0 commit comments

Comments
 (0)