Skip to content

Commit 1ebf322

Browse files
committed
Support sameSite attribute when constructing a cookie
Cookie toString() includes sameSite attribute if set
1 parent f0d8cd1 commit 1ebf322

File tree

5 files changed

+188
-76
lines changed

5 files changed

+188
-76
lines changed

src/changes/changes.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,12 @@
99
<body>
1010
<release version="2.67.0" date="xxxx, 2022" description="Chrome/Edge 107, Firefox 107, Bugfixes">
1111
<action type="update" dev="rbri">
12+
Support sameSite attribute when constructing a cookie.
13+
</action>
14+
<action type="update" dev="rbri">
15+
Cookie toString() includes sameSite attribute if set.
16+
</action>
17+
<action type="fix" dev="rbri">
1218
Treat localhost as secure origin when processing cookies.
1319
</action>
1420
<action type="update" dev="rbri">

src/main/java/com/gargoylesoftware/htmlunit/util/Cookie.java

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ public Cookie(final String domain, final String name, final String value) {
6060
*/
6161
public Cookie(final String domain, final String name, final String value, final String path, final Date expires,
6262
final boolean secure) {
63-
this(domain, name, value, path, expires, secure, false);
63+
this(domain, name, value, path, expires, secure, false, null);
6464
}
6565

6666
/**
@@ -76,6 +76,23 @@ public Cookie(final String domain, final String name, final String value, final
7676
*/
7777
public Cookie(final String domain, final String name, final String value, final String path, final Date expires,
7878
final boolean secure, final boolean httpOnly) {
79+
this(domain, name, value, path, expires, secure, httpOnly, null);
80+
}
81+
82+
/**
83+
* Creates a new cookie with the specified name and value which applies to the specified domain,
84+
* the specified path, and expires on the specified date.
85+
* @param domain the domain to which this cookie applies
86+
* @param name the cookie name
87+
* @param value the cookie name
88+
* @param path the path to which this cookie applies
89+
* @param expires the date on which this cookie expires
90+
* @param secure whether or not this cookie is secure (i.e. HTTPS vs HTTP)
91+
* @param httpOnly whether or not this cookie should be only used for HTTP(S) headers
92+
* @param sameSite the sameSite attribute
93+
*/
94+
public Cookie(final String domain, final String name, final String value, final String path, final Date expires,
95+
final boolean secure, final boolean httpOnly, final String sameSite) {
7996
if (domain == null) {
8097
throw new IllegalArgumentException("Cookie domain must be specified");
8198
}
@@ -87,12 +104,18 @@ public Cookie(final String domain, final String name, final String value, final
87104
cookie.setAttribute(ClientCookie.DOMAIN_ATTR, domain);
88105

89106
cookie.setPath(path);
90-
cookie.setExpiryDate(expires);
107+
if (expires != null) {
108+
cookie.setExpiryDate(expires);
109+
}
91110
cookie.setSecure(secure);
92111
if (httpOnly) {
93112
cookie.setAttribute("httponly", "true");
94113
}
95114

115+
if (sameSite != null) {
116+
cookie.setAttribute("samesite", sameSite);
117+
}
118+
96119
httpClientCookie_ = cookie;
97120
}
98121

@@ -117,20 +140,19 @@ public Cookie(final ClientCookie clientCookie) {
117140
*/
118141
public Cookie(final String domain, final String name, final String value, final String path, final int maxAge,
119142
final boolean secure) {
143+
this(domain, name, value, path, convertToExpiryDate(maxAge), secure);
144+
}
120145

121-
final BasicClientCookie cookie = new BasicClientCookie(name, value == null ? "" : value);
122-
cookie.setDomain(domain);
123-
cookie.setPath(path);
124-
cookie.setSecure(secure);
125-
146+
private static Date convertToExpiryDate(final int maxAge) {
126147
if (maxAge < -1) {
127148
throw new IllegalArgumentException("invalid max age: " + maxAge);
128149
}
150+
129151
if (maxAge >= 0) {
130-
cookie.setExpiryDate(new Date(System.currentTimeMillis() + (maxAge * 1000L)));
152+
return new Date(System.currentTimeMillis() + (maxAge * 1000L));
131153
}
132154

133-
httpClientCookie_ = cookie;
155+
return null;
134156
}
135157

136158
/**
@@ -207,7 +229,8 @@ public String toString() {
207229
+ (getPath() == null ? "" : ";path=" + getPath())
208230
+ (getExpires() == null ? "" : ";expires=" + getExpires())
209231
+ (isSecure() ? ";secure" : "")
210-
+ (isHttpOnly() ? ";httpOnly" : "");
232+
+ (isHttpOnly() ? ";httpOnly" : "")
233+
+ (getSameSite() == null ? "" : ";sameSite=" + getSameSite());
211234
}
212235

213236
/**

src/test/java/com/gargoylesoftware/htmlunit/CookieManager2Test.java

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ public void resettingCookie() throws Exception {
9191
* @throws Exception if the test fails
9292
*/
9393
@Test
94-
@Alerts("my_key=")
94+
@Alerts("my_key=§")
9595
public void cookie_nullValue() throws Exception {
9696
final WebClient webClient = getWebClient();
9797
final MockWebConnection webConnection = new MockWebConnection();
@@ -106,15 +106,15 @@ public void cookie_nullValue() throws Exception {
106106
final List<String> collectedAlerts = new ArrayList<>();
107107
webClient.setAlertHandler(new CollectingAlertHandler(collectedAlerts));
108108

109-
webClient.getPage(URL_FIRST);
110-
assertEquals(getExpectedAlerts(), collectedAlerts);
109+
final HtmlPage page = webClient.getPage(URL_FIRST);
110+
assertEquals(getExpectedAlerts()[0], page.getTitleText());
111111
}
112112

113113
/**
114114
* @throws Exception if the test fails
115115
*/
116116
@Test
117-
@Alerts("my_name=my_data")
117+
@Alerts("my_name=my_data§")
118118
public void cookie_maxAgeMinusOne() throws Exception {
119119
final WebClient webClient = getWebClient();
120120
final MockWebConnection webConnection = new MockWebConnection();
@@ -126,11 +126,8 @@ public void cookie_maxAgeMinusOne() throws Exception {
126126
final CookieManager mgr = webClient.getCookieManager();
127127
mgr.addCookie(new Cookie(URL_FIRST.getHost(), "my_name", "my_data", "/", -1, false));
128128

129-
final List<String> collectedAlerts = new ArrayList<>();
130-
webClient.setAlertHandler(new CollectingAlertHandler(collectedAlerts));
131-
132-
webClient.getPage(URL_FIRST);
133-
assertEquals(getExpectedAlerts(), collectedAlerts);
129+
final HtmlPage page = webClient.getPage(URL_FIRST);
130+
assertEquals(getExpectedAlerts()[0], page.getTitleText());
134131
}
135132

136133
/**

src/test/java/com/gargoylesoftware/htmlunit/CookieManager4Test.java

Lines changed: 40 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,8 @@ public void domain() throws Exception {
146146
MimeType.TEXT_HTML, responseHeader);
147147

148148
final URL firstUrl = new URL(URL_HOST1);
149-
loadPageWithAlerts2(firstUrl);
149+
loadPage2(firstUrl, StandardCharsets.ISO_8859_1);
150+
verifyTitle2(getWebDriver(), getExpectedAlerts());
150151
}
151152

152153
/**
@@ -188,7 +189,7 @@ public void storedDomain1() throws Exception {
188189
MimeType.TEXT_HTML, responseHeader);
189190

190191
final WebDriver driver = loadPage2(new URL(URL_HOST1), StandardCharsets.ISO_8859_1);
191-
verifyAlerts(driver, getExpectedAlerts()[0]);
192+
verifyTitle2(driver, getExpectedAlerts()[0]);
192193

193194
assertEquals(getExpectedAlerts()[1], driver.manage().getCookieNamed("c1").toString());
194195
assertEquals(getExpectedAlerts()[2], driver.manage().getCookieNamed("c2").toString());
@@ -227,7 +228,7 @@ public void storedDomain2() throws Exception {
227228
MimeType.TEXT_HTML, responseHeader);
228229

229230
final WebDriver driver = loadPage2(new URL(URL_HOST2), StandardCharsets.ISO_8859_1);
230-
verifyAlerts(driver, getExpectedAlerts()[0]);
231+
verifyTitle2(driver, getExpectedAlerts()[0]);
231232

232233
assertEquals(getExpectedAlerts()[1], driver.manage().getCookieNamed("c1").toString());
233234
assertEquals(getExpectedAlerts()[2], driver.manage().getCookieNamed("c2").toString());
@@ -264,7 +265,7 @@ public void storedDomain3() throws Exception {
264265
MimeType.TEXT_HTML, responseHeader);
265266

266267
final WebDriver driver = loadPage2(new URL(URL_HOST3), StandardCharsets.ISO_8859_1);
267-
verifyAlerts(driver, getExpectedAlerts()[0]);
268+
verifyTitle2(driver, getExpectedAlerts()[0]);
268269

269270
assertEquals(getExpectedAlerts()[1], driver.manage().getCookieNamed("c1").toString());
270271
assertEquals(getExpectedAlerts()[2], driver.manage().getCookieNamed("c2").toString());
@@ -303,7 +304,7 @@ public void storedDomain4() throws Exception {
303304
MimeType.TEXT_HTML, responseHeader);
304305

305306
final WebDriver driver = loadPage2(new URL(URL_HOST4), StandardCharsets.ISO_8859_1);
306-
verifyAlerts(driver, getExpectedAlerts()[0]);
307+
verifyTitle2(driver, getExpectedAlerts()[0]);
307308

308309
assertEquals(getExpectedAlerts()[1], driver.manage().getCookieNamed("c12").toString());
309310
if (driver.manage().getCookieNamed("c11") != null) {
@@ -349,7 +350,7 @@ public void storedDomainFromJs1() throws Exception {
349350
+ "</html>";
350351

351352
final WebDriver driver = loadPage2(html, new URL(URL_HOST1));
352-
verifyAlerts(driver);
353+
verifyTitle2(driver);
353354

354355
assertEquals(4, driver.manage().getCookies().size());
355356
assertEquals(getExpectedAlerts()[0], driver.manage().getCookieNamed("c1").toString());
@@ -391,7 +392,7 @@ public void storedDomainFromJs2() throws Exception {
391392
+ "</html>";
392393

393394
final WebDriver driver = loadPage2(html, new URL(URL_HOST2));
394-
verifyAlerts(driver);
395+
verifyTitle2(driver);
395396

396397
assertEquals(2, driver.manage().getCookies().size());
397398
assertEquals(getExpectedAlerts()[0], driver.manage().getCookieNamed("c1").toString());
@@ -431,7 +432,7 @@ public void storedDomainFromJs3() throws Exception {
431432
+ "</html>";
432433

433434
final WebDriver driver = loadPage2(html, new URL(URL_HOST3));
434-
verifyAlerts(driver);
435+
verifyTitle2(driver);
435436

436437
assertEquals(2, driver.manage().getCookies().size());
437438
assertEquals(getExpectedAlerts()[0], driver.manage().getCookieNamed("c1").toString());
@@ -475,7 +476,7 @@ public void storedDomainFromJs4() throws Exception {
475476
+ "</html>";
476477

477478
final WebDriver driver = loadPage2(html, new URL(URL_HOST4));
478-
verifyAlerts(driver);
479+
verifyTitle2(driver);
479480

480481
final String[] expected = getExpectedAlerts();
481482
assertEquals(Integer.parseInt(expected[0]), driver.manage().getCookies().size());
@@ -499,7 +500,8 @@ public void domainDuplicate() throws Exception {
499500

500501
final URL firstUrl = new URL(URL_HOST1);
501502

502-
loadPageWithAlerts2(firstUrl);
503+
loadPage2(firstUrl, StandardCharsets.ISO_8859_1);
504+
verifyTitle2(getWebDriver(), getExpectedAlerts());
503505
}
504506

505507
/**
@@ -516,7 +518,8 @@ public void domainDuplicateLeadingDot() throws Exception {
516518

517519
final URL firstUrl = new URL(URL_HOST1);
518520

519-
loadPageWithAlerts2(firstUrl);
521+
loadPage2(firstUrl, StandardCharsets.ISO_8859_1);
522+
verifyTitle2(getWebDriver(), getExpectedAlerts());
520523
}
521524

522525
/**
@@ -604,7 +607,8 @@ public void domain2() throws Exception {
604607
final URL firstUrl = new URL(URL_HOST1);
605608
getMockWebConnection().setResponse(firstUrl, html, 200, "Ok", MimeType.TEXT_HTML, responseHeader1);
606609

607-
loadPageWithAlerts2(firstUrl);
610+
loadPage2(firstUrl, StandardCharsets.ISO_8859_1);
611+
verifyTitle2(getWebDriver(), getExpectedAlerts());
608612
}
609613

610614
/**
@@ -634,7 +638,8 @@ public void domain3() throws Exception {
634638
final URL firstUrl = new URL(URL_HOST3);
635639
getMockWebConnection().setResponse(firstUrl, html, 200, "Ok", MimeType.TEXT_HTML, responseHeader1);
636640

637-
loadPageWithAlerts2(firstUrl);
641+
loadPage2(firstUrl, StandardCharsets.ISO_8859_1);
642+
verifyTitle2(getWebDriver(), getExpectedAlerts());
638643
}
639644

640645
/**
@@ -660,7 +665,8 @@ public void differentHostsSameDomain() throws Exception {
660665
final URL firstUrl = new URL(URL_HOST1);
661666
getMockWebConnection().setResponse(firstUrl, html, 200, "Ok", MimeType.TEXT_HTML, responseHeader1);
662667

663-
loadPageWithAlerts2(firstUrl);
668+
loadPage2(firstUrl, StandardCharsets.ISO_8859_1);
669+
verifyTitle2(getWebDriver(), getExpectedAlerts());
664670
}
665671

666672
/**
@@ -685,7 +691,8 @@ public void differentHostsSameDomainCookieFromJS() throws Exception {
685691
final URL firstUrl = new URL(URL_HOST1);
686692
getMockWebConnection().setResponse(firstUrl, html);
687693

688-
loadPageWithAlerts2(firstUrl);
694+
loadPage2(firstUrl, StandardCharsets.ISO_8859_1);
695+
verifyTitle2(getWebDriver(), getExpectedAlerts());
689696
}
690697

691698
/**
@@ -710,7 +717,8 @@ public void differentHostsSameDomainCookieFromMeta() throws Exception {
710717
final URL firstUrl = new URL(URL_HOST1);
711718
getMockWebConnection().setResponse(firstUrl, html);
712719

713-
loadPageWithAlerts2(firstUrl);
720+
loadPage2(firstUrl, StandardCharsets.ISO_8859_1);
721+
verifyTitle2(getWebDriver(), getExpectedAlerts());
714722
}
715723

716724
private void testCookies(final URL url, final String cookie1, final String cookie2) throws Exception {
@@ -720,7 +728,8 @@ private void testCookies(final URL url, final String cookie1, final String cooki
720728
getMockWebConnection().setDefaultResponse(CookieManagerTest.HTML_ALERT_COOKIE, 200, "OK", MimeType.TEXT_HTML,
721729
responseHeader);
722730

723-
loadPageWithAlerts2(url);
731+
loadPage2(url, StandardCharsets.ISO_8859_1);
732+
verifyTitle2(getWebDriver(), getExpectedAlerts());
724733
}
725734

726735
/**
@@ -761,7 +770,8 @@ public void sameSite() throws Exception {
761770
getMockWebConnection().setResponse(new URL(URL_HOST1), HTML_ALERT_COOKIE,
762771
200, "OK", MimeType.TEXT_HTML, responseHeader);
763772

764-
final WebDriver driver = loadPageWithAlerts2(new URL(URL_HOST1));
773+
final WebDriver driver = loadPage2(new URL(URL_HOST1), StandardCharsets.ISO_8859_1);
774+
verifyTitle2(driver, getExpectedAlerts());
765775
driver.get(URL_HOST1 + "foo");
766776

767777
final Map<String, String> lastHeaders = getMockWebConnection().getLastAdditionalHeaders();
@@ -810,7 +820,8 @@ public void sameSiteOtherSubdomain() throws Exception {
810820
getMockWebConnection().setResponse(new URL(URL_HOST1), HTML_ALERT_COOKIE,
811821
200, "OK", MimeType.TEXT_HTML, responseHeader);
812822

813-
final WebDriver driver = loadPageWithAlerts2(new URL(URL_HOST1));
823+
final WebDriver driver = loadPage2(new URL(URL_HOST1), StandardCharsets.ISO_8859_1);
824+
verifyTitle2(driver, getExpectedAlerts());
814825
driver.get(URL_HOST2 + "foo");
815826

816827
final Map<String, String> lastHeaders = getMockWebConnection().getLastAdditionalHeaders();
@@ -839,7 +850,8 @@ public void sameSiteOriginOtherSubdomain() throws Exception {
839850
getMockWebConnection().setResponse(new URL(URL_HOST1), HTML_ALERT_COOKIE,
840851
200, "OK", MimeType.TEXT_HTML, responseHeader);
841852

842-
final WebDriver driver = loadPageWithAlerts2(new URL(URL_HOST1));
853+
final WebDriver driver = loadPage2(new URL(URL_HOST1), StandardCharsets.ISO_8859_1);
854+
verifyTitle2(driver, getExpectedAlerts());
843855
driver.get(URL_HOST2 + "foo");
844856
driver.get(URL_HOST1 + "foo");
845857

@@ -893,7 +905,8 @@ public void sameSiteIncludeFromSameDomain() throws Exception {
893905
getMockWebConnection().setResponse(new URL(URL_HOST1 + "include"), html,
894906
200, "OK", MimeType.TEXT_HTML, responseHeader);
895907

896-
final WebDriver driver = loadPageWithAlerts2(new URL(URL_HOST1));
908+
final WebDriver driver = loadPage2(new URL(URL_HOST1), StandardCharsets.ISO_8859_1);
909+
verifyTitle2(driver, getExpectedAlerts());
897910
driver.get(URL_HOST1 + "include");
898911

899912
assertEquals(URL_HOST1 + "css/style.css", getMockWebConnection().getLastWebRequest().getUrl());
@@ -947,7 +960,8 @@ public void sameSiteIncludeFromSubDomain() throws Exception {
947960
getMockWebConnection().setResponse(new URL(URL_HOST2), html,
948961
200, "OK", MimeType.TEXT_HTML, responseHeader);
949962

950-
final WebDriver driver = loadPageWithAlerts2(new URL(URL_HOST1));
963+
final WebDriver driver = loadPage2(new URL(URL_HOST1), StandardCharsets.ISO_8859_1);
964+
verifyTitle2(driver, getExpectedAlerts());
951965
driver.get(URL_HOST2);
952966

953967
assertEquals(URL_HOST1 + "css/style.css", getMockWebConnection().getLastWebRequest().getUrl());
@@ -1001,7 +1015,8 @@ public void sameSiteIFrameFromSameDomain() throws Exception {
10011015
getMockWebConnection().setResponse(new URL(URL_HOST1 + "include"), html,
10021016
200, "OK", MimeType.TEXT_HTML, responseHeader);
10031017

1004-
final WebDriver driver = loadPageWithAlerts2(new URL(URL_HOST1));
1018+
final WebDriver driver = loadPage2(new URL(URL_HOST1), StandardCharsets.ISO_8859_1);
1019+
verifyTitle2(driver, getExpectedAlerts());
10051020
driver.get(URL_HOST1 + "include");
10061021

10071022
assertEquals(URL_HOST1 + "iframe.html", getMockWebConnection().getLastWebRequest().getUrl());
@@ -1055,7 +1070,8 @@ public void sameSiteIFrameFromSubDomain() throws Exception {
10551070
getMockWebConnection().setResponse(new URL(URL_HOST2 + "include"), html,
10561071
200, "OK", MimeType.TEXT_HTML, responseHeader);
10571072

1058-
final WebDriver driver = loadPageWithAlerts2(new URL(URL_HOST1));
1073+
final WebDriver driver = loadPage2(new URL(URL_HOST1), StandardCharsets.ISO_8859_1);
1074+
verifyTitle2(driver, getExpectedAlerts());
10591075
driver.get(URL_HOST2 + "include");
10601076

10611077
assertEquals(URL_HOST1 + "iframe.html", getMockWebConnection().getLastWebRequest().getUrl());

0 commit comments

Comments
 (0)