diff --git a/RELEASE_NOTES.txt b/RELEASE_NOTES.txt index b99295d9e..1defc0a75 100644 --- a/RELEASE_NOTES.txt +++ b/RELEASE_NOTES.txt @@ -1,6 +1,13 @@ Changes since 4.0 Alpha 2 ------------------- +* [HTTPCLIENT-667] Added 'Meta' cookie specification that selects a cookie policy + depending on the format of the cookie(s). It is no longer necessary to know + beforehand what kind of HTTP cookie support the target host provides. + HttpClient is now able to pick up either a lenient or a strict cookie policy + depending on the compliance level of the target server. + Contributed by Oleg Kalnichevski + * [HTTPCLIENT-729] Move HttpRoute and related classes to routing package. Contributed by Roland Weber diff --git a/module-client/src/main/java/org/apache/http/client/params/CookiePolicy.java b/module-client/src/main/java/org/apache/http/client/params/CookiePolicy.java index ea5071f65..92fd198af 100644 --- a/module-client/src/main/java/org/apache/http/client/params/CookiePolicy.java +++ b/module-client/src/main/java/org/apache/http/client/params/CookiePolicy.java @@ -54,6 +54,11 @@ public final class CookiePolicy { */ public static final String RFC_2965 = "rfc2965"; + /** + * The default 'best match' policy. + */ + public static final String BEST_MATCH = "best-match"; + private CookiePolicy() { super(); } diff --git a/module-client/src/main/java/org/apache/http/client/params/HttpClientParams.java b/module-client/src/main/java/org/apache/http/client/params/HttpClientParams.java index d1ce16cd3..dd5ecce3d 100644 --- a/module-client/src/main/java/org/apache/http/client/params/HttpClientParams.java +++ b/module-client/src/main/java/org/apache/http/client/params/HttpClientParams.java @@ -145,7 +145,7 @@ public class HttpClientParams { String cookiePolicy = (String) params.getParameter(ClientPNames.COOKIE_POLICY); if (cookiePolicy == null) { - return CookiePolicy.BROWSER_COMPATIBILITY; + return CookiePolicy.BEST_MATCH; } return cookiePolicy; } diff --git a/module-client/src/main/java/org/apache/http/impl/client/DefaultHttpClient.java b/module-client/src/main/java/org/apache/http/impl/client/DefaultHttpClient.java index c3787f65e..bd5fcde83 100644 --- a/module-client/src/main/java/org/apache/http/impl/client/DefaultHttpClient.java +++ b/module-client/src/main/java/org/apache/http/impl/client/DefaultHttpClient.java @@ -61,6 +61,7 @@ import org.apache.http.impl.auth.BasicSchemeFactory; import org.apache.http.impl.auth.DigestSchemeFactory; import org.apache.http.impl.conn.DefaultHttpRoutePlanner; import org.apache.http.impl.conn.SingleClientConnManager; +import org.apache.http.impl.cookie.BestMatchSpecFactory; import org.apache.http.impl.cookie.BrowserCompatSpecFactory; import org.apache.http.impl.cookie.NetscapeDraftSpecFactory; import org.apache.http.impl.cookie.RFC2109SpecFactory; @@ -198,6 +199,9 @@ public class DefaultHttpClient extends AbstractHttpClient { protected CookieSpecRegistry createCookieSpecRegistry() { CookieSpecRegistry registry = new CookieSpecRegistry(); + registry.register( + CookiePolicy.BEST_MATCH, + new BestMatchSpecFactory()); registry.register( CookiePolicy.BROWSER_COMPATIBILITY, new BrowserCompatSpecFactory()); diff --git a/module-client/src/main/java/org/apache/http/impl/cookie/BestMatchSpec.java b/module-client/src/main/java/org/apache/http/impl/cookie/BestMatchSpec.java index 4b4c58ee6..c0ade8847 100644 --- a/module-client/src/main/java/org/apache/http/impl/cookie/BestMatchSpec.java +++ b/module-client/src/main/java/org/apache/http/impl/cookie/BestMatchSpec.java @@ -104,7 +104,7 @@ public class BestMatchSpec implements CookieSpec { if (helem.getParameterByName("version") != null) { versioned = true; } - if (helem.getParameterByName("expiry") != null) { + if (helem.getParameterByName("expires") != null) { netscape = true; } } @@ -158,9 +158,9 @@ public class BestMatchSpec implements CookieSpec { if (cookies == null) { throw new IllegalArgumentException("List of cookie may not be null"); } - int version = 0; + int version = Integer.MAX_VALUE; for (Cookie cookie: cookies) { - if (cookie.getVersion() > version) { + if (cookie.getVersion() < version) { version = cookie.getVersion(); } } diff --git a/module-client/src/test/java/org/apache/http/impl/cookie/TestAllCookieImpl.java b/module-client/src/test/java/org/apache/http/impl/cookie/TestAllCookieImpl.java index 0d0ae10d2..68c2746f6 100644 --- a/module-client/src/test/java/org/apache/http/impl/cookie/TestAllCookieImpl.java +++ b/module-client/src/test/java/org/apache/http/impl/cookie/TestAllCookieImpl.java @@ -52,6 +52,7 @@ public class TestAllCookieImpl extends TestCase { suite.addTest(TestCookieNetscapeDraft.suite()); suite.addTest(TestCookieRFC2109Spec.suite()); suite.addTest(TestCookieRFC2965Spec.suite()); + suite.addTest(TestCookieBestMatchSpec.suite()); return suite; } diff --git a/module-client/src/test/java/org/apache/http/impl/cookie/TestCookieBestMatchSpec.java b/module-client/src/test/java/org/apache/http/impl/cookie/TestCookieBestMatchSpec.java new file mode 100644 index 000000000..b3e7956bd --- /dev/null +++ b/module-client/src/test/java/org/apache/http/impl/cookie/TestCookieBestMatchSpec.java @@ -0,0 +1,256 @@ +/* + * $HeadURL:$ + * $Revision:$ + * $Date:$ + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package org.apache.http.impl.cookie; + +import java.util.ArrayList; +import java.util.List; + +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; + +import org.apache.http.Header; +import org.apache.http.cookie.ClientCookie; +import org.apache.http.cookie.Cookie; +import org.apache.http.cookie.CookieOrigin; +import org.apache.http.cookie.CookieSpec; +import org.apache.http.cookie.MalformedCookieException; +import org.apache.http.message.BasicHeader; + +/** + * Test cases for 'best match' cookie policy + * + * @author Oleg Kalnichevski + * + * @version $Revision:$ + */ +public class TestCookieBestMatchSpec extends TestCase { + + // ------------------------------------------------------------ Constructor + + public TestCookieBestMatchSpec(String name) { + super(name); + } + + // ------------------------------------------------------- TestCase Methods + + public static Test suite() { + return new TestSuite(TestCookieBestMatchSpec.class); + } + + public void testCookieBrowserCompatParsing() throws Exception { + CookieSpec cookiespec = new BestMatchSpec(); + CookieOrigin origin = new CookieOrigin("a.b.domain.com", 80, "/", false); + + // Make sure the lenient (browser compatible) cookie parsing + // and validation is used for Netscape style cookies + Header header = new BasicHeader("Set-Cookie", "name=value;path=/;domain=domain.com"); + + List cookies = cookiespec.parse(header, origin); + for (int i = 0; i < cookies.size(); i++) { + cookiespec.validate(cookies.get(i), origin); + } + } + + public void testNetscapeCookieParsing() throws Exception { + CookieSpec cookiespec = new BestMatchSpec(); + CookieOrigin origin = new CookieOrigin("myhost.mydomain.com", 80, "/", false); + + Header header = new BasicHeader("Set-Cookie", + "name=value; path=/; domain=.mydomain.com; expires=Thu, 01-Jan-2070 00:00:10 GMT; comment=no_comment"); + List cookies = cookiespec.parse(header, origin); + cookiespec.validate(cookies.get(0), origin); + + header = new BasicHeader("Set-Cookie", + "name=value; path=/; domain=.mydomain.com; expires=Thu, 01-Jan-2070 00:00:10 GMT; version=1"); + try { + cookies = cookiespec.parse(header, origin); + cookiespec.validate(cookies.get(0), origin); + fail("MalformedCookieException exception should have been thrown"); + } catch (MalformedCookieException e) { + // expected + } + } + + public void testCookieStandardCompliantParsing() throws Exception { + CookieSpec cookiespec = new BestMatchSpec(); + CookieOrigin origin = new CookieOrigin("a.b.domain.com", 80, "/", false); + + // Make sure the strict (RFC2965) cookie parsing + // and validation is used for version 1 cookies + Header header = new BasicHeader("Set-Cookie", "name=value;path=/;domain=b.domain.com; version=1"); + + List cookies = cookiespec.parse(header, origin); + for (int i = 0; i < cookies.size(); i++) { + cookiespec.validate(cookies.get(i), origin); + } + + header = new BasicHeader("Set-Cookie", "name=value;path=/;domain=domain.com; version=1"); + try { + cookies = cookiespec.parse(header, origin); + cookiespec.validate(cookies.get(0), origin); + fail("MalformedCookieException exception should have been thrown"); + } catch (MalformedCookieException e) { + // expected + } + } + + public void testCookieBrowserCompatMatch() throws Exception { + CookieSpec cookiespec = new BestMatchSpec(); + CookieOrigin origin = new CookieOrigin("a.b.domain.com", 80, "/", false); + + // Make sure the lenient (browser compatible) cookie matching + // is used for Netscape style cookies + BasicClientCookie cookie = new BasicClientCookie("name", "value"); + cookie.setDomain(".domain.com"); + cookie.setAttribute(ClientCookie.DOMAIN_ATTR, cookie.getDomain()); + cookie.setPath("/"); + cookie.setAttribute(ClientCookie.PATH_ATTR, cookie.getPath()); + + assertTrue(cookiespec.match(cookie, origin)); + } + + public void testCookieStandardCompliantMatch() throws Exception { + CookieSpec cookiespec = new BestMatchSpec(); + CookieOrigin origin = new CookieOrigin("a.b.domain.com", 80, "/", false); + + // Make sure the strict (RFC2965) cookie matching + // is used for version 1 cookies + BasicClientCookie cookie = new BasicClientCookie("name", "value"); + cookie.setVersion(1); + cookie.setDomain(".domain.com"); + cookie.setAttribute(ClientCookie.DOMAIN_ATTR, cookie.getDomain()); + cookie.setPath("/"); + cookie.setAttribute(ClientCookie.PATH_ATTR, cookie.getPath()); + + assertFalse(cookiespec.match(cookie, origin)); + + cookie.setDomain(".b.domain.com"); + + assertTrue(cookiespec.match(cookie, origin)); + } + + public void testCookieBrowserCompatFormatting() throws Exception { + CookieSpec cookiespec = new BestMatchSpec(); + + // Make sure the lenient (browser compatible) cookie formatting + // is used for Netscape style cookies + BasicClientCookie cookie1 = new BasicClientCookie("name1", "value1"); + cookie1.setDomain(".domain.com"); + cookie1.setAttribute(ClientCookie.DOMAIN_ATTR, cookie1.getDomain()); + cookie1.setPath("/"); + cookie1.setAttribute(ClientCookie.PATH_ATTR, cookie1.getPath()); + + BasicClientCookie cookie2 = new BasicClientCookie("name2", "value2"); + cookie2.setVersion(1); + cookie2.setDomain(".domain.com"); + cookie2.setAttribute(ClientCookie.DOMAIN_ATTR, cookie2.getDomain()); + cookie2.setPath("/"); + cookie2.setAttribute(ClientCookie.PATH_ATTR, cookie2.getPath()); + + List cookies = new ArrayList(); + cookies.add(cookie1); + cookies.add(cookie2); + + List
headers = cookiespec.formatCookies(cookies); + assertNotNull(headers); + assertEquals(1, headers.size()); + + Header header = headers.get(0); + assertEquals("name1=value1; name2=value2", header.getValue()); + + } + + public void testCookieStandardCompliantFormatting() throws Exception { + CookieSpec cookiespec = new BestMatchSpec(null, true); + + // Make sure the strict (RFC2965) cookie formatting + // is used for Netscape style cookies + BasicClientCookie cookie1 = new BasicClientCookie("name1", "value1"); + cookie1.setVersion(1); + cookie1.setDomain(".domain.com"); + cookie1.setAttribute(ClientCookie.DOMAIN_ATTR, cookie1.getDomain()); + cookie1.setPath("/"); + cookie1.setAttribute(ClientCookie.PATH_ATTR, cookie1.getPath()); + + BasicClientCookie cookie2 = new BasicClientCookie("name2", "value2"); + cookie2.setVersion(1); + cookie2.setDomain(".domain.com"); + cookie2.setAttribute(ClientCookie.DOMAIN_ATTR, cookie2.getDomain()); + cookie2.setPath("/"); + cookie2.setAttribute(ClientCookie.PATH_ATTR, cookie2.getPath()); + + List cookies = new ArrayList(); + cookies.add(cookie1); + cookies.add(cookie2); + + List
headers = cookiespec.formatCookies(cookies); + assertNotNull(headers); + assertEquals(1, headers.size()); + + Header header = headers.get(0); + assertEquals("$Version=1; name1=\"value1\"; $Path=\"/\"; $Domain=\".domain.com\"; " + + "name2=\"value2\"; $Path=\"/\"; $Domain=\".domain.com\"", + header.getValue()); + + } + + public void testInvalidInput() throws Exception { + CookieSpec cookiespec = new BestMatchSpec(); + try { + cookiespec.parse(null, null); + fail("IllegalArgumentException must have been thrown"); + } catch (IllegalArgumentException ex) { + // expected + } + try { + cookiespec.parse(new BasicHeader("Set-Cookie", "name=value"), null); + fail("IllegalArgumentException must have been thrown"); + } catch (IllegalArgumentException ex) { + // expected + } + try { + cookiespec.formatCookies(null); + fail("IllegalArgumentException must have been thrown"); + } catch (IllegalArgumentException ex) { + // expected + } + try { + List cookies = new ArrayList(); + cookiespec.formatCookies(cookies); + fail("IllegalArgumentException must have been thrown"); + } catch (IllegalArgumentException ex) { + // expected + } + } + +} + diff --git a/module-client/src/test/java/org/apache/http/impl/cookie/TestCookieRFC2109Spec.java b/module-client/src/test/java/org/apache/http/impl/cookie/TestCookieRFC2109Spec.java index 1bd912208..d9cc76e5f 100644 --- a/module-client/src/test/java/org/apache/http/impl/cookie/TestCookieRFC2109Spec.java +++ b/module-client/src/test/java/org/apache/http/impl/cookie/TestCookieRFC2109Spec.java @@ -92,7 +92,7 @@ public class TestCookieRFC2109Spec extends TestCase { /** * Test domain equals host */ - public void testcookiesomainEqualsHost() throws Exception { + public void testCookiesomainEqualsHost() throws Exception { Header header = new BasicHeader("Set-Cookie", "cookie-name=cookie-value; domain=www.b.com; version=1");