Skip to content

Commit d929ad1

Browse files
committed
Switch to an HttpHandler for cookie tests
1 parent 1f51ba8 commit d929ad1

File tree

2 files changed

+184
-1
lines changed

2 files changed

+184
-1
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
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.environment.webserver;
19+
20+
import com.google.common.base.Splitter;
21+
import org.openqa.selenium.Cookie;
22+
import org.openqa.selenium.remote.http.Contents;
23+
import org.openqa.selenium.remote.http.HttpHandler;
24+
import org.openqa.selenium.remote.http.HttpRequest;
25+
import org.openqa.selenium.remote.http.HttpResponse;
26+
27+
import java.io.UncheckedIOException;
28+
import java.time.Instant;
29+
import java.time.LocalDateTime;
30+
import java.time.ZoneId;
31+
import java.time.ZoneOffset;
32+
import java.time.ZonedDateTime;
33+
import java.time.temporal.TemporalAccessor;
34+
import java.util.Collection;
35+
import java.util.Date;
36+
import java.util.List;
37+
import java.util.function.Function;
38+
import java.util.stream.Collectors;
39+
import java.util.stream.StreamSupport;
40+
41+
import static java.nio.charset.StandardCharsets.UTF_8;
42+
import static java.time.format.DateTimeFormatter.RFC_1123_DATE_TIME;
43+
44+
public class CookieHandler implements HttpHandler {
45+
46+
private static final String EPOCH_START = RFC_1123_DATE_TIME.format(
47+
ZonedDateTime.ofInstant(Instant.ofEpochMilli(0), ZoneId.of("UTC")));
48+
private static final String RESPONSE_STRING =
49+
"<html><head><title>Done</title></head><body>%s : %s</body></html>";
50+
51+
@Override
52+
public HttpResponse execute(HttpRequest request) throws UncheckedIOException {
53+
HttpResponse response = new HttpResponse();
54+
response.setHeader("Content-Type", "text/html");
55+
//Dont Cache Anything at the browser
56+
response.setHeader("Cache-Control","no-cache");
57+
response.setHeader("Pragma","no-cache");
58+
response.setHeader ("Expires", EPOCH_START);
59+
60+
61+
String action = request.getQueryParameter("action");
62+
63+
if ("add".equals(action)) {
64+
String name = request.getQueryParameter("name");
65+
66+
StringBuilder cookie = new StringBuilder();
67+
cookie.append(name)
68+
.append("=")
69+
.append(request.getQueryParameter("value"))
70+
.append("; ");
71+
72+
append(cookie, request.getQueryParameter("domain"), str -> "Domain=" + str);
73+
append(cookie, request.getQueryParameter("path"), str -> "Path=" + str);
74+
append(cookie, request.getQueryParameter("expiry"), str -> "Max-Age=" + Integer.parseInt(str));
75+
append(cookie, request.getQueryParameter( "secure"), str -> "Secure");
76+
append(cookie, request.getQueryParameter( "httpOnly"), str -> "HttpOnly");
77+
78+
response.addHeader("Set-Cookie", cookie.toString());
79+
80+
response.setContent(Contents.string(String.format(RESPONSE_STRING, "Cookie added", name), UTF_8));
81+
} else if ("delete".equals(action)) {
82+
String name = request.getQueryParameter("name");
83+
for (Cookie cookie : getCookies(request)) {
84+
if (!cookie.getName().equals(name)) {
85+
addCookie(response, new Cookie.Builder(name, "").path("/").expiresOn(new Date(0)).build());
86+
}
87+
}
88+
response.setContent(Contents.string(String.format(RESPONSE_STRING, "Cookie deleted", name), UTF_8));
89+
} else if ("deleteAll".equals(action)) {
90+
for (Cookie cookie : getCookies(request)) {
91+
addCookie(response, new Cookie.Builder(cookie.getName(), "").path("/").expiresOn(new Date(0)).build());
92+
}
93+
response.setContent(Contents.string(String.format(RESPONSE_STRING, "All cookies deleted", ""), UTF_8));
94+
} else {
95+
response.setContent(Contents.string(String.format(RESPONSE_STRING, "Unrecognized action", action), UTF_8));
96+
}
97+
98+
return response;
99+
}
100+
101+
private <X> void append(StringBuilder builder, X fromCookie, Function<X, String> value) {
102+
if (fromCookie == null) {
103+
return;
104+
}
105+
106+
builder.append(value.apply(fromCookie)).append("; ");
107+
}
108+
109+
private Collection<Cookie> getCookies(HttpRequest request) {
110+
return StreamSupport.stream(request.getHeaders("Cookie").spliterator(), false)
111+
.map(this::parse)
112+
.collect(Collectors.toList());
113+
}
114+
115+
private void addCookie(HttpResponse response, Cookie cook) {
116+
StringBuilder cookie = new StringBuilder();
117+
118+
// TODO: escape string as necessary
119+
String name = cook.getName();
120+
cookie.append(name)
121+
.append("=")
122+
.append(cook.getValue())
123+
.append("; ");
124+
125+
append(cookie, cook.getDomain(), str -> "Domain=" + str);
126+
append(cookie, cook.getPath(), str -> "Path=" + str);
127+
append(cookie, cook.getExpiry(), date -> {
128+
ZonedDateTime zonedDateTime = ZonedDateTime.ofInstant(date.toInstant(), ZoneId.of("UTC"));
129+
return "Expiry=" + RFC_1123_DATE_TIME.format(zonedDateTime);
130+
});
131+
append(cookie, cook.isSecure(), val -> "Secure");
132+
append(cookie, cook.isHttpOnly(), val -> "HttpOnly");
133+
134+
response.addHeader("Set-Cookie", cookie.toString());
135+
}
136+
137+
private Cookie parse(String cookieString) {
138+
String[] split = cookieString.split("=", 2);
139+
140+
if (split.length < 2) {
141+
throw new IllegalStateException("Illegal cookie: " + cookieString);
142+
}
143+
144+
int index = split[1].indexOf(";");
145+
if (index == -1) {
146+
return new Cookie(split[0], split[1]);
147+
}
148+
149+
List<String> keysAndValues = Splitter.on(";").trimResults().omitEmptyStrings().splitToList(split[1]);
150+
Cookie.Builder builder = new Cookie.Builder(split[0], keysAndValues.get(0));
151+
152+
keysAndValues.stream()
153+
.skip(1)
154+
.forEach(keyAndValue -> {
155+
List<String> parts = Splitter.on("=").limit(2).trimResults().omitEmptyStrings().splitToList(keyAndValue);
156+
String key = parts.get(0).toLowerCase();
157+
switch (key) {
158+
case "domain":
159+
builder.domain(parts.get(1));
160+
break;
161+
162+
case "expires":
163+
TemporalAccessor temporalAccessor = RFC_1123_DATE_TIME.parse(parts.get(1));
164+
builder.expiresOn(Date.from(Instant.from(temporalAccessor)));
165+
break;
166+
167+
case "httponly":
168+
builder.isHttpOnly(true);
169+
break;
170+
171+
case "path":
172+
builder.path(parts.get(1));
173+
break;
174+
175+
case "secure":
176+
builder.isSecure(true);
177+
break;
178+
}
179+
});
180+
181+
return builder.build();
182+
}
183+
}

java/client/test/org/openqa/selenium/environment/webserver/JettyAppServer.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -146,14 +146,14 @@ public JettyAppServer(String hostName, int httpPort, int httpsPort) {
146146
// Serves every file under DEFAULT_CONTEXT_PATH/utf8 as UTF-8 to the browser
147147
addServlet(defaultContext, "/utf8/*", Utf8Servlet.class);
148148

149-
addServlet(defaultContext, "/cookie", CookieServlet.class);
150149
addServlet(defaultContext, "/quitquitquit", KillSwitchServlet.class);
151150
addServlet(defaultContext, "/generated/*", GeneratedJsTestServlet.class);
152151
addServlet(defaultContext, "/createPage", CreatePageServlet.class);
153152

154153
// Default route
155154
Route route = Route.combine(
156155
Route.get("/basicAuth").to(BasicAuthHandler::new),
156+
Route.get("/cookie").to(CookieHandler::new),
157157
Route.get("/encoding").to(EncodingHandler::new),
158158
Route.get("/redirect").to(RedirectHandler::new),
159159
Route.get("/sleep").to(SleepingHandler::new),

0 commit comments

Comments
 (0)