Author: remm Date: Tue Sep 25 08:22:40 2007 New Revision: 579298 URL: http://svn.apache.org/viewvc?rev=579298&view=rev Log: - Patch update.
Modified: tomcat/tc6.0.x/trunk/STATUS Modified: tomcat/tc6.0.x/trunk/STATUS URL: http://svn.apache.org/viewvc/tomcat/tc6.0.x/trunk/STATUS?rev=579298&r1=579297&r2=579298&view=diff ============================================================================== --- tomcat/tc6.0.x/trunk/STATUS (original) +++ tomcat/tc6.0.x/trunk/STATUS Tue Sep 25 08:22:40 2007 @@ -15,7 +15,7 @@ limitations under the License. ================================================================================ -$Id: BUILDING.txt 562769 2007-08-04 22:08:32Z markt $ +$Revision: $ $Date: $ ================================= Apache Tomcat 6.0 Patch Proposals @@ -26,7 +26,551 @@ [ New proposals should be added at the end of the list ] * New cookie parser (third party contribution) - http://people.apache.org/~jfclere/patches/Cookies.java.patch +1: -1: jfclere: The tests must done another way. + +Index: java/org/apache/tomcat/util/http/Cookies.java +=================================================================== +--- java/org/apache/tomcat/util/http/Cookies.java (revision 579106) ++++ java/org/apache/tomcat/util/http/Cookies.java (working copy) +@@ -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,294 @@ + 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"); ++ ++ /** ++ * 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; ++ } ++ ++ /** ++ * 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; ++ ++ while (pos < end) { ++ isSpecial = 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 ++ 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) { ++ 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"); + +- // wrong +- test("$Version=1;foo=\"bar\";$Domain=apache.org;$Port=8080;a=b"); ++ } 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); ++ } else { ++ // Name Only ++ sc.getValue().setString(""); ++ } ++ continue; ++ } ++ } + } + +- 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 )); ++ /** ++ * 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; + } +- */ + + } + --------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]