diff --git a/module-client/src/main/java/org/apache/http/impl/client/DefaultClientRequestDirector.java b/module-client/src/main/java/org/apache/http/impl/client/DefaultClientRequestDirector.java index d9b6b9892..0e5299ec6 100644 --- a/module-client/src/main/java/org/apache/http/impl/client/DefaultClientRequestDirector.java +++ b/module-client/src/main/java/org/apache/http/impl/client/DefaultClientRequestDirector.java @@ -81,7 +81,6 @@ import org.apache.http.entity.BufferedHttpEntity; import org.apache.http.message.BasicHttpRequest; import org.apache.http.params.HttpConnectionParams; import org.apache.http.params.HttpParams; -import org.apache.http.params.HttpParamsLinker; import org.apache.http.params.HttpProtocolParams; import org.apache.http.protocol.HTTP; import org.apache.http.protocol.HttpContext; diff --git a/module-client/src/main/java/org/apache/http/impl/cookie/AbstractCookieSpec.java b/module-client/src/main/java/org/apache/http/impl/cookie/AbstractCookieSpec.java index af723f522..003debf84 100644 --- a/module-client/src/main/java/org/apache/http/impl/cookie/AbstractCookieSpec.java +++ b/module-client/src/main/java/org/apache/http/impl/cookie/AbstractCookieSpec.java @@ -31,16 +31,13 @@ package org.apache.http.impl.cookie; -import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; -import java.util.List; import java.util.Map; import org.apache.http.cookie.CookieAttributeHandler; import org.apache.http.cookie.CookieSpec; - /** * Abstract cookie specification which can delegate the job of parsing, * validation or matching cookie attributes to a number of arbitrary @@ -52,11 +49,6 @@ import org.apache.http.cookie.CookieSpec; */ public abstract class AbstractCookieSpec implements CookieSpec { - /** - * Stores the list of attribute handlers - */ - private final List attribHandlerList; - /** * Stores attribute name -> attribute handler mappings */ @@ -68,7 +60,6 @@ public abstract class AbstractCookieSpec implements CookieSpec { public AbstractCookieSpec() { super(); this.attribHandlerMap = new HashMap(10); - this.attribHandlerList = new ArrayList(10); } public void registerAttribHandler( @@ -79,9 +70,6 @@ public abstract class AbstractCookieSpec implements CookieSpec { if (handler == null) { throw new IllegalArgumentException("Attribute handler may not be null"); } - if (!this.attribHandlerList.contains(handler)) { - this.attribHandlerList.add(handler); - } this.attribHandlerMap.put(name, handler); } @@ -116,7 +104,7 @@ public abstract class AbstractCookieSpec implements CookieSpec { } protected Iterator getAttribHandlerIterator() { - return this.attribHandlerList.iterator(); + return this.attribHandlerMap.values().iterator(); } } diff --git a/module-client/src/main/java/org/apache/http/impl/cookie/BasicClientCookie2.java b/module-client/src/main/java/org/apache/http/impl/cookie/BasicClientCookie2.java index d5579e968..5e78c95a0 100644 --- a/module-client/src/main/java/org/apache/http/impl/cookie/BasicClientCookie2.java +++ b/module-client/src/main/java/org/apache/http/impl/cookie/BasicClientCookie2.java @@ -31,6 +31,8 @@ package org.apache.http.impl.cookie; +import java.util.Date; + import org.apache.http.cookie.SetCookie2; /** @@ -80,5 +82,9 @@ public class BasicClientCookie2 extends BasicClientCookie implements SetCookie2 return !this.discard && super.isPersistent(); } + public boolean isExpired(final Date date) { + return this.discard || super.isExpired(date); + } + } diff --git a/module-client/src/main/java/org/apache/http/impl/cookie/BasicMaxAgeHandler.java b/module-client/src/main/java/org/apache/http/impl/cookie/BasicMaxAgeHandler.java index e1c0e6981..96d273ce7 100644 --- a/module-client/src/main/java/org/apache/http/impl/cookie/BasicMaxAgeHandler.java +++ b/module-client/src/main/java/org/apache/http/impl/cookie/BasicMaxAgeHandler.java @@ -56,6 +56,10 @@ public class BasicMaxAgeHandler extends AbstractCookieAttributeHandler { throw new MalformedCookieException ("Invalid max-age attribute: " + value); } + if (age < 0) { + throw new MalformedCookieException ("Negative max-age attribute: " + + value); + } cookie.setExpiryDate(new Date(System.currentTimeMillis() + age * 1000L)); } diff --git a/module-client/src/main/java/org/apache/http/impl/cookie/RFC2109Spec.java b/module-client/src/main/java/org/apache/http/impl/cookie/RFC2109Spec.java index d93e0ef80..3bd00897d 100644 --- a/module-client/src/main/java/org/apache/http/impl/cookie/RFC2109Spec.java +++ b/module-client/src/main/java/org/apache/http/impl/cookie/RFC2109Spec.java @@ -186,7 +186,7 @@ public class RFC2109Spec extends CookieSpecBase { * @param param The parameter. * @param version The cookie version */ - private void formatParamAsVer(final CharArrayBuffer buffer, + protected void formatParamAsVer(final CharArrayBuffer buffer, final String name, final String value, int version) { buffer.append(name); buffer.append("="); @@ -208,7 +208,7 @@ public class RFC2109Spec extends CookieSpecBase { * @param cookie The {@link Cookie} to be formatted as string * @param version The version to use. */ - private void formatCookieAsVer(final CharArrayBuffer buffer, + protected void formatCookieAsVer(final CharArrayBuffer buffer, final Cookie cookie, int version) { formatParamAsVer(buffer, cookie.getName(), cookie.getValue(), version); if (cookie.getPath() != null) { diff --git a/module-client/src/main/java/org/apache/http/impl/cookie/RFC2965PortAttributeHandler.java b/module-client/src/main/java/org/apache/http/impl/cookie/RFC2965PortAttributeHandler.java index 265541aee..7743ae8ca 100644 --- a/module-client/src/main/java/org/apache/http/impl/cookie/RFC2965PortAttributeHandler.java +++ b/module-client/src/main/java/org/apache/http/impl/cookie/RFC2965PortAttributeHandler.java @@ -78,7 +78,7 @@ public class RFC2965PortAttributeHandler implements CookieAttributeHandler { } if (cookie instanceof SetCookie2) { SetCookie2 cookie2 = (SetCookie2) cookie; - if (portValue != null && !portValue.equals("")) { + if (portValue != null && portValue.trim().length() > 0) { int[] ports = parsePortAttribute(portValue); cookie2.setPorts(ports); } diff --git a/module-client/src/main/java/org/apache/http/impl/cookie/RFC2965Spec.java b/module-client/src/main/java/org/apache/http/impl/cookie/RFC2965Spec.java new file mode 100644 index 000000000..669115c02 --- /dev/null +++ b/module-client/src/main/java/org/apache/http/impl/cookie/RFC2965Spec.java @@ -0,0 +1,224 @@ +/* + * $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.HashMap; +import java.util.Iterator; +import java.util.Map; + +import org.apache.http.Header; +import org.apache.http.HeaderElement; +import org.apache.http.NameValuePair; +import org.apache.http.cookie.ClientCookie; +import org.apache.http.cookie.Cookie; +import org.apache.http.cookie.CookieAttributeHandler; +import org.apache.http.cookie.CookieOrigin; +import org.apache.http.cookie.CookieVersionSupport; +import org.apache.http.cookie.MalformedCookieException; +import org.apache.http.cookie.SM; +import org.apache.http.message.BufferedHeader; +import org.apache.http.util.CharArrayBuffer; + +/** + *

RFC 2965 specific cookie management functions.

+ * + * @author jain.samit@gmail.com (Samit Jain) + * @author Oleg Kalnichevski + * + * @since 3.1 + */ +public class RFC2965Spec extends RFC2109Spec implements CookieVersionSupport { + + /** + * Default constructor + * + */ + public RFC2965Spec() { + this(null, false); + } + + public RFC2965Spec(final String[] datepatterns, boolean oneHeader) { + super(datepatterns, oneHeader); + registerAttribHandler(ClientCookie.DOMAIN_ATTR, new RFC2965DomainAttributeHandler()); + registerAttribHandler(ClientCookie.PORT_ATTR, new RFC2965PortAttributeHandler()); + registerAttribHandler(ClientCookie.COMMENTURL_ATTR, new RFC2965CommentUrlAttributeHandler()); + registerAttribHandler(ClientCookie.DISCARD_ATTR, new RFC2965DiscardAttributeHandler()); + registerAttribHandler(ClientCookie.VERSION_ATTR, new RFC2965VersionAttributeHandler()); + } + + public Cookie[] parse( + final Header header, + CookieOrigin origin) throws MalformedCookieException { + if (header == null) { + throw new IllegalArgumentException("Header may not be null"); + } + if (origin == null) { + throw new IllegalArgumentException("Cookie origin may not be null"); + } + + origin = adjustEffectiveHost(origin); + + HeaderElement[] elems = header.getElements(); + + Cookie[] cookies = new Cookie[elems.length]; + for (int i = 0; i < elems.length; i++) { + HeaderElement headerelement = elems[i]; + + String name = headerelement.getName(); + String value = headerelement.getValue(); + if (name == null || name.equals("")) { + throw new MalformedCookieException("Cookie name may not be empty"); + } + + BasicClientCookie2 cookie = new BasicClientCookie2(name, value); + cookie.setPath(getDefaultPath(origin)); + cookie.setDomain(getDefaultDomain(origin)); + cookie.setPorts(new int [] { origin.getPort() }); + + // cycle through the parameters + NameValuePair[] attribs = headerelement.getParameters(); + + // Eliminate duplicate attributes. The first occurrence takes precedence + // See RFC2965: 3.2 Origin Server Role + Map attribmap = new HashMap(attribs.length); + for (int j = attribs.length - 1; j >= 0; j--) { + NameValuePair param = attribs[j]; + attribmap.put(param.getName().toLowerCase(), param); + } + for (Iterator it = attribmap.entrySet().iterator(); it.hasNext(); ) { + Map.Entry entry = (Map.Entry) it.next(); + NameValuePair attrib = (NameValuePair) entry.getValue(); + String s = attrib.getName().toLowerCase(); + + cookie.setAttribute(s, attrib.getValue()); + + CookieAttributeHandler handler = findAttribHandler(s); + if (handler != null) { + handler.parse(cookie, attrib.getValue()); + } + } + cookies[i] = cookie; + } + return cookies; + } + + public void validate(final Cookie cookie, CookieOrigin origin) + throws MalformedCookieException { + if (cookie == null) { + throw new IllegalArgumentException("Cookie may not be null"); + } + if (origin == null) { + throw new IllegalArgumentException("Cookie origin may not be null"); + } + origin = adjustEffectiveHost(origin); + super.validate(cookie, origin); + } + + public boolean match(final Cookie cookie, CookieOrigin origin) { + if (cookie == null) { + throw new IllegalArgumentException("Cookie may not be null"); + } + if (origin == null) { + throw new IllegalArgumentException("Cookie origin may not be null"); + } + origin = adjustEffectiveHost(origin); + return super.match(cookie, origin); + } + + /** + * Adds valid Port attribute value, e.g. "8000,8001,8002" + */ + protected void formatCookieAsVer(final CharArrayBuffer buffer, + final Cookie cookie, int version) { + super.formatCookieAsVer(buffer, cookie, version); + // format port attribute + if (cookie instanceof ClientCookie) { + // Test if the port attribute as set by the origin server is not blank + String s = ((ClientCookie) cookie).getAttribute(ClientCookie.PORT_ATTR); + if (s != null) { + buffer.append("; $Port"); + buffer.append("=\""); + if (s.trim().length() > 0) { + int[] ports = cookie.getPorts(); + if (ports != null) { + for (int i = 0, len = ports.length; i < len; i++) { + if (i > 0) { + buffer.append(","); + } + buffer.append(Integer.toString(ports[i])); + } + } + } + buffer.append("\""); + } + } + } + + /** + * Set 'effective host name' as defined in RFC 2965. + *

+ * If a host name contains no dots, the effective host name is + * that name with the string .local appended to it. Otherwise + * the effective host name is the same as the host name. Note + * that all effective host names contain at least one dot. + * + * @param host host name where cookie is received from or being sent to. + * @return + */ + private static CookieOrigin adjustEffectiveHost(final CookieOrigin origin) { + String effectiveHost = origin.getHost(); + if (effectiveHost.indexOf('.') < 0) { + effectiveHost += ".local"; + return new CookieOrigin( + effectiveHost, + origin.getPort(), + origin.getPath(), + origin.isSecure()); + } else { + return origin; + } + } + + public int getVersion() { + return 1; + } + + public Header getVersionHeader() { + CharArrayBuffer buffer = new CharArrayBuffer(40); + buffer.append(SM.COOKIE_2); + buffer.append(": "); + buffer.append("$Version="); + buffer.append(Integer.toString(getVersion())); + return new BufferedHeader(buffer); + } + +} + diff --git a/module-client/src/main/java/org/apache/http/impl/cookie/RFC2965SpecFactory.java b/module-client/src/main/java/org/apache/http/impl/cookie/RFC2965SpecFactory.java new file mode 100644 index 000000000..87113cf41 --- /dev/null +++ b/module-client/src/main/java/org/apache/http/impl/cookie/RFC2965SpecFactory.java @@ -0,0 +1,57 @@ +/* + * $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 org.apache.http.cookie.CookieSpec; +import org.apache.http.cookie.CookieSpecFactory; +import org.apache.http.cookie.params.CookieSpecPNames; +import org.apache.http.params.HttpParams; + +/** + * + * @author Oleg Kalnichevski + * + * @since 4.0 + */ +public class RFC2965SpecFactory implements CookieSpecFactory { + + public CookieSpec newInstance(final HttpParams params) { + if (params != null) { + return new RFC2965Spec( + (String []) params.getParameter(CookieSpecPNames.DATE_PATTERNS), + params.getBooleanParameter(CookieSpecPNames.SINGLE_COOKIE_HEADER, false)); + } else { + return new RFC2965Spec(); + } + } + +} diff --git a/module-client/src/main/java/org/apache/http/impl/cookie/RFC2965VersionAttributeHandler.java b/module-client/src/main/java/org/apache/http/impl/cookie/RFC2965VersionAttributeHandler.java index d6e18a5e2..7e4fc1ccd 100644 --- a/module-client/src/main/java/org/apache/http/impl/cookie/RFC2965VersionAttributeHandler.java +++ b/module-client/src/main/java/org/apache/http/impl/cookie/RFC2965VersionAttributeHandler.java @@ -50,7 +50,7 @@ public class RFC2965VersionAttributeHandler implements CookieAttributeHandler { } if (cookie instanceof ClientCookie) { if (cookie instanceof ClientCookie - && ((ClientCookie) cookie).containsAttribute(ClientCookie.VERSION_ATTR)) { + && !((ClientCookie) cookie).containsAttribute(ClientCookie.VERSION_ATTR)) { throw new MalformedCookieException( "Violates RFC 2965. Version attribute is required."); } diff --git a/module-client/src/test/java/org/apache/http/impl/cookie/TestAbstractCookieSpec.java b/module-client/src/test/java/org/apache/http/impl/cookie/TestAbstractCookieSpec.java index 63b38d65c..f1761ad82 100644 --- a/module-client/src/test/java/org/apache/http/impl/cookie/TestAbstractCookieSpec.java +++ b/module-client/src/test/java/org/apache/http/impl/cookie/TestAbstractCookieSpec.java @@ -111,6 +111,8 @@ public class TestAbstractCookieSpec extends TestCase { Iterator it = cookiespec.getAttribHandlerIterator(); assertNotNull(it.next()); assertNotNull(it.next()); + assertNotNull(it.next()); + assertNotNull(it.next()); assertFalse(it.hasNext()); } 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 fd690e904..6058a8191 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 @@ -50,6 +50,7 @@ public class TestAllCookieImpl extends TestCase { suite.addTest(TestBrowserCompatSpec.suite()); suite.addTest(TestCookieNetscapeDraft.suite()); suite.addTest(TestCookieRFC2109Spec.suite()); + suite.addTest(TestCookieRFC2965Spec.suite()); return suite; } diff --git a/module-client/src/test/java/org/apache/http/impl/cookie/TestCookieRFC2965Spec.java b/module-client/src/test/java/org/apache/http/impl/cookie/TestCookieRFC2965Spec.java new file mode 100644 index 000000000..008519ece --- /dev/null +++ b/module-client/src/test/java/org/apache/http/impl/cookie/TestCookieRFC2965Spec.java @@ -0,0 +1,963 @@ +/* + * $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 junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; + +import java.util.Date; + +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 RFC2965 cookie spec + * + * @author jain.samit@gmail.com (Samit Jain) + */ +public class TestCookieRFC2965Spec extends TestCase { + + // ------------------------------------------------------------ Constructor + + public TestCookieRFC2965Spec(String name) { + super(name); + } + + // ------------------------------------------------------- TestCase Methods + + public static Test suite() { + return new TestSuite(TestCookieRFC2965Spec.class); + } + + // ------------------------------------------------------- Test Cookie Parsing + + /** + * Test parsing cookie "Path" attribute. + */ + public void testParsePath() throws Exception { + CookieSpec cookiespec = new RFC2965Spec(); + CookieOrigin origin = new CookieOrigin("www.domain.com", 80, "/", false); + Header header = new BasicHeader("Set-Cookie2", "name=value;Path=/;Version=1;Path="); + Cookie[] parsed = cookiespec.parse(header, origin); + assertNotNull(parsed); + assertEquals(1, parsed.length); + // only the first occurrence of path attribute is considered, others ignored + ClientCookie cookie = (ClientCookie) parsed[0]; + assertEquals("/", cookie.getPath()); + assertTrue(cookie.containsAttribute(ClientCookie.PATH_ATTR)); + } + + public void testParsePathDefault() throws Exception { + CookieSpec cookiespec = new RFC2965Spec(); + CookieOrigin origin = new CookieOrigin("www.domain.com", 80, "/path/", false); + // Path is OPTIONAL, defaults to the request path + Header header = new BasicHeader("Set-Cookie2", "name=value;Version=1"); + Cookie[] parsed = cookiespec.parse(header, origin); + assertNotNull(parsed); + assertEquals(1, parsed.length); + ClientCookie cookie = (ClientCookie) parsed[0]; + assertEquals("/path", cookie.getPath()); + assertFalse(cookie.containsAttribute(ClientCookie.PATH_ATTR)); + } + + public void testParseNullPath() throws Exception { + CookieSpec cookiespec = new RFC2965Spec(); + CookieOrigin origin = new CookieOrigin("www.domain.com", 80, "/", false); + Header header = new BasicHeader("Set-Cookie2", "name=value;Path=;Version=1"); + Cookie[] parsed = cookiespec.parse(header, origin); + assertNotNull(parsed); + assertEquals(1, parsed.length); + ClientCookie cookie = (ClientCookie) parsed[0]; + assertEquals("/", cookie.getPath()); + assertTrue(cookie.containsAttribute(ClientCookie.PATH_ATTR)); + } + + public void testParseBlankPath() throws Exception { + CookieSpec cookiespec = new RFC2965Spec(); + CookieOrigin origin = new CookieOrigin("www.domain.com", 80, "/", false); + Header header = new BasicHeader("Set-Cookie2", "name=value;Path=\" \";Version=1"); + Cookie[] parsed = cookiespec.parse(header, origin); + assertNotNull(parsed); + assertEquals(1, parsed.length); + ClientCookie cookie = (ClientCookie) parsed[0]; + assertEquals("/", cookie.getPath()); + assertTrue(cookie.containsAttribute(ClientCookie.PATH_ATTR)); + } + + /** + * Test parsing cookie "Domain" attribute. + */ + public void testParseDomain() throws Exception { + CookieSpec cookiespec = new RFC2965Spec(); + CookieOrigin origin = new CookieOrigin("www.domain.com", 80, "/", false); + Header header = new BasicHeader("Set-Cookie2", "name=value;Domain=.domain.com;Version=1;Domain="); + Cookie[] parsed = cookiespec.parse(header, origin); + assertNotNull(parsed); + assertEquals(1, parsed.length); + // only the first occurrence of domain attribute is considered, others ignored + ClientCookie cookie = (ClientCookie) parsed[0]; + assertEquals(".domain.com", cookie.getDomain()); + assertTrue(cookie.containsAttribute(ClientCookie.DOMAIN_ATTR)); + + // should put a leading dot if there is no dot in front of domain + header = new BasicHeader("Set-Cookie2", "name=value;Domain=domain.com;Version=1"); + parsed = cookiespec.parse(header, origin); + assertNotNull(parsed); + assertEquals(1, parsed.length); + cookie = (ClientCookie) parsed[0]; + assertEquals(".domain.com", cookie.getDomain()); + } + + public void testParseDomainDefault() throws Exception { + CookieSpec cookiespec = new RFC2965Spec(); + CookieOrigin origin = new CookieOrigin("www.domain.com", 80, "/", false); + // Domain is OPTIONAL, defaults to the request host + Header header = new BasicHeader("Set-Cookie2", "name=value;Version=1"); + Cookie[] parsed = cookiespec.parse(header, origin); + assertNotNull(parsed); + assertEquals(1, parsed.length); + ClientCookie cookie = (ClientCookie) parsed[0]; + assertEquals("www.domain.com", cookie.getDomain()); + assertFalse(cookie.containsAttribute(ClientCookie.DOMAIN_ATTR)); + } + + public void testParseNullDomain() throws Exception { + CookieSpec cookiespec = new RFC2965Spec(); + CookieOrigin origin = new CookieOrigin("www.domain.com", 80, "/", false); + // domain cannot be null + Header header = new BasicHeader("Set-Cookie2", "name=value;Domain=;Version=1"); + try { + cookiespec.parse(header, origin); + fail("MalformedCookieException should have been thrown"); + } catch (MalformedCookieException ex) { + // expected + } + } + + public void testParseBlankDomain() throws Exception { + CookieSpec cookiespec = new RFC2965Spec(); + CookieOrigin origin = new CookieOrigin("www.domain.com", 80, "/", false); + Header header = new BasicHeader("Set-Cookie2", "name=value;Domain=\" \";Version=1"); + try { + cookiespec.parse(header, origin); + fail("MalformedCookieException should have been thrown"); + } catch (MalformedCookieException ex) { + // expected + } + } + + /** + * Test parsing cookie "Port" attribute. + */ + public void testParsePort() throws Exception { + CookieSpec cookiespec = new RFC2965Spec(); + CookieOrigin origin = new CookieOrigin("www.domain.com", 80, "/", false); + Header header = new BasicHeader("Set-Cookie2", "name=value;Port=\"80,800,8000\";Version=1;Port=nonsense"); + Cookie[] parsed = cookiespec.parse(header, origin); + assertNotNull(parsed); + assertEquals(1, parsed.length); + // only the first occurrence of port attribute is considered, others ignored + ClientCookie cookie = (ClientCookie) parsed[0]; + int[] ports = cookie.getPorts(); + assertNotNull(ports); + assertEquals(3, ports.length); + assertEquals(80, ports[0]); + assertEquals(800, ports[1]); + assertEquals(8000, ports[2]); + assertTrue(cookie.containsAttribute(ClientCookie.PORT_ATTR)); + } + + public void testParsePortDefault() throws Exception { + CookieSpec cookiespec = new RFC2965Spec(); + CookieOrigin origin = new CookieOrigin("www.domain.com", 80, "/", false); + // Port is OPTIONAL, cookie can be accepted from any port + Header header = new BasicHeader("Set-Cookie2", "name=value;Version=1"); + Cookie[] parsed = cookiespec.parse(header, origin); + assertNotNull(parsed); + assertEquals(1, parsed.length); + ClientCookie cookie = (ClientCookie) parsed[0]; + assertFalse(cookie.containsAttribute(ClientCookie.PORT_ATTR)); + } + + public void testParseNullPort() throws Exception { + CookieSpec cookiespec = new RFC2965Spec(); + CookieOrigin origin = new CookieOrigin("www.domain.com", 80, "/", false); + // null port defaults to request port + Header header = new BasicHeader("Set-Cookie2", "name=value;Port=;Version=1"); + Cookie[] parsed = cookiespec.parse(header, origin); + assertNotNull(parsed); + assertEquals(1, parsed.length); + ClientCookie cookie = (ClientCookie) parsed[0]; + int[] ports = cookie.getPorts(); + assertNotNull(ports); + assertEquals(1, ports.length); + assertEquals(80, ports[0]); + assertEquals("", cookie.getAttribute(ClientCookie.PORT_ATTR)); + } + + public void testParseBlankPort() throws Exception { + CookieSpec cookiespec = new RFC2965Spec(); + CookieOrigin origin = new CookieOrigin("www.domain.com", 80, "/", false); + // blank port defaults to request port + Header header = new BasicHeader("Set-Cookie2", "name=value;Port=\" \";Version=1"); + Cookie[] parsed = cookiespec.parse(header, origin); + assertNotNull(parsed); + assertEquals(1, parsed.length); + ClientCookie cookie = (ClientCookie) parsed[0]; + int[] ports = cookie.getPorts(); + assertNotNull(ports); + assertEquals(1, ports.length); + assertEquals(80, ports[0]); + assertEquals(" ", cookie.getAttribute(ClientCookie.PORT_ATTR)); + } + + public void testParseInvalidPort() throws Exception { + CookieSpec cookiespec = new RFC2965Spec(); + CookieOrigin origin = new CookieOrigin("www.domain.com", 80, "/", false); + Header header = new BasicHeader("Set-Cookie2", "name=value;Port=nonsense;Version=1"); + try { + cookiespec.parse(header, origin); + fail("MalformedCookieException should have been thrown"); + } catch (MalformedCookieException ex) { + // expected + } + } + + public void testParseNegativePort() throws Exception { + CookieSpec cookiespec = new RFC2965Spec(); + CookieOrigin origin = new CookieOrigin("www.domain.com", 80, "/", false); + Header header = new BasicHeader("Set-Cookie2", "name=value;Port=\"80,-800,8000\";Version=1"); + try { + cookiespec.parse(header, origin); + fail("MalformedCookieException should have been thrown"); + } catch (MalformedCookieException ex) { + // expected + } + } + + /** + * test parsing cookie name/value. + */ + public void testParseNameValue() throws Exception { + CookieSpec cookiespec = new RFC2965Spec(); + CookieOrigin origin = new CookieOrigin("www.domain.com", 80, "/", false); + Header header = new BasicHeader("Set-Cookie2", "name=value;Version=1;"); + Cookie[] parsed = cookiespec.parse(header, origin); + assertNotNull(parsed); + assertEquals(1, parsed.length); + ClientCookie cookie = (ClientCookie) parsed[0]; + assertEquals("name", cookie.getName()); + assertEquals("value", cookie.getValue()); + } + + /** + * test parsing cookie "Version" attribute. + */ + public void testParseVersion() throws Exception { + CookieSpec cookiespec = new RFC2965Spec(); + CookieOrigin origin = new CookieOrigin("www.domain.com", 80, "/", false); + Header header = new BasicHeader("Set-Cookie2", "name=value;Version=1;"); + Cookie[] parsed = cookiespec.parse(header, origin); + assertNotNull(parsed); + assertEquals(1, parsed.length); + ClientCookie cookie = (ClientCookie) parsed[0]; + assertEquals(1, cookie.getVersion()); + assertTrue(cookie.containsAttribute(ClientCookie.VERSION_ATTR)); + } + + public void testParseNullVersion() throws Exception { + CookieSpec cookiespec = new RFC2965Spec(); + CookieOrigin origin = new CookieOrigin("www.domain.com", 80, "/", false); + // version cannot be null + Header header = new BasicHeader("Set-Cookie2", "name=value;Version=;"); + try { + cookiespec.parse(header, origin); + fail("MalformedCookieException should have been thrown"); + } catch (MalformedCookieException ex) { + // expected + } + } + + public void testParseNegativeVersion() throws Exception { + CookieSpec cookiespec = new RFC2965Spec(); + CookieOrigin origin = new CookieOrigin("www.domain.com", 80, "/", false); + Header header = new BasicHeader("Set-Cookie2", "name=value;Version=-1;"); + try { + cookiespec.parse(header, origin); + fail("MalformedCookieException should have been thrown"); + } catch (MalformedCookieException ex) { + // expected + } + } + /** + * test parsing cookie "Max-age" attribute. + */ + public void testParseMaxage() throws Exception { + CookieSpec cookiespec = new RFC2965Spec(); + CookieOrigin origin = new CookieOrigin("www.domain.com", 80, "/", false); + Header header = new BasicHeader("Set-Cookie2", "name=value;Max-age=3600;Version=1;Max-age=nonsense"); + Cookie[] parsed = cookiespec.parse(header, origin); + assertNotNull(parsed); + assertEquals(1, parsed.length); + // only the first occurence of max-age attribute is considered, others ignored + ClientCookie cookie = (ClientCookie) parsed[0]; + assertFalse(cookie.isExpired(new Date())); + } + + public void testParseMaxageDefault() throws Exception { + CookieSpec cookiespec = new RFC2965Spec(); + CookieOrigin origin = new CookieOrigin("www.domain.com", 80, "/", false); + // Max-age is OPTIONAL, defaults to session cookie + Header header = new BasicHeader("Set-Cookie2", "name=value;Version=1"); + Cookie[] parsed = cookiespec.parse(header, origin); + assertNotNull(parsed); + assertEquals(1, parsed.length); + ClientCookie cookie = (ClientCookie) parsed[0]; + assertFalse(cookie.isPersistent()); + } + + public void testParseNullMaxage() throws Exception { + CookieSpec cookiespec = new RFC2965Spec(); + CookieOrigin origin = new CookieOrigin("www.domain.com", 80, "/", false); + Header header = new BasicHeader("Set-Cookie2", "name=value;Max-age=;Version=1"); + try { + cookiespec.parse(header, origin); + fail("MalformedCookieException should have been thrown"); + } catch (MalformedCookieException ex) { + // expected + } + } + + public void testParseNegativeMaxage() throws Exception { + CookieSpec cookiespec = new RFC2965Spec(); + CookieOrigin origin = new CookieOrigin("www.domain.com", 80, "/", false); + Header header = new BasicHeader("Set-Cookie2", "name=value;Max-age=-3600;Version=1;"); + try { + cookiespec.parse(header, origin); + fail("MalformedCookieException should have been thrown"); + } catch (MalformedCookieException ex) { + // expected + } + } + + /** + * test parsing "Secure" attribute. + */ + public void testParseSecure() throws Exception { + CookieSpec cookiespec = new RFC2965Spec(); + CookieOrigin origin = new CookieOrigin("www.domain.com", 80, "/", false); + Header header = new BasicHeader("Set-Cookie2", "name=value;Secure;Version=1"); + Cookie[] parsed = cookiespec.parse(header, origin); + assertNotNull(parsed); + assertEquals(1, parsed.length); + ClientCookie cookie = (ClientCookie) parsed[0]; + assertTrue(cookie.isSecure()); + } + + /** + * test parsing "Discard" attribute. + */ + public void testParseDiscard() throws Exception { + CookieSpec cookiespec = new RFC2965Spec(); + CookieOrigin origin = new CookieOrigin("www.domain.com", 80, "/", false); + Header header = new BasicHeader("Set-Cookie2", "name=value;Discard;Max-age=36000;Version=1"); + Cookie[] parsed = cookiespec.parse(header, origin); + assertNotNull(parsed); + assertEquals(1, parsed.length); + ClientCookie cookie = (ClientCookie) parsed[0]; + // discard overrides max-age + assertFalse(cookie.isPersistent()); + + // Discard is OPTIONAL, default behavior is dictated by max-age + header = new BasicHeader("Set-Cookie2", "name=value;Max-age=36000;Version=1"); + parsed = cookiespec.parse(header, origin); + assertNotNull(parsed); + assertEquals(1, parsed.length); + cookie = (ClientCookie) parsed[0]; + assertTrue(cookie.isPersistent()); + } + + /** + * test parsing "Comment", "CommentURL" and + * "Secure" attributes. + */ + public void testParseOtherAttributes() throws Exception { + CookieSpec cookiespec = new RFC2965Spec(); + CookieOrigin origin = new CookieOrigin("www.domain.com", 80, "/", false); + Header header = new BasicHeader("Set-Cookie2", "name=value;Comment=\"good cookie\";" + + "CommentURL=\"www.domain.com/goodcookie/\";Secure;Version=1"); + Cookie[] parsed = cookiespec.parse(header, origin); + assertNotNull(parsed); + assertEquals(1, parsed.length); + ClientCookie cookie = (ClientCookie) parsed[0]; + assertEquals("good cookie", cookie.getComment()); + assertEquals("www.domain.com/goodcookie/", cookie.getCommentURL()); + assertTrue(cookie.isSecure()); + + // Comment, CommentURL, Secure are OPTIONAL + header = new BasicHeader("Set-Cookie2", "name=value;Version=1"); + parsed = cookiespec.parse(header, origin); + assertNotNull(parsed); + assertEquals(1, parsed.length); + cookie = (ClientCookie) parsed[0]; + assertFalse(cookie.isSecure()); + } + + /** + * Test parsing header with 2 cookies (separated by comma) + */ + public void testCookiesWithComma() throws Exception { + CookieSpec cookiespec = new RFC2965Spec(); + CookieOrigin origin = new CookieOrigin("www.domain.com", 80, "/", false); + Header header = new BasicHeader("Set-Cookie2", "a=b,c"); + Cookie[] parsed = cookiespec.parse(header, origin); + assertNotNull(parsed); + assertEquals(2, parsed.length); + assertEquals("a", parsed[0].getName()); + assertEquals("b", parsed[0].getValue()); + assertEquals("c", parsed[1].getName()); + assertEquals(null, parsed[1].getValue()); + } + + // ------------------------------------------------------- Test Cookie Validation + + /** + * Test Domain validation when domain is not specified + * in Set-Cookie2 header. + */ + public void testValidateNoDomain() throws Exception { + CookieSpec cookiespec = new RFC2965Spec(); + CookieOrigin origin = new CookieOrigin("www.domain.com", 80, "/", false); + Header header = new BasicHeader("Set-Cookie2", "name=value;Version=1"); + Cookie[] parsed = cookiespec.parse(header, origin); + for (int i = 0; i < parsed.length; i++) { + cookiespec.validate(parsed[i], origin); + } + assertNotNull(parsed); + assertEquals(1, parsed.length); + ClientCookie cookie = (ClientCookie) parsed[0]; + // cookie domain must string match request host + assertEquals("www.domain.com", cookie.getDomain()); + } + + /** + * Test Domain validation. Cookie domain attribute must have a + * leading dot. + */ + public void testValidateDomainLeadingDot() throws Exception { + CookieSpec cookiespec = new RFC2965Spec(); + CookieOrigin origin = new CookieOrigin("www.domain.com", 80, "/", false); + Header header = new BasicHeader("Set-Cookie2", "name=value;Domain=domain.com;Version=1"); + Cookie[] parsed = cookiespec.parse(header, origin); + for (int i = 0; i < parsed.length; i++) { + cookiespec.validate(parsed[i], origin); + } + assertNotNull(parsed); + assertEquals(1, parsed.length); + ClientCookie cookie = (ClientCookie) parsed[0]; + assertEquals(".domain.com", cookie.getDomain()); + } + + /** + * Test Domain validation. Domain must have at least one embedded dot. + */ + public void testValidateDomainEmbeddedDot() throws Exception { + CookieSpec cookiespec = new RFC2965Spec(); + CookieOrigin origin = new CookieOrigin("b.com", 80, "/", false); + Header header = new BasicHeader("Set-Cookie2", "name=value; domain=.com; version=1"); + try { + Cookie[] parsed = cookiespec.parse(header, origin); + for (int i = 0; i < parsed.length; i++) { + cookiespec.validate(parsed[i], origin); + } + fail("MalformedCookieException should have been thrown"); + } catch (MalformedCookieException expected) {} + + origin = new CookieOrigin("www.domain.com", 80, "/", false); + header = new BasicHeader("Set-Cookie2", "name=value;Domain=domain.com;Version=1"); + Cookie[] parsed = cookiespec.parse(header, origin); + for (int i = 0; i < parsed.length; i++) { + cookiespec.validate(parsed[i], origin); + } + assertNotNull(parsed); + assertEquals(1, parsed.length); + } + + /** + * Test local Domain validation. Simple host names + * (without any dots) are valid only when cookie domain is specified + * as ".local". + */ + public void testValidateDomainLocal() throws Exception { + CookieSpec cookiespec = new RFC2965Spec(); + CookieOrigin origin = new CookieOrigin("simplehost", 80, "/", false); + // when domain is specified as .local, simple host names are valid + Header header = new BasicHeader("Set-Cookie2", "name=value; domain=.local; version=1"); + Cookie[] parsed = cookiespec.parse(header, origin); + for (int i = 0; i < parsed.length; i++) { + cookiespec.validate(parsed[i], origin); + } + assertNotNull(parsed); + assertEquals(1, parsed.length); + ClientCookie cookie = (ClientCookie) parsed[0]; + assertEquals(".local", cookie.getDomain()); + + // when domain is NOT specified as .local, simple host names are invalid + header = new BasicHeader("Set-Cookie2", "name=value; domain=domain.com; version=1"); + try { + // since domain is not .local, this must fail + parsed = cookiespec.parse(header, origin); + for (int i = 0; i < parsed.length; i++) { + cookiespec.validate(parsed[i], origin); + } + fail("MalformedCookieException should have been thrown"); + } catch (MalformedCookieException expected) {} + } + + + /** + * Test Domain validation. Effective host name + * must domain-match domain attribute. + */ + public void testValidateDomainEffectiveHost() throws Exception { + CookieSpec cookiespec = new RFC2965Spec(); + + // cookie domain does not domain-match request host + Header header = new BasicHeader("Set-Cookie2", "name=value; domain=.domain.com; version=1"); + try { + CookieOrigin origin = new CookieOrigin("www.domain.org", 80, "/", false); + Cookie[] parsed = cookiespec.parse(header, origin); + for (int i = 0; i < parsed.length; i++) { + cookiespec.validate(parsed[i], origin); + } + fail("MalformedCookieException should have been thrown"); + } catch (MalformedCookieException expected) {} + + CookieOrigin origin = new CookieOrigin("www.domain.com", 80, "/", false); + // cookie domain domain-matches request host + header = new BasicHeader("Set-Cookie2", "name=value; domain=.domain.com; version=1"); + Cookie[] parsed = cookiespec.parse(header, origin); + for (int i = 0; i < parsed.length; i++) { + cookiespec.validate(parsed[i], origin); + } + assertNotNull(parsed); + assertEquals(1, parsed.length); + } + + /** + * Test local Domain validation. + * Effective host name minus domain must not contain any dots. + */ + public void testValidateDomainIllegal() throws Exception { + CookieSpec cookiespec = new RFC2965Spec(); + CookieOrigin origin = new CookieOrigin("a.b.domain.com", 80, "/", false); + Header header = new BasicHeader("Set-Cookie2", "name=value; domain=.domain.com; version=1"); + try { + Cookie[] parsed = cookiespec.parse(header, origin); + for (int i = 0; i < parsed.length; i++) { + cookiespec.validate(parsed[i], origin); + } + fail("MalformedCookieException should have been thrown"); + } catch (MalformedCookieException expected) {} + } + + /** + * Test cookie Path validation. Cookie path attribute must path-match + * request path. + */ + public void testValidatePath() throws Exception { + CookieSpec cookiespec = new RFC2965Spec(); + Header header = new BasicHeader("Set-Cookie2", "name=value;path=/path;version=1"); + try { + CookieOrigin origin = new CookieOrigin("www.domain.com", 80, "/", false); + Cookie[] parsed = cookiespec.parse(header, origin); + for (int i = 0; i < parsed.length; i++) { + cookiespec.validate(parsed[i], origin); + } + fail("MalformedCookieException exception should have been thrown"); + } catch (MalformedCookieException expected) {} + + // path-matching is case-sensitive + header = new BasicHeader("Set-Cookie2", "name=value;path=/Path;version=1"); + try { + CookieOrigin origin = new CookieOrigin("www.domain.com", 80, "/path", false); + Cookie[] parsed = cookiespec.parse(header, origin); + for (int i = 0; i < parsed.length; i++) { + cookiespec.validate(parsed[i], origin); + } + fail("MalformedCookieException exception should have been thrown"); + } catch (MalformedCookieException expected) {} + + CookieOrigin origin = new CookieOrigin("www.domain.com", 80, "/path/path1", false); + header = new BasicHeader("Set-Cookie2", "name=value;path=/path;version=1"); + Cookie[] parsed = cookiespec.parse(header, origin); + for (int i = 0; i < parsed.length; i++) { + cookiespec.validate(parsed[i], origin); + } + assertNotNull(parsed); + assertEquals(1, parsed.length); + assertEquals("/path", parsed[0].getPath()); + } + + /** + * Test cookie name validation. + */ + public void testValidateCookieName() throws Exception { + CookieSpec cookiespec = new RFC2965Spec(); + CookieOrigin origin = new CookieOrigin("127.0.0.1", 80, "/", false); + // cookie name must not contain blanks + Header header = new BasicHeader("Set-Cookie2", "invalid name=value; version=1"); + try { + Cookie[] parsed = cookiespec.parse(header, origin); + for (int i = 0; i < parsed.length; i++) { + cookiespec.validate(parsed[i], origin); + } + fail("MalformedCookieException exception should have been thrown"); + } catch (MalformedCookieException expected) {} + + // cookie name must not start with '$'. + header = new BasicHeader("Set-Cookie2", "$invalid_name=value; version=1"); + try { + Cookie[] parsed = cookiespec.parse(header, origin); + for (int i = 0; i < parsed.length; i++) { + cookiespec.validate(parsed[i], origin); + } + fail("MalformedCookieException exception should have been thrown"); + } catch (MalformedCookieException expected) {} + + // valid name + header = new BasicHeader("Set-Cookie2", "name=value; version=1"); + Cookie[] parsed = cookiespec.parse(header, origin); + assertNotNull(parsed); + assertEquals(1, parsed.length); + ClientCookie cookie = (ClientCookie) parsed[0]; + assertEquals("name", cookie.getName()); + assertEquals("value", cookie.getValue()); + } + + /** + * Test cookie Port validation. Request port must be in the + * port attribute list. + */ + public void testValidatePort() throws Exception { + Header header = new BasicHeader("Set-Cookie2", "name=value; Port=\"80,800\"; version=1"); + CookieSpec cookiespec = new RFC2965Spec(); + try { + CookieOrigin origin = new CookieOrigin("www.domain.com", 8000, "/", false); + Cookie[] parsed = cookiespec.parse(header, origin); + for (int i = 0; i < parsed.length; i++) { + cookiespec.validate(parsed[i], origin); + } + fail("MalformedCookieException should have been thrown"); + } catch (MalformedCookieException e) {} + + // valid port list + CookieOrigin origin = new CookieOrigin("www.domain.com", 80, "/", false); + Cookie[] parsed = cookiespec.parse(header, origin); + for (int i = 0; i < parsed.length; i++) { + cookiespec.validate(parsed[i], origin); + } + assertNotNull(parsed); + assertEquals(1, parsed.length); + ClientCookie cookie = (ClientCookie) parsed[0]; + int[] ports = cookie.getPorts(); + assertNotNull(ports); + assertEquals(2, ports.length); + assertEquals(80, ports[0]); + assertEquals(800, ports[1]); + } + + /** + * Test cookie Version validation. + */ + public void testValidateVersion() throws Exception { + CookieSpec cookiespec = new RFC2965Spec(); + // version attribute is REQUIRED + Header header = new BasicHeader("Set-Cookie2", "name=value"); + try { + CookieOrigin origin = new CookieOrigin("www.domain.com", 8000, "/", false); + Cookie[] parsed = cookiespec.parse(header, origin); + for (int i = 0; i < parsed.length; i++) { + cookiespec.validate(parsed[i], origin); + } + fail("MalformedCookieException should have been thrown"); + } catch (MalformedCookieException e) {} + } + + // ------------------------------------------------------- Test Cookie Matching + + /** + * test cookie Path matching. Cookie path attribute must path-match + * path of the request URI. + */ + public void testMatchPath() throws Exception { + BasicClientCookie2 cookie = new BasicClientCookie2("name", "value"); + cookie.setDomain(".domain.com"); + cookie.setPath("/path"); + cookie.setPorts(new int[] {80}); + CookieSpec cookiespec = new RFC2965Spec(); + CookieOrigin origin1 = new CookieOrigin("www.domain.com", 80, "/", false); + assertFalse(cookiespec.match(cookie, origin1)); + CookieOrigin origin2 = new CookieOrigin("www.domain.com", 80, "/path/path1", false); + assertTrue(cookiespec.match(cookie, origin2)); + } + + /** + * test cookie Domain matching. + */ + public void testMatchDomain() throws Exception { + BasicClientCookie2 cookie = new BasicClientCookie2("name", "value"); + cookie.setDomain(".domain.com"); + cookie.setPath("/"); + cookie.setPorts(new int[] {80}); + CookieSpec cookiespec = new RFC2965Spec(); + // effective host name minus domain must not contain any dots + CookieOrigin origin1 = new CookieOrigin("a.b.domain.com" /* request host */, 80, "/", false); + assertFalse(cookiespec.match(cookie, origin1)); + // The effective host name MUST domain-match the Domain + // attribute of the cookie. + CookieOrigin origin2 = new CookieOrigin("www.domain.org" /* request host */, 80, "/", false); + assertFalse(cookiespec.match(cookie, origin2)); + CookieOrigin origin3 = new CookieOrigin("www.domain.com" /* request host */, 80, "/", false); + assertTrue(cookiespec.match(cookie, origin3)); + } + + /** + * test cookie local Domain matching. + */ + public void testMatchDomainLocal() throws Exception { + BasicClientCookie2 cookie = new BasicClientCookie2("name", "value"); + cookie.setDomain(".local"); + cookie.setPath("/"); + cookie.setPorts(new int[] {80}); + CookieSpec cookiespec = new RFC2965Spec(); + CookieOrigin origin1 = new CookieOrigin("host" /* request host */, 80, "/", false); + assertTrue(cookiespec.match(cookie, origin1)); + CookieOrigin origin2 = new CookieOrigin("host.com" /* request host */, 80, "/", false); + assertFalse(cookiespec.match(cookie, origin2)); + } + + /** + * test cookie Port matching. + */ + public void testMatchPort() throws Exception { + // cookie can be sent to any port if port attribute not specified + BasicClientCookie2 cookie = new BasicClientCookie2("name", "value"); + cookie.setDomain(".domain.com"); + cookie.setPath("/"); + cookie.setPorts(null); + + CookieSpec cookiespec = new RFC2965Spec(); + CookieOrigin origin1 = new CookieOrigin("www.domain.com", 8080 /* request port */, "/", false); + assertTrue(cookiespec.match(cookie, origin1)); + CookieOrigin origin2 = new CookieOrigin("www.domain.com", 323 /* request port */, "/", false); + assertTrue(cookiespec.match(cookie, origin2)); + + // otherwise, request port must be in cookie's port list + cookie = new BasicClientCookie2("name", "value"); + cookie.setDomain(".domain.com"); + cookie.setPath("/"); + cookie.setPorts(new int[] {80, 8080}); + cookie.setAttribute(ClientCookie.PORT_ATTR, "80, 8080"); + CookieOrigin origin3 = new CookieOrigin("www.domain.com", 434 /* request port */, "/", false); + assertFalse(cookiespec.match(cookie, origin3)); + CookieOrigin origin4 = new CookieOrigin("www.domain.com", 8080 /* request port */, "/", false); + assertTrue(cookiespec.match(cookie, origin4)); + } + + /** + * test cookie expiration. + */ + public void testCookieExpiration() throws Exception { + Date now = new Date(); + + Date beforeOneHour = new Date(now.getTime() - 3600 * 1000L); + BasicClientCookie2 cookie = new BasicClientCookie2("name", "value"); + cookie.setDomain(".domain.com"); + cookie.setPath("/"); + cookie.setPorts(null); + cookie.setExpiryDate(beforeOneHour); + + assertTrue(cookie.isExpired(now)); + + Date afterOneHour = new Date(now.getTime() + 3600 * 1000L); + cookie = new BasicClientCookie2("name", "value"); + cookie.setDomain(".domain.com"); + cookie.setPath("/"); + cookie.setPorts(null); + cookie.setExpiryDate(afterOneHour); + + assertFalse(cookie.isExpired(now)); + + // discard attributes overrides cookie age, makes it a session cookie. + cookie.setDiscard(true); + assertFalse(cookie.isPersistent()); + assertTrue(cookie.isExpired(now)); + } + + /** + * test cookie Secure attribute. + */ + public void testCookieSecure() throws Exception { + CookieSpec cookiespec = new RFC2965Spec(); + // secure cookie can only be sent over a secure connection + BasicClientCookie2 cookie = new BasicClientCookie2("name", "value"); + cookie.setDomain(".domain.com"); + cookie.setPath("/"); + cookie.setSecure(true); + CookieOrigin origin1 = new CookieOrigin("www.domain.com", 80, "/", false); + assertFalse(cookiespec.match(cookie, origin1)); + CookieOrigin origin2 = new CookieOrigin("www.domain.com", 80, "/", true); + assertTrue(cookiespec.match(cookie, origin2)); + } + + // ------------------------------------------------------- Test Cookie Formatting + + /** + * Tests RFC 2965 compliant cookie formatting. + */ + public void testRFC2965CookieFormatting() throws Exception { + CookieSpec cookiespec = new RFC2965Spec(null, true); + BasicClientCookie2 cookie1 = new BasicClientCookie2("name1", "value"); + cookie1.setDomain(".domain.com"); + cookie1.setPath("/"); + cookie1.setPorts(new int[] {80,8080}); + cookie1.setVersion(1); + // domain, path, port specified + cookie1.setAttribute(ClientCookie.DOMAIN_ATTR, ".domain.com"); + cookie1.setAttribute(ClientCookie.PATH_ATTR, "/"); + cookie1.setAttribute(ClientCookie.PORT_ATTR, "80,8080"); + + Header[] headers = cookiespec.formatCookies(new Cookie[] {cookie1}); + assertNotNull(headers); + assertEquals(1, headers.length); + assertEquals("$Version=1; name1=\"value\"; $Path=\"/\"; $Domain=\".domain.com\"; $Port=\"80,8080\"", + headers[0].getValue()); + + + BasicClientCookie2 cookie2 = new BasicClientCookie2("name2", "value"); + cookie2.setDomain(".domain.com"); + cookie2.setPath("/a/"); + cookie2.setPorts(new int[] {80,8080}); + cookie2.setVersion(2); + // domain, path specified but port unspecified + cookie2.setAttribute(ClientCookie.DOMAIN_ATTR, ".domain.com"); + cookie2.setAttribute(ClientCookie.PATH_ATTR, "/a/"); + + headers = cookiespec.formatCookies(new Cookie[] {cookie2}); + assertNotNull(headers); + assertEquals(1, headers.length); + assertEquals("$Version=2; name2=\"value\"; $Path=\"/a/\"; $Domain=\".domain.com\"", + headers[0].getValue()); + + BasicClientCookie2 cookie3 = new BasicClientCookie2("name3", "value"); + cookie3.setDomain(".domain.com"); + cookie3.setPath("/a/b/"); + cookie3.setPorts(new int[] {80,8080}); + cookie3.setVersion(1); + // path specified, port specified but blank, domain unspecified + cookie3.setAttribute(ClientCookie.PATH_ATTR, "/a/b/"); + cookie3.setAttribute(ClientCookie.PORT_ATTR, " "); + + headers = cookiespec.formatCookies(new Cookie[] {cookie3}); + assertNotNull(headers); + assertEquals(1, headers.length); + assertEquals("$Version=1; name3=\"value\"; $Path=\"/a/b/\"; $Port=\"\"", + headers[0].getValue()); + + headers = cookiespec.formatCookies(new Cookie[] {cookie3, cookie2, cookie1}); + assertNotNull(headers); + assertEquals(1, headers.length); + assertEquals("$Version=1; " + + "name3=\"value\"; $Path=\"/a/b/\"; $Port=\"\"; " + + "name2=\"value\"; $Path=\"/a/\"; $Domain=\".domain.com\"; " + + "name1=\"value\"; $Path=\"/\"; $Domain=\".domain.com\"; $Port=\"80,8080\"", + headers[0].getValue()); + } + + /** + * Tests RFC 2965 compliant cookies formatting. + */ + public void testRFC2965CookiesFormatting() throws Exception { + CookieSpec cookiespec = new RFC2965Spec(null, true); + BasicClientCookie2 cookie1 = new BasicClientCookie2("name1", "value1"); + cookie1.setDomain(".domain.com"); + cookie1.setPath("/"); + cookie1.setPorts(new int[] {80,8080}); + cookie1.setVersion(1); + // domain, path, port specified + cookie1.setAttribute(ClientCookie.DOMAIN_ATTR, ".domain.com"); + cookie1.setAttribute(ClientCookie.PATH_ATTR, "/"); + cookie1.setAttribute(ClientCookie.PORT_ATTR, "80,8080"); + + BasicClientCookie2 cookie2 = new BasicClientCookie2("name2", ""); + cookie2.setDomain(".domain.com"); + cookie2.setPath("/"); + cookie2.setPorts(new int[] {80,8080}); + cookie2.setVersion(1); + // value null, domain, path specified + cookie2.setAttribute(ClientCookie.DOMAIN_ATTR, ".domain.com"); + cookie2.setAttribute(ClientCookie.PATH_ATTR, "/"); + + Header[] headers = cookiespec.formatCookies(new Cookie[] {cookie1, cookie2}); + assertNotNull(headers); + assertEquals(1, headers.length); + + assertEquals("$Version=1; name1=\"value1\"; $Path=\"/\"; $Domain=\".domain.com\"; $Port=\"80,8080\"; " + + "name2=\"\"; $Path=\"/\"; $Domain=\".domain.com\"", + headers[0].getValue()); + } + + // ------------------------------------------------------- Backward compatibility tests + + /** + * Test backward compatibility with Set-Cookie header. + */ + public void testCompatibilityWithSetCookie() throws Exception { + CookieSpec cookiespec = new RFC2965Spec(); + CookieOrigin origin = new CookieOrigin("www.domain.com", 80, "/", false); + Header header = new BasicHeader("Set-Cookie", "name=value; domain=.domain.com; version=1"); + Cookie[] parsed = cookiespec.parse(header, origin); + assertNotNull(parsed); + assertEquals(1, parsed.length); + assertEquals("name", parsed[0].getName()); + assertEquals("value", parsed[0].getValue()); + assertEquals(".domain.com", parsed[0].getDomain()); + assertEquals("/", parsed[0].getPath()); + } + +} +