Skip to content

Commit 7cae246

Browse files
committed
[grid] Ensuring response headers are spec compliant. There should be Content-Type and Cache-Control headers. Adding a filter that sets these headers in Router if the remote end did not set them.
1 parent ffa5af6 commit 7cae246

File tree

4 files changed

+75
-4
lines changed

4 files changed

+75
-4
lines changed

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

+3
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import org.openqa.selenium.grid.config.Config;
2323
import org.openqa.selenium.grid.web.CheckContentTypeHeader;
2424
import org.openqa.selenium.grid.web.CheckOriginHeader;
25+
import org.openqa.selenium.grid.web.EnsureSpecCompliantResponseHeaders;
2526
import org.openqa.selenium.internal.Require;
2627
import org.openqa.selenium.remote.http.Filter;
2728
import org.openqa.selenium.remote.http.HttpClient;
@@ -52,6 +53,8 @@ public Filter getSpecComplianceChecks() {
5253
// Base case: we do nothing
5354
Filter toReturn = httpHandler -> httpHandler;
5455

56+
toReturn = toReturn.andThen(new EnsureSpecCompliantResponseHeaders());
57+
5558
if (config.getBool(NETWORK_SECTION, "relax-checks").orElse(false)) {
5659
return toReturn;
5760
}

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

+3-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,9 @@ public EnsureSpecCompliantHeaders(Collection<String> allowedOriginHosts, Set<Str
3232
Require.nonNull("Allowed origins list", allowedOriginHosts);
3333
Require.nonNull("URLs to skip checks on", skipChecksOn);
3434

35-
filter = new CheckOriginHeader(allowedOriginHosts, skipChecksOn).andThen(new CheckContentTypeHeader(skipChecksOn));
35+
filter = new CheckOriginHeader(allowedOriginHosts, skipChecksOn)
36+
.andThen(new CheckContentTypeHeader(skipChecksOn))
37+
.andThen(new EnsureSpecCompliantResponseHeaders());
3638
}
3739

3840
@Override
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
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 static java.net.HttpURLConnection.HTTP_INTERNAL_ERROR;
21+
import static org.openqa.selenium.json.Json.JSON_UTF_8;
22+
23+
import com.google.common.collect.ImmutableMap;
24+
import com.google.common.collect.ImmutableSet;
25+
import com.google.common.net.MediaType;
26+
27+
import org.openqa.selenium.internal.Require;
28+
import org.openqa.selenium.remote.http.Contents;
29+
import org.openqa.selenium.remote.http.Filter;
30+
import org.openqa.selenium.remote.http.HttpHandler;
31+
import org.openqa.selenium.remote.http.HttpResponse;
32+
33+
import java.util.Set;
34+
35+
public class EnsureSpecCompliantResponseHeaders implements Filter {
36+
37+
@Override
38+
public HttpHandler apply(HttpHandler httpHandler) {
39+
Require.nonNull("Next handler", httpHandler);
40+
41+
return req -> {
42+
HttpResponse res = httpHandler.execute(req);
43+
if (res.getHeader("Content-Type") == null) {
44+
res.addHeader("Content-Type", JSON_UTF_8);
45+
}
46+
if (res.getHeader("Cache-Control") == null) {
47+
res.addHeader("Cache-Control", "no-cache");
48+
}
49+
return res;
50+
};
51+
}
52+
}

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

+17-3
Original file line numberDiff line numberDiff line change
@@ -105,21 +105,23 @@ public static Collection<Supplier<TestData>> buildGrids() {
105105
private TearDownFixture[] fixtures;
106106

107107
private HttpClient.Factory clientFactory;
108+
private HttpClient client;
108109

109110
@Before
110111
public void setFields() {
111112
TestData data = values.get();
112113
this.server = data.server;
113114
this.fixtures = data.fixtures;
114115
this.clientFactory = HttpClient.Factory.createDefault();
116+
this.client = clientFactory.createClient(server.getUrl());
115117
}
116118

117119
@After
118120
public void stopServers() {
121+
Safely.safelyCall(client::close);
119122
Safely.safelyCall(this.fixtures);
120123
}
121124

122-
123125
private static TestData createStandalone() {
124126
StringBuilder rawCaps = new StringBuilder();
125127
try (JsonOutput out = new Json().newOutput(rawCaps)) {
@@ -325,6 +327,8 @@ private static void waitUntilReady(Server<?> server, Duration duration, boolean
325327
return Boolean.TRUE.equals(status != null &&
326328
Boolean.parseBoolean(status.get("ready").toString()));
327329
});
330+
331+
Safely.safelyCall(client::close);
328332
}
329333

330334
private static void waitUntilReady(Server<?> server, Duration duration) {
@@ -414,7 +418,6 @@ public void shouldAllowPassthroughForW3CMode() {
414418
"capabilities", ImmutableMap.of(
415419
"alwaysMatch", ImmutableMap.of("browserName", "cheese")))));
416420

417-
HttpClient client = clientFactory.createClient(server.getUrl());
418421
HttpResponse response = client.execute(request);
419422

420423
assertEquals(200, response.getStatus());
@@ -452,7 +455,6 @@ public void shouldAllowPassthroughForJWPMode() {
452455
"desiredCapabilities", ImmutableMap.of(
453456
"browserName", "cheese"))));
454457

455-
HttpClient client = clientFactory.createClient(server.getUrl());
456458
HttpResponse response = client.execute(request);
457459

458460
assertEquals(200, response.getStatus());
@@ -479,6 +481,18 @@ public void shouldDoProtocolTranslationFromJWPLocalEndToW3CRemoteEnd() {
479481

480482
}
481483

484+
@Test
485+
public void responseShouldHaveContentTypeAndCacheControlHeaders() {
486+
HttpClient client = HttpClient.Factory.createDefault().createClient(server.getUrl());
487+
488+
HttpResponse response = client.execute(new HttpRequest(GET, "/status"));
489+
490+
assertThat(response.getHeader("Content-Type"))
491+
.isEqualTo("application/json; charset=utf-8");
492+
assertThat(response.getHeader("Cache-Control"))
493+
.isEqualTo("no-cache");
494+
}
495+
482496
private static class TestData {
483497
public final Server<?> server;
484498
public final TearDownFixture[] fixtures;

0 commit comments

Comments
 (0)