You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@commons.apache.org by ol...@apache.org on 2005/08/27 12:15:28 UTC
svn commit: r240400 - in
/jakarta/commons/proper/httpclient/branches/COOKIE_2_BRANCH/src:
java/org/apache/commons/httpclient/
java/org/apache/commons/httpclient/cookie/
test/org/apache/commons/httpclient/cookie/
Author: olegk
Date: Sat Aug 27 03:15:11 2005
New Revision: 240400
URL: http://svn.apache.org/viewcvs?rev=240400&view=rev
Log:
Initial check-in of the Cookie2 patch by Samit Jain
Added:
jakarta/commons/proper/httpclient/branches/COOKIE_2_BRANCH/src/java/org/apache/commons/httpclient/cookie/RFC2965Spec.java (with props)
jakarta/commons/proper/httpclient/branches/COOKIE_2_BRANCH/src/test/org/apache/commons/httpclient/cookie/TestCookieRFC2965Spec.java (with props)
Modified:
jakarta/commons/proper/httpclient/branches/COOKIE_2_BRANCH/src/java/org/apache/commons/httpclient/Cookie.java
jakarta/commons/proper/httpclient/branches/COOKIE_2_BRANCH/src/java/org/apache/commons/httpclient/Header.java
jakarta/commons/proper/httpclient/branches/COOKIE_2_BRANCH/src/java/org/apache/commons/httpclient/HttpMethodBase.java
jakarta/commons/proper/httpclient/branches/COOKIE_2_BRANCH/src/java/org/apache/commons/httpclient/cookie/CookiePolicy.java
jakarta/commons/proper/httpclient/branches/COOKIE_2_BRANCH/src/java/org/apache/commons/httpclient/cookie/CookieSpec.java
jakarta/commons/proper/httpclient/branches/COOKIE_2_BRANCH/src/java/org/apache/commons/httpclient/cookie/CookieSpecBase.java
jakarta/commons/proper/httpclient/branches/COOKIE_2_BRANCH/src/java/org/apache/commons/httpclient/cookie/IgnoreCookiesSpec.java
Modified: jakarta/commons/proper/httpclient/branches/COOKIE_2_BRANCH/src/java/org/apache/commons/httpclient/Cookie.java
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/httpclient/branches/COOKIE_2_BRANCH/src/java/org/apache/commons/httpclient/Cookie.java?rev=240400&r1=240399&r2=240400&view=diff
==============================================================================
--- jakarta/commons/proper/httpclient/branches/COOKIE_2_BRANCH/src/java/org/apache/commons/httpclient/Cookie.java (original)
+++ jakarta/commons/proper/httpclient/branches/COOKIE_2_BRANCH/src/java/org/apache/commons/httpclient/Cookie.java Sat Aug 27 03:15:11 2005
@@ -63,6 +63,17 @@
*/
public class Cookie extends NameValuePair implements Serializable, Comparator {
+ // 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";
+
// ----------------------------------------------------------- Constructors
/**
@@ -145,6 +156,32 @@
}
/**
+ * 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 <tt>null</tt> 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 Cookie(String domain, String name, String value,
+ String path, Date expires, boolean secure, int[] ports) {
+
+ this(domain, name, value, path, expires, secure);
+ this.setPorts(ports);
+ }
+
+ /**
* Returns the comment describing the purpose of this cookie, or
* <tt>null</tt> if no such comment has been defined.
*
@@ -168,6 +205,50 @@
cookieComment = comment;
}
+ /**
+ * 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;
+ }
+
/**
* Returns the expiration {@link Date} of the cookie, or <tt>null</tt>
* if none exists.
@@ -198,6 +279,15 @@
cookieExpiryDate = expiryDate;
}
+ /**
+ * Set the Discard attribute.
+ *
+ * @see #isPersistent()
+ *
+ */
+ public void setDiscard(boolean toDiscard) {
+ discard = toDiscard;
+ }
/**
* Returns <tt>false</tt> if the cookie should be discarded at the end
@@ -207,7 +297,7 @@
* of the "session"; <tt>true</tt> otherwise
*/
public boolean isPersistent() {
- return (null != cookieExpiryDate);
+ return (null != cookieExpiryDate) && !discard;
}
@@ -313,13 +403,15 @@
}
/**
- * Returns true if this cookie has expired.
+ * Returns true if this cookie has expired. Also returns
+ * true for a session cookie (discard=true).
*
* @return <tt>true</tt> if the cookie has expired.
*/
public boolean isExpired() {
- return (cookieExpiryDate != null
- && cookieExpiryDate.getTime() <= System.currentTimeMillis());
+ return (cookieExpiryDate != null &&
+ cookieExpiryDate.getTime() <= System.currentTimeMillis())
+ || discard;
}
/**
@@ -337,11 +429,18 @@
/**
* Indicates whether the cookie had a path specified in a
- * path attribute of the <tt>Set-Cookie</tt> header. This value
- * is important for generating the <tt>Cookie</tt> header because
- * some cookie specifications require that the <tt>Cookie</tt> header
- * should only include a path attribute if the cookie's path
- * was specified in the <tt>Set-Cookie</tt> header.
+ * path attribute of the <tt>Set-Cookie</tt> header. This property
+ * has two uses:
+ * <ul>
+ * <li>This value is important for generating the <tt>Cookie</tt>
+ * header because some cookie specifications require that the
+ * <tt>Cookie</tt> header should only include a path attribute if
+ * the cookie's path was specified in the <tt>Set-Cookie</tt> header.
+ * </li>
+ * <li>This also ensures that if an attribute appears more than once in
+ * a cookie response header, the first appearance of the attribute is used.
+ * </li>
+ * </ul>
*
* @param value <tt>true</tt> if the cookie's path was explicitly
* set, <tt>false</tt> otherwise.
@@ -367,12 +466,18 @@
/**
* Indicates whether the cookie had a domain specified in a
- * domain attribute of the <tt>Set-Cookie</tt> header. This value
- * is important for generating the <tt>Cookie</tt> header because
- * some cookie specifications require that the <tt>Cookie</tt> header
+ * domain attribute of the <tt>Set-Cookie</tt> header. This property
+ * has two uses:
+ * <ul>
+ * <li>This value is important for generating the <tt>Cookie</tt> header
+ * because some cookie specifications require that the <tt>Cookie</tt> header
* should only include a domain attribute if the cookie's domain
* was specified in the <tt>Set-Cookie</tt> header.
- *
+ * </li>
+ * <li>This also ensures that if an attribute appears more than once in
+ * a cookie response header, the first appearance of the attribute is used.
+ * </li>
+ * </ul>
* @param value <tt>true</tt> if the cookie's domain was explicitly
* set, <tt>false</tt> otherwise.
*
@@ -396,6 +501,82 @@
}
/**
+ * Indicates whether the cookie had a ports list specified in the
+ * Port attribute of the <tt>Set-Cookie2</tt> header. This ensures
+ * that if an attribute appears more than once in a cookie response
+ * header, the first appearance of the attribute is used.
+ *
+ * @param value <tt>true</tt> if the cookie's ports were explicitly
+ * set, <tt>false</tt> otherwise.
+ *
+ * @see #isPortAttributeSpecified
+ */
+ public void setPortAttributeSpecified(boolean value) {
+ hasPortAttribute = value;
+ }
+
+ /**
+ * Returns <tt>true</tt> if cookie's ports were set via the Port
+ * attribute in the <tt>Set-Cookie2</tt> header.
+ *
+ * @return <tt>true</tt> if the cookie's ports were explicitly
+ * set, <tt>false</tt> otherwise.
+ *
+ * @see #setPortAttributeSpecified
+ */
+ public boolean isPortAttributeSpecified() {
+ return hasPortAttribute;
+ }
+
+ /**
+ * Indicates whether the Port attribute in <tt>Set-Cookie2</tt> header
+ * is blank.
+ *
+ * @param value
+ *
+ * @see #isPortAttributeBlank
+ */
+ public void setPortAttributeBlank(boolean value) {
+ isPortAttributeBlank = value;
+ }
+
+ /**
+ * Returns <tt>true</tt> if Port attribute in <tt>Set-Cookie2</tt> header
+ * did not have any value.
+ *
+ * @return <tt>true</tt> if Port attribute in <tt>Set-Cookie2</tt> header
+ * did not have any value.
+ *
+ * @see #setPortAttributeBlank
+ */
+ public boolean isPortAttributeBlank() {
+ return isPortAttributeBlank;
+ }
+
+ /**
+ * Returns the response header name this cookie was obtained from.
+ * Currently must be either 'set-cookie' or set-cookie2'.
+ *
+ * @return response header name
+ * @see #setHeaderName(String)
+ */
+ public String getHeaderName() {
+ return headerName;
+ }
+
+ /**
+ * Sets the header name of this cookie.
+ *
+ * @param headerName Currently must be either 'set-cookie' or
+ * set-cookie2'.
+ * @see #getHeaderName()
+ */
+ public void setHeaderName(String headerName) {
+ this.headerName = headerName;
+ }
+
+
+ /**
* Returns a hash code in keeping with the
* {@link Object#hashCode} general hashCode contract.
* @return A hash code
@@ -498,37 +679,72 @@
// ----------------------------------------------------- Instance Variables
- /** Comment attribute. */
- private String cookieComment;
+ /** Comment attribute. */
+ private String cookieComment;
+
+ /**
+ * Comment URL attribute
+ * Note: Required for RFC 2965 cookie specification.
+ */
+ private String cookieCommentURL;
+
+ /** Domain attribute. */
+ private String cookieDomain;
- /** Domain attribute. */
- private String cookieDomain;
+ /**
+ * Port attribute.
+ * Note: Required for RFC 2965 cookie specification.
+ */
+ private int[] cookiePorts;
- /** Expiration {@link Date}. */
- private Date cookieExpiryDate;
+ /** Expiration {@link Date}. */
+ private Date cookieExpiryDate;
- /** Path attribute. */
- private String cookiePath;
+ /**
+ * Discard attribute.
+ * Note: Required for RFC 2965 cookie specification.
+ */
+ private boolean discard = false;
- /** My secure flag. */
- private boolean isSecure;
+ /** Path attribute. */
+ private String cookiePath;
- /**
- * Specifies if the set-cookie header included a Path attribute for this
- * cookie
- */
- private boolean hasPathAttribute = false;
+ /** My secure flag. */
+ private boolean isSecure;
- /**
- * Specifies if the set-cookie header included a Domain attribute for this
- * cookie
- */
- private boolean hasDomainAttribute = false;
+ /**
+ * the name of the response header this cookie was obtained from.
+ */
+ private String headerName;
+
+ /**
+ * Specifies if the set-cookie header included a Path attribute for this
+ * cookie
+ */
+ private boolean hasPathAttribute = false;
+
+ /**
+ * Specifies if the set-cookie header included a Domain attribute for this
+ * cookie
+ */
+ private boolean hasDomainAttribute = false;
+
+ /**
+ * Specifies if the set-cookie2 header included a Port attribute for this
+ * cookie
+ */
+ private boolean hasPortAttribute = false;
+
+ /**
+ * Specifies if the set-cookie2 header's Port attribute did not have
+ * any value.
+ */
+ private boolean isPortAttributeBlank = false;
- /** The version of the cookie specification I was created from. */
- private int cookieVersion = 0;
+ /** The version of the cookie specification I was created from. */
+ private int cookieVersion = -1;
- // -------------------------------------------------------------- Constants
+ // -------------------------------------------------------------- Constants
/**
* Collator for Cookie comparisons. Could be replaced with references to
Modified: jakarta/commons/proper/httpclient/branches/COOKIE_2_BRANCH/src/java/org/apache/commons/httpclient/Header.java
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/httpclient/branches/COOKIE_2_BRANCH/src/java/org/apache/commons/httpclient/Header.java?rev=240400&r1=240399&r2=240400&view=diff
==============================================================================
--- jakarta/commons/proper/httpclient/branches/COOKIE_2_BRANCH/src/java/org/apache/commons/httpclient/Header.java (original)
+++ jakarta/commons/proper/httpclient/branches/COOKIE_2_BRANCH/src/java/org/apache/commons/httpclient/Header.java Sat Aug 27 03:15:11 2005
@@ -45,6 +45,10 @@
* Autogenerated header flag.
*/
private boolean isAutogenerated = false;
+
+ public static String SET_COOKIE_KEY = "set-cookie";
+
+ public static String SET_COOKIE2_KEY = "set-cookie2";
/**
* Default constructor.
Modified: jakarta/commons/proper/httpclient/branches/COOKIE_2_BRANCH/src/java/org/apache/commons/httpclient/HttpMethodBase.java
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/httpclient/branches/COOKIE_2_BRANCH/src/java/org/apache/commons/httpclient/HttpMethodBase.java?rev=240400&r1=240399&r2=240400&view=diff
==============================================================================
--- jakarta/commons/proper/httpclient/branches/COOKIE_2_BRANCH/src/java/org/apache/commons/httpclient/HttpMethodBase.java (original)
+++ jakarta/commons/proper/httpclient/branches/COOKIE_2_BRANCH/src/java/org/apache/commons/httpclient/HttpMethodBase.java Sat Aug 27 03:15:11 2005
@@ -44,6 +44,7 @@
import org.apache.commons.httpclient.protocol.Protocol;
import org.apache.commons.httpclient.util.EncodingUtil;
import org.apache.commons.httpclient.util.ExceptionUtil;
+import org.apache.commons.httpclient.util.ParameterFormatter;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -1190,9 +1191,23 @@
getRequestHeaderGroup().addHeader(new Header("Cookie", s, true));
}
}
+ // add a Cookie2 request header specifying the highest cookie version
+ // this client understands. There is no harm in always sending it.
+ // TODO (jain): need to refactor this into spec class somehow
+ getRequestHeaderGroup().addHeader(buildCookie2RequestHeader(matcher));
}
}
+ Header buildCookie2RequestHeader(CookieSpec spec) {
+ ParameterFormatter formatter = new ParameterFormatter();
+ StringBuffer buffer = new StringBuffer();
+ formatter.format(buffer,
+ new NameValuePair("$Version",
+ Integer.toString(spec.getCookieVersion())));
+ Header header = new Header("Cookie2", buffer.toString(), true);
+ return header;
+ }
+
/**
* Generates <tt>Host</tt> request header, as long as no <tt>Host</tt> request
* header already exists.
@@ -1460,13 +1475,34 @@
LOG.trace("enter HttpMethodBase.processResponseHeaders(HttpState, "
+ "HttpConnection)");
- Header[] headers = getResponseHeaderGroup().getHeaders("set-cookie2");
- //Only process old style set-cookie headers if new style headres
- //are not present
- if (headers.length == 0) {
- headers = getResponseHeaderGroup().getHeaders("set-cookie");
- }
-
+ // process cookie headers
+
+ // process set-cookie headers
+ Header[] headers = getResponseHeaderGroup().getHeaders("set-cookie");
+ processCookieHeaders(headers, state, conn);
+
+ // process set-cookie2 headers. Older cookie specs will ignore this
+ // header. For newer specs (like RFC 2965), set-cookie2 header will
+ // replace set-cookie header (obtained in the same response) for the
+ // same cookie.
+ headers = getResponseHeaderGroup().getHeaders("set-cookie2");
+ processCookieHeaders(headers, state, conn);
+ }
+
+ /**
+ * This method processes the specified cookie headers. It is invoked from
+ * within {@link #processResponseHeaders(HttpState,HttpConnection)}
+ *
+ * @param headers cookie {@link Header}s to be processed
+ * @param state the {@link HttpState state} information associated with
+ * this HTTP method
+ * @param conn the {@link HttpConnection connection} used to execute
+ * this HTTP method
+ */
+ protected void processCookieHeaders(Header[] headers, HttpState state, HttpConnection conn) {
+ LOG.trace("enter HttpMethodBase.processCookieHeaders(Header[], HttpState, "
+ + "HttpConnection)");
+
CookieSpec parser = getCookieSpec(state);
String host = this.params.getVirtualHost();
if (host == null) {
@@ -1476,6 +1512,7 @@
Header header = headers[i];
Cookie[] cookies = null;
try {
+ System.out.println("(jain) Header[" + i + "]: " + headers[i]);
cookies = parser.parse(
host,
conn.getPort(),
@@ -1493,6 +1530,9 @@
for (int j = 0; j < cookies.length; j++) {
Cookie cookie = cookies[j];
try {
+ System.out.println("(jain) Cookie [" + j + "]: " +
+ cookies[j].getName() + " = " +
+ cookies[j].getValue() + " has version " + cookies[j].getVersion());
parser.validate(
host,
conn.getPort(),
Modified: jakarta/commons/proper/httpclient/branches/COOKIE_2_BRANCH/src/java/org/apache/commons/httpclient/cookie/CookiePolicy.java
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/httpclient/branches/COOKIE_2_BRANCH/src/java/org/apache/commons/httpclient/cookie/CookiePolicy.java?rev=240400&r1=240399&r2=240400&view=diff
==============================================================================
--- jakarta/commons/proper/httpclient/branches/COOKIE_2_BRANCH/src/java/org/apache/commons/httpclient/cookie/CookiePolicy.java (original)
+++ jakarta/commons/proper/httpclient/branches/COOKIE_2_BRANCH/src/java/org/apache/commons/httpclient/cookie/CookiePolicy.java Sat Aug 27 03:15:11 2005
@@ -79,8 +79,15 @@
* @since 3.0
*/
public static final String RFC_2109 = "rfc2109";
-
- /**
+
+ /**
+ * The RFC 2965 compliant policy.
+ *
+ * @since 3.0
+ */
+ public static final String RFC_2965 = "rfc2965";
+
+ /**
* The policy that ignores cookies.
*
* @since 3.0
@@ -97,6 +104,7 @@
static {
CookiePolicy.registerCookieSpec(DEFAULT, RFC2109Spec.class);
CookiePolicy.registerCookieSpec(RFC_2109, RFC2109Spec.class);
+ CookiePolicy.registerCookieSpec(RFC_2965, RFC2965Spec.class);
CookiePolicy.registerCookieSpec(BROWSER_COMPATIBILITY, CookieSpecBase.class);
CookiePolicy.registerCookieSpec(NETSCAPE, NetscapeDraftSpec.class);
CookiePolicy.registerCookieSpec(IGNORE_COOKIES, IgnoreCookiesSpec.class);
@@ -124,7 +132,14 @@
*/
public static final int RFC2109 = 2;
- /**
+ /**
+ * The <tt>RFC2965</tt> RFC 2965 compliant policy.
+ *
+ * @deprecated Use {@link #RFC_2965}
+ */
+ public static final int RFC2965 = 3;
+
+ /**
* The default cookie policy.
*
* @deprecated Use {@link #DEFAULT}
@@ -242,6 +257,8 @@
return new NetscapeDraftSpec();
case RFC2109:
return new RFC2109Spec();
+ case RFC2965:
+ return new RFC2965Spec();
default:
return getDefaultSpec();
}
Modified: jakarta/commons/proper/httpclient/branches/COOKIE_2_BRANCH/src/java/org/apache/commons/httpclient/cookie/CookieSpec.java
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/httpclient/branches/COOKIE_2_BRANCH/src/java/org/apache/commons/httpclient/cookie/CookieSpec.java?rev=240400&r1=240399&r2=240400&view=diff
==============================================================================
--- jakarta/commons/proper/httpclient/branches/COOKIE_2_BRANCH/src/java/org/apache/commons/httpclient/cookie/CookieSpec.java (original)
+++ jakarta/commons/proper/httpclient/branches/COOKIE_2_BRANCH/src/java/org/apache/commons/httpclient/cookie/CookieSpec.java Sat Aug 27 03:15:11 2005
@@ -224,4 +224,11 @@
*/
Header formatCookieHeader(Cookie cookie) throws IllegalArgumentException;
+ /**
+ * Gets the highest cookie version supported by this cookie specification.
+ *
+ * @return highest cookie version supported.
+ */
+ int getCookieVersion();
+
}
Modified: jakarta/commons/proper/httpclient/branches/COOKIE_2_BRANCH/src/java/org/apache/commons/httpclient/cookie/CookieSpecBase.java
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/httpclient/branches/COOKIE_2_BRANCH/src/java/org/apache/commons/httpclient/cookie/CookieSpecBase.java?rev=240400&r1=240399&r2=240400&view=diff
==============================================================================
--- jakarta/commons/proper/httpclient/branches/COOKIE_2_BRANCH/src/java/org/apache/commons/httpclient/cookie/CookieSpecBase.java (original)
+++ jakarta/commons/proper/httpclient/branches/COOKIE_2_BRANCH/src/java/org/apache/commons/httpclient/cookie/CookieSpecBase.java Sat Aug 27 03:15:11 2005
@@ -204,7 +204,8 @@
/**
* Parse the <tt>"Set-Cookie"</tt> {@link Header} into an array of {@link
- * Cookie}s.
+ * Cookie}s. Subclasses that need to support other cookie headers can
+ * override this method.
*
* <P>The syntax for the Set-Cookie response header is:
*
@@ -244,7 +245,24 @@
if (header == null) {
throw new IllegalArgumentException("Header may not be null.");
}
- return parse(host, port, path, secure, header.getValue());
+
+ 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;
}
@@ -340,15 +358,15 @@
}
- public Collection getValidDateFormats() {
- return this.datepatterns;
- }
-
- public void setValidDateFormats(final Collection datepatterns) {
- this.datepatterns = datepatterns;
- }
+ public Collection getValidDateFormats() {
+ return this.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
@@ -572,7 +590,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) {
@@ -653,6 +671,11 @@
public Header formatCookieHeader(Cookie cookie) {
LOG.trace("enter CookieSpecBase.formatCookieHeader(Cookie)");
return new Header("Cookie", formatCookie(cookie));
+ }
+
+ public int getCookieVersion() {
+ // default cookie version is 0
+ return 0;
}
}
Modified: jakarta/commons/proper/httpclient/branches/COOKIE_2_BRANCH/src/java/org/apache/commons/httpclient/cookie/IgnoreCookiesSpec.java
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/httpclient/branches/COOKIE_2_BRANCH/src/java/org/apache/commons/httpclient/cookie/IgnoreCookiesSpec.java?rev=240400&r1=240399&r2=240400&view=diff
==============================================================================
--- jakarta/commons/proper/httpclient/branches/COOKIE_2_BRANCH/src/java/org/apache/commons/httpclient/cookie/IgnoreCookiesSpec.java (original)
+++ jakarta/commons/proper/httpclient/branches/COOKIE_2_BRANCH/src/java/org/apache/commons/httpclient/cookie/IgnoreCookiesSpec.java Sat Aug 27 03:15:11 2005
@@ -61,16 +61,16 @@
/**
* @return <code>null</code>
*/
- public Collection getValidDateFormats() {
- return null;
- }
-
+ public Collection getValidDateFormats() {
+ return null;
+ }
+
/**
* Does nothing.
*/
- public void setValidDateFormats(Collection datepatterns) {
- }
-
+ public void setValidDateFormats(Collection datepatterns) {
+ }
+
/**
* @return <code>null</code>
*/
@@ -147,6 +147,10 @@
*/
public boolean pathMatch(final String path, final String topmostPath) {
return false;
+ }
+
+ public int getCookieVersion() {
+ return -1;
}
}
Added: jakarta/commons/proper/httpclient/branches/COOKIE_2_BRANCH/src/java/org/apache/commons/httpclient/cookie/RFC2965Spec.java
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/httpclient/branches/COOKIE_2_BRANCH/src/java/org/apache/commons/httpclient/cookie/RFC2965Spec.java?rev=240400&view=auto
==============================================================================
--- jakarta/commons/proper/httpclient/branches/COOKIE_2_BRANCH/src/java/org/apache/commons/httpclient/cookie/RFC2965Spec.java (added)
+++ jakarta/commons/proper/httpclient/branches/COOKIE_2_BRANCH/src/java/org/apache/commons/httpclient/cookie/RFC2965Spec.java Sat Aug 27 03:15:11 2005
@@ -0,0 +1,794 @@
+/*
+ * $HeadURL$
+ * $Revision$
+ * $Date$
+ *
+ * ====================================================================
+ *
+ * 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
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.commons.httpclient.cookie;
+
+import org.apache.commons.httpclient.NameValuePair;
+import org.apache.commons.httpclient.Cookie;
+import org.apache.commons.httpclient.HeaderElement;
+import org.apache.commons.httpclient.Header;
+import org.apache.commons.httpclient.util.ParameterFormatter;
+
+import java.util.*;
+
+//TODO: make sure that the syntaz confirms to java 1.2/1.3 spec and does not include 1.4/1.4 features.
+//TODO: refacortinf and naming for easy readabale code
+//TODO: exception handling control flow problem in many parts of code. At many places we dont want to stop processing
+// when an exception occirs. we want to catch the exception and continie processing
+/**
+ * <p>RFC 2965 specific cookie management functions
+ *
+ * @author <a href="mailto:jain.samit@gmail.com">Samit Jain</a>
+ *
+ * @since 3.0
+ */
+
+// TODO: revise all comments and documentation
+
+// TODO: refactoring
+
+public class RFC2965Spec extends CookieSpecBase {
+
+ private final ParameterFormatter formatter;
+
+ /** Default constructor */
+ public RFC2965Spec() {
+ super();
+ this.formatter = new ParameterFormatter();
+ this.formatter.setAlwaysUseQuotes(true);
+ }
+
+ /**
+ * Parses the Set-Cookie2 value into an array of <tt>Cookie</tt>s.
+ *
+ * <P>The syntax for the Set-Cookie2 response header is:
+ *
+ * <PRE>
+ * 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
+ * </PRE>
+ *
+ * @param host the host from which the <tt>Set-Cookie2</tt> value was
+ * received
+ * @param port the port from which the <tt>Set-Cookie2</tt> value was
+ * received
+ * @param path the path from which the <tt>Set-Cookie2</tt> value was
+ * received
+ * @param secure <tt>true</tt> when the <tt>Set-Cookie2</tt> value was
+ * received over secure conection
+ * @param header the <tt>Set-Cookie2</tt> <tt>Header</tt> received from the server
+ * @return an array of <tt>Cookie</tt>s parsed from the Set-Cookie2 value
+ * @throws MalformedCookieException if an exception occurs during parsing
+ */
+ 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)");
+
+ if (header == null) {
+ throw new IllegalArgumentException("Header may not be null.");
+ }
+ Cookie[] cookies = null;
+ String headerName;
+
+ 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)) {
+ CookieSpec rfc2109Spec = CookiePolicy.getCookieSpec(CookiePolicy.RFC_2109);
+ cookies = rfc2109Spec.parse(host, port, path, secure, header.getValue());
+ headerName = Header.SET_COOKIE_KEY;
+ }
+ 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 <tt>Cookie</tt>s.
+ *
+ * <P>The syntax for the Set-Cookie2 response header is:
+ *
+ * <PRE>
+ * 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
+ * </PRE>
+ *
+ * @param host the host from which the <tt>Set-Cookie2</tt> value was
+ * received
+ * @param port the port from which the <tt>Set-Cookie2</tt> value was
+ * received
+ * @param path the path from which the <tt>Set-Cookie2</tt> value was
+ * received
+ * @param secure <tt>true</tt> when the <tt>Set-Cookie2</tt> value was
+ * received over secure conection
+ * @param header the <tt>Set-Cookie2</tt> header string received from the server
+ * @return an array of <tt>Cookie</tt>s parsed from the Set-Cookie2 value
+ * @throws MalformedCookieException if an exception occurs during parsing
+ */
+ 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)");
+
+ checkCommonArguments(host, port, path);
+
+ if (header == null) {
+ throw new IllegalArgumentException("Header may not be null.");
+ }
+
+ if (path.trim().equals("")) {
+ path = PATH_DELIM;
+ }
+ host = host.toLowerCase();
+
+ HeaderElement[] headerElements =
+ HeaderElement.parseElements(header.toCharArray());
+
+ List cookies = new ArrayList();
+
+ for (int i = 0; i < headerElements.length; i++) {
+ HeaderElement headerelement = headerElements[i];
+ Cookie cookie = null;
+ try {
+ cookie = new Cookie(host,
+ headerelement.getName(),
+ headerelement.getValue(),
+ path,
+ null,
+ false,
+ new int[] {port});
+
+ // cycle through the parameters
+ NameValuePair[] parameters = headerelement.getParameters();
+ // could be null. In case only a header element and no parameters.
+ if (parameters != null) {
+ for (int j = 0; j < parameters.length; j++) {
+ parseAttribute(parameters[j], cookie);
+ }
+ }
+ 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
+ if (LOG.isDebugEnabled())
+ LOG.debug("Error occured while parsing cookie: \"" +
+ headerelement + "\". " + e);
+ }
+ }
+ return (Cookie[]) cookies.toArray(new Cookie[0]);
+ }
+
+ 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
+ * <tt>Set-Cookie2</tt> header or <tt>Set-Cookie</tt> header (in case no Set-Cookie2 header was
+ * received).
+ * @param cookie {@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)
+ throws MalformedCookieException {
+
+ if (attribute == null) {
+ throw new IllegalArgumentException("Attribute may not be null.");
+ }
+ if (cookie == null) {
+ throw new IllegalArgumentException("Cookie may not be null.");
+ }
+ 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);
+ }
+ }
+ 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);
+ }
+
+ }
+ 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));
+ }
+ }
+ else if (paramName.equals("comment")) {
+
+ if (cookie.getComment() == null)
+ cookie.setComment(paramValue);
+
+ }
+ else if (paramName.equals("secure")) {
+
+ cookie.setSecure(true);
+
+ }
+ 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());
+ }
+ }
+ }
+ 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);
+ }
+ }
+ else if (paramName.equals("commenturl")) {
+
+ if (cookie.getCommentURL() == null)
+ cookie.setCommentURL(paramValue);
+
+ }
+ else if (paramName.equals("Discard")) {
+ cookie.setDiscard(true);
+
+ }
+ else {
+ //TODO: should we throw an exception here?
+ //should the header be considered malformed
+ if (LOG.isDebugEnabled())
+ LOG.debug("Unrecognized cookie attribute: " +
+ attribute.toString());
+ }
+ }
+
+ private int[] parsePortAttribute(final String paramValue)
+ throws MalformedCookieException {
+ StringTokenizer st = new StringTokenizer(paramValue, ",");
+ int[] ports = new int[st.countTokens()];
+
+ try {
+ int i = 0;
+ while(st.hasMoreTokens()) {
+ ports[i] = Integer.parseInt(st.nextToken().trim());
+ ++i;
+ }
+ } catch (NumberFormatException e) {
+ throw new MalformedCookieException ("Invalid Port "
+ + "attribute: " + e.getMessage());
+ }
+ return ports;
+ }
+
+ /**
+ * 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 <tt>true</tt> when the {@link org.apache.commons.httpclient.Cookie} was received using a
+ * secure connection
+ * @param cookie 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 cookie) throws MalformedCookieException {
+
+ LOG.trace("enter RFC2965Spec.validate(String, int, String, "
+ + "boolean, Cookie)");
+
+ if (cookie.getHeaderName().equalsIgnoreCase(Header.SET_COOKIE_KEY)) {
+ // old-style cookies are validated according to the old rules
+ CookieSpec rfc2109Spec = CookiePolicy.getCookieSpec(CookiePolicy.RFC_2109);
+ rfc2109Spec.validate(host, port, path, secure, cookie);
+ }
+ else if (cookie.getHeaderName().equalsIgnoreCase(Header.SET_COOKIE2_KEY)) {
+ // check validity of arguments
+ checkCommonArguments(host, port, path);
+
+ if (path.trim().equals("")) {
+ path = PATH_DELIM;
+ }
+ host = host.toLowerCase();
+
+ // validate cookie name
+ if (cookie.getName().indexOf(' ') != -1) {
+ throw new MalformedCookieException("Cookie name may not contain blanks");
+ }
+ if (cookie.getName().startsWith("$")) {
+ throw new MalformedCookieException("Cookie name may not start with $");
+ }
+
+ // validate cookie Version attribute. A user agent rejects
+ // cookie if the Version attribute is missing.
+ if (cookie.getVersion() == -1) {
+ throw new MalformedCookieException(
+ "Missing value for Version attribute. Violates RFC 2965.");
+ }
+
+ // validate cookie Path attribute. The value for the Path
+ // attribute must be a prefix of the request-URI (case-sensitive).
+ // TODO (jain): refactor into pathMatch method for clarity
+ if (!pathMatch(path, cookie.getPath())) {
+ throw new MalformedCookieException(
+ "Illegal path attribute \"" + cookie.getPath()
+ + "\". Path of origin: \"" + path + "\"");
+ }
+
+ // validate cookie Domain attribute.
+ final String cookieDomain = cookie.getDomain().toLowerCase();
+
+ if (cookie.isDomainAttributeSpecified()) {
+ // Domain attribute must start with a dot
+ if (!cookieDomain.startsWith(".")) {
+ throw new MalformedCookieException("Domain attribute \""
+ + cookie.getDomain()
+ + "\" violates RFC 2109: domain must start with a dot");
+ }
+
+ // Domain attribute must contain atleast one embedded dot,
+ // or the value must be equal to .local.
+ int dotIndex = cookieDomain.indexOf('.', 1);
+ if ((dotIndex < 0 || dotIndex == cookieDomain.length() - 1)
+ && (!cookieDomain.equals(".local"))) {
+
+ throw new MalformedCookieException(
+ "Domain attribute \"" + cookie.getDomain()
+ + "\" violates RFC 2965: the value contains no embedded dots "
+ + "and the value is not .local");
+ }
+
+ // The effective host name must domain-match domain attribute.
+ String effectiveHost = getEffectiveHost(host, cookieDomain);
+ if (!domainMatch(effectiveHost, cookieDomain)) {
+ throw new MalformedCookieException(
+ "Domain attribute \"" + cookie.getDomain()
+ + "\" violates RFC 2965: effective host name does not "
+ + "domain-match domain attribute.");
+ }
+
+ // effective host name minus domain must not contain any dots
+ String effectiveHostWithoutDomain =
+ effectiveHost.substring(0, effectiveHost.length()
+ - cookieDomain.length());
+ if (effectiveHostWithoutDomain.indexOf('.') != -1) {
+ throw new MalformedCookieException("Domain attribute \""
+ + cookie.getDomain() + "\" violates RFC 2965: "
+ + "effective host minus domain may not contain any dots");
+ }
+ }
+ else {
+ // Domain was not specified in header. In this case, domain must
+ // string match request host (case-insensitive).
+ if (!cookie.getDomain().equals(host)) {
+ throw new MalformedCookieException("Illegal domain attribute: \""
+ + cookie.getDomain() + "\"."
+ + "Domain of origin: \""
+ + host + "\"");
+ }
+ }
+
+ // validate cookie Port attribute.
+ // If the Port attribute is not specified in header, the
+ // cookie can be sent to any port. Otherwise, the request port
+ // must be in the cookie's port list.
+ if (cookie.isPortAttributeSpecified()) {
+ if (!portMatch(port, cookie.getPorts())) {
+ throw new MalformedCookieException(
+ "Port attribute violates RFC 2965: "
+ + "Request port not found in cookie's port list.");
+ }
+ }
+ }
+ else {
+ throw new MalformedCookieException("RFC 2956 violation. Header name of cookie must be either " +
+ "\"set-cookie\" or \"set-cookie2\".");
+ }
+ }
+
+ private String getEffectiveHost(String host, final String cookieDomain) {
+ String effectiveHost = host;
+ if (cookieDomain.equals(".local") && (host.indexOf('.') < 0)) {
+ effectiveHost += cookieDomain;
+ }
+ return effectiveHost;
+ }
+
+ /**
+ * Returns <tt>true</tt> if the given port exists in the given
+ * ports list. Used to validate cookie.
+ *
+ * @param port
+ * @param ports
+ * @return true returns <tt>true</tt> if the given port exists in
+ * the given ports list; <tt>false</tt> otherwise.
+ */
+ private boolean portMatch(int port, int[] ports) {
+ boolean portInList = false;
+ for (int i = 0, len = ports.length; i < len; i++) {
+ if (port == ports[i]) {
+ portInList = true;
+ break;
+ }
+ }
+ return portInList;
+ }
+
+ /**
+ * Return <tt>true</tt> if the cookie should be submitted with a request
+ * with given attributes, <tt>false</tt> 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 <tt>true</tt> if the request is using a secure connection
+ * @param cookie {@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 cookie) {
+
+ LOG.trace("enter RFC2965.match("
+ + "String, int, String, boolean, Cookie");
+
+ if (cookie.getHeaderName().equalsIgnoreCase(Header.SET_COOKIE_KEY)) {
+
+ // old-style cookies are matched according to the old rules
+ CookieSpec rfc2109Spec = CookiePolicy.getCookieSpec(CookiePolicy.RFC_2109);
+ return rfc2109Spec.match(host, port, path, secure, cookie);
+ }
+ else if (cookie.getHeaderName().equalsIgnoreCase(Header.SET_COOKIE2_KEY)) {
+ checkCommonArguments(host, port, path);
+
+ if (cookie == null) {
+ throw new IllegalArgumentException("Cookie may not be null");
+ }
+ if (path.trim().equals("")) {
+ path = PATH_DELIM;
+ }
+ if (cookie.getDomain() == null) {
+ LOG.warn("Invalid cookie state: domain not specified");
+ return false;
+ }
+ if (cookie.getPath() == null) {
+ LOG.warn("Invalid cookie state: path not specified");
+ return false;
+ }
+ host = host.toLowerCase();
+
+ // match cookie Domain attribute.
+ String cookieDomain = cookie.getDomain();
+ String effectiveHost = getEffectiveHost(host, cookieDomain);
+
+ // The effective host name MUST domain-match the Domain
+ // attribute of the cookie.
+ if (!domainMatch(effectiveHost, cookieDomain)) {
+ return false;
+ }
+
+ // match cookie Port attribute.
+ if (cookie.isPortAttributeSpecified()) {
+ // If Port attribute is specified, the port must be in
+ // the cookie port list.
+ if (!portMatch(port, cookie.getPorts())) {
+ return false;
+ }
+ }
+
+ // match cookie Path attribute. The request-URI MUST path-match
+ // the Path attribute of the cookie.
+ if (!pathMatch(path, cookie.getPath())) {
+ return false;
+ }
+
+ // validate cookie's age
+ if (cookie.getExpiryDate() != null
+ && !cookie.getExpiryDate().after(new Date())) {
+ return false;
+ }
+
+ // finally make sure that if cookie Secure attribute is set, then this
+ // request is using a secure connection
+ if (cookie.getSecure()) {
+ return secure;
+ }
+
+ return true;
+ }
+ else {
+ return false;
+ }
+ }
+
+ /**
+ * Compares the given cookies and returns <tt>true</tt> if they match
+ * according to the rules specified in RFC 2965 (section 3.3.3); otherwise
+ * returns false.
+ *
+ * @param cookie1
+ * @param cookie2
+ * @return <tt>true</tt> if cookies match; otherwise false.
+ */
+ public boolean cookieMatch(Cookie cookie1, Cookie cookie2) {
+ //TODO (jain): how is the cookie name compared
+ return cookie1.getName().equals(cookie2.getName()) &&
+ cookie1.getDomain().equalsIgnoreCase(cookie2.getDomain()) &&
+ cookie1.getPath().equals(cookie2.getPath());
+ }
+
+ /**
+ * Return a string suitable for sending in a <tt>"Cookie"</tt> header
+ * as defined in RFC 2965.
+ * @param buffer The string buffer to use for output
+ * @param cookie The {@link org.apache.commons.httpclient.Cookie} to be formatted as string
+ */
+ private void formatCookieAttributes(final StringBuffer buffer, final Cookie cookie) {
+ String value = cookie.getValue();
+ if (value == null) {
+ value = "";
+ }
+ this.formatter.format(buffer, new NameValuePair(cookie.getName(), value));
+
+ if (cookie.getDomain() != null
+ && cookie.isDomainAttributeSpecified()) {
+ buffer.append("; ");
+ this.formatter.format(buffer, new NameValuePair("$Domain", cookie.getDomain()));
+ }
+ if (cookie.getPath() != null && cookie.isPathAttributeSpecified()) {
+ buffer.append("; ");
+ this.formatter.format(buffer, new NameValuePair("$Path", cookie.getPath()));
+ }
+
+ if (cookie.isPortAttributeSpecified()) {
+ buffer.append("; ");
+ String portValue = "";
+ if (!cookie.isPortAttributeBlank()) {
+ portValue = createPortAttributeValue(cookie.getPorts());
+ }
+ this.formatter.format(buffer, new NameValuePair("$Port", portValue));
+ }
+ }
+
+ private String createPortAttributeValue(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]);
+ }
+ return portValue.toString();
+ }
+
+ /**
+ * Return a string suitable for sending in a <tt>"Cookie"</tt> header as
+ * defined in RFC 2965
+ * @param cookie a {@link org.apache.commons.httpclient.Cookie} to be formatted as string
+ * @return a string suitable for sending in a <tt>"Cookie"</tt> header.
+ */
+ public String formatCookie(Cookie cookie) {
+ LOG.trace("enter RFC2965Spec.formatCookie(Cookie)");
+
+ if (cookie == null) {
+ throw new IllegalArgumentException("Cookie may not be null");
+ }
+ int version = cookie.getVersion();
+ if (version < 1 ||
+ cookie.getHeaderName().equalsIgnoreCase(Header.SET_COOKIE_KEY)) {
+ // delegate cookie formatting to rfc2109Spec
+ CookieSpec rfc2109Spec = CookiePolicy.getCookieSpec(CookiePolicy.RFC_2109);
+ return rfc2109Spec.formatCookie(cookie);
+ }
+
+ StringBuffer buffer = new StringBuffer();
+ this.formatter.format(buffer,
+ new NameValuePair("$Version", Integer.toString(version)));
+ buffer.append("; ");
+ formatCookieAttributes(buffer, cookie);
+
+ return buffer.toString();
+ }
+
+ /**
+ * Create a RFC 2965 compliant <tt>"Cookie"</tt> header value containing all
+ * {@link org.apache.commons.httpclient.Cookie}s in <i>cookies</i> suitable for sending in a <tt>"Cookie"
+ * </tt> 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");
+ }
+ int lowestVersion = Integer.MAX_VALUE;
+ // pick the lowest version
+ for (int i = 0; i < cookies.length; i++) {
+ Cookie cookie = cookies[i];
+ if (cookie.getVersion() < lowestVersion) {
+ lowestVersion = cookie.getVersion();
+ }
+ }
+
+ boolean hasOldStyleCookie = false;
+ // check if cookies array contains set-cookie (old style) cookie
+ for (int i = 0; i < cookies.length; i++) {
+ if (cookies[i].getHeaderName().equalsIgnoreCase(Header.SET_COOKIE_KEY)) {
+ hasOldStyleCookie = true;
+ break;
+ }
+ }
+
+ if ((lowestVersion < 1) || hasOldStyleCookie) {
+ // delegate cookie formatting to rfc2109Spec
+ CookieSpec rfc2109Spec = CookiePolicy.getCookieSpec(CookiePolicy.RFC_2109);
+ return rfc2109Spec.formatCookies(cookies);
+ }
+
+ final StringBuffer buffer = new StringBuffer();
+ this.formatter.format(buffer,
+ new NameValuePair("$Version", Integer.toString(lowestVersion)));
+
+ for (int i = 0; i < cookies.length; i++) {
+ buffer.append("; ");
+ formatCookieAttributes(buffer, cookies[i]);
+ }
+ return buffer.toString();
+ }
+
+ /**
+ * Gets the highest cookie version supported by this cookie specification.
+ *
+ * @return highest cookie version supported.
+ */
+ public int getCookieVersion() {
+ return 1;
+ }
+}
Propchange: jakarta/commons/proper/httpclient/branches/COOKIE_2_BRANCH/src/java/org/apache/commons/httpclient/cookie/RFC2965Spec.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: jakarta/commons/proper/httpclient/branches/COOKIE_2_BRANCH/src/java/org/apache/commons/httpclient/cookie/RFC2965Spec.java
------------------------------------------------------------------------------
svn:keywords = Date Author Id Revision HeadURL
Propchange: jakarta/commons/proper/httpclient/branches/COOKIE_2_BRANCH/src/java/org/apache/commons/httpclient/cookie/RFC2965Spec.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: jakarta/commons/proper/httpclient/branches/COOKIE_2_BRANCH/src/test/org/apache/commons/httpclient/cookie/TestCookieRFC2965Spec.java
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/httpclient/branches/COOKIE_2_BRANCH/src/test/org/apache/commons/httpclient/cookie/TestCookieRFC2965Spec.java?rev=240400&view=auto
==============================================================================
--- jakarta/commons/proper/httpclient/branches/COOKIE_2_BRANCH/src/test/org/apache/commons/httpclient/cookie/TestCookieRFC2965Spec.java (added)
+++ jakarta/commons/proper/httpclient/branches/COOKIE_2_BRANCH/src/test/org/apache/commons/httpclient/cookie/TestCookieRFC2965Spec.java Sat Aug 27 03:15:11 2005
@@ -0,0 +1,537 @@
+/*
+ * $Header: $
+ * $Revision$
+ * $Date$
+ * ====================================================================
+ *
+ * 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
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.commons.httpclient.cookie;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.apache.commons.httpclient.Cookie;
+import org.apache.commons.httpclient.Header;
+import org.apache.commons.httpclient.NameValuePair;
+
+/**
+ * Test cases for RFC2965 cookie spec
+ *
+ * @author jain.samit@gmail.com (Samit Jain)
+ */
+public class TestCookieRFC2965Spec extends TestCookieBase {
+
+
+ // ------------------------------------------------------------ Constructor
+
+ public TestCookieRFC2965Spec(String name) {
+ super(name);
+ }
+
+ // ------------------------------------------------------- TestCase Methods
+
+ public static Test suite() {
+ return new TestSuite(TestCookieRFC2965Spec.class);
+ }
+
+ public void testParseAttributeInvalidParams() throws Exception {
+ CookieSpec cookiespec = new RFC2965Spec();
+ Cookie cookie = new Cookie();
+ NameValuePair attribute = new NameValuePair("name", "value");
+ try {
+ cookiespec.parseAttribute(null, cookie);
+ fail("IllegalArgumentException must have been thrown");
+ } catch (IllegalArgumentException expected) {
+ }
+ try {
+ cookiespec.parseAttribute(attribute, null);
+ fail("IllegalArgumentException must have been thrown");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ public void testParseAttributePath() 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);
+
+ }
+
+ public void testParseAttributeDomain() 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);
+
+ // 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");
+ }
+
+ public void testParseAttributePort() 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}));
+
+ 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}));
+
+ 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) {
+ }
+
+ 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);
+ }
+
+ public void testParseAttributeVersion() 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);
+ }
+
+ public void testParseAttributeMaxage() 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) {
+ }
+
+ 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());
+ }
+
+ public void testParseAttributeOthers() 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);
+ assertFalse(cookie.isPersistent());
+ }
+
+ /**
+ * Test domain equals host
+ */
+ public void testParseValidateDomainEqualsHost() throws Exception {
+ Header header = new Header("Set-Cookie2",
+ "cookie-name=cookie-value; domain=www.b.com; version=1");
+ CookieSpec cookiespec = new RFC2965Spec();
+ Cookie[] parsed = cookieParse(cookiespec, "www.b.com", 80, "/", false, header);
+ assertNotNull(parsed);
+ assertEquals(1, parsed.length);
+ assertEquals("www.b.com", parsed[0].getDomain());
+
+ // 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);
+ assertNotNull(parsed);
+ assertEquals(1, parsed.length);
+ assertEquals("www.b.com", parsed[0].getDomain());
+ }
+
+ /**
+ * Test Domain with no leading dot in domain name
+ */
+ 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");
+ CookieSpec cookiespec = new RFC2965Spec();
+ Cookie[] parsed = cookieParse(cookiespec, "www.a.b.com", 80, "/", false, header);
+ assertNotNull(parsed);
+ assertEquals(1, parsed.length);
+ assertEquals(".a.b.com", parsed[0].getDomain());
+
+ // 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
+ }
+ }
+
+ /**
+ * Domain must have atleast least one embedded dot
+ */
+ public void testParseValidateDomainEmbeddedDot() throws Exception {
+ Header header = new Header("Set-Cookie2",
+ "cookie-name=cookie-value; domain=.com; version=1");
+
+ 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
+ }
+ }
+
+ /**
+ * test .local domain
+ */
+ public void testParseValidateDomainLocal() throws Exception {
+ Header header = new Header("Set-Cookie2",
+ "cookie-name=cookie-value; domain=.local; version=1");
+ CookieSpec cookiespec = new RFC2965Spec();
+ Cookie[] parsed = cookieParse(cookiespec, "host", 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
+ }
+
+ /**
+ * effective host name minus domain must not contain any dots.
+ */
+ public void testParseValidateDomainIllegal() throws Exception {
+ Header header = new Header("Set-Cookie2",
+ "cookie-name=cookie-value; domain=.c.com; version=1");
+ CookieSpec cookiespec = new RFC2965Spec();
+ try {
+ Cookie[] parsed = cookieParse(cookiespec, "a.b.c.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-Cookie",
+ "cookie-name=cookie-value; domain=.c.com; version=1");
+ try {
+ Cookie[] parsed = cookieParse(cookiespec, "a.b.c.com", 80, "/", false, header);
+ fail("MalformedCookieException should have been thrown");
+ } catch (MalformedCookieException e) {
+ // expected
+ }
+ }
+
+ /**
+ * test version.
+ */
+ public void testParseValidateVersion() throws Exception {
+ Header header = new Header("Set-Cookie2", "cookie-name=cookie-value");
+ CookieSpec cookiespec = new RFC2965Spec();
+ try {
+ Cookie[] parsed = cookieParse(cookiespec, "c.a.com", 80, "/", false, header);
+ // version attribute is REQUIRED
+ fail("MalformedCookieException should have been thrown");
+ } catch (MalformedCookieException e) {
+ // expected
+ }
+
+ // test backward compatibility with Set-Cookie header
+ header = new Header("Set-Cookie", "cookie-name=cookie-value");
+ try {
+ Cookie[] parsed = cookieParse(cookiespec, "c.a.com", 80, "/", false, header);
+ // version attribute is REQUIRED
+ fail("MalformedCookieException should have been thrown");
+ } catch (MalformedCookieException e) {
+ // expected
+ }
+ }
+
+ /**
+ * test path.
+ */
+ public void testParseValidatePath() throws Exception {
+ Header header = new Header("Set-Cookie2",
+ "cookie-name=cookie-value; domain=127.0.0.1; path=/path");
+ CookieSpec cookiespec = new RFC2965Spec();
+ 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());
+
+ // test backward compatibility with Set-Cookie header
+ header = new Header("Set-Cookie",
+ "cookie-name=cookie-value; domain=127.0.0.1; path=/path");
+ 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);
+ assertNotNull(parsed);
+ assertEquals(1, parsed.length);
+ assertEquals("/path", parsed[0].getDomain());
+ }
+
+ /**
+ * Tests if cookie constructor rejects cookie name containing blanks.
+ */
+ public void testCookieNameWithBlanks() throws Exception {
+ Header header = new Header("Set-Cookie2", "invalid name=");
+ CookieSpec cookiespec = new RFC2965Spec();
+ try {
+ Cookie[] parsed = cookieParse(cookiespec, "127.0.0.1", 80, "/", false, header);
+ fail("MalformedCookieException exception should have been thrown");
+ } catch (MalformedCookieException e) {
+ // expected
+ }
+
+ // backward compatibility with set-cookie
+ header = new Header("Set-Cookie", "invalid name=");
+ try {
+ Cookie[] parsed = cookieParse(cookiespec, "127.0.0.1", 80, "/", false, header);
+ fail("MalformedCookieException exception should have been thrown");
+ } catch (MalformedCookieException e) {
+ // expected
+ }
+ }
+
+
+ /**
+ * Tests if cookie constructor rejects cookie name starting with $.
+ */
+ public void testCookieNameStartingWithDollarSign() throws Exception {
+ Header header = new Header("Set-Cookie2", "$invalid_name=");
+ CookieSpec cookiespec = new RFC2965Spec();
+ try {
+ Cookie[] parsed = cookieParse(cookiespec, "127.0.0.1", 80, "/", false, header);
+ fail("MalformedCookieException exception should have been thrown");
+ } catch (MalformedCookieException e) {
+ // expected
+ }
+
+ // backward compatibility with set-cookie
+ header = new Header("Set-Cookie", "$invalid_name=");
+ try {
+ Cookie[] parsed = cookieParse(cookiespec, "127.0.0.1", 80, "/", false, header);
+ fail("MalformedCookieException exception should have been thrown");
+ } catch (MalformedCookieException e) {
+ // expected
+ }
+ }
+
+ //TODO: test for cookie matching
+ //TODO: test for cookie formatting
+ /**
+ * Tests if cookie values with embedded comma are handled correctly.
+ */
+ public void testCookieWithComma() throws Exception {
+ Header header = new Header("Set-Cookie", "a=b,c");
+
+ 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());
+ }
+
+ public void testFormatInvalidCookies() throws Exception {
+ CookieSpec cookiespec = new RFC2965Spec();
+ try {
+ String s = cookiespec.formatCookie(null);
+ fail("IllegalArgumentException nust have been thrown");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ /**
+ * Tests RFC 2965 compiant cookie formatting.
+ */
+ public void testRFC2965CookieFormatting() throws Exception {
+ 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=\"/\"");
+
+ 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=/");
+ }
+
+ public void testRFC2965CookiesFormatting() throws Exception {
+ 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=/");
+
+ 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=\"/\"");
+ }
+
+ /**
+ * Tests if null cookie values are handled correctly.
+ */
+ public void testNullCookieValueFormatting() {
+ Cookie cookie = new Cookie(".whatever.com", "name", null, "/", null, false);
+ cookie.setDomainAttributeSpecified(true);
+ cookie.setPathAttributeSpecified(true);
+
+ CookieSpec cookiespec = new RFC2965Spec();
+ String s = cookiespec.formatCookie(cookie);
+ assertEquals("$Version=0; name=; $Domain=.whatever.com; $Path=/", s);
+
+ cookie.setVersion(1);
+ s = cookiespec.formatCookie(cookie);
+ assertEquals("$Version=\"1\"; name=\"\"; $Domain=\".whatever.com\"; $Path=\"/\"", s);
+ }
+
+ public void testCookieNullDomainNullPathFormatting() {
+ Cookie cookie = new Cookie(null, "name", null, "/", null, false);
+ cookie.setDomainAttributeSpecified(true);
+ cookie.setPathAttributeSpecified(true);
+
+ CookieSpec cookiespec = new RFC2965Spec();
+ String s = cookiespec.formatCookie(cookie);
+ assertEquals("$Version=0; name=; $Path=/", s);
+
+ cookie.setDomainAttributeSpecified(false);
+ cookie.setPathAttributeSpecified(false);
+ s = cookiespec.formatCookie(cookie);
+ assertEquals("$Version=0; name=", s);
+ }
+
+ 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;
+ }
+
+}
+
Propchange: jakarta/commons/proper/httpclient/branches/COOKIE_2_BRANCH/src/test/org/apache/commons/httpclient/cookie/TestCookieRFC2965Spec.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: jakarta/commons/proper/httpclient/branches/COOKIE_2_BRANCH/src/test/org/apache/commons/httpclient/cookie/TestCookieRFC2965Spec.java
------------------------------------------------------------------------------
svn:keywords = Date Author Id Revision HeadURL
Propchange: jakarta/commons/proper/httpclient/branches/COOKIE_2_BRANCH/src/test/org/apache/commons/httpclient/cookie/TestCookieRFC2965Spec.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
---------------------------------------------------------------------
To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-dev-help@jakarta.apache.org