@@ -90,16 +90,31 @@ public void set(URI uri, Iterable<? extends Cookie> cookies, long createdTimeMil
90
90
lock .lock ();
91
91
try {
92
92
for (Cookie cookie : cookies ) {
93
- cookie = ensureDomainAndPath (cookie , uri );
93
+ final Cookie ensuredCookie = ensureDomainAndPath (cookie , uri );
94
94
// remove similar cookie if present
95
- store .removeLong (cookie );
96
- if ((cookie .maxAge () == Cookie .UNDEFINED_MAX_AGE || cookie .maxAge () > 0 ) &&
97
- cookiePolicy .accept (uri , cookie )) {
98
- store .put (cookie , createdTimeMillis );
99
- final Set <Cookie > cookieSet = filter .computeIfAbsent (cookie .domain (), s -> new HashSet <>());
100
- // remove similar cookie if present
101
- cookieSet .remove (cookie );
102
- cookieSet .add (cookie );
95
+ store .removeLong (ensuredCookie );
96
+ if ((ensuredCookie .maxAge () == Cookie .UNDEFINED_MAX_AGE || ensuredCookie .maxAge () > 0 ) &&
97
+ cookiePolicy .accept (uri , ensuredCookie )) {
98
+ store .put (ensuredCookie , createdTimeMillis );
99
+ final Set <Cookie > cookieSet = filter .computeIfAbsent (ensuredCookie .domain (),
100
+ s -> new HashSet <>());
101
+
102
+ // remove the cookie with the same name, domain, and path if present
103
+ // https://datatracker.ietf.org/doc/html/rfc6265#page-24
104
+ final Cookie oldCookie = cookieSet .stream ()
105
+ .filter (c -> isSameNameAndPath (c , ensuredCookie ))
106
+ .findFirst ()
107
+ .orElse (null );
108
+ if (oldCookie == null ) {
109
+ cookieSet .add (ensuredCookie );
110
+ continue ;
111
+ }
112
+ if (!uri .getScheme ().startsWith ("http" ) && oldCookie .isHttpOnly ()) {
113
+ // if the new cookie is received from a non-HTTP and the old cookie is http-only, skip
114
+ continue ;
115
+ }
116
+ cookieSet .remove (oldCookie );
117
+ cookieSet .add (ensuredCookie );
103
118
}
104
119
}
105
120
} finally {
@@ -213,4 +228,10 @@ private static boolean cookieMatches(Cookie cookie, String host, String path, bo
213
228
final boolean pathMatched = path .startsWith (cookiePath );
214
229
return satisfiedHostOnly && satisfiedSecure && pathMatched ;
215
230
}
231
+
232
+ private static boolean isSameNameAndPath (Cookie oldCookie , Cookie newCookie ) {
233
+ final String oldCookiePath = oldCookie .path ();
234
+ assert oldCookiePath != null ;
235
+ return oldCookie .name ().equals (newCookie .name ()) && oldCookiePath .equals (newCookie .path ());
236
+ }
216
237
}
0 commit comments