You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by co...@locus.apache.org on 2000/11/30 18:42:50 UTC

cvs commit: jakarta-tomcat/src/share/org/apache/tomcat/util/http Cookies.java Headers.java Parameters.java ServerCookie.java package.html

costin      00/11/30 09:42:49

  Added:       src/share/org/apache/tomcat/util/http Cookies.java
                        Headers.java Parameters.java ServerCookie.java
                        package.html
  Log:
  Start work on Cookies, Parameters, Headers.
  
  The code in Cookies is almost ready, all cookie processing was re-done
  from scratch - and will support the full spec, not only name/values.
  It'll also be much faster, and the object model is better ( no need
  for artificial "helpers").
  
  Parameter parsing is also rewritten ( and more efficient ).
  
  Both Parameters and Cookies are now (almost) GC-free. The code will be more
  eficient if there are only few cookies/parameters (2..10 ?), for requests
  with many params we'll add the optimizations later.
  
  The code is not used right now - it'll gradually move in.
  
  Revision  Changes    Path
  1.1                  jakarta-tomcat/src/share/org/apache/tomcat/util/http/Cookies.java
  
  Index: Cookies.java
  ===================================================================
  /*
   * ====================================================================
   *
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 1999 The Apache Software Foundation.  All rights 
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer. 
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution, if
   *    any, must include the following acknowlegement:  
   *       "This product includes software developed by the 
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowlegement may appear in the software itself,
   *    if and wherever such third-party acknowlegements normally appear.
   *
   * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
   *    Foundation" must not be used to endorse or promote products derived
   *    from this software without prior written permission. For written 
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * 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/>.
   *
   * [Additional notices, if required by prior licensing conditions]
   *
   */ 
  
  package org.apache.tomcat.util.http;
  
  import org.apache.tomcat.util.collections.*;
  import org.apache.tomcat.util.MessageBytes;
  import org.apache.tomcat.util.MimeHeaders;
  import org.apache.tomcat.util.ServerCookie;
  import org.apache.tomcat.util.DateTool;
  
  import java.io.*;
  import java.util.*;
  import java.text.*;
  
  /**
   * A collection of cookies - reusable and tuned for server side performance.
   * 
   * @author Costin Manolache
   */
  public final class Cookies { // extends MultiMap {
  
      // expected average number of cookies per request
      public static final int INITIAL_SIZE=4; 
      ServerCookie scookies[]=new ServerCookie[INITIAL_SIZE];
      int cookieCount=-1; // -1 = cookies not processed yet
  
      MimeHeaders headers;
      
      /**
       * 
       */
      public Cookies() {
      }
  
      public void recycle() {
      	for( int i=0; i< cookieCount; i++ ) {
  	    if( scookies[i]!=null )
  		scookies[i].recycle();
  	}
  	cookieCount=-1;
      }
  
      public ServerCookie getCookie( int idx ) {
  	if( cookieCount == -1 ) {
  	    getCookieCount(); // will also update the cookies
  	}
  	return scookies[idx];
      }
  
      public int getCookieCount() {
  	if( cookieCount == -1 ) {
  	    cookieCount=0;
  	    // compute cookies
  	    processCookies(headers);
  	}
  	return cookieCount;
      }
  
      public ServerCookie addCookie() {
  	if( cookieCount >= scookies.length  ) {
  	    ServerCookie scookiesTmp[]=new ServerCookie[2*cookieCount];
  	    System.arraycopy( scookies, 0, scookiesTmp, 0, cookieCount);
  	    scookies=scookiesTmp;
  	}
  	
  	ServerCookie c = scookies[cookieCount];
  	if( c==null ) {
  	    c= new ServerCookie();
  	    scookies[cookieCount]=c;
  	}
  	cookieCount++;
  	return c;
      }
  
  
      // -------------------- Static methods ( used to be CookieTools )
  
      /** Process all Cookie headers of a request, setting them
       *  in a cookie vector
       */
      public  void processCookies( MimeHeaders headers ) {
  	// process each "cookie" header
  	int pos=0;
  	while( pos>=0 ) {
  	    pos=headers.findHeader( "Cookie", pos );
  	    // no more cookie headers headers
  	    if( pos<0 ) break;
  
  	    MessageBytes cookieValue=headers.getValue( pos );
  	    if( cookieValue==null || cookieValue.isNull() ) continue;
  	    if( cookieValue.getType() == MessageBytes.T_BYTES ) {
  		processCookieHeader( cookieValue.getBytes(),
  				     cookieValue.getOffset(),
  				     cookieValue.getLength());
  	    } else {
  		processCookieHeader( cookieValue.toString() );
  	    }
  	}
      }
  
      private  void processCookieHeader(  byte bytes[], int off, int len )
      {
  	if( len<=0 || bytes==null ) return;
  	int end=off+len;
  	int pos=off;
  
  	while( true ) {
  	    // [ skip_spaces name skip_spaces "=" skip_spaces value EXTRA ; ] *
  	    
  	    int startName=skipSpaces(bytes, pos, end);
  	    if( pos>=end )
  		return; // only spaces
  	    
  	    boolean isSpecial=false;
  	    if(bytes[pos]=='$') { pos++; isSpecial=true; }
  	    
  	    int endName= findDelim1( bytes, startName, end); // " =;,"
  	    if(endName >= end )
  		return; // invalid
  	
  	    // current = "=" or " " 
  	    pos= skipSpaces( bytes, endName, end );
  	    if(endName >= end )
  		return; // invalid
  
  	    // cookie without value
  	    if( bytes[pos] == ';' || bytes[pos]==',' ) {
  		// add cookie
  
  		// we may have more cookies
  		continue;
  	    }
  
  	    if( bytes[pos] != '=' ) {
  		// syntax error - ignore the rest
  		// ( we could also skip to the next ';' )
  		return;
  	    }
  	
  	    // we must have "="
  	    pos++;
  	    int startValue=skipSpaces( bytes, pos, end);
  	    int endValue=startValue;
  	    if( bytes[pos]== '\'' || bytes[pos]=='"' ) {
  		startValue++;
  		endValue=indexOf( bytes, startValue, end, bytes[startValue] );
   	    } else {
  		endValue=findDelim2( bytes, startValue, end );
  	    }
  
  	    // process $Version, etc
  	    if( ! isSpecial ) {
  		ServerCookie sc=addCookie();
  		sc.getName().setBytes( bytes, startName, endName );
  		sc.getValue().setBytes( bytes, startValue, endValue );
  		continue;
  	    }
  	    // special - Path, Version, Domain
  	    // XXX TODO
  	}
      }
  
      // -------------------- 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==';' || b==',' )
  		return off;
  	    off++;
  	}
  	return off;
      }
  
      public static int indexOf( byte bytes[], int off, int end, byte qq )
      {
  	while( off < end ) {
  	    byte b=bytes[off];
  	    if( b==qq )
  		return off;
  	    off++;
  	}
  	return off;
      }
  
      public static int indexOf( byte bytes[], int off, int end, char qq )
      {
  	while( off < end ) {
  	    byte b=bytes[off];
  	    if( b==qq )
  		return off;
  	    off++;
  	}
  	return off;
      }
      
  
      // ---------------------------------------------------------
      // -------------------- DEPRECATED, OLD --------------------
      
      private void processCookieHeader(  String cookieString )
      {
  	
  	// normal cookie, with a string value.
  	// This is the original code, un-optimized - it shouldn't
  	// happen in normal case
  
  	StringTokenizer tok = new StringTokenizer(cookieString,
  						  ";", false);
  	while (tok.hasMoreTokens()) {
  	    String token = tok.nextToken();
  	    int i = token.indexOf("=");
  	    if (i > -1) {
  		
  		// XXX
  		// the trims here are a *hack* -- this should
  		// be more properly fixed to be spec compliant
  		
  		String name = token.substring(0, i).trim();
  		String value = token.substring(i+1, token.length()).trim();
  		// RFC 2109 and bug 
  		value=stripQuote( value );
  		ServerCookie cookie = addCookie();
  		
  		cookie.getName().setString(name);
  		cookie.getValue().setString(value);
  	    } else {
  		// we have a bad cookie.... just let it go
  	    }
  	}
      }
  
      /**
       *
       * Strips quotes from the start and end of the cookie string
       * This conforms to RFC 2109
       * 
       * @param value            a <code>String</code> specifying the cookie 
       *                         value (possibly quoted).
       *
       * @see #setValue
       *
       */
      private static String stripQuote( String value )
      {
  	//	log("Strip quote from " + value );
  	if (((value.startsWith("\"")) && (value.endsWith("\""))) ||
  	    ((value.startsWith("'") && (value.endsWith("'"))))) {
  	    try {
  		return value.substring(1,value.length()-1);
  	    } catch (Exception ex) { 
  	    }
  	}
  	return value;
      }  
  
  
  
  }
  
  
  
  1.1                  jakarta-tomcat/src/share/org/apache/tomcat/util/http/Headers.java
  
  Index: Headers.java
  ===================================================================
  /*
   * ====================================================================
   *
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 1999 The Apache Software Foundation.  All rights 
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer. 
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution, if
   *    any, must include the following acknowlegement:  
   *       "This product includes software developed by the 
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowlegement may appear in the software itself,
   *    if and wherever such third-party acknowlegements normally appear.
   *
   * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
   *    Foundation" must not be used to endorse or promote products derived
   *    from this software without prior written permission. For written 
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * 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/>.
   *
   * [Additional notices, if required by prior licensing conditions]
   *
   */ 
  
  package org.apache.tomcat.util.http;
  
  import org.apache.tomcat.util.collections.*;
  import org.apache.tomcat.util.MessageBytes;
  
  import java.io.*;
  import java.util.*;
  import java.text.*;
  
  // XXX many methods should be deprecated and removed after
  // the core is changed. 
  
  /**
   * 
   * @author dac@eng.sun.com
   * @author James Todd [gonzo@eng.sun.com]
   * @author Costin Manolache
   */
  public class Headers extends MultiMap {
      
      /** Initial size - should be == average number of headers per request
       *  XXX  make it configurable ( fine-tuning of web-apps )
       */
      public static final int DEFAULT_HEADER_SIZE=8;
      
      /**
       * Creates a new MimeHeaders object using a default buffer size.
       */
      public Headers() {
  	super( DEFAULT_HEADER_SIZE );
      }
  
      // Old names
      
      /**
       * Clears all header fields.
       */
      public void clear() {
  	super.recycle();
      }
  
      /** Find the index of a header with the given name.
       */
      public int findHeader( String name, int starting ) {
  	return super.findIgnoreCase( name, starting );
      }
      
      // -------------------- --------------------
  
      /**
       * Returns an enumeration of strings representing the header field names.
       * Field names may appear multiple times in this enumeration, indicating
       * that multiple fields with that name exist in this header.
       */
      public Enumeration names() {
  	return new NamesEnumerator(this);
      }
  
      public Enumeration values(String name) {
  	return new ValuesEnumerator(this, name);
      }
  
      // -------------------- Adding headers --------------------
      
      /** Create a new named header , return the MessageBytes
       *  container for the new value
       */
      public MessageBytes addValue( String name ) {
  	int pos=addField();
  	getName(pos).setString(name);
  	return getValue(pos);
      }
  
      /** Create a new named header using un-translated byte[].
  	The conversion to chars can be delayed until
  	encoding is known.
       */
      public MessageBytes addValue(byte b[], int startN, int endN)
      {
  	int pos=addField();
  	getName(pos).setBytes(b, startN, endN);
  	return getValue(pos);
      }
  
      /** Allow "set" operations - 
          return a MessageBytes container for the
  	header value ( existing header or new
  	if this .
      */
      public MessageBytes setValue( String name ) {
   	MessageBytes value=getValue(name);
  	if( value == null ) {
  	    value=addValue( name );
  	}
  	return value;
      }
  
      //-------------------- Getting headers --------------------
      /**
       * Finds and returns a header field with the given name.  If no such
       * field exists, null is returned.  If more than one such field is
       * in the header, an arbitrary one is returned.
       */
      public MessageBytes getValue(String name) {
          int pos=findIgnoreCase( name, 0 );
          if( pos <0 ) return null;
  	return getValue( pos );
      }
  
      // bad shortcut - it'll convert to string ( too early probably,
      // encoding is guessed very late )
      public String getHeader(String name) {
  	int pos=findIgnoreCase( name, 0 );
  	if( pos <0 ) return null;
  	MessageBytes mh = getValue(pos);
  	return mh.toString();
      }
  
      /**
       * Removes a header field with the specified name.  Does nothing
       * if such a field could not be found.
       * @param name the name of the header field to be removed
       */
      public void removeHeader(String name) {
  	int pos=0;
  	while( pos>=0 ) {
  	    // next header with this name
  	    pos=findIgnoreCase( name, pos );
  	    remove( pos );
  	}
      }
  }
  
  /** Enumerate the distinct header names.
      Each nextElement() is O(n) ( a comparation is
      done with all previous elements ).
  
      This is less frequesnt than add() -
      we want to keep add O(1).
  */
  class NamesEnumerator implements Enumeration {
      int pos;
      int size;
      String next;
      MultiMap headers;
  
      NamesEnumerator(MultiMap headers) {
  	this.headers=headers;
  	pos=0;
  	size = headers.size();
  	findNext();
      }
  
      private void findNext() {
  	next=null;
  	for(  ; pos< size; pos++ ) {
  	    next=headers.getName( pos ).toString();
  	    for( int j=0; j<pos ; j++ ) {
  		if( headers.getName( j ).equalsIgnoreCase( next )) {
  		    // duplicate.
  		    next=null;
  		    break;
  		}
  	    }
  	    if( next!=null ) {
  		// it's not a duplicate
  		break;
  	    }
  	}
  	// next time findNext is called it will try the
  	// next element
  	pos++;
      }
      
      public boolean hasMoreElements() {
  	return next!=null;
      }
  
      public Object nextElement() {
  	String current=next;
  	findNext();
  	return current;
      }
  }
  
  /** Enumerate the values for a (possibly ) multiple
      value element.
  */
  class ValuesEnumerator implements Enumeration {
      int pos;
      int size;
      MessageBytes next;
      MultiMap headers;
      String name;
  
      ValuesEnumerator(MultiMap headers, String name) {
          this.name=name;
  	this.headers=headers;
  	pos=0;
  	size = headers.size();
  	findNext();
      }
  
      private void findNext() {
  	next=null;
  	for( ; pos< size; pos++ ) {
  	    MessageBytes n1=headers.getName( pos );
  	    if( n1.equalsIgnoreCase( name )) {
  		next=headers.getValue( pos );
  		break;
  	    }
  	}
  	pos++;
      }
      
      public boolean hasMoreElements() {
  	return next!=null;
      }
  
      public Object nextElement() {
  	MessageBytes current=next;
  	findNext();
  	return current.toString();
      }
  }
  
  class MimeHeaderField {
      // multiple headers with same name - a linked list will
      // speed up name enumerations and search ( both cpu and
      // GC)
      MimeHeaderField next;
      MimeHeaderField prev; 
      
      protected final MessageBytes nameB = new MessageBytes();
      protected final MessageBytes valueB = new MessageBytes();
  
      /**
       * Creates a new, uninitialized header field.
       */
      public MimeHeaderField() {
      }
  
      public void recycle() {
  	nameB.recycle();
  	valueB.recycle();
  	next=null;
      }
  
      public MessageBytes getName() {
  	return nameB;
      }
  
      public MessageBytes getValue() {
  	return valueB;
      }
  }
  
  
  
  1.1                  jakarta-tomcat/src/share/org/apache/tomcat/util/http/Parameters.java
  
  Index: Parameters.java
  ===================================================================
  /*
   * ====================================================================
   *
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 1999 The Apache Software Foundation.  All rights 
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer. 
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution, if
   *    any, must include the following acknowlegement:  
   *       "This product includes software developed by the 
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowlegement may appear in the software itself,
   *    if and wherever such third-party acknowlegements normally appear.
   *
   * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
   *    Foundation" must not be used to endorse or promote products derived
   *    from this software without prior written permission. For written 
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * 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/>.
   *
   * [Additional notices, if required by prior licensing conditions]
   *
   */ 
  
  package org.apache.tomcat.util.http;
  
  import  org.apache.tomcat.util.*;
  import  org.apache.tomcat.util.collections.*;
  import java.io.*;
  import java.util.*;
  import java.text.*;
  
  /**
   * 
   * @author Costin Manolache
   */
  public final class Parameters extends MultiMap {
      public static final int INITIAL_SIZE=4;
  
      private boolean isSet=false;
      private boolean isFormBased=false;
      
      /**
       * 
       */
      public Parameters() {
  	super( INITIAL_SIZE );
      }
  
      public void recycle() {
  	super.recycle();
  	isSet=false;
  	isFormBased=false;
      }
  
      // duplicated
      public static int indexOf( byte bytes[], int off, int end, char qq )
      {
  	while( off < end ) {
  	    byte b=bytes[off];
  	    if( b==qq )
  		return off;
  	    off++;
  	}
  	return off;
      }
  
      public static int indexOf( char chars[], int off, int end, char qq )
      {
  	while( off < end ) {
  	    char b=chars[off];
  	    if( b==qq )
  		return off;
  	    off++;
  	}
  	return off;
      }
  
      public void processParameters( byte bytes[], int start, int len ) {
  	int end=start+len;
  	int pos=start;
  	
          do {
  	    int nameStart=pos;
  	    int nameEnd=indexOf(bytes, nameStart, end, '=' );
  	    int valStart=nameEnd+1;
  	    int valEnd=indexOf(bytes, valStart, end, '&');
  	    
  	    pos=valEnd+1;
  	    
  	    if( nameEnd<=nameStart ) {
  		continue;
  		// invalid chunk - it's better to ignore
  		// XXX log it ?
  	    }
  	    
  	    int field=this.addField();
  	    this.getName( field ).setBytes( bytes,
  					    nameStart, nameEnd );
  	    this.getValue( field ).setBytes( bytes,
  					     valStart, valEnd );
  	} while( pos<end );
      }
  
      public void processParameters( char chars[], int start, int len ) {
  	int end=start+len;
  	int pos=start;
  	
          do {
  	    int nameStart=pos;
  	    int nameEnd=indexOf(chars, nameStart, end, '=' );
  	    int valStart=nameEnd+1;
  	    int valEnd=indexOf(chars, valStart, end, '&');
  	    
  	    pos=valEnd+1;
  	    
  	    if( nameEnd<=nameStart ) {
  		continue;
  		// invalid chunk - it's better to ignore
  		// XXX log it ?
  	    }
  	    
  	    int field=this.addField();
  	    this.getName( field ).setChars( chars,
  					    nameStart, nameEnd );
  	    this.getValue( field ).setChars( chars,
  					     valStart, valEnd );
  	} while( pos<end );
      }
  
      
      public void processParameters( MessageBytes data ) {
  	if( data==null || data.getLength() <= 0 ) return;
  
  	if( data.getType() == MessageBytes.T_BYTES ) {
  	    processParameters( data.getBytes(), data.getOffset(),
  			       data.getLength());
  	} else {
  	    processParameters( data.getChars(), data.getOffset(),
  			       data.getLength());
  	}
      }
  
  
      public void mergeParameters( Parameters extra ) {
  	
      }
      
  }
  
  
  
  1.1                  jakarta-tomcat/src/share/org/apache/tomcat/util/http/ServerCookie.java
  
  Index: ServerCookie.java
  ===================================================================
  /*
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 1999 The Apache Software Foundation.  All rights 
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer. 
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution, if
   *    any, must include the following acknowlegement:  
   *       "This product includes software developed by the 
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowlegement may appear in the software itself,
   *    if and wherever such third-party acknowlegements normally appear.
   *
   * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
   *    Foundation" must not be used to endorse or promote products derived
   *    from this software without prior written permission. For written
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * 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.tomcat.util.http;
  
  import org.apache.tomcat.util.*;
  import java.text.*;
  import java.io.*;
  import java.util.*;
  
  
  /**
   *  Server-side cookie representation.
   *   Allows recycling and uses MessageBytes as low-level
   *  representation ( and thus the byte-> char conversion can be delayed
   *  until we know the charset ).
   *
   *  Tomcat.core uses this recyclable object to represent cookies,
   *  and the facade will convert it to the external representation.
   */
  public class ServerCookie implements Serializable {
      private MessageBytes name=new MessageBytes();
      private MessageBytes value=new MessageBytes();
  
      private MessageBytes comment=new MessageBytes();    // ;Comment=VALUE
      private MessageBytes domain=new MessageBytes();    // ;Domain=VALUE ...
  
      private int maxAge = -1;	// ;Max-Age=VALUE
  				// ;Discard ... implied by maxAge < 0
      private MessageBytes path=new MessageBytes();	// ;Path=VALUE .
      private boolean secure;	// ;Secure
      private int version = 0;	// ;Version=1
  
  
      public ServerCookie() {
  
      }
  
      public void recycle() {
          path.recycle();
      	name.recycle();
      	value.recycle();
      	comment.recycle();
      	maxAge=-1;
      	path.recycle();
          domain.recycle();
      	version=0;
      	secure=false;
      }
  
      public MessageBytes getComment() {
  	return comment;
      }
  
      public MessageBytes getDomain() {
  	return domain;
      }
  
      public void setMaxAge(int expiry) {
  	maxAge = expiry;
      }
  
      public int getMaxAge() {
  	return maxAge;
      }
  
  
      public MessageBytes getPath() {
  	return path;
      }
  
      public void setSecure(boolean flag) {
  	secure = flag;
      }
  
      public boolean getSecure() {
  	return secure;
      }
  
      public MessageBytes getName() {
  	return name;
      }
  
      public MessageBytes getValue() {
  	return value;
      }
  
      public int getVersion() {
  	return version;
      }
  
  
      public void setVersion(int v) {
  	version = v;
      }
  
  
      // -------------------- utils --------------------
      
      // 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 = ",;";
  
      /*
       * 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
       *
       * @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) {
  	int len = value.length();
  
  	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;
      }
  
      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;
      }
  
      // -------------------- Cookie parsing tools
  
      
      /** Return the header name to set the cookie, based on cookie
       *  version
       */
      public String getCookieHeaderName() {
          if (version == 1) {
  	    return "Set-Cookie2";
          } else {
              return "Set-Cookie";
          }
      }
  
      /** Return the header value used to set this cookie
       *  @deprecated Use StringBuffer version
       */
      public String getCookieHeaderValue() {
          StringBuffer buf = new StringBuffer();
  	getCookieHeaderValue( buf );
  	return buf.toString();
      }
  
      /** Return the header value used to set this cookie
       */
      public void getCookieHeaderValue(StringBuffer buf) {
  	ServerCookie cookie=this; 
  	
          // this part is the same for all cookies
          buf.append(cookie.getName());
          buf.append("=");
          maybeQuote(version, buf, cookie.getValue().toString());
  
   	// add version 1 specific information
  	if (version == 1) {
  	    // Version=1 ... required
  	    buf.append (";Version=1");
  
  	    // Comment=comment
  	    if (cookie.getComment() != null) {
  		buf.append (";Comment=");
  		maybeQuote (version, buf, cookie.getComment().toString());
  	    }
  	}
  
  	// add domain information, if present
  
  	if (cookie.getDomain().isNull()) {
  	    buf.append(";Domain=");
  	    maybeQuote (version, buf, cookie.getDomain().toString());
  	}
  
  	// Max-Age=secs/Discard ... or use old "Expires" format
  	if (cookie.getMaxAge() >= 0) {
  	    if (version == 0) {
  		buf.append (";Expires=");
  		DateTool.oldCookieFormat.
  		    format(new Date( System.currentTimeMillis() +
  				     cookie.getMaxAge() *1000L) ,buf,
  			   new FieldPosition(0));
  
  	    } else {
  		buf.append (";Max-Age=");
  		buf.append (cookie.getMaxAge());
  	    }
  	} else if (version == 1)
  	  buf.append (";Discard");
  
  	// Path=path
  	if (cookie.getPath().isNull()) {
  	    buf.append (";Path=");
  	    maybeQuote (version, buf, cookie.getPath().toString());
  	}
  
  	// Secure
  	if (cookie.getSecure()) {
  	  buf.append (";Secure");
  	}
      }
  
      public static void maybeQuote (int version, StringBuffer buf,
                                      String value)
      {
  	if (version == 0 || isToken (value))
  	  buf.append (value);
  	else {
  	    buf.append ('"');
  	    buf.append (value);
  	    buf.append ('"');
  	}
      }
  
  }
  
  
  
  
  1.1                  jakarta-tomcat/src/share/org/apache/tomcat/util/http/package.html
  
  Index: package.html
  ===================================================================
  <html>
  <head>
  <title>util.http</title>
  <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
  </head>
  
  <body bgcolor="#FFFFFF">
  Special utils for handling HTTP-specific entities - headers, parameters,
  cookies, etc.
  
  The utils are not specific to tomcat, but use util.MessageBytes.
  
  </body>
  </html>