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/29 16:28:43 UTC
svn commit: r264149 [2/3] - 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/
Modified: 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=264149&r1=264148&r2=264149&view=diff
==============================================================================
--- jakarta/commons/proper/httpclient/branches/COOKIE_2_BRANCH/src/java/org/apache/commons/httpclient/cookie/RFC2965Spec.java (original)
+++ jakarta/commons/proper/httpclient/branches/COOKIE_2_BRANCH/src/java/org/apache/commons/httpclient/cookie/RFC2965Spec.java Mon Aug 29 07:28:21 2005
@@ -37,40 +37,64 @@
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>
+ * <p>RFC 2965 specific cookie management functions. *
+ * @author @author jain.samit@gmail.com (Samit Jain)
*
* @since 3.0
*/
-// TODO: revise all comments and documentation
-
-// TODO: refactoring
-
public class RFC2965Spec extends CookieSpecBase {
+ /**
+ * Cookie Response Header name for cookies processed
+ * by this spec.
+ */
+ public static String SET_COOKIE2_KEY = "set-cookie2";
+
+ /**
+ * used for formatting RFC 2956 style cookies
+ */
private final ParameterFormatter formatter;
-
- /** Default constructor */
+
+ /**
+ * Stores attribute name -> attribute handler mappings
+ */
+ private static Map attributeHandlerMap = null;
+
+ /**
+ * 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 <tt>Cookie</tt>s.
*
* <P>The syntax for the Set-Cookie2 response header is:
*
* <PRE>
- * set-cookie = "Set-Cookie:" cookies
+ * set-cookie = "Set-Cookie2:" cookies
* cookies = 1#cookie
* cookie = NAME "=" VALUE * (";" cookie-av)
* NAME = attr
@@ -103,85 +127,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().equalsIgnoreCase(Header.SET_COOKIE2_KEY)) {
- cookies = parse(host, port, path, secure, header.getValue());
- headerName = Header.SET_COOKIE2_KEY;
+ if (header.getName() == null) {
+ throw new IllegalArgumentException("Header name may not be null.");
}
- 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 <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
+ * @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)");
-
- checkCommonArguments(host, port, path);
+ + "String, int, String, boolean, String)");
+ // 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 +174,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,37 +196,17 @@
}
}
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]);
- }
-
- 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.");
- }
+ if (cookies.isEmpty())
+ return null;
+ return (Cookie[]) cookies.toArray(new Cookie[cookies.size()]);
}
/**
@@ -255,157 +214,55 @@
* {@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
+ * <tt>Set-Cookie2</tt> 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);
- }
- }
- 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));
- }
+ try {
+ CookieAttributeHandler handler = getAttributeHandler(paramName);
+ handler.parse(cookie, paramValue);
+ } catch (IllegalStateException canIgnore) {
+ // handler not registered for this paramName
}
- else if (paramName.equals("comment")) {
+ // handle other cookie attributes
+ if (paramName.equals(Cookie2.COMMENT)) {
if (cookie.getComment() == null)
cookie.setComment(paramValue);
-
- }
- else if (paramName.equals("secure")) {
-
+ } else if (paramName.equals(Cookie2.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")) {
-
+ } else if (paramName.equals(Cookie2.COMMENTURL)) {
if (cookie.getCommentURL() == null)
cookie.setCommentURL(paramValue);
-
- }
- else if (paramName.equals("Discard")) {
+ } else if (paramName.equals(Cookie2.DISCARD)) {
cookie.setDiscard(true);
-
- }
- else {
- //TODO: should we throw an exception here?
- //should the header be considered malformed
+ } else {
+ // ignore unknown attribute-value pairs
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
*
@@ -414,151 +271,44 @@
* @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
+ * @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 cookie) throws MalformedCookieException {
+ boolean secure, final Cookie cookieParam)
+ throws MalformedCookieException {
LOG.trace("enter RFC2965Spec.validate(String, int, String, "
+ "boolean, Cookie)");
- if (cookie.getHeaderName().equalsIgnoreCase(Header.SET_COOKIE_KEY)) {
+ // 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, 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;
+ rfc2109Spec.validate(host, port, path, secure, cookieParam);
+ return;
}
- 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;
+ /* 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);
}
/**
@@ -568,227 +318,879 @@
* @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
+ * @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 cookie) {
+ boolean secure, final Cookie cookieParam) {
LOG.trace("enter RFC2965.match("
+ "String, int, String, boolean, Cookie");
- if (cookie.getHeaderName().equalsIgnoreCase(Header.SET_COOKIE_KEY)) {
+ // before we do anything, lets check validity of arguments
+ validateArgs(host, port, path);
+ if (cookieParam == null) {
+ throw new IllegalArgumentException("Cookie may not be null");
+ }
+ 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, cookie);
+ return rfc2109Spec.match(host, port, path, secure, cookieParam);
}
- 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 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;
+ }
- // match cookie Domain attribute.
- String cookieDomain = cookie.getDomain();
- String effectiveHost = getEffectiveHost(host, cookieDomain);
+ /**
+ * Return a string suitable for sending in a <tt>"Cookie"</tt> 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 <tt>"Cookie"</tt> header.
+ */
+ public String formatCookie(Cookie cookieParam) {
+ LOG.trace("enter RFC2965Spec.formatCookie(Cookie)");
- // The effective host name MUST domain-match the Domain
- // attribute of the cookie.
- if (!domainMatch(effectiveHost, cookieDomain)) {
- return false;
- }
+ if (cookieParam == null) {
+ throw new IllegalArgumentException("Cookie may not be null");
+ }
+ 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);
+ }
- // 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;
- }
- }
+ /* 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();
+ }
- // match cookie Path attribute. The request-URI MUST path-match
- // the Path attribute of the cookie.
- if (!pathMatch(path, cookie.getPath())) {
- return false;
- }
+ /**
+ * Create a RFC 2965 compliant <tt>"Cookie"</tt> header value containing all
+ * {@link org.apache.commons.httpclient.Cookie}s 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[])");
- // validate cookie's age
- if (cookie.getExpiryDate() != null
- && !cookie.getExpiryDate().after(new Date())) {
- return false;
+ 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;
}
+ }
+ // 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);
+ }
- // finally make sure that if cookie Secure attribute is set, then this
- // request is using a secure connection
- if (cookie.getSecure()) {
- return secure;
- }
+ /* format cookie2 cookies */
+ final StringBuffer buffer = new StringBuffer();
+ // format cookie version
+ CookieAttributeHandler handler = getAttributeHandler(Cookie2.VERSION);
+ handler.format(buffer, null);
- return true;
- }
- else {
- return false;
+ for (int i = 0; i < cookies.length; i++) {
+ Cookie2 cookie = (Cookie2) cookies[i];
+ // format cookie attributes
+ formatCookieAttributes(buffer, cookie);
}
+ return buffer.toString();
}
/**
- * 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.
+ * 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 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 cookie1
- * @param cookie2
- * @return <tt>true</tt> if cookies match; otherwise false.
+ * @param ports int array of ports
*/
- 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());
+ 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]);
+ }
+ return portValue.toString();
}
/**
- * 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
+ * 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 void formatCookieAttributes(final StringBuffer buffer, final Cookie cookie) {
- String value = cookie.getValue();
- if (value == null) {
- value = "";
+ private int[] parsePortAttribute(final String portValue)
+ throws MalformedCookieException {
+ 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) {
+ throw new MalformedCookieException ("Invalid Port "
+ + "attribute: " + e.getMessage());
}
- this.formatter.format(buffer, new NameValuePair(cookie.getName(), value));
+ return ports;
+ }
- if (cookie.getDomain() != null
- && cookie.isDomainAttributeSpecified()) {
- buffer.append("; ");
- this.formatter.format(buffer, new NameValuePair("$Domain", cookie.getDomain()));
+ /**
+ * Validates host, port, path parameters. Refactored out since it is reqd by
+ * many methods. Validation rules are:
+ * <ul>
+ * <li>Host name must not be null or blank.</li>
+ * <li>Path must not be null.</li>
+ * <li>Port must be >0.</li>
+ * </ul>
+ *
+ * @param host host name where cookie was received from or being sent to.
+ * @param port port of host where cookie was received from or being sent to.
+ * @param path path on host where cookie was received from or being sent to.
+ */
+ private void validateArgs(String host, int port, String path) {
+ if (host == null) {
+ throw new IllegalArgumentException(
+ "Host of origin may not be null");
}
- if (cookie.getPath() != null && cookie.isPathAttributeSpecified()) {
- buffer.append("; ");
- this.formatter.format(buffer, new NameValuePair("$Path", cookie.getPath()));
+ 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.");
}
+ }
- if (cookie.isPortAttributeSpecified()) {
- buffer.append("; ");
- String portValue = "";
- if (!cookie.isPortAttributeBlank()) {
- portValue = createPortAttributeValue(cookie.getPorts());
- }
- this.formatter.format(buffer, new NameValuePair("$Port", portValue));
+ /**
+ * Gets attribute handler {@link CookieAttributeHandler} for the
+ * given attribute.
+ *
+ * @param name attribute name. e.g. Domain, Path, etc.
+ * @throws IllegalStateException if handler not found for the
+ * specified attribute.
+ */
+ private CookieAttributeHandler getAttributeHandler(final String name)
+ throws IllegalStateException {
+ CookieAttributeHandler handler =
+ (CookieAttributeHandler)(attributeHandlerMap.get(name));
+ if (handler == null) {
+ throw new IllegalStateException("Handler not registered for " +
+ name + " attribute.");
+ } else {
+ return handler;
}
}
- private String createPortAttributeValue(int[] ports) {
- StringBuffer portValue = new StringBuffer();
+ /**
+ * Gets 'effective host name' as defined in RFC 2965.
+ * <p>
+ * 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";
+ }
+ return effectiveHost;
+ }
+
+ /**
+ * Performs domain-match as defined by the RFC2965.
+ * <p>
+ * Host A's name domain-matches host B's if
+ * <ol>
+ * <ul>their host name strings string-compare equal; or</ul>
+ * <ul>A is a HDN string and has the form NB, where N is a non-empty
+ * name string, B has the form .B', and B' is a HDN string. (So,
+ * x.y.com domain-matches .Y.com but not Y.com.)</ul>
+ * </ol>
+ *
+ * @param host host name where cookie is received from or being sent to.
+ * @param domain The cookie domain attribute.
+ * @return true if the specified host matches the given domain.
+ */
+ public boolean domainMatch(String host, String domain) {
+ boolean match = host.equals(domain)
+ || (domain.startsWith(".") && host.endsWith(domain));
+
+ return match;
+ }
+
+ /**
+ * Returns <tt>true</tt> if the given port exists in the given
+ * ports list.
+ *
+ * @param port port of host where cookie was received from or being sent to.
+ * @param ports port list
+ * @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 (i > 0) {
- portValue.append(",");
+ if (port == ports[i]) {
+ portInList = true;
+ break;
}
- portValue.append(ports[i]);
}
- return portValue.toString();
+ return portInList;
}
/**
- * 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.
+ * Casts the given {@link Cookie} cookie to {@link Cookie2} cookie.
+ * @param cookieParam {@link Cookie}
+ * @return {@link Cookie2}
*/
- public String formatCookie(Cookie cookie) {
- LOG.trace("enter RFC2965Spec.formatCookie(Cookie)");
+ private Cookie2 getCookie2Cookie(Cookie cookieParam) {
+ if (!(cookieParam instanceof Cookie2)) {
+ throw new IllegalArgumentException("Expected Cookie2 cookie.");
+ }
+ return ((Cookie2) cookieParam);
+ }
- if (cookie == null) {
- throw new IllegalArgumentException("Cookie may not be null");
+ /**
+ * <tt>"Path"</tt> attribute handler for RFC 2965 cookie spec.
+ */
+ private class Cookie2PathAttributeHandler
+ implements CookieAttributeHandler {
+
+ /**
+ * Parse cookie path attribute.
+ * @see CookieAttributeHandler#parse(org.apache.commons.httpclient.Cookie, String)
+ */
+ public void parse(Cookie cookieParam, String path)
+ throws MalformedCookieException {
+ if (cookieParam == null) {
+ throw new IllegalArgumentException("Cookie may not be null");
+ }
+ Cookie2 cookie = getCookie2Cookie(cookieParam);
+ if (!cookie.isPathAttributeSpecified()) {
+ if (path == null) {
+ throw new MalformedCookieException(
+ "Missing value for path attribute");
+ }
+ if (path.trim().equals("")) {
+ throw new MalformedCookieException(
+ "Blank value for path attribute");
+ }
+ cookie.setPath(path);
+ cookie.setPathAttributeSpecified(true);
+ }
}
- 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);
+
+ /**
+ * Validate cookie path attribute. The value for the Path attribute must be a
+ * prefix of the request-URI (case-sensitive matching).
+ * @see CookieAttributeHandler#validate(org.apache.commons.httpclient.Cookie, String)
+ */
+ public void validate(Cookie cookieParam, String path)
+ throws MalformedCookieException {
+ if (cookieParam == null) {
+ throw new IllegalArgumentException("Cookie may not be null");
+ }
+ Cookie2 cookie = getCookie2Cookie(cookieParam);
+ if (path == null) {
+ throw new IllegalArgumentException(
+ "Path of origin host may not be null.");
+ }
+ if (cookie.getPath() == null) {
+ throw new MalformedCookieException("Invalid cookie state: " +
+ "path attribute is null.");
+ }
+ if (path.trim().equals("")) {
+ path = PATH_DELIM;
+ }
+
+ if (!pathMatch(path, cookie.getPath())) {
+ throw new MalformedCookieException(
+ "Illegal path attribute \"" + cookie.getPath()
+ + "\". Path of origin: \"" + path + "\"");
+ }
+
}
- StringBuffer buffer = new StringBuffer();
- this.formatter.format(buffer,
- new NameValuePair("$Version", Integer.toString(version)));
- buffer.append("; ");
- formatCookieAttributes(buffer, cookie);
+ /**
+ * Match cookie path attribute. The value for the Path attribute must be a
+ * prefix of the request-URI (case-sensitive matching).
+ * @see CookieAttributeHandler#match(org.apache.commons.httpclient.Cookie, String)
+ */
+ public boolean match(Cookie cookieParam, String path) {
+ if (cookieParam == null) {
+ throw new IllegalArgumentException("Cookie may not be null");
+ }
+ Cookie2 cookie = getCookie2Cookie(cookieParam);
+ if (path == null) {
+ throw new IllegalArgumentException(
+ "Path of destination host may not be null.");
+ }
+ if (cookie.getPath() == null) {
+ LOG.warn("Invalid cookie state: path attribute is null.");
+ return false;
+ }
+ if (path.trim().equals("")) {
+ path = PATH_DELIM;
+ }
- return buffer.toString();
+ if (!pathMatch(path, cookie.getPath())) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Format cookie Path attribute.
+ * @see CookieAttributeHandler#format(StringBuffer, org.apache.commons.httpclient.Cookie)
+ */
+ public void format(StringBuffer buffer, Cookie cookieParam) {
+ if (cookieParam == null) {
+ throw new IllegalArgumentException("Cookie may not be null");
+ }
+ Cookie2 cookie = getCookie2Cookie(cookieParam);
+ if ((cookie.getPath() != null) && (cookie.isPathAttributeSpecified())) {
+ buffer.append("; ");
+ formatter.format(buffer, new NameValuePair("$Path", cookie.getPath()));
+ }
+ }
}
/**
- * 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.
+ * <tt>"Domain"</tt> cookie attribute handler for RFC 2965 cookie spec.
*/
- public String formatCookies(Cookie[] cookies) {
- LOG.trace("enter RFC2965Spec.formatCookieHeader(Cookie[])");
+ private class Cookie2DomainAttributeHandler
+ implements CookieAttributeHandler {
- if (cookies == null) {
- throw new IllegalArgumentException("Cookies may not be null");
+ /**
+ * Parse cookie domain attribute.
+ * @see CookieAttributeHandler#parse(org.apache.commons.httpclient.Cookie, String)
+ */
+ public void parse(Cookie cookieParam, String domain)
+ throws MalformedCookieException {
+ if (cookieParam == null) {
+ throw new IllegalArgumentException("Cookie may not be null");
+ }
+ Cookie2 cookie = getCookie2Cookie(cookieParam);
+ if (!cookie.isDomainAttributeSpecified()) {
+ //TODO (jain): how do we handle the case when domain is specified and equals host?
+ if (domain == null) {
+ throw new MalformedCookieException(
+ "Missing value for domain attribute");
+ }
+ if (domain.trim().equals("")) {
+ throw new MalformedCookieException(
+ "Blank value for domain attribute");
+ }
+ domain = domain.toLowerCase();
+ // put a leading dot if domain does not start with a dot
+ if (!domain.startsWith("."))
+ domain = "." + domain;
+ cookie.setDomain(domain);
+ cookie.setDomainAttributeSpecified(true);
+ }
}
- 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();
+
+ /**
+ * Validate cookie domain attribute.
+ * @see CookieAttributeHandler#validate(org.apache.commons.httpclient.Cookie, String)
+ */
+ public void validate(Cookie cookieParam, String host)
+ throws MalformedCookieException {
+ if (cookieParam == null) {
+ throw new IllegalArgumentException("Cookie may not be null");
+ }
+ Cookie2 cookie = getCookie2Cookie(cookieParam);
+ 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 (cookie.getDomain() == null) {
+ throw new MalformedCookieException("Invalid cookie state: " +
+ "domain not specified");
+ }
+ host = host.toLowerCase();
+ 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);
+ 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 + "\"");
+ }
}
}
- 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;
+ /**
+ * Match cookie domain attribute.
+ * @see CookieAttributeHandler#match(org.apache.commons.httpclient.Cookie, String)
+ */
+ public boolean match(Cookie cookieParam, String host) {
+ if (cookieParam == null) {
+ throw new IllegalArgumentException("Cookie may not be null");
+ }
+ Cookie2 cookie = getCookie2Cookie(cookieParam);
+ if (host == null) {
+ throw new IllegalArgumentException(
+ "Destination Host may not be null");
+ }
+ if (host.trim().equals("")) {
+ throw new IllegalArgumentException(
+ "Destination Host may not be blank");
+ }
+ if (cookie.getDomain() == null) {
+ LOG.warn("Invalid cookie state: domain not specified");
+ return false;
}
+ host = host.toLowerCase();
+ String cookieDomain = cookie.getDomain();
+ String effectiveHost = getEffectiveHost(host);
+
+ // The effective host name MUST domain-match the Domain
+ // attribute of the cookie.
+ if (!domainMatch(effectiveHost, cookieDomain)) {
+ return false;
+ }
+ // effective host name minus domain must not contain any dots
+ String effectiveHostWithoutDomain =
+ effectiveHost.substring(0, effectiveHost.length()
+ - cookieDomain.length());
+ if (effectiveHostWithoutDomain.indexOf('.') != -1) {
+ return false;
+ }
+ return true;
}
- if ((lowestVersion < 1) || hasOldStyleCookie) {
- // delegate cookie formatting to rfc2109Spec
- CookieSpec rfc2109Spec = CookiePolicy.getCookieSpec(CookiePolicy.RFC_2109);
- return rfc2109Spec.formatCookies(cookies);
+ /**
+ * Format cookie domain attribute.
+ * @see CookieAttributeHandler#format(StringBuffer, org.apache.commons.httpclient.Cookie)
+ */
+ public void format(StringBuffer buffer, Cookie cookieParam) {
+ if (cookieParam == null) {
+ throw new IllegalArgumentException("Cookie may not be null");
+ }
+ Cookie2 cookie = getCookie2Cookie(cookieParam);
+ if (cookie.getDomain() != null
+ && cookie.isDomainAttributeSpecified()) {
+ buffer.append("; ");
+ formatter.format(buffer, new NameValuePair("$Domain", cookie.getDomain()));
+ }
}
+ }
- final StringBuffer buffer = new StringBuffer();
- this.formatter.format(buffer,
- new NameValuePair("$Version", Integer.toString(lowestVersion)));
+ /**
+ * <tt>"Port"</tt> cookie attribute handler for RFC 2965 cookie spec.
+ */
+ private class Cookie2PortAttributeHandler
+ implements CookieAttributeHandler {
- for (int i = 0; i < cookies.length; i++) {
- buffer.append("; ");
- formatCookieAttributes(buffer, cookies[i]);
+ /**
+ * Parse cookie port attribute.
+ * @see CookieAttributeHandler#parse(org.apache.commons.httpclient.Cookie, String)
+ */
+ public void parse(Cookie cookieParam, String portValue)
+ throws MalformedCookieException {
+ if (cookieParam == null) {
+ throw new IllegalArgumentException("Cookie may not be null");
+ }
+ Cookie2 cookie = getCookie2Cookie(cookieParam);
+ if (!cookie.isPortAttributeSpecified()) {
+ if ((portValue == null) || (portValue.trim().equals(""))) {
+ // If the Port attribute is present but has no value, the
+ // cookie can only be sent to the request-port.
+ // Since the default port list contains only request-port, we don't
+ // need to do anything here.
+ cookie.setPortAttributeBlank(true);
+ } else {
+ int[] ports = parsePortAttribute(portValue);
+ cookie.setPorts(ports);
+ }
+ cookie.setPortAttributeSpecified(true);
+ }
+ }
+
+ /**
+ * Validate cookie port attribute. If the Port attribute was specified
+ * in header, the request port must be in cookie's port list.
+ * @see CookieAttributeHandler#validate(org.apache.commons.httpclient.Cookie, String)
+ */
+ public void validate(Cookie cookieParam, String value)
+ throws MalformedCookieException {
+ if (cookieParam == null) {
+ throw new IllegalArgumentException("Cookie may not be null");
+ }
+ Cookie2 cookie = getCookie2Cookie(cookieParam);
+ int port = -1;
+ try {
+ port = Integer.parseInt(value);
+ } catch (NumberFormatException e) {
+ port = -1;
+ }
+ if (port < 0) {
+ throw new IllegalArgumentException("Invalid port of host.");
+ }
+
+ 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.");
+ }
+ }
+ }
+
+ /**
+ * Match 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.
+ * @see CookieAttributeHandler#match(org.apache.commons.httpclient.Cookie, String)
+ */
+ public boolean match(Cookie cookieParam, String value) {
+ if (cookieParam == null) {
+ throw new IllegalArgumentException("Cookie may not be null");
+ }
+ Cookie2 cookie = getCookie2Cookie(cookieParam);
+ int port = -1;
+ try {
+ port = Integer.parseInt(value);
+ } catch (NumberFormatException e) {
+ port = -1;
+ }
+ if (port < 0) {
+ throw new IllegalArgumentException("Invalid port of destination: " + value);
+ }
+
+ if (cookie.isPortAttributeSpecified()) {
+ if (cookie.getPorts() == null) {
+ LOG.warn("Invalid cookie state: port not specified");
+ return false;
+ }
+ if (!portMatch(port, cookie.getPorts())) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * @see CookieAttributeHandler#format(StringBuffer, org.apache.commons.httpclient.Cookie)
+ */
+ public void format(StringBuffer buffer, Cookie cookieParam) {
+ if (cookieParam == null) {
+ throw new IllegalArgumentException("Cookie may not be null");
+ }
+ Cookie2 cookie = getCookie2Cookie(cookieParam);
+ if (cookie.isPortAttributeSpecified()) {
+ String portValue = "";
+ if (!cookie.isPortAttributeBlank()) {
+ portValue = createPortAttribute(cookie.getPorts());
+ }
+ buffer.append("; ");
+ formatter.format(buffer, new NameValuePair("$Port", portValue));
+ }
}
- return buffer.toString();
}
+ /**
+ * <tt>"Name"</tt> cookie attribute handler for RFC 2965 cookie spec.
+ */
+ private class Cookie2NameAttributeHandler
+ implements CookieAttributeHandler {
+
+ /**
+ * Parse cookie name.
+ * @see CookieAttributeHandler#parse(org.apache.commons.httpclient.Cookie, String)
+ */
+ public void parse(Cookie cookie, String value)
+ throws MalformedCookieException {
+ }
+
+ /**
+ * validate cookie name.
+ * @see CookieAttributeHandler#validate(org.apache.commons.httpclient.Cookie, String)
+ */
+ public void validate(Cookie cookieParam, String value)
+ throws MalformedCookieException {
+ if (cookieParam == null) {
+ throw new IllegalArgumentException("Cookie may not be null");
+ }
+ Cookie2 cookie = getCookie2Cookie(cookieParam);
+ 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 $");
+ }
+ }
+
+ /**
+ * @see CookieAttributeHandler#match(org.apache.commons.httpclient.Cookie, String)
+ */
+ public boolean match(Cookie cookie, String value) {
+ return true;
+ }
+
+ /**
+ * @see CookieAttributeHandler#format(StringBuffer, org.apache.commons.httpclient.Cookie)
+ */
+ public void format(StringBuffer buffer, Cookie cookieParam) {
+ if (cookieParam == null) {
+ throw new IllegalArgumentException("Cookie may not be null");
+ }
+ Cookie2 cookie = getCookie2Cookie(cookieParam);
+ // format cookie name and value
+ String value = cookie.getValue();
+ if (value == null) {
+ value = "";
+ }
+ buffer.append("; ");
+ formatter.format(buffer, new NameValuePair(cookie.getName(), value));
+ }
+ }
+
+ /**
+ * <tt>"Max-age"</tt> cookie attribute handler for RFC 2965 cookie spec.
+ */
+ private class Cookie2MaxageAttributeHandler
+ implements CookieAttributeHandler {
+
+ /**
+ * Parse cookie max-age attribute.
+ * @see CookieAttributeHandler#parse(org.apache.commons.httpclient.Cookie, String)
+ */
+ public void parse(Cookie cookieParam, String value)
+ throws MalformedCookieException {
+ if (cookieParam == null) {
+ throw new IllegalArgumentException("Cookie may not be null");
+ }
+ Cookie2 cookie = getCookie2Cookie(cookieParam);
+ if (cookie.getExpiryDate() == null) {
+ if (value == null) {
+ throw new MalformedCookieException(
+ "Missing value for max-age attribute");
+ }
+ int age = -1;
+ try {
+ age = Integer.parseInt(value);
+ } catch (NumberFormatException e) {
+ age = -1;
+ }
+ if (age < 0) {
+ throw new MalformedCookieException ("Invalid max-age attribute.");
+ }
+ cookie.setExpiryDate(
+ new Date(System.currentTimeMillis() + age * 1000L));
+ }
+ }
+
+ /**
+ * validate cookie max-age attribute.
+ * @see CookieAttributeHandler#validate(org.apache.commons.httpclient.Cookie, String)
+ */
+ public void validate(Cookie cookie, String value) {
+ }
+
+ /**
+ * @see CookieAttributeHandler#match(org.apache.commons.httpclient.Cookie, String)
+ */
+ public boolean match(Cookie cookie, String value) {
+ return true;
+ }
+
+ /**
+ * @see CookieAttributeHandler#format(StringBuffer, org.apache.commons.httpclient.Cookie)
+ */
+ public void format(StringBuffer buffer, Cookie cookie) {
+ }
+ }
+
/**
- * Gets the highest cookie version supported by this cookie specification.
- *
- * @return highest cookie version supported.
+ * <tt>"Version"</tt> cookie attribute handler for RFC 2965 cookie spec.
*/
- public int getCookieVersion() {
- return 1;
+ private class Cookie2VersionAttributeHandler
+ implements CookieAttributeHandler {
+
+ /**
+ * Parse cookie version attribute.
+ * @see CookieAttributeHandler#parse(org.apache.commons.httpclient.Cookie, String)
+ */
+ public void parse(Cookie cookieParam, String value)
+ throws MalformedCookieException {
+ if (cookieParam == null) {
+ throw new IllegalArgumentException("Cookie may not be null");
+ }
+ Cookie2 cookie = getCookie2Cookie(cookieParam);
+ if (!cookie.isVersionAttributeSpecified()) {
+ if (value == null) {
+ throw new MalformedCookieException(
+ "Missing value for version attribute");
+ }
+ int version = -1;
+ try {
+ version = Integer.parseInt(value);
+ } catch (NumberFormatException e) {
+ version = -1;
+ }
+ if (version < 0) {
+ throw new MalformedCookieException("Invalid cookie version.");
+ }
+ cookie.setVersion(Integer.parseInt(value));
+ cookie.setVersionAttributeSpecified(true);
+ }
+ }
+
+ /**
+ * validate cookie version attribute. Version attribute is REQUIRED.
+ * @see CookieAttributeHandler#validate(org.apache.commons.httpclient.Cookie, String)
+ */
+ public void validate(Cookie cookieParam, String value)
+ throws MalformedCookieException {
+ if (cookieParam == null) {
+ throw new IllegalArgumentException("Cookie may not be null");
+ }
+ Cookie2 cookie = getCookie2Cookie(cookieParam);
+ if (!cookie.isVersionAttributeSpecified()) {
+ throw new MalformedCookieException(
+ "Violates RFC 2965. Version attribute is required.");
+ }
+ //TODO (jain): other versions for set-cookie2 ?
+ if (cookie.getVersion() != 1) {
+ throw new MalformedCookieException(
+ "Violates RFC 2965. Invalid value for Version attribute." +
+ "Must be \"1\".");
+ }
+ }
+
+ /**
+ * @see CookieAttributeHandler#match(org.apache.commons.httpclient.Cookie, String)
+ */
+ public boolean match(Cookie cookie, String value) {
+ return true;
+ }
+
+ /**
+ * @see CookieAttributeHandler#format(StringBuffer, org.apache.commons.httpclient.Cookie)
+ */
+ public void format(StringBuffer buffer, Cookie cookie) {
+ formatter.format(buffer, new NameValuePair("$Version", "1"));
+ }
}
}
Added: jakarta/commons/proper/httpclient/branches/COOKIE_2_BRANCH/src/test/org/apache/commons/httpclient/cookie/TestCookie2.java
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/httpclient/branches/COOKIE_2_BRANCH/src/test/org/apache/commons/httpclient/cookie/TestCookie2.java?rev=264149&view=auto
==============================================================================
--- jakarta/commons/proper/httpclient/branches/COOKIE_2_BRANCH/src/test/org/apache/commons/httpclient/cookie/TestCookie2.java (added)
+++ jakarta/commons/proper/httpclient/branches/COOKIE_2_BRANCH/src/test/org/apache/commons/httpclient/cookie/TestCookie2.java Mon Aug 29 07:28:21 2005
@@ -0,0 +1,127 @@
+/*
+ * $Header: /cvsroot/httpc-cookie2/httpc-cookie2/httpcookie2SVN-patch.082805-2100.diff,v 1.1 2005/08/29 05:01:58 sjain700 Exp $
+ * $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 java.util.*;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.apache.commons.httpclient.Cookie;
+import org.apache.commons.httpclient.Header;
+
+
+/**
+ * Test cases for {@link Cookie2}.
+ *
+ * @author Samit Jain (jain.samit@gmail.com)
+ */
+public class TestCookie2 extends TestCookieBase {
+
+
+ // ------------------------------------------------------------ Constructor
+
+ public TestCookie2(String name) {
+ super(name);
+ }
+
+ // ------------------------------------------------------- TestCase Methods
+
+ public static Test suite() {
+ return new TestSuite(TestCookie2.class);
+ }
+
+ /**
+ * Tests default constructor.
+ */
+ public void testDefaultConstuctor() {
+ Cookie2 dummy = new Cookie2();
+ // check cookie properties (default values)
+ assertNull(dummy.getPorts());
+ assertFalse(dummy.getSecure());
+ assertFalse(dummy.isExpired());
+ assertFalse(dummy.isDomainAttributeSpecified());
+ assertFalse(dummy.isPathAttributeSpecified());
+ assertFalse(dummy.isPortAttributeSpecified());
+ assertFalse(dummy.isVersionAttributeSpecified());
+ assertFalse(dummy.isPersistent());
+
+ Cookie2 dummy2 = new Cookie2();
+ assertEquals(dummy, dummy2);
+ }
+
+ public void testComparator() throws Exception {
+ Header setCookie2 = null;
+ Cookie[] parsed = null;
+ List cookies = new LinkedList();
+ CookieSpec cookiespec = new RFC2965Spec();
+ // Cookie 0
+ setCookie2 = new Header("Set-Cookie2","cookie-name=Cookie0; Version=1");
+ parsed = cookieParse(cookiespec, "domain.com", 80,
+ "/path/path1", true, setCookie2);
+ cookies.add(parsed[0]);
+ // Cookie 1
+ setCookie2 = new Header("Set-Cookie2","cookie-name=Cookie1; Version=1");
+ parsed = cookieParse(cookiespec, "domain.com", 80, "/path", true, setCookie2);
+ cookies.add(parsed[0]);
+ // Cookie 2
+ setCookie2 = new Header("Set-Cookie2","cookie-name=Cookie2; Version=1");
+ parsed = cookieParse(cookiespec, "domain.com", 80, "/", true, setCookie2);
+ cookies.add(parsed[0]);
+ // Cookie 3
+ setCookie2 = new Header("Set-Cookie2","cookie-name=Cookie3; Version=1");
+ parsed = cookieParse(cookiespec, "domain.com", 80,
+ "/path/path1/path2", true, setCookie2);
+ cookies.add(parsed[0]);
+ // Cookie 4
+ setCookie2 = new Header("Set-Cookie2","cookie-name=Cookie4; Version=1");
+ parsed = cookieParse(cookiespec, "domain.com", 80,
+ "/path/path1/path2/path3", true, setCookie2);
+ cookies.add(parsed[0]);
+
+ // The ascending order should be:
+ // 2, 1, 0, 3, 4
+ int[] expectedOrder = new int[] {2, 1, 0, 3, 4};
+ Set sortedCookies = new TreeSet(parsed[0]);
+ sortedCookies.addAll(cookies);
+
+ int pass = 0;
+ for (Iterator itr = sortedCookies.iterator(); itr.hasNext(); ++pass) {
+ Cookie2 cookie = (Cookie2) itr.next();
+ assertTrue("sortedCookies[" + pass + "] should be cookies[" + expectedOrder[pass] + "]",
+ cookie == cookies.get(expectedOrder[pass]));
+ }
+
+ try {
+ parsed[0].compare(parsed[0], "foo");
+ fail("Should have thrown an exception trying to compare non-cookies");
+ } catch (ClassCastException expected) {}
+ }
+}
+
Propchange: jakarta/commons/proper/httpclient/branches/COOKIE_2_BRANCH/src/test/org/apache/commons/httpclient/cookie/TestCookie2.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: jakarta/commons/proper/httpclient/branches/COOKIE_2_BRANCH/src/test/org/apache/commons/httpclient/cookie/TestCookie2.java
------------------------------------------------------------------------------
svn:keywords = Date Author Id Revision HeadURL
Propchange: jakarta/commons/proper/httpclient/branches/COOKIE_2_BRANCH/src/test/org/apache/commons/httpclient/cookie/TestCookie2.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Modified: jakarta/commons/proper/httpclient/branches/COOKIE_2_BRANCH/src/test/org/apache/commons/httpclient/cookie/TestCookieAll.java
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/httpclient/branches/COOKIE_2_BRANCH/src/test/org/apache/commons/httpclient/cookie/TestCookieAll.java?rev=264149&r1=264148&r2=264149&view=diff
==============================================================================
--- jakarta/commons/proper/httpclient/branches/COOKIE_2_BRANCH/src/test/org/apache/commons/httpclient/cookie/TestCookieAll.java (original)
+++ jakarta/commons/proper/httpclient/branches/COOKIE_2_BRANCH/src/test/org/apache/commons/httpclient/cookie/TestCookieAll.java Mon Aug 29 07:28:21 2005
@@ -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());
---------------------------------------------------------------------
To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-dev-help@jakarta.apache.org