Index: src/test/org/apache/commons/httpclient/cookie/TestCookieAll.java
===================================================================
--- src/test/org/apache/commons/httpclient/cookie/TestCookieAll.java (revision 264062)
+++ src/test/org/apache/commons/httpclient/cookie/TestCookieAll.java (working copy)
@@ -43,8 +43,10 @@
public static Test suite() {
TestSuite suite = new TestSuite();
suite.addTest(TestCookie.suite());
+ suite.addTest(TestCookie2.suite());
suite.addTest(TestCookieCompatibilitySpec.suite());
suite.addTest(TestCookieRFC2109Spec.suite());
+ suite.addTest(TestCookieRFC2965Spec.suite());
suite.addTest(TestCookieNetscapeDraft.suite());
suite.addTest(TestCookieIgnoreSpec.suite());
suite.addTest(TestCookiePolicy.suite());
Index: src/test/org/apache/commons/httpclient/cookie/TestCookieRFC2965Spec.java
===================================================================
--- src/test/org/apache/commons/httpclient/cookie/TestCookieRFC2965Spec.java (revision 264062)
+++ src/test/org/apache/commons/httpclient/cookie/TestCookieRFC2965Spec.java (working copy)
@@ -1,5 +1,5 @@
/*
- * $Header: /cvsroot/httpc-cookie2/httpc-cookie2/httpcookie2SVN-patch.082805-2100.diff,v 1.1 2005/08/29 05:01:58 sjain700 Exp $
+ * $Header: /cvsroot/httpc-cookie2/httpc-cookie2/httpcookie2SVN-patch.082805-2100.diff,v 1.1 2005/08/29 05:01:58 sjain700 Exp $
* $Revision: 1.1 $
* $Date: 2005/08/29 05:01:58 $
* ====================================================================
@@ -34,7 +34,11 @@
import org.apache.commons.httpclient.Cookie;
import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.NameValuePair;
+import org.apache.commons.httpclient.Cookie2;
+import java.util.Date;
+import java.util.Arrays;
+
/**
* Test cases for RFC2965 cookie spec
*
@@ -42,7 +46,6 @@
*/
public class TestCookieRFC2965Spec extends TestCookieBase {
-
// ------------------------------------------------------------ Constructor
public TestCookieRFC2965Spec(String name) {
@@ -55,483 +58,700 @@
return new TestSuite(TestCookieRFC2965Spec.class);
}
- public void testParseAttributeInvalidParams() throws Exception {
+
+ // ------------------------------------------------------- Test Cookie Parsing
+
+ /**
+ * Test parse with invalid params.
+ */
+ public void testParseInvalidParams() throws Exception {
CookieSpec cookiespec = new RFC2965Spec();
- Cookie cookie = new Cookie();
- NameValuePair attribute = new NameValuePair("name", "value");
try {
- cookiespec.parseAttribute(null, cookie);
+ // invalid header
+ cookiespec.parse("www.domain.com", 80, "/", false, (Header) null /* header */);
fail("IllegalArgumentException must have been thrown");
- } catch (IllegalArgumentException expected) {
- }
+ } catch (IllegalArgumentException expected) {}
+
+ Header header = new Header("Set-Cookie2", "name=value;Version=1");
try {
- cookiespec.parseAttribute(attribute, null);
+ // invalid request host
+ cookiespec.parse(null /* host */, 80, "/", false, header);
fail("IllegalArgumentException must have been thrown");
- } catch (IllegalArgumentException expected) {
- }
+ } catch (IllegalArgumentException expected) {}
+ try {
+ // invalid request port
+ cookiespec.parse("www.domain.com", -32 /* port */, "/", false, header);
+ fail("IllegalArgumentException must have been thrown");
+ } catch (IllegalArgumentException expected) {}
+ try {
+ // invalid request path
+ cookiespec.parse("www.domain.com", 80, null /* path */, false, header);
+ fail("IllegalArgumentException must have been thrown");
+ } catch (IllegalArgumentException expected) {}
}
- public void testParseAttributePath() throws Exception {
+ /**
+ * Test parsing cookie "Path" attribute.
+ */
+ public void testParsePath() throws Exception {
CookieSpec cookiespec = new RFC2965Spec();
- Cookie cookie = new Cookie();
- try {
- cookiespec.parseAttribute(new NameValuePair("path", null), cookie);
- fail("MalformedCookieException must have been thrown");
- } catch (MalformedCookieException expected) {
- }
- try {
- cookiespec.parseAttribute(new NameValuePair("path", " "), cookie);
- fail("MalformedCookieException must have been thrown");
- } catch (MalformedCookieException expected) {
- }
- cookiespec.parseAttribute(new NameValuePair("path", "/"), cookie);
- assertEquals(cookie.getPath(), "/");
- // this will be ignored since path is already specified for this cookie
- cookiespec.parseAttribute(new NameValuePair("path", null), cookie);
+ // path cannot be null
+ Header header = new Header("Set-Cookie2", "name=value;Path=;Version=1");
+ Cookie[] parsed = cookiespec.parse("www.domain.com", 80, "/", false, header);
+ assertNull(parsed);
+ //path cannot be blank
+ header = new Header("Set-Cookie2", "name=value;Path=\" \";Version=1");
+ parsed = cookiespec.parse("www.domain.com", 80, "/", false, header);
+ assertNull(parsed);
+
+ // valid path
+ header = new Header("Set-Cookie2", "name=value;Path=/;Version=1;Path=");
+ parsed = cookiespec.parse("www.domain.com", 80, "/", false, header);
+ assertNotNull(parsed);
+ assertEquals(1, parsed.length);
+ // only the first occurence of path attribute is considered, others ignored
+ Cookie2 cookie = (Cookie2) parsed[0];
+ assertEquals("/", cookie.getPath());
+ assertTrue(cookie.isPathAttributeSpecified());
+
+ // Path is OPTIONAL, defaults to the request path
+ header = new Header("Set-Cookie2", "name=value;Version=1");
+ parsed = cookiespec.parse("www.domain.com", 80, "/path" /* request path */, false, header);
+ assertNotNull(parsed);
+ assertEquals(1, parsed.length);
+ cookie = (Cookie2) parsed[0];
+ assertEquals("/path", cookie.getPath());
+ assertFalse(cookie.isPathAttributeSpecified());
}
- public void testParseAttributeDomain() throws Exception {
+ /**
+ * Test parsing cookie "Domain" attribute.
+ */
+ public void testParseDomain() throws Exception {
CookieSpec cookiespec = new RFC2965Spec();
- Cookie cookie = new Cookie();
- try {
- cookiespec.parseAttribute(new NameValuePair("domain", null), cookie);
- fail("MalformedCookieException must have been thrown");
- } catch (MalformedCookieException expected) {
- }
- try {
- cookiespec.parseAttribute(new NameValuePair("domain", " "), cookie);
- fail("MalformedCookieException must have been thrown");
- } catch (MalformedCookieException expected) {
- }
- cookiespec.parseAttribute(new NameValuePair("domain", ".domain.com"), cookie);
- assertEquals(cookie.getDomain(), ".domain.com");
- // this will be ignored since domain is already specified for this cookie
- cookiespec.parseAttribute(new NameValuePair("domain", null), cookie);
+ // domain cannot be null
+ Header header = new Header("Set-Cookie2", "name=value;Domain=;Version=1");
+ Cookie[] parsed = cookiespec.parse("www.domain.com", 80, "/", false, header);
+ assertNull(parsed);
- // puts a dot in front of domain if missing
- cookie = new Cookie();
- cookiespec.parseAttribute(new NameValuePair("domain", "domain.com"), cookie);
- assertEquals(cookie.getDomain(), ".domain.com");
+ // domain cannot be blank
+ header = new Header("Set-Cookie2", "name=value;Domain=\" \";Version=1");
+ parsed = cookiespec.parse("www.domain.com", 80, "/", false, header);
+ assertNull(parsed);
+
+ header = new Header("Set-Cookie2", "name=value;Domain=.domain.com;Version=1;Domain=");
+ parsed = cookiespec.parse("www.domain.com", 80, "/", false, header);
+ assertNotNull(parsed);
+ assertEquals(1, parsed.length);
+ // only the first occurence of domain attribute is considered, others ignored
+ Cookie2 cookie = (Cookie2) parsed[0];
+ assertEquals(".domain.com", cookie.getDomain());
+ assertTrue(cookie.isDomainAttributeSpecified());
+
+ // should put a leading dot if there is no dot in front of domain
+ header = new Header("Set-Cookie2", "name=value;Domain=domain.com;Version=1");
+ parsed = cookiespec.parse("www.domain.com", 80, "/", false, header);
+ assertNotNull(parsed);
+ assertEquals(1, parsed.length);
+ cookie = (Cookie2) parsed[0];
+ assertEquals(".domain.com", cookie.getDomain());
+
+ // Domain is OPTIONAL, defaults to the request host
+ header = new Header("Set-Cookie2", "name=value;Version=1");
+ parsed = cookiespec.parse("www.domain.com" /* request host */, 80, "/", false, header);
+ assertNotNull(parsed);
+ assertEquals(1, parsed.length);
+ cookie = (Cookie2) parsed[0];
+ assertEquals("www.domain.com", cookie.getDomain());
+ assertFalse(cookie.isDomainAttributeSpecified());
}
- public void testParseAttributePort() throws Exception {
+ /**
+ * Test parsing cookie "Port" attribute.
+ */
+ public void testParsePort() throws Exception {
CookieSpec cookiespec = new RFC2965Spec();
-
- Cookie cookie = new Cookie();
- cookie.setPorts(new int[] {80}); //default to request port
- cookiespec.parseAttribute(new NameValuePair("port", null), cookie);
// null port defaults to request port
- assertTrue(equalArray(cookie.getPorts(), new int[] {80}));
+ Header header = new Header("Set-Cookie2", "name=value;Port=;Version=1");
+ Cookie[] parsed = cookiespec.parse("www.domain.com", 80 /* request port */, "/", false, header);
+ assertNotNull(parsed);
+ assertEquals(1, parsed.length);
+ Cookie2 cookie = (Cookie2) parsed[0];
+ assertEquals(new int[] {80}, cookie.getPorts());
+ assertTrue(cookie.isPortAttributeSpecified() && cookie.isPortAttributeBlank());
- cookie = new Cookie();
- cookie.setPorts(new int[] {80}); //default to request port
- cookiespec.parseAttribute(new NameValuePair("port", " "), cookie);
// blank port defaults to request port
- assertTrue(equalArray(cookie.getPorts(), new int[] {80}));
+ header = new Header("Set-Cookie2", "name=value;Port=\" \";Version=1");
+ parsed = cookiespec.parse("www.domain.com", 80 /* request port */, "/", false, header);
+ assertNotNull(parsed);
+ assertEquals(1, parsed.length);
+ cookie = (Cookie2) parsed[0];
+ assertEquals(new int[] {80}, cookie.getPorts());
+ assertTrue(cookie.isPortAttributeSpecified() && cookie.isPortAttributeBlank());
- try {
- cookie = new Cookie();
- cookiespec.parseAttribute(new NameValuePair("port", "nonsense"), cookie);
- // anything else not an 'int' must fail
- fail("MalformedCookieException must have been thrown");
- } catch (MalformedCookieException expected) {
- }
+ // invalid port
+ header = new Header("Set-Cookie2", "name=value;Port=nonsense;Version=1");
+ parsed = cookiespec.parse("www.domain.com", 80, "/", false, header);
+ assertNull(parsed);
- cookie = new Cookie();
- cookiespec.parseAttribute(new NameValuePair("port", "80,8080"), cookie);
- assertTrue(equalArray(cookie.getPorts(), new int[] {80,8080}));
- // this will be ignored since port already specified for this cookie
- cookiespec.parseAttribute(new NameValuePair("port", "nonsense"), cookie);
+ // all ports must be > 0
+ header = new Header("Set-Cookie2", "name=value;Port=\"80,-800,8000\";Version=1");
+ parsed = cookiespec.parse("www.domain.com", 80, "/", false, header);
+ assertNull(parsed);
+
+ // valid ports
+ header = new Header("Set-Cookie2", "name=value;Port=\"80,800,8000\";Version=1;Port=nonsense");
+ parsed = cookiespec.parse("www.domain.com", 80, "/", false, header);
+ assertNotNull(parsed);
+ assertEquals(1, parsed.length);
+ // only the first occurence of port attribute is considered, others ignored
+ cookie = (Cookie2) parsed[0];
+ assertEquals(new int[] {80,800,8000}, cookie.getPorts());
+ assertTrue(cookie.isPortAttributeSpecified());
+
+ // Port is OPTIONAL, cookie can be accepted from any port
+ header = new Header("Set-Cookie2", "name=value;Version=1");
+ parsed = cookiespec.parse("www.domain.com", 80, "/", false, header);
+ assertNotNull(parsed);
+ assertEquals(1, parsed.length);
+ cookie = (Cookie2) parsed[0];
+ assertFalse(cookie.isPortAttributeSpecified());
}
- public void testParseAttributeVersion() throws Exception {
+ /**
+ * test parsing cookie name/value.
+ */
+ public void testParseNameValue() throws Exception {
CookieSpec cookiespec = new RFC2965Spec();
- Cookie cookie = new Cookie();
- try {
- cookiespec.parseAttribute(new NameValuePair("version", null), cookie);
- fail("MalformedCookieException must have been thrown");
- } catch (MalformedCookieException expected) {
- }
-
- try {
- cookiespec.parseAttribute(new NameValuePair("version", "nonsense"), cookie);
- fail("MalformedCookieException must have been thrown");
- } catch (MalformedCookieException expected) {
- }
- cookiespec.parseAttribute(new NameValuePair("version", "1"), cookie);
- assertEquals(cookie.getVersion(), 1);
+ Header header = new Header("Set-Cookie2", "name=value;Version=1;");
+ Cookie[] parsed = cookiespec.parse("www.domain.com", 80, "/", false, header);
+ assertNotNull(parsed);
+ assertEquals(1, parsed.length);
+ Cookie2 cookie = (Cookie2) parsed[0];
+ assertEquals("name", cookie.getName());
+ assertEquals("value", cookie.getValue());
}
- public void testParseAttributeMaxage() throws Exception {
+ /**
+ * test parsing cookie "Version" attribute.
+ */
+ public void testParseVersion() throws Exception {
CookieSpec cookiespec = new RFC2965Spec();
- Cookie cookie = new Cookie();
- try {
- cookiespec.parseAttribute(new NameValuePair("max-age", null), cookie);
- fail("MalformedCookieException must have been thrown");
- } catch (MalformedCookieException expected) {
- }
+ // version cannot ne null
+ Header header = new Header("Set-Cookie2", "name=value;Version=;");
+ Cookie[] parsed = cookiespec.parse("www.domain.com", 80, "/", false, header);
+ assertNull(parsed);
- try {
- cookiespec.parseAttribute(new NameValuePair("max-age", "nonsense"), cookie);
- fail("MalformedCookieException must have been thrown");
- } catch (MalformedCookieException expected) {
- }
- cookiespec.parseAttribute(new NameValuePair("max-age", "3600"), cookie);
- assertNotNull(cookie.getExpiryDate());
+ // version must be > 0
+ header = new Header("Set-Cookie2", "name=value;Version=-1;");
+ parsed = cookiespec.parse("www.domain.com", 80, "/", false, header);
+ assertNull(parsed);
+
+ // valid version
+ header = new Header("Set-Cookie2", "name=value;Version=1;");
+ parsed = cookiespec.parse("www.domain.com", 80, "/", false, header);
+ assertNotNull(parsed);
+ assertEquals(1, parsed.length);
+ Cookie2 cookie = (Cookie2) parsed[0];
+ assertEquals(1, cookie.getVersion());
+ assertTrue(cookie.isVersionAttributeSpecified());
}
- public void testParseAttributeOthers() throws Exception {
+ /**
+ * test parsing cookie "Max-age" attribute.
+ */
+ public void testParseMaxage() throws Exception {
CookieSpec cookiespec = new RFC2965Spec();
- Cookie cookie = new Cookie();
- // test comment
- String comment = "test cookie";
- cookiespec.parseAttribute(new NameValuePair("comment", comment), cookie);
- assertEquals(cookie.getComment(), comment);
- // test comment URL
- String commentURL = "www.sourceforge.com/cookiedesc";
- cookiespec.parseAttribute(new NameValuePair("commenturl", commentURL), cookie);
- assertEquals(cookie.getCommentURL(), commentURL);
- // test secure
- cookiespec.parseAttribute(new NameValuePair("secure", null), cookie);
- assertTrue(cookie.getSecure());
- // test discard
- cookiespec.parseAttribute(new NameValuePair("discard", null), cookie);
+ // max-age cannot be null
+ Header header = new Header("Set-Cookie2", "name=value;Max-age=;Version=1");
+ Cookie[] parsed = cookiespec.parse("www.domain.com", 80, "/", false, header);
+ assertNull(parsed);
+
+ // max-age must be > 0
+ header = new Header("Set-Cookie2", "name=value;Max-age=-3600;Version=1;");
+ parsed = cookiespec.parse("www.domain.com", 80, "/", false, header);
+ assertNull(parsed);
+
+ // valid max-age
+ header = new Header("Set-Cookie2", "name=value;Max-age=3600;Version=1;Max-age=nonsense");
+ parsed = cookiespec.parse("www.domain.com", 80, "/", false, header);
+ assertNotNull(parsed);
+ assertEquals(1, parsed.length);
+ // only the first occurence of max-age attribute is considered, others ignored
+ Cookie2 cookie = (Cookie2) parsed[0];
+ assertFalse(cookie.isExpired());
+
+ // Max-age is OPTIONAL, defaults to session cookie
+ header = new Header("Set-Cookie2", "name=value;Version=1");
+ parsed = cookiespec.parse("www.domain.com", 80, "/", false, header);
+ assertNotNull(parsed);
+ assertEquals(1, parsed.length);
+ cookie = (Cookie2) parsed[0];
assertFalse(cookie.isPersistent());
}
/**
- * Test domain equals host
+ * test parsing "Discard" attribute.
*/
- public void testParseValidateDomainEqualsHost() throws Exception {
- Header header = new Header("Set-Cookie2",
- "cookie-name=cookie-value; domain=www.b.com; version=1");
+ public void testParseDiscard() throws Exception {
CookieSpec cookiespec = new RFC2965Spec();
- Cookie[] parsed = cookieParse(cookiespec, "www.b.com", 80, "/", false, header);
+ Header header = new Header("Set-Cookie2", "name=value;Discard;Max-age=36000;Version=1");
+ Cookie[] parsed = cookiespec.parse("www.domain.com", 80, "/", false, header);
assertNotNull(parsed);
assertEquals(1, parsed.length);
- assertEquals("www.b.com", parsed[0].getDomain());
+ Cookie2 cookie = (Cookie2) parsed[0];
+ // discard overrides max-age
+ assertFalse(cookie.isPersistent());
- // test backward compatibility with Set-Cookie header
- header = new Header("Set-Cookie",
- "cookie-name=cookie-value; domain=www.b.com; version=1");
- parsed = cookieParse(cookiespec, "www.b.com", 80, "/", false, header);
+ // Discard is OPTIONAL, default behavior is dictated by max-age
+ header = new Header("Set-Cookie2", "name=value;Version=1");
+ parsed = cookiespec.parse("www.domain.com", 80, "/", false, header);
assertNotNull(parsed);
assertEquals(1, parsed.length);
- assertEquals("www.b.com", parsed[0].getDomain());
}
/**
- * Test Domain with no leading dot in domain name
+ * test parsing "Comment", "CommentURL" and
+ * "Secure" attributes.
*/
- public void testParseValidateDomainLeadingDot() throws Exception {
- // set-cookie2 header works without a leading dot
- Header header = new Header("Set-Cookie2",
- "cookie-name=cookie-value; domain=a.b.com; version=1");
+ public void testParseOtherAttributes() throws Exception {
CookieSpec cookiespec = new RFC2965Spec();
- Cookie[] parsed = cookieParse(cookiespec, "www.a.b.com", 80, "/", false, header);
+ Header header = new Header("Set-Cookie2", "name=value;Comment=\"good cookie\";" +
+ "CommentURL=\"www.domain.com/goodcookie/\";Secure;Version=1");
+ Cookie[] parsed = cookiespec.parse("www.domain.com", 80, "/", false, header);
assertNotNull(parsed);
assertEquals(1, parsed.length);
- assertEquals(".a.b.com", parsed[0].getDomain());
+ Cookie2 cookie = (Cookie2) parsed[0];
+ assertEquals("good cookie", cookie.getComment());
+ assertEquals("www.domain.com/goodcookie/", cookie.getCommentURL());
+ assertTrue(cookie.getSecure());
- // test backward compatibility; set-cookie header must fail
- // without a leading dot in domain name
- header = new Header("Set-Cookie",
- "cookie-name=cookie-value; domain=a.b.com; version=1");
- try {
- parsed = cookieParse(cookiespec, "www.a.b.com", 80, "/", false, header);
- fail("MalformedCookieException should have been thrown");
- } catch (MalformedCookieException e) {
- // expected
- }
+ // Comment, CommentURL, Secure are OPTIONAL
+ header = new Header("Set-Cookie2", "name=value;Version=1");
+ parsed = cookiespec.parse("www.domain.com", 80, "/", false, header);
+ assertNotNull(parsed);
+ assertEquals(1, parsed.length);
+ cookie = (Cookie2) parsed[0];
+ assertFalse(cookie.getSecure());
}
/**
- * Domain must have atleast least one embedded dot
+ * Test parsing header with 2 cookies (separated by comma)
*/
- public void testParseValidateDomainEmbeddedDot() throws Exception {
- Header header = new Header("Set-Cookie2",
- "cookie-name=cookie-value; domain=.com; version=1");
+ public void testCookiesWithComma() throws Exception {
+ CookieSpec cookiespec = new RFC2965Spec();
+ Header header = new Header("Set-Cookie2", "a=b,c");
+ Cookie[] parsed = cookiespec.parse("www.domain.com", 80, "/", false, header);
+ 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();
- try {
- Cookie[] parsed = cookieParse(cookiespec, "b.com", 80, "/", false, header);
- fail("MalformedCookieException should have been thrown");
- } catch (MalformedCookieException e) {
- // expected
- }
-
- // test backward compatibility with Set-Cookie header
- header = new Header("Set-Cookie2",
- "cookie-name=cookie-value; domain=.com; version=1");
- try {
- Cookie[] parsed = cookieParse(cookiespec, "b.com", 80, "/", false, header);
- fail("MalformedCookieException should have been thrown");
- } catch (MalformedCookieException e) {
- // expected
- }
+ Header header = new Header("Set-Cookie2", "name=value;Version=1");
+ Cookie[] parsed = cookieParse(cookiespec, "www.domain.com" /* request host */, 80, "/", false, header);
+ assertNotNull(parsed);
+ assertEquals(1, parsed.length);
+ Cookie2 cookie = (Cookie2) parsed[0];
+ // cookie domain must string match request host
+ assertEquals("www.domain.com", cookie.getDomain());
}
/**
- * test .local domain
+ * Test Domain validation. Cookie domain attribute must have a
+ * leading dot.
*/
- public void testParseValidateDomainLocal() throws Exception {
- Header header = new Header("Set-Cookie2",
- "cookie-name=cookie-value; domain=.local; version=1");
+ public void testValidateDomainLeadingDot() throws Exception {
CookieSpec cookiespec = new RFC2965Spec();
- Cookie[] parsed = cookieParse(cookiespec, "host", 80, "/", false, header);
+ Header header = new Header("Set-Cookie2", "name=value;Domain=domain.com;Version=1");
+ Cookie[] parsed = cookieParse(cookiespec, "www.domain.com", 80, "/", false, header);
assertNotNull(parsed);
assertEquals(1, parsed.length);
- assertEquals(".local", parsed[0].getDomain());
- //TODO: however simple host name like 'host' should not be accepted if domain name is not .local
+ Cookie2 cookie = (Cookie2) parsed[0];
+ assertEquals(".domain.com", cookie.getDomain());
}
/**
- * effective host name minus domain must not contain any dots.
+ * Test Domain validation. Domain must have atleast one embedded dot.
*/
- public void testParseValidateDomainIllegal() throws Exception {
- Header header = new Header("Set-Cookie2",
- "cookie-name=cookie-value; domain=.c.com; version=1");
+ public void testValidateDomainEmbeddedDot() throws Exception {
CookieSpec cookiespec = new RFC2965Spec();
+ Header header = new Header("Set-Cookie2", "name=value; domain=.com; version=1");
try {
- Cookie[] parsed = cookieParse(cookiespec, "a.b.c.com", 80, "/", false, header);
+ cookieParse(cookiespec, "b.com", 80, "/", false, header);
fail("MalformedCookieException should have been thrown");
- } catch (MalformedCookieException e) {
- // expected
- }
+ } catch (MalformedCookieException expected) {}
- // test backward compatibility with Set-Cookie header
- header = new Header("Set-Cookie",
- "cookie-name=cookie-value; domain=.c.com; version=1");
+ header = new Header("Set-Cookie2", "name=value;Domain=domain.com;Version=1");
+ Cookie[] parsed = cookieParse(cookiespec, "www.domain.com", 80, "/", false, header);
+ 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();
+ // when domain is specified as .local, simple host names are valid
+ Header header = new Header("Set-Cookie2", "name=value; domain=.local; version=1");
+ Cookie[] parsed = cookieParse(cookiespec, "simplehost" /* request host */, 80, "/", false, header);
+ assertNotNull(parsed);
+ assertEquals(1, parsed.length);
+ Cookie2 cookie = (Cookie2) parsed[0];
+ assertEquals(".local", cookie.getDomain());
+
+ // when domain is NOT specified as .local, simple host names are invalid
+ header = new Header("Set-Cookie2", "name=value; domain=domain.com; version=1");
try {
- Cookie[] parsed = cookieParse(cookiespec, "a.b.c.com", 80, "/", false, header);
+ // since domain is not .local, this must fail
+ parsed = cookieParse(cookiespec, "simplehost" /* request host */, 80, "/", false, header);
fail("MalformedCookieException should have been thrown");
- } catch (MalformedCookieException e) {
- // expected
- }
+ } catch (MalformedCookieException expected) {}
}
+
/**
- * test version.
+ * Test Domain validation. Effective host name
+ * must domain-match domain attribute.
*/
- public void testParseValidateVersion() throws Exception {
- Header header = new Header("Set-Cookie2", "cookie-name=cookie-value");
+ public void testValidateDomainEffectiveHost() throws Exception {
CookieSpec cookiespec = new RFC2965Spec();
+
+ // cookie domain does not domain-match request host
+ Header header = new Header("Set-Cookie2", "name=value; domain=.domain.com; version=1");
try {
- Cookie[] parsed = cookieParse(cookiespec, "c.a.com", 80, "/", false, header);
- // version attribute is REQUIRED
+ cookieParse(cookiespec, "www.domain.org" /* request host */, 80, "/", false, header);
fail("MalformedCookieException should have been thrown");
- } catch (MalformedCookieException e) {
- // expected
- }
+ } catch (MalformedCookieException expected) {}
- // test backward compatibility with Set-Cookie header
- header = new Header("Set-Cookie", "cookie-name=cookie-value");
+ // cookie domain domain-matches request host
+ header = new Header("Set-Cookie2", "name=value; domain=.domain.com; version=1");
+ Cookie[] parsed = cookieParse(cookiespec, "www.domain.com" /* request host */, 80, "/", false, header);
+ 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();
+ Header header = new Header("Set-Cookie2", "name=value; domain=.domain.com; version=1");
try {
- Cookie[] parsed = cookieParse(cookiespec, "c.a.com", 80, "/", false, header);
- // version attribute is REQUIRED
+ cookieParse(cookiespec, "a.b.domain.com" /* request host */, 80, "/", false, header);
fail("MalformedCookieException should have been thrown");
- } catch (MalformedCookieException e) {
- // expected
- }
+ } catch (MalformedCookieException expected) {}
}
/**
- * test path.
+ * Test cookie Path validation. Cookie path attribute must path-match
+ * request path.
*/
- public void testParseValidatePath() throws Exception {
- Header header = new Header("Set-Cookie2",
- "cookie-name=cookie-value; domain=127.0.0.1; path=/path");
+ public void testValidatePath() throws Exception {
CookieSpec cookiespec = new RFC2965Spec();
+ Header header = new Header("Set-Cookie2", "name=value;path=/path;version=1");
try {
- Cookie[] parsed = cookieParse(cookiespec, "127.0.0.1", 80, "/", false, header);
- fail("HttpException exception should have been thrown");
- } catch (MalformedCookieException e) {
- // expected
- }
- Cookie[] parsed = cookieParse(cookiespec, "127.0.0.1", 80, "/path/path1", false, header);
- assertNotNull(parsed);
- assertEquals(1, parsed.length);
- assertEquals("/path", parsed[0].getDomain());
+ cookieParse(cookiespec, "www.domain.com", 80, "/" /* request path */, false, header);
+ fail("MalformedCookieException exception should have been thrown");
+ } catch (MalformedCookieException expected) {}
- // test backward compatibility with Set-Cookie header
- header = new Header("Set-Cookie",
- "cookie-name=cookie-value; domain=127.0.0.1; path=/path");
+ // path-matching is case-sensitive
+ header = new Header("Set-Cookie2", "name=value;path=/Path;version=1");
try {
- parsed = cookieParse(cookiespec, "127.0.0.1", 80, "/", false, header);
- fail("HttpException exception should have been thrown");
- } catch (MalformedCookieException e) {
- // expected
- }
- parsed = cookieParse(cookiespec, "127.0.0.1", 80, "/path/path1", false, header);
+ cookieParse(cookiespec, "www.domain.com", 80, "/path" /* request path */, false, header);
+ fail("MalformedCookieException exception should have been thrown");
+ } catch (MalformedCookieException expected) {}
+
+ header = new Header("Set-Cookie2", "name=value;path=/path;version=1");
+ Cookie[] parsed = cookieParse(cookiespec, "www.domain.com",
+ 80, "/path/path1" /* request path */, false, header);
assertNotNull(parsed);
assertEquals(1, parsed.length);
- assertEquals("/path", parsed[0].getDomain());
+ assertEquals("/path", parsed[0].getPath());
}
/**
- * Tests if cookie constructor rejects cookie name containing blanks.
+ * Test cookie name validation.
*/
- public void testCookieNameWithBlanks() throws Exception {
- Header header = new Header("Set-Cookie2", "invalid name=");
+ public void testValidateCookieName() throws Exception {
CookieSpec cookiespec = new RFC2965Spec();
+ // cookie name must not contain blanks
+ Header header = new Header("Set-Cookie2", "invalid name=value; version=1");
try {
- Cookie[] parsed = cookieParse(cookiespec, "127.0.0.1", 80, "/", false, header);
+ cookieParse(cookiespec, "127.0.0.1", 80, "/", false, header);
fail("MalformedCookieException exception should have been thrown");
- } catch (MalformedCookieException e) {
- // expected
- }
+ } catch (MalformedCookieException expected) {}
- // backward compatibility with set-cookie
- header = new Header("Set-Cookie", "invalid name=");
+ // cookie name must not start with '$'.
+ header = new Header("Set-Cookie2", "$invalid_name=value; version=1");
try {
- Cookie[] parsed = cookieParse(cookiespec, "127.0.0.1", 80, "/", false, header);
+ cookieParse(cookiespec, "127.0.0.1", 80, "/", false, header);
fail("MalformedCookieException exception should have been thrown");
- } catch (MalformedCookieException e) {
- // expected
- }
+ } catch (MalformedCookieException expected) {}
+
+ // valid name
+ header = new Header("Set-Cookie2", "name=value; version=1");
+ Cookie[] parsed = cookieParse(cookiespec, "www.domain.com", 80, "/", false, header);
+ assertNotNull(parsed);
+ assertEquals(1, parsed.length);
+ Cookie2 cookie = (Cookie2) 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 Header("Set-Cookie2", "name=value; Port=\"80,800\"; version=1");
+ CookieSpec cookiespec = new RFC2965Spec();
+ try {
+ cookieParse(cookiespec, "www.domain.com", 8000 /* request port */, "/", false, header);
+ fail("MalformedCookieException should have been thrown");
+ } catch (MalformedCookieException e) {}
+ // valid port list
+ Cookie[] parsed = cookieParse(cookiespec, "www.domain.com", 80 /* request port */, "/", false, header);
+ assertNotNull(parsed);
+ assertEquals(1, parsed.length);
+ Cookie2 cookie = (Cookie2) parsed[0];
+ assertEquals(new int[] {80, 800}, cookie.getPorts());
+ }
+
/**
- * Tests if cookie constructor rejects cookie name starting with $.
+ * Test cookie Version validation.
*/
- public void testCookieNameStartingWithDollarSign() throws Exception {
- Header header = new Header("Set-Cookie2", "$invalid_name=");
+ public void testValidateVersion() throws Exception {
CookieSpec cookiespec = new RFC2965Spec();
+ // version attribute is REQUIRED
+ Header header = new Header("Set-Cookie2", "name=value");
try {
- Cookie[] parsed = cookieParse(cookiespec, "127.0.0.1", 80, "/", false, header);
- fail("MalformedCookieException exception should have been thrown");
- } catch (MalformedCookieException e) {
- // expected
- }
+ cookieParse(cookiespec, "www.domain.com", 8000, "/", false, header);
+ fail("MalformedCookieException should have been thrown");
+ } catch (MalformedCookieException e) {}
- // backward compatibility with set-cookie
- header = new Header("Set-Cookie", "$invalid_name=");
+ // version attribute must be 1 with Set-Cookie2 header
+ header = new Header("Set-Cookie2", "name=value; version=0");
try {
- Cookie[] parsed = cookieParse(cookiespec, "127.0.0.1", 80, "/", false, header);
- fail("MalformedCookieException exception should have been thrown");
- } catch (MalformedCookieException e) {
- // expected
- }
+ cookieParse(cookiespec, "www.domain.com", 8000, "/", false, header);
+ fail("MalformedCookieException should have been thrown");
+ } catch (MalformedCookieException e) {}
}
- //TODO: test for cookie matching
- //TODO: test for cookie formatting
+ // ------------------------------------------------------- Test Cookie Matching
+
/**
- * Tests if cookie values with embedded comma are handled correctly.
+ * test cookie Path matching. Cookie path attribute must path-match
+ * path of the request URI.
*/
- public void testCookieWithComma() throws Exception {
- Header header = new Header("Set-Cookie", "a=b,c");
+ public void testMatchPath() throws Exception {
+ Cookie2 cookie = new Cookie2(".domain.com", "name",
+ "value", "/path" /* path */, null, false, new int[] {80});
+ CookieSpec cookiespec = new RFC2965Spec();
+ assertFalse(cookiespec.match("www.domain.com", 80, "/" /* request path */, false, cookie));
+ assertTrue(cookiespec.match("www.domain.com", 80, "/path/path1" /* request path */, false, cookie));
+ }
+ /**
+ * test cookie Domain matching.
+ */
+ public void testMatchDomain() throws Exception {
+ Cookie2 cookie = new Cookie2(".domain.com" /* domain */, "name",
+ "value", "/", null, false, new int[] {80});
CookieSpec cookiespec = new RFC2965Spec();
- Cookie[] cookies = cookiespec.parse("localhost", 80, "/", false, header);
- assertEquals("number of cookies", 2, cookies.length);
- assertEquals("a", cookies[0].getName());
- assertEquals("b", cookies[0].getValue());
- assertEquals("c", cookies[1].getName());
- assertEquals(null, cookies[1].getValue());
+ // effective host name minus domain must not contain any dots
+ assertFalse(cookiespec.match("a.b.domain.com" /* request host */, 80, "/", false, cookie));
+ // The effective host name MUST domain-match the Domain
+ // attribute of the cookie.
+ assertFalse(cookiespec.match("www.domain.org" /* request host */, 80, "/", false, cookie));
+ assertTrue(cookiespec.match("www.domain.com" /* request host */, 80, "/", false, cookie));
}
- public void testFormatInvalidCookies() throws Exception {
+ /**
+ * test cookie local Domain matching.
+ */
+ public void testMatchDomainLocal() throws Exception {
+ Cookie2 cookie = new Cookie2(".local" /* domain */, "name",
+ "value", "/", null, false, new int[] {80});
CookieSpec cookiespec = new RFC2965Spec();
- try {
- String s = cookiespec.formatCookie(null);
- fail("IllegalArgumentException nust have been thrown");
- } catch (IllegalArgumentException expected) {
- }
+ assertTrue(cookiespec.match("host" /* request host */, 80, "/", false, cookie));
+ assertFalse(cookiespec.match("host.com" /* request host */, 80, "/", false, cookie));
}
/**
- * Tests RFC 2965 compiant cookie formatting.
+ * test cookie Port matching.
*/
- public void testRFC2965CookieFormatting() throws Exception {
+ public void testMatchPort() throws Exception {
+ // cookie can be sent to any port if port attribute not specified
+ Cookie2 cookie = new Cookie2(".domain.com", "name",
+ "value", "/", null, false, null /* ports */);
CookieSpec cookiespec = new RFC2965Spec();
- Header header = new Header("Set-Cookie",
- "name=\"value\"; version=\"1\"; path=\"/\"; domain=\".mydomain.com\"");
- Cookie[] cookies = cookiespec.parse("myhost.mydomain.com", 80, "/", false, header );
- cookiespec.validate("myhost.mydomain.com", 80, "/", false, cookies[0]);
- String s1 = cookiespec.formatCookie(cookies[0]);
- assertEquals(s1, "$Version=\"1\"; name=\"value\"; $Domain=\".mydomain.com\"; $Path=\"/\"");
+ cookie.setPortAttributeSpecified(false);
+ assertTrue(cookiespec.match("www.domain.com", 8080 /* request port */, "/", false, cookie));
+ assertTrue(cookiespec.match("www.domain.com", 323 /* request port */, "/", false, cookie));
- header = new Header( "Set-Cookie",
- "name=value; path=/; domain=.mydomain.com");
- cookies = cookiespec.parse("myhost.mydomain.com", 80, "/", false, header );
- cookiespec.validate("myhost.mydomain.com", 80, "/", false, cookies[0]);
- String s2 = cookiespec.formatCookie(cookies[0]);
- assertEquals(s2, "$Version=0; name=value; $Domain=.mydomain.com; $Path=/");
+ // otherwise, request port must be in cookie's port list
+ cookie = new Cookie2(".domain.com", "name",
+ "value", "/", null, false, new int[] {80, 8080} /* ports */);
+ cookie.setPortAttributeSpecified(true);
+ assertFalse(cookiespec.match("www.domain.com", 434 /* request port */, "/", false, cookie));
+ assertTrue(cookiespec.match("www.domain.com", 8080 /* request port */, "/", false, cookie));
}
- public void testRFC2965CookiesFormatting() throws Exception {
+ /**
+ * test cookie expiration.
+ */
+ public void testCookieExpiration() throws Exception {
+ Date afterOneHour = new Date(System.currentTimeMillis() + 3600 * 1000L);
+ Cookie2 cookie = new Cookie2(".domain.com", "name",
+ "value", "/", afterOneHour /* expiry */, false, null);
CookieSpec cookiespec = new RFC2965Spec();
- Header header = new Header("Set-Cookie",
- "name1=value1; path=/; domain=.mydomain.com, " +
- "name2=\"value2\"; version=\"1\"; path=\"/\"; domain=\".mydomain.com\"");
- Cookie[] cookies = cookieParse(cookiespec, "myhost.mydomain.com", 80, "/", false, header);
- assertNotNull(cookies);
- assertEquals(2, cookies.length);
- String s1 = cookiespec.formatCookies(cookies);
- assertEquals(s1,
- "$Version=0; name1=value1; $Domain=.mydomain.com; $Path=/; " +
- "name2=value2; $Domain=.mydomain.com; $Path=/");
+ assertTrue(cookiespec.match("www.domain.com", 80, "/", false, cookie));
- header = new Header("Set-Cookie",
- "name1=value1; version=1; path=/; domain=.mydomain.com, " +
- "name2=\"value2\"; version=\"1\"; path=\"/\"; domain=\".mydomain.com\"");
- cookies = cookieParse(cookiespec, "myhost.mydomain.com", 80, "/", false, header);
- assertNotNull(cookies);
- assertEquals(2, cookies.length);
- String s2 = cookiespec.formatCookies(cookies);
- assertEquals(s2,
- "$Version=\"1\"; name1=\"value1\"; $Domain=\".mydomain.com\"; $Path=\"/\"; " +
- "name2=\"value2\"; $Domain=\".mydomain.com\"; $Path=\"/\"");
+ Date beforeOneHour = new Date(System.currentTimeMillis() - 3600 * 1000L);
+ cookie = new Cookie2(".domain.com", "name",
+ "value", "/", beforeOneHour /* expiry */, false, null);
+ assertFalse(cookiespec.match("www.domain.com", 80, "/", false, cookie));
+
+ // discard attributes overrides cookie age, makes it a session cookie.
+ cookie.setDiscard(true);
+ assertFalse(cookie.isPersistent());
+ assertTrue(cookiespec.match("www.domain.com", 80, "/", false, cookie));
}
/**
- * Tests if null cookie values are handled correctly.
+ * test cookie Secure attribute.
*/
- public void testNullCookieValueFormatting() {
- Cookie cookie = new Cookie(".whatever.com", "name", null, "/", null, false);
- cookie.setDomainAttributeSpecified(true);
- cookie.setPathAttributeSpecified(true);
+ public void testCookieSecure() throws Exception {
+ CookieSpec cookiespec = new RFC2965Spec();
+ // secure cookie can only be sent over a secure connection
+ Cookie2 cookie = new Cookie2(".domain.com", "name",
+ "value", "/", null, true /* secure */, null);
+ assertFalse(cookiespec.match("www.domain.com", 80, "/", false /* request secure */, cookie));
+ assertTrue(cookiespec.match("www.domain.com", 80, "/", true /* request secure */, cookie));
+ }
+ // ------------------------------------------------------- Test Cookie Formatting
+
+ public void testFormatInvalidCookie() throws Exception {
CookieSpec cookiespec = new RFC2965Spec();
- String s = cookiespec.formatCookie(cookie);
- assertEquals("$Version=0; name=; $Domain=.whatever.com; $Path=/", s);
+ try {
+ cookiespec.formatCookie(null);
+ fail("IllegalArgumentException nust have been thrown");
+ } catch (IllegalArgumentException expected) {}
+ }
+ /**
+ * Tests RFC 2965 compliant cookie formatting.
+ */
+ public void testRFC2965CookieFormatting() throws Exception {
+ CookieSpec cookiespec = new RFC2965Spec();
+ Cookie2 cookie = new Cookie2(".domain.com", "name",
+ "value", "/", null, false, new int[] {80,8080});
cookie.setVersion(1);
- s = cookiespec.formatCookie(cookie);
- assertEquals("$Version=\"1\"; name=\"\"; $Domain=\".whatever.com\"; $Path=\"/\"", s);
- }
+ // domain, path, port specified
+ cookie.setDomainAttributeSpecified(true);
+ cookie.setPathAttributeSpecified(true);
+ cookie.setPortAttributeSpecified(true);
+ assertEquals("$Version=\"1\"; name=\"value\"; $Domain=\".domain.com\"; $Path=\"/\"; $Port=\"80,8080\"",
+ cookiespec.formatCookie(cookie));
- public void testCookieNullDomainNullPathFormatting() {
- Cookie cookie = new Cookie(null, "name", null, "/", null, false);
+ // domain, path specified but port unspecified
cookie.setDomainAttributeSpecified(true);
cookie.setPathAttributeSpecified(true);
+ cookie.setPortAttributeSpecified(false);
+ assertEquals("$Version=\"1\"; name=\"value\"; $Domain=\".domain.com\"; $Path=\"/\"",
+ cookiespec.formatCookie(cookie));
+ // path specified, port specified but blank, domain unspecified
+ cookie.setDomainAttributeSpecified(false);
+ cookie.setPathAttributeSpecified(true);
+ cookie.setPortAttributeSpecified(true);
+ cookie.setPortAttributeBlank(true);
+ assertEquals("$Version=\"1\"; name=\"value\"; $Path=\"/\"; $Port=\"\"",
+ cookiespec.formatCookie(cookie));
+ }
+
+ /**
+ * Tests RFC 2965 compliant cookies formatting.
+ */
+ public void testRFC2965CookiesFormatting() throws Exception {
CookieSpec cookiespec = new RFC2965Spec();
- String s = cookiespec.formatCookie(cookie);
- assertEquals("$Version=0; name=; $Path=/", s);
+ Cookie2 cookie1 = new Cookie2(".domain.com", "name1",
+ "value1", "/", null, false, new int[] {80,8080});
+ cookie1.setVersion(1);
+ // domain, path, port specified
+ cookie1.setDomainAttributeSpecified(true);
+ cookie1.setPathAttributeSpecified(true);
+ cookie1.setPortAttributeSpecified(true);
+ Cookie2 cookie2 = new Cookie2(".domain.com", "name2",
+ null, "/", null, false, null);
+ cookie2.setVersion(1);
+ // value null, domain, path, port specified
+ cookie2.setDomainAttributeSpecified(true);
+ cookie2.setPathAttributeSpecified(true);
+ cookie2.setPortAttributeSpecified(false);
+ Cookie[] cookies = new Cookie[] {cookie1, cookie2};
+ assertEquals("$Version=\"1\"; name1=\"value1\"; $Domain=\".domain.com\"; $Path=\"/\"; $Port=\"80,8080\"; " +
+ "name2=\"\"; $Domain=\".domain.com\"; $Path=\"/\"", cookiespec.formatCookies(cookies));
+ }
- cookie.setDomainAttributeSpecified(false);
- cookie.setPathAttributeSpecified(false);
- s = cookiespec.formatCookie(cookie);
- assertEquals("$Version=0; name=", s);
+ // ------------------------------------------------------- Backward compatibility tests
+
+ /**
+ * Test backward compatibility with Set-Cookie header.
+ */
+ public void testCompatibilityWithSetCookie() throws Exception {
+ CookieSpec cookiespec = new RFC2965Spec();
+ Header header = new Header("Set-Cookie", "name=value; domain=.domain.com; version=1");
+ Cookie[] parsed = cookieParse(cookiespec, "www.domain.com", 80, "/", false, header);
+ 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());
}
- private boolean equalArray(int[] a1, int[] a2) {
- if (a1 == null && a2 == null)
- return true;
- if (a1 == null || a2 == null)
- return false;
- if (a1.length != a2.length)
- return false;
- for (int i = 0; i < a1.length; i++) {
- if (a1[i] != a2[i])
- return false;
- }
- return true;
+ // ------------------------------------------------------- Helper Methods
+
+ /**
+ * Asserts that two arrays are equal (deep equality).
+ * @param expected expected array
+ * @param actual actual array
+ */
+ public static void assertEquals(int[] expected, int[] actual) {
+ if (Arrays.equals(expected, actual))
+ return;
+ failNotEquals(null, Arrays.toString(expected), Arrays.toString(actual));
}
+ static private void failNotEquals(String message, Object expected, Object actual) {
+ fail(format(message, expected, actual));
+ }
+
+ static private String format(String message, Object expected, Object actual) {
+ String formatted= "";
+ if (message != null)
+ formatted = message+" ";
+ return formatted + "expected:<"+expected+"> but was:<"+actual+">";
+ }
+
}
Index: src/test/org/apache/commons/httpclient/cookie/TestCookie2.java
===================================================================
--- src/test/org/apache/commons/httpclient/cookie/TestCookie2.java (revision 0)
+++ src/test/org/apache/commons/httpclient/cookie/TestCookie2.java (revision 0)
@@ -0,0 +1,128 @@
+/*
+ * $Header: /cvsroot/httpc-cookie2/httpc-cookie2/httpcookie2SVN-patch.082805-2100.diff,v 1.1 2005/08/29 05:01:58 sjain700 Exp $
+ * $Revision: 1.1 $
+ * $Date: 2005/08/29 05:01:58 $
+ * ====================================================================
+ *
+ * Copyright 1999-2004 The Apache Software Foundation
+ *
+ * Licensed 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
+ *
The syntax for the Set-Cookie response header is:
*
@@ -245,24 +244,7 @@
if (header == null) {
throw new IllegalArgumentException("Header may not be null.");
}
-
- Cookie[] cookies;
- // the default implementation only accepts Set-Cookie header
- if (header.getName().equalsIgnoreCase(Header.SET_COOKIE_KEY)) {
-
- cookies = parse(host, port, path, secure, header.getValue());
- // set header name of cookies so we can identify later whether cookie
- // came from set-cookie header
- for (int i = 0; i < cookies.length; i++) {
- cookies[i].setHeaderName(Header.SET_COOKIE_KEY);
- }
- }
- else {
- throw new MalformedCookieException("Header name is not valid. " +
- "Default implementation supports " +
- "only \"set-cookie\" header.");
- }
- return cookies;
+ return parse(host, port, path, secure, header.getValue());
}
@@ -358,15 +340,15 @@
}
- public Collection getValidDateFormats() {
- return this.datepatterns;
- }
+ public Collection getValidDateFormats() {
+ return this.datepatterns;
+ }
- public void setValidDateFormats(final Collection datepatterns) {
- this.datepatterns = datepatterns;
- }
+ public void setValidDateFormats(final Collection datepatterns) {
+ this.datepatterns = datepatterns;
+ }
- /**
+ /**
* Performs most common {@link Cookie} validation
*
* @param host the host from which the {@link Cookie} was received
@@ -590,7 +572,7 @@
*/
private static void addInPathOrder(List list, Cookie addCookie) {
int i = 0;
- // TODO (jain): better implementation of sorting cookies
+
for (i = 0; i < list.size(); i++) {
Cookie c = (Cookie) list.get(i);
if (addCookie.compare(addCookie, c) > 0) {
@@ -673,9 +655,4 @@
return new Header("Cookie", formatCookie(cookie));
}
- public int getCookieVersion() {
- // default cookie version is 0
- return 0;
- }
-
}
Index: src/java/org/apache/commons/httpclient/cookie/RFC2109Spec.java
===================================================================
--- src/java/org/apache/commons/httpclient/cookie/RFC2109Spec.java (revision 264062)
+++ src/java/org/apache/commons/httpclient/cookie/RFC2109Spec.java (working copy)
@@ -53,8 +53,14 @@
public class RFC2109Spec extends CookieSpecBase {
private final ParameterFormatter formatter;
-
- /** Default constructor */
+
+ /**
+ * Cookie Response Header name for cookies processed
+ * by this spec.
+ */
+ public static String SET_COOKIE_KEY = "set-cookie";
+
+ /** Default constructor */
public RFC2109Spec() {
super();
this.formatter = new ParameterFormatter();
Index: src/java/org/apache/commons/httpclient/cookie/CookieSpec.java
===================================================================
--- src/java/org/apache/commons/httpclient/cookie/CookieSpec.java (revision 264062)
+++ src/java/org/apache/commons/httpclient/cookie/CookieSpec.java (working copy)
@@ -1,5 +1,5 @@
/*
- * $Header: /cvsroot/httpc-cookie2/httpc-cookie2/httpcookie2SVN-patch.082805-2100.diff,v 1.1 2005/08/29 05:01:58 sjain700 Exp $
+ * $Header: /cvsroot/httpc-cookie2/httpc-cookie2/httpcookie2SVN-patch.082805-2100.diff,v 1.1 2005/08/29 05:01:58 sjain700 Exp $
* $Revision: 1.1 $
* $Date: 2005/08/29 05:01:58 $
*
@@ -74,7 +74,7 @@
Cookie[] parse(String host, int port, String path, boolean secure,
final String header)
throws MalformedCookieException, IllegalArgumentException;
-
+ //TODO(jain): why is this method public
/**
* Parse the "Set-Cookie" Header into an array of Cookies.
*
@@ -103,7 +103,7 @@
*/
void parseAttribute(NameValuePair attribute, Cookie cookie)
throws MalformedCookieException, IllegalArgumentException;
-
+ //TODO(jain): why is this method public
/**
* Validate the cookie according to validation rules defined by the
* cookie specification.
@@ -224,11 +224,4 @@
*/
Header formatCookieHeader(Cookie cookie) throws IllegalArgumentException;
- /**
- * Gets the highest cookie version supported by this cookie specification.
- *
- * @return highest cookie version supported.
- */
- int getCookieVersion();
-
}
Index: src/java/org/apache/commons/httpclient/cookie/CookiePolicy.java
===================================================================
--- src/java/org/apache/commons/httpclient/cookie/CookiePolicy.java (revision 264062)
+++ src/java/org/apache/commons/httpclient/cookie/CookiePolicy.java (working copy)
@@ -1,5 +1,5 @@
/*
- * $Header: /cvsroot/httpc-cookie2/httpc-cookie2/httpcookie2SVN-patch.082805-2100.diff,v 1.1 2005/08/29 05:01:58 sjain700 Exp $
+ * $Header: /cvsroot/httpc-cookie2/httpc-cookie2/httpcookie2SVN-patch.082805-2100.diff,v 1.1 2005/08/29 05:01:58 sjain700 Exp $
* $Revision: 1.1 $
* $Date: 2005/08/29 05:01:58 $
*
@@ -320,4 +320,11 @@
public static CookieSpec getCompatibilitySpec() {
return getSpecByPolicy(COMPATIBILITY);
}
+
+ public static int getCookieVersionBySpec(CookieSpec spec) {
+ if ((spec instanceof RFC2109Spec) ||
+ (spec instanceof RFC2965Spec))
+ return 1;
+ return 0;
+ }
}
Index: src/java/org/apache/commons/httpclient/cookie/IgnoreCookiesSpec.java
===================================================================
--- src/java/org/apache/commons/httpclient/cookie/IgnoreCookiesSpec.java (revision 264062)
+++ src/java/org/apache/commons/httpclient/cookie/IgnoreCookiesSpec.java (working copy)
@@ -1,5 +1,5 @@
/*
- * $Header: /cvsroot/httpc-cookie2/httpc-cookie2/httpcookie2SVN-patch.082805-2100.diff,v 1.1 2005/08/29 05:01:58 sjain700 Exp $
+ * $Header: /cvsroot/httpc-cookie2/httpc-cookie2/httpcookie2SVN-patch.082805-2100.diff,v 1.1 2005/08/29 05:01:58 sjain700 Exp $
* $Revision: 1.1 $
* $Date: 2005/08/29 05:01:58 $
*
@@ -149,8 +149,4 @@
return false;
}
- public int getCookieVersion() {
- return -1;
- }
-
}
Index: src/java/org/apache/commons/httpclient/cookie/RFC2965Spec.java
===================================================================
--- src/java/org/apache/commons/httpclient/cookie/RFC2965Spec.java (revision 264062)
+++ src/java/org/apache/commons/httpclient/cookie/RFC2965Spec.java (working copy)
@@ -1,76 +1,93 @@
/*
- * $HeadURL$
- * $Revision: 1.1 $
- * $Date: 2005/08/29 05:01:58 $
- *
- * ====================================================================
- *
- * Copyright 2002-2004 The Apache Software Foundation
- *
- * Licensed 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
- *
RFC 2965 specific cookie management functions + *
RFC 2965 specific cookie management functions. * - * @author Samit Jain - * + * @author jain.samit@gmail.com (Samit Jain) * @since 3.0 */ +public class RFC2965Spec extends CookieSpecBase { -// TODO: revise all comments and documentation + /** + * used for formatting RFC 2956 style cookies + */ + private final ParameterFormatter formatter; -// TODO: refactoring + /** + * Stores attribute name -> attribute handler mappings + */ + private static Map attributeHandlerMap = null; -public class RFC2965Spec extends CookieSpecBase { + /** + * Cookie Response Header name for cookies processed + * by this spec. + */ + public static String SET_COOKIE2_KEY = "set-cookie2"; - private final ParameterFormatter formatter; - - /** Default constructor */ + /** + * Default constructor + */ public RFC2965Spec() { super(); this.formatter = new ParameterFormatter(); this.formatter.setAlwaysUseQuotes(true); + initializeAttributeHandlerMap(); } /** + * initializes attribute name -> attribute handler mappings. + * Called from constructor. + */ + private void initializeAttributeHandlerMap() { + if (attributeHandlerMap == null) { + attributeHandlerMap = new HashMap(); + attributeHandlerMap.put(Cookie2.COOKIE_NAME_KEY, new Cookie2NameAttributeHandler()); + attributeHandlerMap.put(Cookie2.PATH, new Cookie2PathAttributeHandler()); + attributeHandlerMap.put(Cookie2.DOMAIN, new Cookie2DomainAttributeHandler()); + attributeHandlerMap.put(Cookie2.PORT, new Cookie2PortAttributeHandler()); + attributeHandlerMap.put(Cookie2.VERSION, new Cookie2VersionAttributeHandler()); + attributeHandlerMap.put(Cookie2.MAXAGE, new Cookie2MaxageAttributeHandler()); + } + } + + /** * Parses the Set-Cookie2 value into an array of Cookies. * *
The syntax for the Set-Cookie2 response header is: * *
- * set-cookie = "Set-Cookie:" cookies
+ * set-cookie = "Set-Cookie2:" cookies
* cookies = 1#cookie
* cookie = NAME "=" VALUE * (";" cookie-av)
* NAME = attr
@@ -103,85 +120,41 @@
public Cookie[] parse(
String host, int port, String path, boolean secure, final Header header)
throws MalformedCookieException {
- //TODO (jain): should not throw MalformedCookieException since that is for a cookie
- // should throw a more general exception -- like MalformedHeaderException or something ...
LOG.trace("enter RFC2965.parse("
- + "String, port, path, boolean, String)");
+ + "String, int, String, boolean, Header)");
if (header == null) {
throw new IllegalArgumentException("Header may not be null.");
}
- Cookie[] cookies = null;
- String headerName;
+ if (header.getName() == null) {
+ throw new IllegalArgumentException("Header name may not be null.");
+ }
- if (header.getName().equalsIgnoreCase(Header.SET_COOKIE2_KEY)) {
- cookies = parse(host, port, path, secure, header.getValue());
- headerName = Header.SET_COOKIE2_KEY;
- }
- else if (header.getName().equalsIgnoreCase(Header.SET_COOKIE_KEY)) {
+ if (header.getName().equalsIgnoreCase(SET_COOKIE2_KEY)) {
+ // parse cookie2 cookies
+ return parse(host, port, path, secure, header.getValue());
+ } else if (header.getName().equalsIgnoreCase(RFC2109Spec.SET_COOKIE_KEY)) {
+ // delegate parsing of old-style cookies to rfc2109Spec
CookieSpec rfc2109Spec = CookiePolicy.getCookieSpec(CookiePolicy.RFC_2109);
- cookies = rfc2109Spec.parse(host, port, path, secure, header.getValue());
- headerName = Header.SET_COOKIE_KEY;
- }
- else {
+ return rfc2109Spec.parse(host, port, path, secure, header.getValue());
+ } else {
throw new MalformedCookieException("Header name is not valid. " +
"RFC 2965 supports \"set-cookie\" " +
"and \"set-cookie2\" headers.");
}
-
- // set header name of cookies so we can identify later whether cookie
- // came from set-cookie header or set-cookie2 header or ...
- for (int i = 0; i < cookies.length; i++) {
- cookies[i].setHeaderName(headerName);
- }
- return cookies;
}
/**
- * Parses the Set-Cookie2 value into an array of Cookies.
- *
- * The syntax for the Set-Cookie2 response header is:
- *
- *
- * set-cookie = "Set-Cookie:" cookies
- * cookies = 1#cookie
- * cookie = NAME "=" VALUE * (";" cookie-av)
- * NAME = attr
- * VALUE = value
- * cookie-av = "Comment" "=" value
- * | "CommentURL" "=" <"> http_URL <">
- * | "Discard"
- * | "Domain" "=" value
- * | "Max-Age" "=" value
- * | "Path" "=" value
- * | "Port" [ "=" <"> portlist <"> ]
- * | "Secure"
- * | "Version" "=" 1*DIGIT
- * portlist = 1#portnum
- * portnum = 1*DIGIT
- *
- *
- * @param host the host from which the Set-Cookie2 value was
- * received
- * @param port the port from which the Set-Cookie2 value was
- * received
- * @param path the path from which the Set-Cookie2 value was
- * received
- * @param secure true when the Set-Cookie2 value was
- * received over secure conection
- * @param header the Set-Cookie2 header string received from the server
- * @return an array of Cookies parsed from the Set-Cookie2 value
- * @throws MalformedCookieException if an exception occurs during parsing
+ * @see #parse(String, int, String, boolean, org.apache.commons.httpclient.Header)
*/
public Cookie[] parse(String host, int port, String path,
boolean secure, final String header)
throws MalformedCookieException {
-
LOG.trace("enter RFC2965Spec.parse("
- + "String, port, path, boolean, Header)");
+ + "String, int, String, boolean, String)");
- checkCommonArguments(host, port, path);
-
+ // before we do anything, lets check validity of arguments
+ validateArgs(host, port, path);
if (header == null) {
throw new IllegalArgumentException("Header may not be null.");
}
@@ -194,13 +167,12 @@
HeaderElement[] headerElements =
HeaderElement.parseElements(header.toCharArray());
- List cookies = new ArrayList();
-
+ List cookies = new LinkedList();
for (int i = 0; i < headerElements.length; i++) {
HeaderElement headerelement = headerElements[i];
- Cookie cookie = null;
+ Cookie2 cookie = null;
try {
- cookie = new Cookie(host,
+ cookie = new Cookie2(host,
headerelement.getName(),
headerelement.getValue(),
path,
@@ -217,186 +189,303 @@
}
}
cookies.add(cookie);
- }
- catch (Exception e) {
- // TODO (jain): Not sure what to do here? Oleg suggested stop processing
- // when a cookie is malformed. However continue processing when cookie is well formed but there
- // was a problem in parsing an attribute. How to implement this?
-
- // log the error and continue processing other cookies in header
+ } catch (Exception e) {
+ //TODO(jain): when do we consider the header malformed and stop processing it?
+ // throw this cookie, continue processing other cookies in header
if (LOG.isDebugEnabled())
LOG.debug("Error occured while parsing cookie: \"" +
headerelement + "\". " + e);
}
}
- return (Cookie[]) cookies.toArray(new Cookie[0]);
+ if (cookies.isEmpty())
+ return null;
+ return (Cookie[]) cookies.toArray(new Cookie[cookies.size()]);
}
- private void checkCommonArguments(String host, int port, String path) {
- if (host == null) {
- throw new IllegalArgumentException(
- "Host of origin may not be null");
- }
- if (host.trim().equals("")) {
- throw new IllegalArgumentException(
- "Host of origin may not be blank");
- }
- if (port < 0) {
- throw new IllegalArgumentException("Invalid port: " + port);
- }
- if (path == null) {
- throw new IllegalArgumentException(
- "Path of origin may not be null.");
- }
- }
-
/**
* Parse RFC 2965 specific cookie attribute and update the corresponsing
* {@link org.apache.commons.httpclient.Cookie} properties.
*
* @param attribute {@link org.apache.commons.httpclient.NameValuePair} cookie attribute from the
- * Set-Cookie2 header or Set-Cookie header (in case no Set-Cookie2 header was
- * received).
- * @param cookie {@link org.apache.commons.httpclient.Cookie} to be updated
+ * Set-Cookie2 header.
+ * @param cookieParam {@link org.apache.commons.httpclient.Cookie} to be updated
* @throws MalformedCookieException if an exception occurs during parsing
*/
public void parseAttribute(
- final NameValuePair attribute, final Cookie cookie)
+ final NameValuePair attribute, final Cookie cookieParam)
throws MalformedCookieException {
-
if (attribute == null) {
throw new IllegalArgumentException("Attribute may not be null.");
}
- if (cookie == null) {
+ if (attribute.getName() == null) {
+ throw new IllegalArgumentException("Attribute Name may not be null.");
+ }
+ if (cookieParam == null) {
throw new IllegalArgumentException("Cookie may not be null.");
}
+ if (!(cookieParam instanceof Cookie2)) {
+ throw new IllegalArgumentException("Expected Cookie2 cookies.");
+ }
+ Cookie2 cookie = (Cookie2) cookieParam;
final String paramName = attribute.getName().toLowerCase();
final String paramValue = attribute.getValue();
- if (paramName.equals("path")) {
- //TODO: if Path attribute is specified in header without any value, what to do?
- if (!cookie.isPathAttributeSpecified()) {
- if (paramValue == null) {
- throw new MalformedCookieException(
- "Missing value for path attribute");
- }
- if (paramValue.trim().equals("")) {
- throw new MalformedCookieException(
- "Blank value for path attribute");
- }
- cookie.setPath(paramValue);
- cookie.setPathAttributeSpecified(true);
- }
+ try {
+ CookieAttributeHandler handler = getAttributeHandler(paramName);
+ handler.parse(cookie, paramValue);
+ } catch (IllegalStateException canIgnore) {
+ // handler not registered for this paramName
}
- else if (paramName.equals("domain")) {
- //TODO: if Domain attribute is specified in header without any value, what to do?
- if (!cookie.isDomainAttributeSpecified()) {
- if (paramValue == null) {
- throw new MalformedCookieException(
- "Missing value for domain attribute");
- }
- if (paramValue.trim().equals("")) {
- throw new MalformedCookieException(
- "Blank value for domain attribute");
- }
- // domain is lowercased before storing in cookie since
- // domain matching is case-insensitive
- String domain = paramValue.toLowerCase();
- if (!domain.startsWith("."))
- domain = "." + domain;
- cookie.setDomain(domain);
- cookie.setDomainAttributeSpecified(true);
- }
+ // handle other cookie attributes
+ if (paramName.equals(Cookie2.COMMENT)) {
+ if (cookie.getComment() == null)
+ cookie.setComment(paramValue);
+ } else if (paramName.equals(Cookie2.SECURE)) {
+ cookie.setSecure(true);
+ } else if (paramName.equals(Cookie2.COMMENTURL)) {
+ if (cookie.getCommentURL() == null)
+ cookie.setCommentURL(paramValue);
+ } else if (paramName.equals(Cookie2.DISCARD)) {
+ cookie.setDiscard(true);
+ } else {
+ // ignore unknown attribute-value pairs
+ if (LOG.isDebugEnabled())
+ LOG.debug("Unrecognized cookie attribute: " +
+ attribute.toString());
}
- else if (paramName.equals("max-age")) {
+ }
- if (cookie.getExpiryDate() == null) {
- if (paramValue == null) {
- throw new MalformedCookieException(
- "Missing value for max-age attribute");
- }
- int age;
- try {
- age = Integer.parseInt(paramValue);
- } catch (NumberFormatException e) {
- throw new MalformedCookieException ("Invalid max-age "
- + "attribute: " + e.getMessage());
- }
- cookie.setExpiryDate(
- new Date(System.currentTimeMillis() + age * 1000L));
- }
+ /**
+ * Performs RFC 2965 compliant {@link org.apache.commons.httpclient.Cookie} validation
+ *
+ * @param host the host from which the {@link org.apache.commons.httpclient.Cookie} was received
+ * @param port the port from which the {@link org.apache.commons.httpclient.Cookie} was received
+ * @param path the path from which the {@link org.apache.commons.httpclient.Cookie} was received
+ * @param secure true when the {@link org.apache.commons.httpclient.Cookie} was received using a
+ * secure connection
+ * @param cookieParam The cookie to validate
+ * @throws MalformedCookieException if an exception occurs during
+ * validation
+ */
+ public void validate(String host, int port, String path,
+ boolean secure, final Cookie cookieParam)
+ throws MalformedCookieException {
+
+ LOG.trace("enter RFC2965Spec.validate(String, int, String, "
+ + "boolean, Cookie)");
+
+ // before we do anything, lets check validity of arguments
+ validateArgs(host, port, path);
+
+ if (!(cookieParam instanceof Cookie2)) {
+ // old-style cookies are validated according to the old rules
+ CookieSpec rfc2109Spec = CookiePolicy.getCookieSpec(CookiePolicy.RFC_2109);
+ rfc2109Spec.validate(host, port, path, secure, cookieParam);
+ return;
}
- else if (paramName.equals("comment")) {
- if (cookie.getComment() == null)
- cookie.setComment(paramValue);
+ /* validate cookie2 cookies */
+ Cookie2 cookie = (Cookie2) cookieParam;
+ // validate cookie name
+ CookieAttributeHandler handler = getAttributeHandler(Cookie2.COOKIE_NAME_KEY);
+ handler.validate(cookie, null);
+ // validate cookie path attribute
+ handler = getAttributeHandler(Cookie2.PATH);
+ handler.validate(cookie, path);
+ // validate cookie domain attribute
+ handler = getAttributeHandler(Cookie2.DOMAIN);
+ handler.validate(cookie, host);
+ // validate cookie port attribute
+ handler = getAttributeHandler(Cookie2.PORT);
+ handler.validate(cookie, String.valueOf(port));
+ // validate cookie version attribute
+ handler = getAttributeHandler(Cookie2.VERSION);
+ handler.validate(cookie, null);
+ }
+ /**
+ * Return true if the cookie should be submitted with a request
+ * with given attributes, false otherwise.
+ * @param host the host to which the request is being submitted
+ * @param port the port to which the request is being submitted (ignored)
+ * @param path the path to which the request is being submitted
+ * @param secure true if the request is using a secure connection
+ * @param cookieParam {@link Cookie} to be matched
+ * @return true if the cookie matches the criterium
+ */
+ public boolean match(String host, int port, String path,
+ boolean secure, final Cookie cookieParam) {
+
+ LOG.trace("enter RFC2965.match("
+ + "String, int, String, boolean, Cookie");
+
+ // before we do anything, lets check validity of arguments
+ validateArgs(host, port, path);
+ if (cookieParam == null) {
+ throw new IllegalArgumentException("Cookie may not be null");
}
- else if (paramName.equals("secure")) {
- cookie.setSecure(true);
+ if (!(cookieParam instanceof Cookie2)) {
+ // old-style cookies are matched according to the old rules
+ CookieSpec rfc2109Spec = CookiePolicy.getCookieSpec(CookiePolicy.RFC_2109);
+ return rfc2109Spec.match(host, port, path, secure, cookieParam);
+ }
+ /* match cookie2 cookies */
+ Cookie2 cookie = (Cookie2) cookieParam;
+ // match cookie path attribute
+ CookieAttributeHandler handler = getAttributeHandler(Cookie2.PATH);
+ if (!handler.match(cookie, path))
+ return false;
+ // match cookie domain attribute
+ handler = getAttributeHandler(Cookie2.DOMAIN);
+ if (!handler.match(cookie, host))
+ return false;
+ // match cookie port attribute
+ handler = getAttributeHandler(Cookie2.PORT);
+ if (!handler.match(cookie, String.valueOf(port)))
+ return false;
+ // check if cookie has expired
+ if (cookie.isPersistent() && cookie.isExpired())
+ return false;
+ // finally make sure that if cookie Secure attribute is set, then this
+ // request is made using a secure connection
+ if (cookie.getSecure())
+ return secure;
+ // if we get to this stage, we have a match
+ return true;
+ }
+
+ /**
+ * Return a string suitable for sending in a "Cookie" header as
+ * defined in RFC 2965
+ * @param cookieParam a {@link org.apache.commons.httpclient.Cookie} to be formatted as string
+ * @return a string suitable for sending in a "Cookie" header.
+ */
+ public String formatCookie(Cookie cookieParam) {
+ LOG.trace("enter RFC2965Spec.formatCookie(Cookie)");
+
+ if (cookieParam == null) {
+ throw new IllegalArgumentException("Cookie may not be null");
}
- else if (paramName.equals("version")) {
- if (cookie.getVersion() == -1) {
- if (paramValue == null) {
- throw new MalformedCookieException(
- "Missing value for version attribute");
- }
- try {
- cookie.setVersion(Integer.parseInt(paramValue));
- } catch (NumberFormatException e) {
- throw new MalformedCookieException("Invalid version: "
- + e.getMessage());
- }
- }
+ if (!(cookieParam instanceof Cookie2)) {
+ // old-style cookies are formatted according to the old rules
+ CookieSpec rfc2109Spec = CookiePolicy.getCookieSpec(CookiePolicy.RFC_2109);
+ return rfc2109Spec.formatCookie(cookieParam);
}
- else if (paramName.equals("port")) {
- if (!cookie.isPortAttributeSpecified()) {
- if ((paramValue == null) || (paramValue.trim().equals(""))) {
- // If the Port attribute is present but has no value, the
- // cookie MUST only be sent to the request-port it was received from.
- // Since the default port list only contains request-port, we don't
- // need to do anything here.
- cookie.setPortAttributeBlank(true);
- }
- else {
- int[] ports = parsePortAttribute(paramValue);
- cookie.setPorts(ports);
- }
- cookie.setPortAttributeSpecified(true);
+ /* format cookie2 cookie */
+ Cookie2 cookie = (Cookie2) cookieParam;
+ final StringBuffer buffer = new StringBuffer();
+ // format cookie version
+ CookieAttributeHandler handler = getAttributeHandler(Cookie2.VERSION);
+ handler.format(buffer, cookie);
+ // format cookie attributes
+ formatCookieAttributes(buffer, cookie);
+ return buffer.toString();
+ }
+
+ /**
+ * Create a RFC 2965 compliant "Cookie" header value containing all
+ * {@link org.apache.commons.httpclient.Cookie}s suitable for
+ * sending in a "Cookie" header
+ * @param cookies an array of {@link org.apache.commons.httpclient.Cookie}s to be formatted
+ * @return a string suitable for sending in a Cookie header.
+ */
+ public String formatCookies(Cookie[] cookies) {
+ LOG.trace("enter RFC2965Spec.formatCookieHeader(Cookie[])");
+
+ if (cookies == null) {
+ throw new IllegalArgumentException("Cookies may not be null");
+ }
+ // check if cookies array contains a set-cookie (old style) cookie
+ boolean hasOldStyleCookie = false;
+ for (int i = 0; i < cookies.length; i++) {
+ if (!(cookies[i] instanceof Cookie2)) {
+ hasOldStyleCookie = true;
+ break;
}
}
- else if (paramName.equals("commenturl")) {
+ // TODO(jain): check this logic?
+ if (hasOldStyleCookie) {
+ // delegate old-style cookie formatting to rfc2109Spec
+ CookieSpec rfc2109Spec = CookiePolicy.getCookieSpec(CookiePolicy.RFC_2109);
+ return rfc2109Spec.formatCookies(cookies);
+ }
- if (cookie.getCommentURL() == null)
- cookie.setCommentURL(paramValue);
+ /* format cookie2 cookies */
+ final StringBuffer buffer = new StringBuffer();
+ // format cookie version
+ CookieAttributeHandler handler = getAttributeHandler(Cookie2.VERSION);
+ handler.format(buffer, null);
+ for (int i = 0; i < cookies.length; i++) {
+ Cookie2 cookie = (Cookie2) cookies[i];
+ // format cookie attributes
+ formatCookieAttributes(buffer, cookie);
}
- else if (paramName.equals("Discard")) {
- cookie.setDiscard(true);
+ return buffer.toString();
+ }
+ /**
+ * Return a string suitable for sending in a "Cookie" header
+ * as defined in RFC 2965.
+ * @param buffer The string buffer to use for output
+ * @param cookie The {@link Cookie2} to be formatted as string
+ */
+ private void formatCookieAttributes(final StringBuffer buffer, final Cookie2 cookie) {
+ // format cookie name and value
+ CookieAttributeHandler handler = getAttributeHandler(Cookie2.COOKIE_NAME_KEY);
+ handler.format(buffer, cookie);
+ // format domain attribute
+ handler = getAttributeHandler(Cookie2.DOMAIN);
+ handler.format(buffer, cookie);
+ // format path attribute
+ handler = getAttributeHandler(Cookie2.PATH);
+ handler.format(buffer, cookie);
+ // format port attribute
+ handler = getAttributeHandler(Cookie2.PORT);
+ handler.format(buffer, cookie);
+ }
+
+ /**
+ * Retrieves valid Port attribute value for the given ports array.
+ * e.g. "8000,8001,8002"
+ *
+ * @param ports int array of ports
+ */
+ private String createPortAttribute(int[] ports) {
+ StringBuffer portValue = new StringBuffer();
+ for (int i = 0, len = ports.length; i < len; i++) {
+ if (i > 0) {
+ portValue.append(",");
+ }
+ portValue.append(ports[i]);
}
- else {
- //TODO: should we throw an exception here?
- //should the header be considered malformed
- if (LOG.isDebugEnabled())
- LOG.debug("Unrecognized cookie attribute: " +
- attribute.toString());
- }
+ return portValue.toString();
}
- private int[] parsePortAttribute(final String paramValue)
+ /**
+ * Parses the given Port attribute value (e.g. "8000,8001,8002")
+ * into an array of ports.
+ *
+ * @param portValue port attribute value
+ * @return parsed array of ports
+ * @throws MalformedCookieException if there is a problem in
+ * parsing due to invalid portValue.
+ */
+ private int[] parsePortAttribute(final String portValue)
throws MalformedCookieException {
- StringTokenizer st = new StringTokenizer(paramValue, ",");
+ StringTokenizer st = new StringTokenizer(portValue, ",");
int[] ports = new int[st.countTokens()];
-
try {
int i = 0;
while(st.hasMoreTokens()) {
ports[i] = Integer.parseInt(st.nextToken().trim());
+ if (ports[i] < 0) {
+ throw new MalformedCookieException ("Invalid Port attribute.");
+ }
++i;
}
} catch (NumberFormatException e) {
@@ -407,78 +496,306 @@
}
/**
- * Performs RFC 2965 compliant {@link org.apache.commons.httpclient.Cookie} validation
+ * Validates host, port, path parameters. Refactored out since it is reqd by
+ * many methods. Validation rules are:
+ * + * 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 String getEffectiveHost(String host) { + String effectiveHost = host; + if (host.indexOf('.') < 0) { + effectiveHost += ".local"; } - else if (cookie.getHeaderName().equalsIgnoreCase(Header.SET_COOKIE2_KEY)) { - // check validity of arguments - checkCommonArguments(host, port, path); + return effectiveHost; + } - if (path.trim().equals("")) { - path = PATH_DELIM; + /** + * Performs domain-match as defined by the RFC2965. + *
+ * Host A's name domain-matches host B's if + *
+ * Cookie class for {@link org.apache.commons.httpclient.cookie.RFC2965Spec} + * cookie specification. It extends {@link Cookie} class and adds newer cookie + * attributes and functions required for this specification. + *
+ * + * @author Samit Jain (jain.samit@gmail.com) + */ +public class Cookie2 extends Cookie { + + // string constants for cookie attributes + public static final String DOMAIN = "domain"; + public static final String PATH = "path"; + public static final String PORT = "port"; + public static final String VERSION = "version"; + public static final String SECURE = "secure"; + public static final String MAXAGE = "max-age"; + public static final String COMMENT = "comment"; + public static final String COMMENTURL = "commenturl"; + public static final String DISCARD = "discard"; + + public static final String COOKIE_NAME_KEY = "cookieName"; + + /** + * Default constructor. Creates a blank cookie + */ + public Cookie2() { + super(null, "noname", null, null, null, false); + } + + /** + * Creates a cookie with the given name, value and domain attribute. + * + * @param name the cookie name + * @param value the cookie value + * @param domain the domain this cookie can be sent to + */ + public Cookie2(String domain, String name, String value) { + super(domain, name, value); + } + + /** + * Creates a cookie with the given name, value, domain attribute, + * path attribute, expiration attribute, and secure attribute + * + * @param name the cookie name + * @param value the cookie value + * @param domain the domain this cookie can be sent to + * @param path the path prefix for which this cookie can be sent + * @param expires the {@link Date} at which this cookie expires, + * or null if the cookie expires at the end + * of the session + * @param secure if true this cookie can only be sent over secure + * connections + * @throws IllegalArgumentException If cookie name is null or blank, + * cookie name contains a blank, or cookie name starts with character $ + * + */ + public Cookie2(String domain, String name, String value, + String path, Date expires, boolean secure) { + super(domain, name, value, path, expires, secure); + } + + /** + * Creates a cookie with the given name, value, domain attribute, + * path attribute, expiration attribute, secure attribute, and ports + * attribute. + * + * @param name the cookie name + * @param value the cookie value + * @param domain the domain this cookie can be sent to + * @param path the path prefix for which this cookie can be sent + * @param expires the {@link Date} at which this cookie expires, + * or null if the cookie expires at the end + * of the session + * @param secure if true this cookie can only be sent over secure + * connections + * @param ports the ports for which this cookie can be sent + * @throws IllegalArgumentException If cookie name is null or blank, + * cookie name contains a blank, or cookie name starts with character $ + * + */ + public Cookie2(String domain, String name, String value, + String path, Date expires, boolean secure, int[] ports) { + super(domain, name, value, path, expires, secure); + setPorts(ports); + } + + /** + * If a user agent (web browser) presents this cookie to a user, the + * cookie's purpose will be described by the information at this URL. + * + * @see #setCommentURL(String) + */ + public String getCommentURL() { + return cookieCommentURL; + } + + /** + * If a user agent (web browser) presents this cookie to a user, the + * cookie's purpose will be described by the information at this URL. + * + * @param commentURL + * + * @see #getCommentURL() + */ + public void setCommentURL(String commentURL) { + this.cookieCommentURL = commentURL; + } + + /** + * Get the Port attribute. It restricts the ports to which a cookie + * may be returned in a Cookie request header. + * + * @see #setPorts(int[]) + */ + public int[] getPorts() { + return cookiePorts; + } + + /** + * Set the Port attribute. It restricts the ports to which a cookie + * may be returned in a Cookie request header. + * + * @param ports + * + * @see #getPorts() + */ + public void setPorts(int[] ports) { + this.cookiePorts = ports; + } + + /** + * Set the Discard attribute. + * + * Note: Discard attribute overrides Max-age. + * + * @see #isPersistent() + */ + public void setDiscard(boolean toDiscard) { + discard = toDiscard; + } + + /** + * Returns false if the cookie should be discarded at the end + * of the "session"; true otherwise. + * + * @return false if the cookie should be discarded at the end + * of the "session"; true otherwise + */ + public boolean isPersistent() { + return (null != getExpiryDate()) && !discard; + } + + /** + * Indicates whether the cookie had a domain attribute specified in the + * Set-Cookie2 response header. This property + * has two uses: + *+ * This value is required for generating + * the Cookie request header because the specification requires that if + * Set-Cookie2 header contains a blank value for port attribute, + * the Cookie header should also contain a port attribute with no value. + * + * @param value true if port attribute is specified as blank in response + * header. + * + * @see #isPortAttributeBlank + */ + public void setPortAttributeBlank(boolean value) { + isPortAttributeBlank = value; + } + + /** + * @return true if the port attribute in Set-Cookie2 header + * had no value (was of the form Port=""). + * + * @see #setPortAttributeBlank + */ + public boolean isPortAttributeBlank() { + return isPortAttributeBlank; + } + + /** + * Indicates whether the cookie had a version attribute specified in the + * Set-Cookie2 response header. This property has two uses: + *