Author: jfrederic.clere(a)jboss.com
Date: 2009-04-15 06:15:38 -0400 (Wed, 15 Apr 2009)
New Revision: 1008
Added:
branches/JBOSSWEB_2_0_0_GA_CP/test/
branches/JBOSSWEB_2_0_0_GA_CP/test/build.xml
branches/JBOSSWEB_2_0_0_GA_CP/test/org/
branches/JBOSSWEB_2_0_0_GA_CP/test/org/apache/
branches/JBOSSWEB_2_0_0_GA_CP/test/org/apache/catalina/
branches/JBOSSWEB_2_0_0_GA_CP/test/org/apache/catalina/tomcat/
branches/JBOSSWEB_2_0_0_GA_CP/test/org/apache/catalina/tomcat/util/
branches/JBOSSWEB_2_0_0_GA_CP/test/org/apache/catalina/tomcat/util/http/
branches/JBOSSWEB_2_0_0_GA_CP/test/org/apache/catalina/tomcat/util/http/TestCookies.java
Modified:
branches/JBOSSWEB_2_0_0_GA_CP/src/share/classes/org/apache/catalina/connector/Request.java
branches/JBOSSWEB_2_0_0_GA_CP/src/share/classes/org/apache/catalina/connector/Response.java
branches/JBOSSWEB_2_0_0_GA_CP/src/share/classes/org/apache/tomcat/util/http/Cookies.java
branches/JBOSSWEB_2_0_0_GA_CP/src/share/classes/org/apache/tomcat/util/http/ServerCookie.java
Log:
Port the fixes and tests for the cookies.
Modified:
branches/JBOSSWEB_2_0_0_GA_CP/src/share/classes/org/apache/catalina/connector/Request.java
===================================================================
---
branches/JBOSSWEB_2_0_0_GA_CP/src/share/classes/org/apache/catalina/connector/Request.java 2009-04-14
16:35:14 UTC (rev 1007)
+++
branches/JBOSSWEB_2_0_0_GA_CP/src/share/classes/org/apache/catalina/connector/Request.java 2009-04-15
10:15:38 UTC (rev 1008)
@@ -2334,6 +2334,25 @@
}
}
+ protected String unescape(String s) {
+ if (s==null)
+ return null;
+ if (s.indexOf('\\') == -1)
+ return s;
+ StringBuffer buf = new StringBuffer();
+ for (int i=0; i<s.length(); i++) {
+ char c = s.charAt(i);
+ if (c!='\\') buf.append(c);
+ else {
+ if (++i >= s.length())
+ throw new IllegalArgumentException();//invalid escape, hence invalid
cookie
+ c = s.charAt(i);
+ buf.append(c);
+ }
+ }
+ return buf.toString();
+ }
+
/**
* Parse cookies.
*/
@@ -2352,14 +2371,19 @@
for (int i = 0; i < count; i++) {
ServerCookie scookie = serverCookies.getCookie(i);
try {
- Cookie cookie = new Cookie(scookie.getName().toString(),
- scookie.getValue().toString());
- cookie.setPath(scookie.getPath().toString());
- cookie.setVersion(scookie.getVersion());
+ /*
+ we must unescape the '\\' escape character
+ */
+ Cookie cookie = new Cookie(scookie.getName().toString(),null);
+ int version = scookie.getVersion();
+ cookie.setVersion(version);
+ cookie.setValue(unescape(scookie.getValue().toString()));
+ cookie.setPath(unescape(scookie.getPath().toString()));
String domain = scookie.getDomain().toString();
- if (domain != null) {
- cookie.setDomain(scookie.getDomain().toString());
- }
+ if (domain!=null)
+ cookie.setDomain(unescape(domain));//avoid NPE
+ String comment = scookie.getComment().toString();
+ cookie.setComment(version==1?unescape(comment):null);
cookies[idx++] = cookie;
} catch(IllegalArgumentException e) {
// Ignore bad cookie
Modified:
branches/JBOSSWEB_2_0_0_GA_CP/src/share/classes/org/apache/catalina/connector/Response.java
===================================================================
---
branches/JBOSSWEB_2_0_0_GA_CP/src/share/classes/org/apache/catalina/connector/Response.java 2009-04-14
16:35:14 UTC (rev 1007)
+++
branches/JBOSSWEB_2_0_0_GA_CP/src/share/classes/org/apache/catalina/connector/Response.java 2009-04-15
10:15:38 UTC (rev 1008)
@@ -949,9 +949,9 @@
if (isCommitted())
return;
- cookies.add(cookie);
-
final StringBuffer sb = new StringBuffer();
+ // web application code can receive a IllegalArgumentException
+ // from the appendCookieValue invocation
if (SecurityUtil.isPackageProtectionEnabled()) {
AccessController.doPrivileged(new PrivilegedAction() {
public Object run(){
@@ -969,12 +969,13 @@
cookie.getPath(), cookie.getDomain(), cookie.getComment(),
cookie.getMaxAge(), cookie.getSecure());
}
-
+ // if we reached here, no exception, cookie is valid
// the header name is Set-Cookie for both "old" and v.1 ( RFC2109 )
// RFC2965 is not supported by browsers and the Servlet spec
// asks for 2109.
addHeader("Set-Cookie", sb.toString());
+ cookies.add(cookie);
}
Modified:
branches/JBOSSWEB_2_0_0_GA_CP/src/share/classes/org/apache/tomcat/util/http/Cookies.java
===================================================================
---
branches/JBOSSWEB_2_0_0_GA_CP/src/share/classes/org/apache/tomcat/util/http/Cookies.java 2009-04-14
16:35:14 UTC (rev 1007)
+++
branches/JBOSSWEB_2_0_0_GA_CP/src/share/classes/org/apache/tomcat/util/http/Cookies.java 2009-04-15
10:15:38 UTC (rev 1008)
@@ -45,7 +45,28 @@
boolean unprocessed=true;
MimeHeaders headers;
-
+
+ /*
+ List of Separator Characters (see isSeparator())
+ Excluding the '/' char violates the RFC, but
+ it looks like a lot of people put '/'
+ in unquoted values: '/': ; //47
+ '\t':9 ' ':32 '\"':34 '\'':39 '(':40
')':41 ',':44 ':':58 ';':59 '<':60
+ '=':61 '>':62 '?':63 '@':64 '[':91
'\\':92 ']':93 '{':123 '}':125
+ */
+ public static final char SEPARATORS[] = { '\t', ' ',
'\"', '\'', '(', ')', ',',
+ ':', ';', '<', '=', '>',
'?', '@', '[', '\\', ']', '{', '}'
};
+
+ protected static final boolean separators[] = new boolean[128];
+ static {
+ for (int i = 0; i < 128; i++) {
+ separators[i] = false;
+ }
+ for (int i = 0; i < SEPARATORS.length; i++) {
+ separators[SEPARATORS[i]] = true;
+ }
+ }
+
/**
* Construct a new cookie collection, that will extract
* the information from headers.
@@ -182,181 +203,6 @@
}
}
- /** Process a byte[] header - allowing fast processing of the
- * raw data
- */
- void processCookieHeader( byte bytes[], int off, int len )
- {
- if( len<=0 || bytes==null ) return;
- int end=off+len;
- int pos=off;
-
- int version=0; //sticky
- ServerCookie sc=null;
-
-
- while( pos<end ) {
- byte cc;
- // [ skip_spaces name skip_spaces "=" skip_spaces value EXTRA ; ]
*
- if( dbg>0 ) log( "Start: " + pos + " " + end );
-
- pos=skipSpaces(bytes, pos, end);
- if( pos>=end )
- return; // only spaces
- int startName=pos;
- if( dbg>0 ) log( "SN: " + pos );
-
- // Version should be the first token
- boolean isSpecial=false;
- if(bytes[pos]=='$') { pos++; isSpecial=true; }
-
- pos= findDelim1( bytes, startName, end); // " =;,"
- int endName=pos;
- // current = "=" or " " or DELIM
- pos= skipSpaces( bytes, endName, end );
- if( dbg>0 ) log( "DELIM: " + endName + " " +
(char)bytes[pos]);
-
- if(pos >= end ) {
- // it's a name-only cookie ( valid in RFC2109 )
- if( ! isSpecial ) {
- sc=addCookie();
- sc.getName().setBytes( bytes, startName,
- endName-startName );
- sc.getValue().setString("");
- sc.setVersion( version );
- if( dbg>0 ) log( "Name only, end: " + startName + "
" +
- endName);
- }
- return;
- }
-
- cc=bytes[pos];
- pos++;
- if( cc==';' || cc==',' || pos>=end ) {
- if( ! isSpecial && startName!= endName ) {
- sc=addCookie();
- sc.getName().setBytes( bytes, startName,
- endName-startName );
- sc.getValue().setString("");
- sc.setVersion( version );
- if( dbg>0 ) log( "Name only: " + startName + "
" + endName);
- }
- continue;
- }
-
- // we should have "=" ( tested all other alternatives )
- int startValue=skipSpaces( bytes, pos, end);
- int endValue=startValue;
-
- cc=bytes[pos];
- if( cc=='"' ) {
- endValue=findDelim3( bytes, startValue+1, end, cc );
- if (endValue == -1) {
- endValue=findDelim2( bytes, startValue+1, end );
- } else startValue++;
- pos=endValue+1; // to skip to next cookie
- } else {
- endValue=findDelim2( bytes, startValue, end );
- pos=endValue+1;
- }
-
- // if not $Version, etc
- if( ! isSpecial ) {
- sc=addCookie();
- sc.getName().setBytes( bytes, startName, endName-startName );
- sc.getValue().setBytes( bytes, startValue, endValue-startValue);
- sc.setVersion( version );
- if( dbg>0 ) {
- log( "New: " + sc.getName() + "X=X" +
sc.getValue());
- }
- continue;
- }
-
- // special - Path, Version, Domain, Port
- if( dbg>0 ) log( "Special: " + startName + " " +
endName);
- // XXX TODO
- if( equals( "$Version", bytes, startName, endName ) ) {
- if(dbg>0 ) log( "Found version " );
- if( bytes[startValue]=='1' && endValue==startValue+1 ) {
- version=1;
- if(dbg>0 ) log( "Found version=1" );
- }
- continue;
- }
- if( sc==null ) {
- // Path, etc without a previous cookie
- continue;
- }
- if( equals( "$Path", bytes, startName, endName ) ) {
- sc.getPath().setBytes( bytes,
- startValue,
- endValue-startValue );
- }
- if( equals( "$Domain", bytes, startName, endName ) ) {
- sc.getDomain().setBytes( bytes,
- startValue,
- endValue-startValue );
- }
- if( equals( "$Port", bytes, startName, endName ) ) {
- // sc.getPort().setBytes( bytes,
- // startValue,
- // endValue-startValue );
- }
- }
- }
-
- // -------------------- Utils --------------------
- public static int skipSpaces( byte bytes[], int off, int end ) {
- while( off < end ) {
- byte b=bytes[off];
- if( b!= ' ' ) return off;
- off ++;
- }
- return off;
- }
-
- public static int findDelim1( byte bytes[], int off, int end )
- {
- while( off < end ) {
- byte b=bytes[off];
- if( b==' ' || b=='=' || b==';' || b==',' )
- return off;
- off++;
- }
- return off;
- }
-
- public static int findDelim2( byte bytes[], int off, int end )
- {
- while( off < end ) {
- byte b=bytes[off];
- if( b==';' || b==',' )
- return off;
- off++;
- }
- return off;
- }
-
- /*
- * search for cc but skip \cc as required by rfc2616
- * (according to rfc2616 cc should be ")
- */
- public static int findDelim3( byte bytes[], int off, int end, byte cc )
- {
- while( off < end ) {
- byte b=bytes[off];
- if ( b== '\\' ) {
- off++;
- off++;
- continue;
- }
- if( b==cc )
- return off;
- off++;
- }
- return -1;
- }
-
// XXX will be refactored soon!
public static boolean equals( String s, byte b[], int start, int end) {
int blen = end-start;
@@ -440,42 +286,302 @@
log.debug("Cookies: " + s);
}
- /*
- public static void main( String args[] ) {
- test("foo=bar; a=b");
- test("foo=bar;a=b");
- test("foo=bar;a=b;");
- test("foo=bar;a=b; ");
- test("foo=bar;a=b; ;");
- test("foo=;a=b; ;");
- test("foo;a=b; ;");
- // v1
- test("$Version=1; foo=bar;a=b");
- test("$Version=\"1\"; foo='bar'; $Path=/path;
$Domain=\"localhost\"");
- test("$Version=1;foo=bar;a=b; ; ");
- test("$Version=1;foo=;a=b; ; ");
- test("$Version=1;foo= ;a=b; ; ");
- test("$Version=1;foo;a=b; ; ");
- test("$Version=1;foo=\"bar\";a=b; ; ");
- test("$Version=1;foo=\"bar\";$Path=/examples;a=b; ; ");
- test("$Version=1;foo=\"bar\";$Domain=apache.org;a=b");
-
test("$Version=1;foo=\"bar\";$Domain=apache.org;a=b;$Domain=yahoo.com");
- // rfc2965
-
test("$Version=1;foo=\"bar\";$Domain=apache.org;$Port=8080;a=b");
- // wrong
-
test("$Version=1;foo=\"bar\";$Domain=apache.org;$Port=8080;a=b");
+ /**
+ * Returns true if the byte is a separator character as
+ * defined in RFC2619. Since this is called often, this
+ * function should be organized with the most probable
+ * outcomes first.
+ * JVK
+ */
+ public static final boolean isSeparator(final byte c) {
+ if (c > 0 && c < 126)
+ return separators[c];
+ else
+ return false;
}
+
+ /**
+ * Returns true if the byte is a whitespace character as
+ * defined in RFC2619
+ * JVK
+ */
+ public static final boolean isWhiteSpace(final byte c) {
+ // This switch statement is slightly slower
+ // for my vm than the if statement.
+ // Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_07-164)
+ /*
+ switch (c) {
+ case ' ':;
+ case '\t':;
+ case '\n':;
+ case '\r':;
+ case '\f':;
+ return true;
+ default:;
+ return false;
+ }
+ */
+ if (c == ' ' || c == '\t' || c == '\n' || c ==
'\r' || c == '\f')
+ return true;
+ else
+ return false;
+ }
- public static void test( String s ) {
- System.out.println("Processing " + s );
- Cookies cs=new Cookies(null);
- cs.processCookieHeader( s.getBytes(), 0, s.length());
- for( int i=0; i< cs.getCookieCount() ; i++ ) {
- System.out.println("Cookie: " + cs.getCookie( i ));
+ /**
+ * Parses a cookie header after the initial "Cookie:"
+ * [WS][$]token[WS]=[WS](token|QV)[;|,]
+ * RFC 2965
+ * JVK
+ */
+ public final void processCookieHeader(byte bytes[], int off, int len){
+ if( len<=0 || bytes==null ) return;
+ int end=off+len;
+ int pos=off;
+ int nameStart=0;
+ int nameEnd=0;
+ int valueStart=0;
+ int valueEnd=0;
+ int version = 0;
+ ServerCookie sc=null;
+ boolean isSpecial;
+ boolean isQuoted;
+
+ while (pos < end) {
+ isSpecial = false;
+ isQuoted = false;
+
+ // Skip whitespace and non-token characters (separators)
+ while (pos < end &&
+ (isSeparator(bytes[pos]) || isWhiteSpace(bytes[pos])))
+ {pos++; }
+
+ if (pos >= end)
+ return;
+
+ // Detect Special cookies
+ if (bytes[pos] == '$') {
+ isSpecial = true;
+ pos++;
+ }
+
+ // Get the cookie name. This must be a token
+ valueEnd = valueStart = nameStart = pos;
+ pos = nameEnd = getTokenEndPosition(bytes,pos,end);
+
+ // Skip whitespace
+ while (pos < end && isWhiteSpace(bytes[pos])) {pos++; };
+
+
+ // Check for an '=' -- This could also be a name-only
+ // cookie at the end of the cookie header, so if we
+ // are past the end of the header, but we have a name
+ // skip to the name-only part.
+ if (pos < end && bytes[pos] == '=') {
+
+ // Skip whitespace
+ do {
+ pos++;
+ } while (pos < end && isWhiteSpace(bytes[pos]));
+
+ if (pos >= end)
+ return;
+
+ // Determine what type of value this is, quoted value,
+ // token, name-only with an '=', or other (bad)
+ switch (bytes[pos]) {
+ case '"':; // Quoted Value
+ isQuoted = true;
+ valueStart=pos + 1; // strip "
+ // getQuotedValue returns the position before
+ // at the last qoute. This must be dealt with
+ // when the bytes are copied into the cookie
+ valueEnd=getQuotedValueEndPosition(bytes,
+ valueStart, end);
+ // We need pos to advance
+ pos = valueEnd;
+ // Handles cases where the quoted value is
+ // unterminated and at the end of the header,
+ // e.g. [myname="value]
+ if (pos >= end)
+ return;
+ break;
+ case ';':
+ case ',':
+ // Name-only cookie with an '=' after the name token
+ // This may not be RFC compliant
+ valueStart = valueEnd = -1;
+ // The position is OK (On a delimiter)
+ break;
+ default:;
+ if (!isSeparator(bytes[pos])) {
+ // Token
+ valueStart=pos;
+ // getToken returns the position at the delimeter
+ // or other non-token character
+ valueEnd=getTokenEndPosition(bytes, valueStart, end);
+ // We need pos to advance
+ pos = valueEnd;
+ } else {
+ // INVALID COOKIE, advance to next delimiter
+ // The starting character of the cookie value was
+ // not valid.
+ log("Invalid cookie. Value not a token or quoted
value");
+ while (pos < end && bytes[pos] != ';'
&&
+ bytes[pos] != ',')
+ {pos++; };
+ pos++;
+ // Make sure no special avpairs can be attributed to
+ // the previous cookie by setting the current cookie
+ // to null
+ sc = null;
+ continue;
+ }
+ }
+ } else {
+ // Name only cookie
+ valueStart = valueEnd = -1;
+ pos = nameEnd;
+
+ }
+
+ // We should have an avpair or name-only cookie at this
+ // point. Perform some basic checks to make sure we are
+ // in a good state.
+
+ // Skip whitespace
+ while (pos < end && isWhiteSpace(bytes[pos])) {pos++; };
+
+
+ // Make sure that after the cookie we have a separator. This
+ // is only important if this is not the last cookie pair
+ while (pos < end && bytes[pos] != ';' &&
bytes[pos] != ',') {
+ pos++;
+ }
+
+ pos++;
+
+ /*
+ if (nameEnd <= nameStart || valueEnd < valueStart ) {
+ // Something is wrong, but this may be a case
+ // of having two ';' characters in a row.
+ // log("Cookie name/value does not conform to RFC 2965");
+ // Advance to next delimiter (ignoring everything else)
+ while (pos < end && bytes[pos] != ';' &&
bytes[pos] != ',')
+ { pos++; };
+ pos++;
+ // Make sure no special cookies can be attributed to
+ // the previous cookie by setting the current cookie
+ // to null
+ sc = null;
+ continue;
+ }
+ */
+
+ // All checks passed. Add the cookie, start with the
+ // special avpairs first
+ if (isSpecial) {
+ isSpecial = false;
+ // $Version must be the first avpair in the cookie header
+ // (sc must be null)
+ if (equals( "Version", bytes, nameStart, nameEnd) &&
+ sc == null) {
+ // Set version
+ if( bytes[valueStart] =='1' && valueEnd ==
(valueStart+1)) {
+ version=1;
+ } else {
+ // unknown version (Versioning is not very strict)
+ }
+ continue;
+ }
+
+ // We need an active cookie for Path/Port/etc.
+ if (sc == null) {
+ continue;
+ }
+
+ // Domain is more common, so it goes first
+ if (equals( "Domain", bytes, nameStart, nameEnd)) {
+ sc.getDomain().setBytes( bytes,
+ valueStart,
+ valueEnd-valueStart);
+ continue;
+ }
+
+ if (equals( "Path", bytes, nameStart, nameEnd)) {
+ sc.getPath().setBytes( bytes,
+ valueStart,
+ valueEnd-valueStart);
+ continue;
+ }
+
+
+ if (equals( "Port", bytes, nameStart, nameEnd)) {
+ // sc.getPort is not currently implemented.
+ // sc.getPort().setBytes( bytes,
+ // valueStart,
+ // valueEnd-valueStart );
+ continue;
+ }
+
+ // Unknown cookie, complain
+ log("Unknown Special Cookie");
+
+ } else { // Normal Cookie
+ sc = addCookie();
+ sc.setVersion( version );
+ sc.getName().setBytes( bytes, nameStart,
+ nameEnd-nameStart);
+
+ if (valueStart != -1) { // Normal AVPair
+ sc.getValue().setBytes( bytes, valueStart,
+ valueEnd-valueStart);
+ if (isQuoted) {
+ // We know this is a byte value so this is safe
+ ServerCookie.unescapeDoubleQuotes(
+ sc.getValue().getByteChunk());
+ }
+ } else {
+ // Name Only
+ sc.getValue().setString("");
+ }
+ continue;
+ }
}
-
}
- */
+ /**
+ * Given the starting position of a token, this gets the end of the
+ * token, with no separator characters in between.
+ * JVK
+ */
+ public static final int getTokenEndPosition(byte bytes[], int off, int end){
+ int pos = off;
+ while (pos < end && !isSeparator(bytes[pos])) {pos++; };
+
+ if (pos > end)
+ return end;
+ return pos;
+ }
+
+ /**
+ * Given a starting position after an initial quote chracter, this gets
+ * the position of the end quote. This escapes anything after a '\' char
+ * JVK RFC 2616
+ */
+ public static final int getQuotedValueEndPosition(byte bytes[], int off, int end){
+ int pos = off;
+ while (pos < end) {
+ if (bytes[pos] == '"') {
+ return pos;
+ } else if (bytes[pos] == '\\' && pos < (end - 1)) {
+ pos+=2;
+ } else {
+ pos++;
+ }
+ }
+ // Error, we have reached the end of the header w/o a end quote
+ return end;
+ }
+
}
Modified:
branches/JBOSSWEB_2_0_0_GA_CP/src/share/classes/org/apache/tomcat/util/http/ServerCookie.java
===================================================================
---
branches/JBOSSWEB_2_0_0_GA_CP/src/share/classes/org/apache/tomcat/util/http/ServerCookie.java 2009-04-14
16:35:14 UTC (rev 1007)
+++
branches/JBOSSWEB_2_0_0_GA_CP/src/share/classes/org/apache/tomcat/util/http/ServerCookie.java 2009-04-15
10:15:38 UTC (rev 1008)
@@ -21,13 +21,14 @@
import java.text.FieldPosition;
import java.util.Date;
+import org.apache.tomcat.util.buf.ByteChunk;
import org.apache.tomcat.util.buf.DateTool;
import org.apache.tomcat.util.buf.MessageBytes;
/**
* Server-side cookie representation.
- * Allows recycling and uses MessageBytes as low-level
+ * Allows recycling and uses MessageBytes as low-level
* representation ( and thus the byte-> char conversion can be delayed
* until we know the charset ).
*
@@ -40,309 +41,410 @@
private static org.apache.commons.logging.Log log=
org.apache.commons.logging.LogFactory.getLog(ServerCookie.class );
+ // Version 0 (Netscape) attributes
private MessageBytes name=MessageBytes.newInstance();
private MessageBytes value=MessageBytes.newInstance();
+ // Expires - Not stored explicitly. Generated from Max-Age (see V1)
+ private MessageBytes path=MessageBytes.newInstance();
+ private MessageBytes domain=MessageBytes.newInstance();
+ private boolean secure;
+
+ // Version 1 (RFC2109) attributes
+ private MessageBytes comment=MessageBytes.newInstance();
+ private int maxAge = -1;
+ private int version = 0;
- private MessageBytes comment=MessageBytes.newInstance(); // ;Comment=VALUE
- private MessageBytes domain=MessageBytes.newInstance(); // ;Domain=VALUE ...
+ /**
+ * If set to true, we parse cookies according to the servlet spec,
+ */
+ public static final boolean STRICT_SERVLET_COMPLIANCE =
+
Boolean.valueOf(System.getProperty("org.apache.catalina.STRICT_SERVLET_COMPLIANCE",
"false")).booleanValue();
- private int maxAge = -1; // ;Max-Age=VALUE
- // ;Discard ... implied by maxAge < 0
- // RFC2109: maxAge=0 will end a session
- private MessageBytes path=MessageBytes.newInstance(); // ;Path=VALUE .
- private boolean secure; // ;Secure
- private int version = 0; // ;Version=1
- //XXX CommentURL, Port -> use notes ?
-
+ // Note: Servlet Spec =< 2.5 only refers to Netscape and RFC2109,
+ // not RFC2965
+
+ // Version 1 (RFC2965) attributes
+ // TODO Add support for CommentURL
+ // Discard - implied by maxAge <0
+ // TODO Add support for Port
+
public ServerCookie() {
-
}
public void recycle() {
path.recycle();
- name.recycle();
- value.recycle();
- comment.recycle();
- maxAge=-1;
- path.recycle();
+ name.recycle();
+ value.recycle();
+ comment.recycle();
+ maxAge=-1;
+ path.recycle();
domain.recycle();
- version=0;
- secure=false;
+ version=0;
+ secure=false;
}
public MessageBytes getComment() {
- return comment;
+ return comment;
}
public MessageBytes getDomain() {
- return domain;
+ return domain;
}
public void setMaxAge(int expiry) {
- maxAge = expiry;
+ maxAge = expiry;
}
public int getMaxAge() {
- return maxAge;
+ return maxAge;
}
-
public MessageBytes getPath() {
- return path;
+ return path;
}
public void setSecure(boolean flag) {
- secure = flag;
+ secure = flag;
}
public boolean getSecure() {
- return secure;
+ return secure;
}
public MessageBytes getName() {
- return name;
+ return name;
}
public MessageBytes getValue() {
- return value;
+ return value;
}
public int getVersion() {
- return version;
+ return version;
}
-
public void setVersion(int v) {
- version = v;
+ version = v;
}
// -------------------- utils --------------------
public String toString() {
- return "Cookie " + getName() + "=" + getValue() + " ; "
- + getVersion() + " " + getPath() + " " + getDomain();
+ return "Cookie " + getName() + "=" + getValue() + " ;
"
+ + getVersion() + " " + getPath() + " " + getDomain();
}
- // Note -- disabled for now to allow full Netscape compatibility
- // from RFC 2068, token special case characters
- //
- // private static final String tspecials = "()<>@,;:\\\"/[]?={}
\t";
private static final String tspecials = ",; ";
- private static final String tspecials2 = ",; \"";
+ private static final String tspecials2 = "()<>@,;:\\\"/[]?={}
\t";
+ private static final String tspecials2NoSlash = "()<>@,;:\\\"[]?={}
\t";
/*
* Tests a string and returns true if the string counts as a
* reserved token in the Java language.
*
- * @param value the <code>String</code> to be tested
+ * @param value the <code>String</code> to be tested
*
- * @return <code>true</code> if the <code>String</code> is
- * a reserved token; <code>false</code>
- * if it is not
+ * @return <code>true</code> if the <code>String</code>
is a reserved
+ * token; <code>false</code> if it is not
*/
public static boolean isToken(String value) {
- if( value==null) return true;
- int len = value.length();
+ return isToken(value,null);
+ }
+
+ public static boolean isToken(String value, String literals) {
+ String tspecials = (literals==null?ServerCookie.tspecials:literals);
+ if( value==null) return true;
+ int len = value.length();
- for (int i = 0; i < len; i++) {
- char c = value.charAt(i);
+ for (int i = 0; i < len; i++) {
+ char c = value.charAt(i);
- if (c < 0x20 || c >= 0x7f || tspecials.indexOf(c) != -1)
- return false;
- }
- return true;
+ if (tspecials.indexOf(c) != -1)
+ return false;
+ }
+ return true;
}
+ public static boolean containsCTL(String value, int version) {
+ if( value==null) return false;
+ int len = value.length();
+ for (int i = 0; i < len; i++) {
+ char c = value.charAt(i);
+ if (c < 0x20 || c >= 0x7f) {
+ if (c == 0x09)
+ continue; //allow horizontal tabs
+ return true;
+ }
+ }
+ return false;
+ }
+
public static boolean isToken2(String value) {
- if( value==null) return true;
- int len = value.length();
+ return isToken2(value,null);
+ }
- for (int i = 0; i < len; i++) {
- char c = value.charAt(i);
+ public static boolean isToken2(String value, String literals) {
+ String tspecials2 = (literals==null?ServerCookie.tspecials2:literals);
+ if( value==null) return true;
+ int len = value.length();
- if (c < 0x20 || c >= 0x7f || tspecials2.indexOf(c) != -1)
- return false;
- }
- return true;
+ for (int i = 0; i < len; i++) {
+ char c = value.charAt(i);
+ if (tspecials2.indexOf(c) != -1)
+ return false;
+ }
+ return true;
}
+ /**
+ * @deprecated - Not used
+ */
public static boolean checkName( String name ) {
- if (!isToken(name)
- || name.equalsIgnoreCase("Comment") // rfc2019
- || name.equalsIgnoreCase("Discard") // 2019++
- || name.equalsIgnoreCase("Domain")
- || name.equalsIgnoreCase("Expires") // (old cookies)
- || name.equalsIgnoreCase("Max-Age") // rfc2019
- || name.equalsIgnoreCase("Path")
- || name.equalsIgnoreCase("Secure")
- || name.equalsIgnoreCase("Version")
- ) {
- return false;
- }
- return true;
+ if (!isToken(name)
+ || name.equalsIgnoreCase("Comment") // rfc2019
+ || name.equalsIgnoreCase("Discard") // rfc2965
+ || name.equalsIgnoreCase("Domain") // rfc2019
+ || name.equalsIgnoreCase("Expires") // Netscape
+ || name.equalsIgnoreCase("Max-Age") // rfc2019
+ || name.equalsIgnoreCase("Path") // rfc2019
+ || name.equalsIgnoreCase("Secure") // rfc2019
+ || name.equalsIgnoreCase("Version") // rfc2019
+ // TODO remaining RFC2965 attributes
+ ) {
+ return false;
+ }
+ return true;
}
// -------------------- Cookie parsing tools
- /** Return the header name to set the cookie, based on cookie
- * version
+ /**
+ * Return the header name to set the cookie, based on cookie version.
*/
public String getCookieHeaderName() {
- return getCookieHeaderName(version);
+ return getCookieHeaderName(version);
}
- /** Return the header name to set the cookie, based on cookie
- * version
+ /**
+ * Return the header name to set the cookie, based on cookie version.
*/
public static String getCookieHeaderName(int version) {
- if( dbg>0 ) log( (version==1) ? "Set-Cookie2" : "Set-Cookie");
+ // TODO Re-enable logging when RFC2965 is implemented
+ // log( (version==1) ? "Set-Cookie2" : "Set-Cookie");
if (version == 1) {
- // RFC2109
- return "Set-Cookie";
- // XXX RFC2965 is not standard yet, and Set-Cookie2
- // is not supported by Netscape 4, 6, IE 3, 5 .
- // It is supported by Lynx, and there is hope
- // return "Set-Cookie2";
+ // XXX RFC2965 not referenced in Servlet Spec
+ // Set-Cookie2 is not supported by Netscape 4, 6, IE 3, 5
+ // Set-Cookie2 is supported by Lynx and Opera
+ // Need to check on later IE and FF releases but for now...
+ // RFC2109
+ return "Set-Cookie";
+ // return "Set-Cookie2";
} else {
- // Old Netscape
- return "Set-Cookie";
+ // Old Netscape
+ return "Set-Cookie";
}
}
- private static final String ancientDate=DateTool.formatOldCookie(new Date(10000));
+ private static final String ancientDate =
+ DateTool.formatOldCookie(new Date(10000));
- public static void appendCookieValue( StringBuffer buf,
- int version,
- String name,
- String value,
- String path,
- String domain,
- String comment,
- int maxAge,
- boolean isSecure )
+ // TODO RFC2965 fields also need to be passed
+ public static void appendCookieValue( StringBuffer headerBuf,
+ int version,
+ String name,
+ String value,
+ String path,
+ String domain,
+ String comment,
+ int maxAge,
+ boolean isSecure )
{
- // this part is the same for all cookies
- buf.append( name );
+ StringBuffer buf = new StringBuffer();
+ // Servlet implementation checks name
+ buf.append( name );
buf.append("=");
- maybeQuote2(version, buf, value);
+ // Servlet implementation does not check anything else
+
+ version = maybeQuote2(version, buf, value,true);
- // XXX Netscape cookie: "; "
- // add version 1 specific information
- if (version == 1) {
- // Version=1 ... required
- buf.append ("; Version=1");
+ // Add version 1 specific information
+ if (version == 1) {
+ // Version=1 ... required
+ buf.append ("; Version=1");
- // Comment=comment
- if ( comment!=null ) {
- buf.append ("; Comment=");
- maybeQuote (version, buf, comment);
- }
- }
-
- // add domain information, if present
+ // Comment=comment
+ if ( comment!=null ) {
+ buf.append ("; Comment=");
+ maybeQuote2(version, buf, comment);
+ }
+ }
+
+ // Add domain information, if present
+ if (domain!=null) {
+ buf.append("; Domain=");
+ maybeQuote2(version, buf, domain);
+ }
- if (domain!=null) {
- buf.append("; Domain=");
- maybeQuote (version, buf, domain);
- }
-
- // Max-Age=secs/Discard ... or use old "Expires" format
- if (maxAge >= 0) {
- if (version == 0) {
- // XXX XXX XXX We need to send both, for
- // interoperatibility (long word )
- buf.append ("; Expires=");
- // Wdy, DD-Mon-YY HH:MM:SS GMT ( Expires netscape format )
- // To expire we need to set the time back in future
- // ( pfrieden(a)dChain.com )
+ // Max-Age=secs ... or use old "Expires" format
+ // TODO RFC2965 Discard
+ if (maxAge >= 0) {
+ if (version == 0) {
+ // Wdy, DD-Mon-YY HH:MM:SS GMT ( Expires Netscape format )
+ buf.append ("; Expires=");
+ // To expire immediately we need to set the time in past
if (maxAge == 0)
- buf.append( ancientDate );
- else
+ buf.append( ancientDate );
+ else
DateTool.formatOldCookie
(new Date( System.currentTimeMillis() +
maxAge *1000L), buf,
new FieldPosition(0));
- } else {
- buf.append ("; Max-Age=");
- buf.append (maxAge);
- }
- }
+ } else {
+ buf.append ("; Max-Age=");
+ buf.append (maxAge);
+ }
+ }
- // Path=path
- if (path!=null) {
- buf.append ("; Path=");
- maybeQuote (version, buf, path);
- }
+ // Path=path
+ if (path!=null) {
+ buf.append ("; Path=");
+ if (version==0) {
+ maybeQuote2(version, buf, path);
+ } else {
+ maybeQuote2(version, buf, path, ServerCookie.tspecials2NoSlash, false);
+ }
+ }
- // Secure
- if (isSecure) {
- buf.append ("; Secure");
- }
-
-
+ // Secure
+ if (isSecure) {
+ buf.append ("; Secure");
+ }
+
+ headerBuf.append(buf);
}
- public static void maybeQuote (int version, StringBuffer buf,
- String value) {
+ /**
+ * @deprecated - Not used
+ */
+ @Deprecated
+ public static void maybeQuote (int version, StringBuffer buf,String value) {
// special case - a \n or \r shouldn't happen in any case
if (isToken(value)) {
buf.append(value);
} else {
buf.append('"');
- buf.append(escapeDoubleQuotes(value));
+ buf.append(escapeDoubleQuotes(value,0,value.length()));
buf.append('"');
}
}
- public static void maybeQuote2 (int version, StringBuffer buf,
- String value) {
- // special case - a \n or \r shouldn't happen in any case
- if (isToken2(value)) {
- buf.append(value);
- } else {
+
+ public static boolean alreadyQuoted (String value) {
+ if (value==null || value.length()==0) return false;
+ return (value.charAt(0)=='\"' &&
value.charAt(value.length()-1)=='\"');
+ }
+
+ /**
+ * Quotes values using rules that vary depending on Cookie version.
+ * @param version
+ * @param buf
+ * @param value
+ */
+ public static int maybeQuote2 (int version, StringBuffer buf, String value) {
+ return maybeQuote2(version,buf,value,false);
+ }
+
+ public static int maybeQuote2 (int version, StringBuffer buf, String value, boolean
allowVersionSwitch) {
+ return maybeQuote2(version,buf,value,null,allowVersionSwitch);
+ }
+
+ public static int maybeQuote2 (int version, StringBuffer buf, String value, String
literals, boolean allowVersionSwitch) {
+ if (value==null || value.length()==0) {
+ buf.append("\"\"");
+ }else if (containsCTL(value,version))
+ throw new IllegalArgumentException("Control character in cookie value,
consider BASE64 encoding your value");
+ else if (alreadyQuoted(value)) {
buf.append('"');
- buf.append(escapeDoubleQuotes(value));
+ buf.append(escapeDoubleQuotes(value,1,value.length()-1));
buf.append('"');
+ } else if (allowVersionSwitch && (!STRICT_SERVLET_COMPLIANCE) &&
version==0 && !isToken2(value, literals)) {
+ buf.append('"');
+ buf.append(escapeDoubleQuotes(value,0,value.length()));
+ buf.append('"');
+ version = 1;
+ } else if (version==0 && !isToken(value,literals)) {
+ buf.append('"');
+ buf.append(escapeDoubleQuotes(value,0,value.length()));
+ buf.append('"');
+ } else if (version==1 && !isToken2(value,literals)) {
+ buf.append('"');
+ buf.append(escapeDoubleQuotes(value,0,value.length()));
+ buf.append('"');
+ }else {
+ buf.append(value);
}
+ return version;
}
- // log
- static final int dbg=1;
- public static void log(String s ) {
- if (log.isDebugEnabled())
- log.debug("ServerCookie: " + s);
- }
-
/**
* Escapes any double quotes in the given string.
*
* @param s the input string
- *
+ * @param beginIndex start index inclusive
+ * @param endIndex exclusive
* @return The (possibly) escaped string
*/
- private static String escapeDoubleQuotes(String s) {
+ private static String escapeDoubleQuotes(String s, int beginIndex, int endIndex) {
if (s == null || s.length() == 0 || s.indexOf('"') == -1) {
return s;
}
StringBuffer b = new StringBuffer();
- char p = s.charAt(0);
- for (int i = 0; i < s.length(); i++) {
+ for (int i = beginIndex; i < endIndex; i++) {
char c = s.charAt(i);
- if (c == '"' && p != '\\')
+ if (c == '\\' ) {
+ b.append(c);
+ //ignore the character after an escape, just append it
+ if (++i>=endIndex) throw new IllegalArgumentException("Invalid
escape character in cookie value.");
+ b.append(s.charAt(i));
+ } else if (c == '"')
b.append('\\').append('"');
else
b.append(c);
- p = c;
}
return b.toString();
}
+ /**
+ * Unescapes any double quotes in the given cookie value.
+ *
+ * @param bc The cookie value to modify
+ */
+ public static void unescapeDoubleQuotes(ByteChunk bc) {
+
+ if (bc == null || bc.getLength() == 0 || bc.indexOf('"', 0) == -1)
{
+ return;
+ }
+
+ int src = bc.getStart();
+ int end = bc.getEnd();
+ int dest = src;
+ byte[] buffer = bc.getBuffer();
+
+ while (src < end) {
+ if (buffer[src] == '\\' && src < end &&
buffer[src+1] == '"') {
+ src++;
+ }
+ buffer[dest] = buffer[src];
+ dest ++;
+ src ++;
+ }
+ bc.setEnd(dest);
+ }
}
-
Added: branches/JBOSSWEB_2_0_0_GA_CP/test/build.xml
===================================================================
--- branches/JBOSSWEB_2_0_0_GA_CP/test/build.xml (rev 0)
+++ branches/JBOSSWEB_2_0_0_GA_CP/test/build.xml 2009-04-15 10:15:38 UTC (rev 1008)
@@ -0,0 +1,70 @@
+<?xml version="1.0"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+
http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<project name="Tomcat 6.0" default="all"
basedir=".">
+
+ <!-- See "build.properties.sample" in the top level directory for all
-->
+ <!-- property values you must customize for successful building!!! -->
+ <property file="${user.home}/build.properties"/>
+ <property file="build.properties"/>
+
+ <property file="build.properties.default"/>
+
+ <property name="test.classes"
value="${basedir}/output/classes"/>
+ <property name="tomcat.build" value="${basedir}/../output"/>
+
+ <property name="compile.source" value="1.5"/>
+ <property name="compile.debug" value="true"/>
+
+ <property name="junit.jar" value="${junit.home}/junit.jar"/>
+ <property name="test.runner"
value="junit.textui.TestRunner"/>
+
+ <path id="tomcat.test.classpath">
+ <pathelement location="${test.classes}"/>
+ <pathelement location="${junit.jar}"/>
+ <fileset dir="${tomcat.build}/jars/">
+ <include name="jbossweb.jar"/>
+ </fileset>
+ <fileset dir="${tomcat.build}/../lib">
+ <include name="commons-logging.jar"/>
+ </fileset>
+ </path>
+
+ <target name="compile">
+
+ <mkdir dir="${test.classes}"/>
+
+ <!-- Compile -->
+ <javac srcdir="." destdir="${test.classes}"
+ debug="${compile.debug}"
+ deprecation="${compile.deprecation}"
+ source="${compile.source}"
+ optimize="${compile.optimize}">
+ <classpath refid="tomcat.test.classpath" />
+ <include name="org/apache/catalina/tomcat/util/**" />
+ </javac>
+
+ </target>
+
+ <target name="all" depends="compile">
+ <java dir="${test.classes}" classname="${test.runner}"
fork="yes" failonerror="${test.failonerror}">
+ <arg
value="org.apache.catalina.tomcat.util.http.TestCookies"/>
+ <classpath refid="tomcat.test.classpath"/>
+ </java>
+
+ </target>
+</project>
Added:
branches/JBOSSWEB_2_0_0_GA_CP/test/org/apache/catalina/tomcat/util/http/TestCookies.java
===================================================================
---
branches/JBOSSWEB_2_0_0_GA_CP/test/org/apache/catalina/tomcat/util/http/TestCookies.java
(rev 0)
+++
branches/JBOSSWEB_2_0_0_GA_CP/test/org/apache/catalina/tomcat/util/http/TestCookies.java 2009-04-15
10:15:38 UTC (rev 1008)
@@ -0,0 +1,280 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *
http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.tomcat.util.http;
+
+import org.apache.tomcat.util.http.Cookies;
+import org.apache.tomcat.util.http.ServerCookie;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+import junit.textui.TestRunner;
+
+import java.lang.Exception;
+
+
+public class TestCookies extends TestCase {
+ public static void main( String args[] ) {
+ TestRunner.run(suite());
+ }
+ public static Test suite() {
+ TestSuite suite = new TestSuite();
+ suite.addTest(new TestSuite(TestCookies.class));
+ return suite;
+ }
+/*
+ int i = 10000000;
+ // These tests are not really representative
+ while (i-- > 0) {
+ test("session=1234567890;name=\"John Q. Public\";");
+ }
+// runtests();
+ }
+ */
+
+ public void testCookies() throws Exception {
+ test("foo=bar; a=b", "foo", "bar", "a",
"b");
+ test("foo=bar;a=b", "foo", "bar", "a",
"b");
+ test("foo=bar;a=b;", "foo", "bar", "a",
"b");
+ test("foo=bar;a=b; ", "foo", "bar", "a",
"b");
+ test("foo=bar;a=b; ;", "foo", "bar", "a",
"b");
+ test("foo=;a=b; ;", "foo", "", "a",
"b");
+ test("foo;a=b; ;", "foo", "", "a",
"b");
+ // v1
+ test("$Version=1; foo=bar;a=b", "foo", "bar",
"a", "b");
+
+ // OK
+ test("$Version=1;foo=bar;a=b; ; ", "foo", "bar",
"a", "b");
+ test("$Version=1;foo=;a=b; ; ", "foo", "",
"a", "b");
+ test("$Version=1;foo= ;a=b; ; ", "foo", "",
"a", "b");
+ test("$Version=1;foo;a=b; ; ", "foo", "",
"a", "b");
+ test("$Version=1;foo=\"bar\";a=b; ; ", "foo",
"bar", "a", "b");
+
+ test("$Version=1;foo=\"bar\";$Domain=apache.org;a=b",
"foo", "bar", "a", "b");
+
test("$Version=1;foo=\"bar\";$Domain=apache.org;a=b;$Domain=yahoo.com",
"foo", "bar", "a", "b");
+ // rfc2965
+
test("$Version=1;foo=\"bar\";$Domain=apache.org;$Port=8080;a=b",
"foo", "bar", "a", "b");
+
+ // make sure these never split into two cookies - JVK
+
test("$Version=1;foo=\"b\"ar\";$Domain=apache.org;$Port=8080;a=b",
"foo", "b", "a", "b"); // Incorrectly escaped.
+
test("$Version=1;foo=\"b\\\"ar\";$Domain=apache.org;$Port=8080;a=b",
"foo", "b\"ar", "a", "b"); // correctly
escaped.
+
test("$Version=1;foo=\"b'ar\";$Domain=apache.org;$Port=8080;a=b",
"foo", "b'ar", "a", "b");
+ // JFC: sure it is "b" and not b'ar ?
+ test("$Version=1;foo=b'ar;$Domain=apache.org;$Port=8080;a=b",
"foo", "b", "a", "b");
+
+ // Ends in quoted value
+ test("foo=bar;a=\"b\"", "foo", "bar",
"a", "b");
+ test("foo=bar;a=\"b\";", "foo", "bar",
"a", "b");
+
+ // Last character is an escape character
+
test("$Version=1;foo=b'ar;$Domain=\"apache.org\";$Port=8080;a=\"b\\\"",
"foo", "b");
+
test("$Version=1;foo=b'ar;$Domain=\"apache.org\";$Port=8080;a=\"b\\",
"foo", "b");
+
+ // Bad... a token cannot be quoted with ' chars
+ test("$Version=\"1\"; foo='bar'; $Path=/path;
$Domain=\"localhost\"");
+
+ // wrong, path should not have '/' JVK
+ test("$Version=1;foo=\"bar\";$Path=/examples;a=b; ; ",
"foo", "bar", "a", "b");
+
+ // wrong
+
test("$Version=1;foo=\"bar\";$Domain=apache.org;$Port=8080;a=b",
"foo", "bar", "a", "b");
+
+ // Test name-only at the end of the header
+ test("foo;a=b;bar", "foo", "", "a",
"b", "bar", "");
+ test("foo;a=b;bar;", "foo", "", "a",
"b", "bar", "");
+ test("foo;a=b;bar ", "foo", "", "a",
"b", "bar", "");
+ test("foo;a=b;bar ;", "foo", "", "a",
"b", "bar", "");
+
+ // Multiple delimiters next to each other
+
+ // BUG -- the ' ' needs to be skipped.
+ test("foo;a=b; ;bar", "foo", "", "a",
"b", "bar", "");
+ // BUG -- ';' needs skipping
+ test("foo;a=b;;bar", "foo", "", "a",
"b", "bar", "");
+ test("foo;a=b; ;;bar=rab", "foo", "",
"a", "b", "bar", "rab");
+ // These pass currently
+ test("foo;a=b;; ;bar=rab", "foo", "",
"a", "b", "bar", "rab");
+
+ // '#' is a valid cookie name (not a separator)
+ test("foo;a=b;;#;bar=rab","foo", "", "a",
"b", "#", "", "bar", "rab");
+
+
+ test("foo;a=b;;\\;bar=rab", "foo", "",
"a", "b", "bar", "rab");
+
+ // Try all the separators of version1 in version0 cookie.
+ // Won't work we only parse version1 cookie result 1 cookie.
+ test("a=()<>@:\\\"/[]?={}\t; foo=bar", "foo",
"bar");
+
+ // Test the version.
+ test("$Version=1;foo=bar", 1);
+ test("$Version=0;foo=bar", 0);
+ }
+
+ public static void test( String s, int val ) throws Exception {
+ System.out.println("Processing [" + s + "]");
+ Cookies cs=new Cookies(null);
+ cs.processCookieHeader( s.getBytes(), 0, s.length());
+ int num = cs.getCookieCount();
+ if (num != 1)
+ throw new Exception("wrong number of cookies " + num);
+ ServerCookie co = cs.getCookie(0);
+ System.out.println("One Cookie: " + co);
+ if (co.getVersion() != val)
+ throw new Exception("wrong version " + co.getVersion() + " !=
" + val);
+ }
+ public static void test( String s ) throws Exception {
+ System.out.println("Processing [" + s + "]");
+ Cookies cs=new Cookies(null);
+ cs.processCookieHeader( s.getBytes(), 0, s.length());
+
+ int num = cs.getCookieCount();
+ for( int i=0; i< num ; i++ ) {
+ System.out.println("Cookie: " + cs.getCookie( i ));
+ }
+ if (num != 0)
+ throw new Exception("wrong number of cookies " + num);
+ }
+ public static void test( String s, String name, String val ) throws Exception {
+ System.out.println("Processing [" + s + "]");
+ Cookies cs=new Cookies(null);
+ cs.processCookieHeader( s.getBytes(), 0, s.length());
+
+ int num = cs.getCookieCount();
+ if (num != 1)
+ throw new Exception("wrong number of cookies " + num);
+ ServerCookie co = cs.getCookie(0);
+ System.out.println("One Cookie: " + co);
+ String coname = co.getName().toString();
+ String coval = co.getValue().toString();
+ if ( ! name.equals(coname))
+ throw new Exception("wrong name " + coname + " != " +
name);
+ if ( ! val.equals(coval))
+ throw new Exception("wrong value " + coval + " != " +
val);
+ }
+ public static void test( String s, String name, String val, String name2, String val2
) throws Exception {
+ System.out.println("Processing [" + s + "]");
+ Cookies cs=new Cookies(null);
+ cs.processCookieHeader( s.getBytes(), 0, s.length());
+
+ int num = cs.getCookieCount();
+ if (num != 2)
+ throw new Exception("wrong number of cookies " + num);
+ ServerCookie co = cs.getCookie(0);
+ System.out.println("1 - Cookie: " + co);
+ ServerCookie co2 = cs.getCookie(1);
+ System.out.println("2 - Cookie: " + co2);
+
+ String coname = co.getName().toString();
+ String coval = co.getValue().toString();
+ if ( ! name.equals(coname))
+ throw new Exception("1 - wrong name " + coname + " != " +
name);
+ if ( ! val.equals(coval))
+ throw new Exception("1 - wrong value " + coval + " != " +
val);
+
+ String coname2 = co2.getName().toString();
+ String coval2 = co2.getValue().toString();
+ if ( ! name2.equals(coname2))
+ throw new Exception("2 - wrong name " + coname2 + " != " +
name2);
+ if ( ! val2.equals(coval2))
+ throw new Exception("2 - wrong value " + coval2 + " != " +
val2);
+ }
+ public static void test( String s, String name, String val, String name2,
+ String val2, String name3, String val3 ) throws Exception {
+ System.out.println("Processing [" + s + "]");
+ Cookies cs=new Cookies(null);
+ cs.processCookieHeader( s.getBytes(), 0, s.length());
+
+ int num = cs.getCookieCount();
+ if (num != 3)
+ throw new Exception("wrong number of cookies " + num);
+ ServerCookie co = cs.getCookie(0);
+ System.out.println("1 - Cookie: " + co);
+ ServerCookie co2 = cs.getCookie(1);
+ System.out.println("2 - Cookie: " + co2);
+ ServerCookie co3 = cs.getCookie(2);
+ System.out.println("3 - Cookie: " + co3);
+
+ String coname = co.getName().toString();
+ String coval = co.getValue().toString();
+ if ( ! name.equals(coname))
+ throw new Exception("1 - wrong name " + coname + " != " +
name);
+ if ( ! val.equals(coval))
+ throw new Exception("1 - wrong value " + coval + " != " +
val);
+
+ String coname2 = co2.getName().toString();
+ String coval2 = co2.getValue().toString();
+ if ( ! name2.equals(coname2))
+ throw new Exception("2 - wrong name " + coname2 + " != " +
name2);
+ if ( ! val2.equals(coval2))
+ throw new Exception("2 - wrong value " + coval2 + " != " +
val2);
+
+ String coname3 = co3.getName().toString();
+ String coval3 = co3.getValue().toString();
+ if ( ! name3.equals(coname3))
+ throw new Exception("3 - wrong name " + coname3 + " != " +
name3);
+ if ( ! val2.equals(coval2))
+ throw new Exception("3 - wrong value " + coval3 + " != " +
val3);
+ }
+ public static void test( String s, String name, String val, String name2,
+ String val2, String name3, String val3,
+ String name4, String val4 ) throws Exception {
+ System.out.println("Processing [" + s + "]");
+ Cookies cs=new Cookies(null);
+ cs.processCookieHeader( s.getBytes(), 0, s.length());
+
+ int num = cs.getCookieCount();
+ if (num != 4)
+ throw new Exception("wrong number of cookies " + num);
+ ServerCookie co = cs.getCookie(0);
+ System.out.println("1 - Cookie: " + co);
+ ServerCookie co2 = cs.getCookie(1);
+ System.out.println("2 - Cookie: " + co2);
+ ServerCookie co3 = cs.getCookie(2);
+ System.out.println("3 - Cookie: " + co3);
+ ServerCookie co4 = cs.getCookie(3);
+ System.out.println("4 - Cookie: " + co4);
+
+ String coname = co.getName().toString();
+ String coval = co.getValue().toString();
+ if ( ! name.equals(coname))
+ throw new Exception("1 - wrong name " + coname + " != " +
name);
+ if ( ! val.equals(coval))
+ throw new Exception("1 - wrong value " + coval + " != " +
val);
+
+ String coname2 = co2.getName().toString();
+ String coval2 = co2.getValue().toString();
+ if ( ! name2.equals(coname2))
+ throw new Exception("2 - wrong name " + coname2 + " != " +
name2);
+ if ( ! val2.equals(coval2))
+ throw new Exception("2 - wrong value " + coval2 + " != " +
val2);
+
+ String coname3 = co3.getName().toString();
+ String coval3 = co3.getValue().toString();
+ if ( ! name3.equals(coname3))
+ throw new Exception("3 - wrong name " + coname3 + " != " +
name3);
+ if ( ! val3.equals(coval3))
+ throw new Exception("3 - wrong value " + coval3 + " != " +
val3);
+
+ String coname4 = co4.getName().toString();
+ String coval4 = co4.getValue().toString();
+ if ( ! name4.equals(coname4))
+ throw new Exception("4 - wrong name " + coname4 + " != " +
name4);
+ if ( ! val4.equals(coval4))
+ throw new Exception("4 - wrong value " + coval4 + " != " +
val4);
+ }
+}