Re-designed Public Suffix matching algorhithm based on recommendations published at https://publicsuffix.org/list/
git-svn-id: https://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk@1622478 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
4ee8c3e011
commit
2466424d5d
|
@ -104,6 +104,6 @@ public class PublicSuffixFilter implements CookieAttributeHandler {
|
||||||
if (matcher == null) {
|
if (matcher == null) {
|
||||||
matcher = new PublicSuffixMatcher(this.suffixes, this.exceptions);
|
matcher = new PublicSuffixMatcher(this.suffixes, this.exceptions);
|
||||||
}
|
}
|
||||||
return matcher.match(cookie.getDomain());
|
return matcher.matches(cookie.getDomain());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,7 @@ package org.apache.http.conn.util;
|
||||||
|
|
||||||
import java.net.IDN;
|
import java.net.IDN;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
@ -66,36 +67,55 @@ public final class PublicSuffixMatcher {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean match(final String domain) {
|
/**
|
||||||
String s = domain;
|
* Returns registrable part of the domain for the given domain name of {@code null}
|
||||||
if (s == null) {
|
* if given domain represents a public suffix.
|
||||||
return false;
|
*
|
||||||
|
* @param domain
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public String getDomainRoot(final String domain) {
|
||||||
|
if (domain == null) {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
if (s.startsWith(".")) {
|
if (domain.startsWith(".")) {
|
||||||
s = s.substring(1);
|
return null;
|
||||||
}
|
}
|
||||||
s = IDN.toUnicode(s);
|
String domainName = null;
|
||||||
|
String segment = domain.toLowerCase(Locale.ROOT);
|
||||||
|
while (segment != null) {
|
||||||
|
|
||||||
// An exception rule takes priority over any other matching rule.
|
// An exception rule takes priority over any other matching rule.
|
||||||
if (this.exceptions != null && this.exceptions.containsKey(s)) {
|
if (this.exceptions != null && this.exceptions.containsKey(IDN.toUnicode(segment))) {
|
||||||
return false;
|
return segment;
|
||||||
}
|
}
|
||||||
|
|
||||||
do {
|
if (this.rules.containsKey(IDN.toUnicode(segment))) {
|
||||||
if (this.rules.containsKey(s)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// patterns
|
|
||||||
if (s.startsWith("*.")) {
|
|
||||||
s = s.substring(2);
|
|
||||||
}
|
|
||||||
final int nextdot = s.indexOf('.');
|
|
||||||
if (nextdot == -1) {
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
s = "*" + s.substring(nextdot);
|
|
||||||
} while (!s.isEmpty());
|
|
||||||
|
|
||||||
|
final int nextdot = segment.indexOf('.');
|
||||||
|
final String nextSegment = nextdot != -1 ? segment.substring(nextdot + 1) : null;
|
||||||
|
|
||||||
|
if (nextSegment != null) {
|
||||||
|
if (this.rules.containsKey("*." + IDN.toUnicode(nextSegment))) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (nextdot != -1) {
|
||||||
|
domainName = segment;
|
||||||
|
}
|
||||||
|
segment = nextSegment;
|
||||||
|
}
|
||||||
|
return domainName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean matches(final String domain) {
|
||||||
|
if (domain == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
final String domainRoot = getDomainRoot(domain.startsWith(".") ? domain.substring(1) : domain);
|
||||||
|
return domainRoot == null;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,7 +70,8 @@ public class PublicSuffixDomainFilter implements CommonCookieAttributeHandler {
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean match(final Cookie cookie, final CookieOrigin origin) {
|
public boolean match(final Cookie cookie, final CookieOrigin origin) {
|
||||||
if (matcher.match(cookie.getDomain())) {
|
final String domain = cookie.getDomain();
|
||||||
|
if (matcher.matches(domain) && !domain.equalsIgnoreCase("localhost")) {
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
return handler.match(cookie, origin);
|
return handler.match(cookie, origin);
|
||||||
|
|
|
@ -57,29 +57,36 @@ public class TestPublicSuffixMatcher {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testParse() throws Exception {
|
public void testGetDomainRoot() throws Exception {
|
||||||
Assert.assertTrue(matcher.match(".jp"));
|
Assert.assertEquals("example.xx", matcher.getDomainRoot("example.XX"));
|
||||||
Assert.assertTrue(matcher.match(".ac.jp"));
|
Assert.assertEquals("example.xx", matcher.getDomainRoot("www.example.XX"));
|
||||||
Assert.assertTrue(matcher.match(".any.tokyo.jp"));
|
Assert.assertEquals("example.xx", matcher.getDomainRoot("www.blah.blah.example.XX"));
|
||||||
|
Assert.assertEquals(null, matcher.getDomainRoot("xx"));
|
||||||
|
Assert.assertEquals(null, matcher.getDomainRoot("jp"));
|
||||||
|
Assert.assertEquals(null, matcher.getDomainRoot("example"));
|
||||||
|
Assert.assertEquals("example.example", matcher.getDomainRoot("example.example"));
|
||||||
|
Assert.assertEquals(null, matcher.getDomainRoot("ac.jp"));
|
||||||
|
Assert.assertEquals(null, matcher.getDomainRoot("any.tokyo.jp"));
|
||||||
|
Assert.assertEquals("metro.tokyo.jp", matcher.getDomainRoot("metro.tokyo.jp"));
|
||||||
|
Assert.assertEquals("blah.blah.tokyo.jp", matcher.getDomainRoot("blah.blah.tokyo.jp"));
|
||||||
|
Assert.assertEquals("blah.ac.jp", matcher.getDomainRoot("blah.blah.ac.jp"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMatch() throws Exception {
|
||||||
|
Assert.assertTrue(matcher.matches(".jp"));
|
||||||
|
Assert.assertTrue(matcher.matches(".ac.jp"));
|
||||||
|
Assert.assertTrue(matcher.matches(".any.tokyo.jp"));
|
||||||
// exception
|
// exception
|
||||||
Assert.assertFalse(matcher.match(".metro.tokyo.jp"));
|
Assert.assertFalse(matcher.matches(".metro.tokyo.jp"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testUnicode() throws Exception {
|
public void testMatchUnicode() throws Exception {
|
||||||
Assert.assertTrue(matcher.match(".h\u00E5.no")); // \u00E5 is <aring>
|
Assert.assertTrue(matcher.matches(".h\u00E5.no")); // \u00E5 is <aring>
|
||||||
Assert.assertTrue(matcher.match(".xn--h-2fa.no"));
|
Assert.assertTrue(matcher.matches(".xn--h-2fa.no"));
|
||||||
Assert.assertTrue(matcher.match(".h\u00E5.no"));
|
Assert.assertTrue(matcher.matches(".h\u00E5.no"));
|
||||||
Assert.assertTrue(matcher.match(".xn--h-2fa.no"));
|
Assert.assertTrue(matcher.matches(".xn--h-2fa.no"));
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testWhitespace() throws Exception {
|
|
||||||
Assert.assertTrue(matcher.match(".xx"));
|
|
||||||
// yy appears after whitespace
|
|
||||||
Assert.assertFalse(matcher.match(".yy"));
|
|
||||||
// zz is commented
|
|
||||||
Assert.assertFalse(matcher.match(".zz"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -482,12 +482,24 @@ public class TestBasicCookieAttribHandlers {
|
||||||
cookie.setDomain("co.uk");
|
cookie.setDomain("co.uk");
|
||||||
Assert.assertFalse(h.match(cookie, new CookieOrigin("apache.co.uk", 80, "/stuff", false)));
|
Assert.assertFalse(h.match(cookie, new CookieOrigin("apache.co.uk", 80, "/stuff", false)));
|
||||||
|
|
||||||
|
cookie.setDomain(".co.com");
|
||||||
|
Assert.assertTrue(h.match(cookie, new CookieOrigin("apache.co.com", 80, "/stuff", false)));
|
||||||
|
|
||||||
|
cookie.setDomain("co.com");
|
||||||
|
Assert.assertFalse(h.match(cookie, new CookieOrigin("apache.co.com", 80, "/stuff", false)));
|
||||||
|
|
||||||
cookie.setDomain(".com");
|
cookie.setDomain(".com");
|
||||||
Assert.assertFalse(h.match(cookie, new CookieOrigin("apache.com", 80, "/stuff", false)));
|
Assert.assertFalse(h.match(cookie, new CookieOrigin("apache.com", 80, "/stuff", false)));
|
||||||
|
|
||||||
cookie.setDomain("com");
|
cookie.setDomain("com");
|
||||||
Assert.assertFalse(h.match(cookie, new CookieOrigin("apache.com", 80, "/stuff", false)));
|
Assert.assertFalse(h.match(cookie, new CookieOrigin("apache.com", 80, "/stuff", false)));
|
||||||
|
|
||||||
|
cookie.setDomain("apache.com");
|
||||||
|
Assert.assertTrue(h.match(cookie, new CookieOrigin("apache.com", 80, "/stuff", false)));
|
||||||
|
|
||||||
|
cookie.setDomain(".apache.com");
|
||||||
|
Assert.assertTrue(h.match(cookie, new CookieOrigin("www.apache.com", 80, "/stuff", false)));
|
||||||
|
|
||||||
cookie.setDomain("localhost");
|
cookie.setDomain("localhost");
|
||||||
Assert.assertTrue(h.match(cookie, new CookieOrigin("localhost", 80, "/stuff", false)));
|
Assert.assertTrue(h.match(cookie, new CookieOrigin("localhost", 80, "/stuff", false)));
|
||||||
}
|
}
|
||||||
|
|
|
@ -95,19 +95,4 @@ public class TestPublicSuffixListParser {
|
||||||
Assert.assertFalse(filter.match(cookie, new CookieOrigin("apache.h\u00E5.no", 80, "/stuff", false)));
|
Assert.assertFalse(filter.match(cookie, new CookieOrigin("apache.h\u00E5.no", 80, "/stuff", false)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testWhitespace() throws Exception {
|
|
||||||
final BasicClientCookie cookie = new BasicClientCookie("name", "value");
|
|
||||||
cookie.setDomain(".xx");
|
|
||||||
Assert.assertFalse(filter.match(cookie, new CookieOrigin("apache.xx", 80, "/stuff", false)));
|
|
||||||
|
|
||||||
// yy appears after whitespace
|
|
||||||
cookie.setDomain(".yy");
|
|
||||||
Assert.assertTrue(filter.match(cookie, new CookieOrigin("apache.yy", 80, "/stuff", false)));
|
|
||||||
|
|
||||||
// zz is commented
|
|
||||||
cookie.setDomain(".zz");
|
|
||||||
Assert.assertTrue(filter.match(cookie, new CookieOrigin("apache.zz", 80, "/stuff", false)));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue