Ported RFC2965 specific attribute handlers

git-svn-id: https://svn.apache.org/repos/asf/jakarta/httpcomponents/httpclient/trunk@578408 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Oleg Kalnichevski 2007-09-22 11:53:57 +00:00
parent 1ccb1cfc27
commit 51ed99f3ff
7 changed files with 458 additions and 1 deletions

View File

@ -39,7 +39,7 @@ package org.apache.http.cookie;
* *
* @since 4.0 * @since 4.0
*/ */
public interface SetCookie2 extends Cookie { public interface SetCookie2 extends SetCookie {
/** /**
* If a user agent (web browser) presents this cookie to a user, the * If a user agent (web browser) presents this cookie to a user, the
@ -53,5 +53,14 @@ public interface SetCookie2 extends Cookie {
*/ */
void setPorts(int[] ports); void setPorts(int[] ports);
/**
* Set the Discard attribute.
*
* Note: <tt>Discard</tt> attribute overrides <tt>Max-age</tt>.
*
* @see #isPersistent()
*/
void setDiscard(boolean discard);
} }

View File

@ -44,6 +44,7 @@ public class BasicClientCookie2 extends BasicClientCookie implements SetCookie2
private String commentURL; private String commentURL;
private int[] ports; private int[] ports;
private boolean discard;
/** /**
* Default Constructor taking a name and a value. The value may be null. * Default Constructor taking a name and a value. The value may be null.
@ -71,5 +72,13 @@ public class BasicClientCookie2 extends BasicClientCookie implements SetCookie2
this.commentURL = commentURL; this.commentURL = commentURL;
} }
public void setDiscard(boolean discard) {
this.discard = discard;
}
public boolean isPersistent() {
return !this.discard && super.isPersistent();
}
} }

View File

@ -0,0 +1,35 @@
package org.apache.http.impl.cookie;
import org.apache.http.cookie.Cookie;
import org.apache.http.cookie.CookieAttributeHandler;
import org.apache.http.cookie.CookieOrigin;
import org.apache.http.cookie.MalformedCookieException;
import org.apache.http.cookie.SetCookie;
import org.apache.http.cookie.SetCookie2;
/**
* <tt>"CommantURL"</tt> cookie attribute handler for RFC 2965 cookie spec.
*/
public class RFC2965CommentUrlAttributeHandler implements CookieAttributeHandler {
public RFC2965CommentUrlAttributeHandler() {
super();
}
public void parse(final SetCookie cookie, final String commenturl)
throws MalformedCookieException {
if (cookie instanceof SetCookie2) {
SetCookie2 cookie2 = (SetCookie2) cookie;
cookie2.setCommentURL(commenturl);
}
}
public void validate(final Cookie cookie, final CookieOrigin origin)
throws MalformedCookieException {
}
public boolean match(final Cookie cookie, final CookieOrigin origin) {
return true;
}
}

View File

@ -0,0 +1,35 @@
package org.apache.http.impl.cookie;
import org.apache.http.cookie.Cookie;
import org.apache.http.cookie.CookieAttributeHandler;
import org.apache.http.cookie.CookieOrigin;
import org.apache.http.cookie.MalformedCookieException;
import org.apache.http.cookie.SetCookie;
import org.apache.http.cookie.SetCookie2;
/**
* <tt>"Discard"</tt> cookie attribute handler for RFC 2965 cookie spec.
*/
public class RFC2965DiscardAttributeHandler implements CookieAttributeHandler {
public RFC2965DiscardAttributeHandler() {
super();
}
public void parse(final SetCookie cookie, final String commenturl)
throws MalformedCookieException {
if (cookie instanceof SetCookie2) {
SetCookie2 cookie2 = (SetCookie2) cookie;
cookie2.setDiscard(true);
}
}
public void validate(final Cookie cookie, final CookieOrigin origin)
throws MalformedCookieException {
}
public boolean match(final Cookie cookie, final CookieOrigin origin) {
return true;
}
}

View File

@ -0,0 +1,165 @@
package org.apache.http.impl.cookie;
import org.apache.http.cookie.ClientCookie;
import org.apache.http.cookie.Cookie;
import org.apache.http.cookie.CookieAttributeHandler;
import org.apache.http.cookie.CookieOrigin;
import org.apache.http.cookie.MalformedCookieException;
import org.apache.http.cookie.SetCookie;
/**
* <tt>"Domain"</tt> cookie attribute handler for RFC 2965 cookie spec.
*
* @author jain.samit@gmail.com (Samit Jain)
*
* @since 3.1
*/
public class RFC2965DomainAttributeHandler implements CookieAttributeHandler {
public RFC2965DomainAttributeHandler() {
super();
}
/**
* Parse cookie domain attribute.
*/
public void parse(final SetCookie cookie, String domain)
throws MalformedCookieException {
if (cookie == null) {
throw new IllegalArgumentException("Cookie may not be null");
}
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();
if (!domain.startsWith(".")) {
// Per RFC 2965 section 3.2.2
// "... If an explicitly specified value does not start with
// a dot, the user agent supplies a leading dot ..."
// That effectively implies that the domain attribute
// MAY NOT be an IP address of a host name
domain = "." + domain;
}
cookie.setDomain(domain);
}
/**
* 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;
}
/**
* Validate cookie domain attribute.
*/
public void validate(final Cookie cookie, final CookieOrigin origin)
throws MalformedCookieException {
if (cookie == null) {
throw new IllegalArgumentException("Cookie may not be null");
}
if (origin == null) {
throw new IllegalArgumentException("Cookie origin may not be null");
}
String host = origin.getHost().toLowerCase();
if (cookie.getDomain() == null) {
throw new MalformedCookieException("Invalid cookie state: " +
"domain not specified");
}
String cookieDomain = cookie.getDomain().toLowerCase();
if (cookie instanceof ClientCookie
&& ((ClientCookie) cookie).containsAttribute(ClientCookie.DOMAIN_ATTR)) {
// 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 at least 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.
if (!domainMatch(host, 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 = host.substring(
0, host.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 + "\"");
}
}
}
/**
* Match cookie domain attribute.
*/
public boolean match(final Cookie cookie, final CookieOrigin origin) {
if (cookie == null) {
throw new IllegalArgumentException("Cookie may not be null");
}
if (origin == null) {
throw new IllegalArgumentException("Cookie origin may not be null");
}
String host = origin.getHost().toLowerCase();
String cookieDomain = cookie.getDomain();
// The effective host name MUST domain-match the Domain
// attribute of the cookie.
if (!domainMatch(host, cookieDomain)) {
return false;
}
// effective host name minus domain must not contain any dots
String effectiveHostWithoutDomain = host.substring(
0, host.length() - cookieDomain.length());
if (effectiveHostWithoutDomain.indexOf('.') != -1) {
return false;
}
return true;
}
}

View File

@ -0,0 +1,140 @@
package org.apache.http.impl.cookie;
import java.util.StringTokenizer;
import org.apache.http.cookie.ClientCookie;
import org.apache.http.cookie.Cookie;
import org.apache.http.cookie.CookieAttributeHandler;
import org.apache.http.cookie.CookieOrigin;
import org.apache.http.cookie.MalformedCookieException;
import org.apache.http.cookie.SetCookie;
import org.apache.http.cookie.SetCookie2;
/**
* <tt>"Port"</tt> cookie attribute handler for RFC 2965 cookie spec.
*/
public class RFC2965PortAttributeHandler implements CookieAttributeHandler {
/**
* @param spec
*/
public RFC2965PortAttributeHandler() {
super();
}
/**
* 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 static 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());
}
return ports;
}
/**
* 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 static 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;
}
/**
* Parse cookie port attribute.
*/
public void parse(final SetCookie cookie, final String portValue)
throws MalformedCookieException {
if (cookie == null) {
throw new IllegalArgumentException("Cookie may not be null");
}
if (cookie instanceof SetCookie2) {
SetCookie2 cookie2 = (SetCookie2) cookie;
if (portValue != null && !portValue.equals("")) {
int[] ports = parsePortAttribute(portValue);
cookie2.setPorts(ports);
}
}
}
/**
* Validate cookie port attribute. If the Port attribute was specified
* in header, the request port must be in cookie's port list.
*/
public void validate(final Cookie cookie, final CookieOrigin origin)
throws MalformedCookieException {
if (cookie == null) {
throw new IllegalArgumentException("Cookie may not be null");
}
if (origin == null) {
throw new IllegalArgumentException("Cookie origin may not be null");
}
int port = origin.getPort();
if (cookie instanceof ClientCookie
&& ((ClientCookie) cookie).containsAttribute(ClientCookie.PORT_ATTR)) {
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.
*/
public boolean match(final Cookie cookie, final CookieOrigin origin) {
if (cookie == null) {
throw new IllegalArgumentException("Cookie may not be null");
}
if (origin == null) {
throw new IllegalArgumentException("Cookie origin may not be null");
}
int port = origin.getPort();
if (cookie instanceof ClientCookie
&& ((ClientCookie) cookie).containsAttribute(ClientCookie.PORT_ATTR)) {
if (cookie.getPorts() == null) {
// Invalid cookie state: port not specified
return false;
}
if (!portMatch(port, cookie.getPorts())) {
return false;
}
}
return true;
}
}

View File

@ -0,0 +1,64 @@
package org.apache.http.impl.cookie;
import org.apache.http.cookie.ClientCookie;
import org.apache.http.cookie.Cookie;
import org.apache.http.cookie.CookieAttributeHandler;
import org.apache.http.cookie.CookieOrigin;
import org.apache.http.cookie.MalformedCookieException;
import org.apache.http.cookie.SetCookie;
/**
* <tt>"Version"</tt> cookie attribute handler for RFC 2965 cookie spec.
*/
public class RFC2965VersionAttributeHandler implements CookieAttributeHandler {
public RFC2965VersionAttributeHandler() {
super();
}
/**
* Parse cookie version attribute.
*/
public void parse(final SetCookie cookie, final String value)
throws MalformedCookieException {
if (cookie == null) {
throw new IllegalArgumentException("Cookie may not be null");
}
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(version);
}
/**
* validate cookie version attribute. Version attribute is REQUIRED.
*/
public void validate(final Cookie cookie, final CookieOrigin origin)
throws MalformedCookieException {
if (cookie == null) {
throw new IllegalArgumentException("Cookie may not be null");
}
if (cookie instanceof ClientCookie) {
if (cookie instanceof ClientCookie
&& ((ClientCookie) cookie).containsAttribute(ClientCookie.VERSION_ATTR)) {
throw new MalformedCookieException(
"Violates RFC 2965. Version attribute is required.");
}
}
}
public boolean match(final Cookie cookie, final CookieOrigin origin) {
return true;
}
}