8210311: IllegalArgumentException in CookieManager - Comparison method violates its general contract

Reviewed-by: chegar, dfuchs
This commit is contained in:
Michael McMahon 2018-09-13 12:07:01 +01:00
parent b395d380e8
commit b5fb6b3566
6 changed files with 240 additions and 19 deletions

View file

@ -241,7 +241,7 @@ public class CookieManager extends CookieHandler
}
// apply sort rule (RFC 2965 sec. 3.3.4)
List<String> cookieHeader = sortByPath(cookies);
List<String> cookieHeader = sortByPathAndAge(cookies);
return Map.of("Cookie", cookieHeader);
}
@ -402,11 +402,12 @@ public class CookieManager extends CookieHandler
/*
* sort cookies with respect to their path: those with more specific Path attributes
* precede those with less specific, as defined in RFC 2965 sec. 3.3.4
* sort cookies with respect to their path and age: those with more longer Path attributes
* precede those with shorter, as defined in RFC 6265. Cookies with the same length
* path are distinguished by creation time (older first). Method made PP to enable testing.
*/
private List<String> sortByPath(List<HttpCookie> cookies) {
Collections.sort(cookies, new CookiePathComparator());
static List<String> sortByPathAndAge(List<HttpCookie> cookies) {
Collections.sort(cookies, new CookieComparator());
List<String> cookieHeader = new java.util.ArrayList<>();
for (HttpCookie cookie : cookies) {
@ -424,22 +425,36 @@ public class CookieManager extends CookieHandler
}
static class CookiePathComparator implements Comparator<HttpCookie> {
// Comparator compares the length of the path. Longer paths should precede shorter ones.
// As per rfc6265 cookies with equal path lengths sort on creation time.
static class CookieComparator implements Comparator<HttpCookie> {
public int compare(HttpCookie c1, HttpCookie c2) {
if (c1 == c2) return 0;
if (c1 == null) return -1;
if (c2 == null) return 1;
// path rule only applies to the cookies with same name
if (!c1.getName().equals(c2.getName())) return 0;
// those with more specific Path attributes precede those with less specific
if (c1.getPath().startsWith(c2.getPath()))
String p1 = c1.getPath();
String p2 = c2.getPath();
p1 = (p1 == null) ? "" : p1;
p2 = (p2 == null) ? "" : p2;
int len1 = p1.length();
int len2 = p2.length();
if (len1 > len2)
return -1;
else if (c2.getPath().startsWith(c1.getPath()))
if (len2 > len1)
return 1;
else
return 0;
// Check creation time. Sort older first
long creation1 = c1.getCreationTime();
long creation2 = c2.getCreationTime();
if (creation1 < creation2) {
return -1;
}
if (creation1 > creation2) {
return 1;
}
return 0;
}
}
}

View file

@ -142,6 +142,13 @@ public final class HttpCookie implements Cloneable {
}
private HttpCookie(String name, String value, String header) {
this(name, value, header, System.currentTimeMillis());
}
/**
* Package private for testing purposes.
*/
HttpCookie(String name, String value, String header, long creationTime) {
name = name.trim();
if (name.length() == 0 || !isToken(name) || name.charAt(0) == '$') {
throw new IllegalArgumentException("Illegal cookie name");
@ -152,7 +159,7 @@ public final class HttpCookie implements Cloneable {
toDiscard = false;
secure = false;
whenCreated = System.currentTimeMillis();
whenCreated = creationTime;
portlist = null;
this.header = header;
}
@ -756,6 +763,11 @@ public final class HttpCookie implements Cloneable {
throw new RuntimeException(e.getMessage());
}
}
// ---------------- Package private operations --------------
long getCreationTime() {
return whenCreated;
}
// ---------------- Private operations --------------