FileDocCategorySizeDatePackage
Cookies.javaAPI DocApache Tomcat 6.0.1416439Fri Jul 20 04:20:32 BST 2007org.apache.tomcat.util.http

Cookies

public final class Cookies extends Object
A collection of cookies - reusable and tuned for server side performance. Based on RFC2965 ( and 2109 ) This class is not synchronized.
author
Costin Manolache
author
kevin seguin

Fields Summary
private static org.apache.juli.logging.Log
log
public static final int
INITIAL_SIZE
ServerCookie[]
scookies
int
cookieCount
boolean
unprocessed
MimeHeaders
headers
static final int
dbg
Constructors Summary
public Cookies(MimeHeaders headers)
Construct a new cookie collection, that will extract the information from headers.

param
headers Cookies are lazy-evaluated and will extract the information from the provided headers.

    
                                         
       
        this.headers=headers;
    
public Cookies()
Construct a new uninitialized cookie collection. Use {@link #setHeaders} to initialize.

    
Methods Summary
public ServerCookieaddCookie()
Register a new, unitialized cookie. Cookies are recycled, and most of the time an existing ServerCookie object is returned. The caller can set the name/value and attributes for the cookie

        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;
    
public static booleanequals(java.lang.String s, byte[] b, int start, int end)

        int blen = end-start;
        if (b == null || blen != s.length()) {
            return false;
        }
        int boff = start;
        for (int i = 0; i < blen; i++) {
            if (b[boff++] != s.charAt(i)) {
                return false;
            }
        }
        return true;
    
public static intfindDelim1(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 intfindDelim2(byte[] bytes, int off, int end)

        while( off < end ) {
            byte b=bytes[off];
            if( b==';" || b=='," )
                return off;
            off++;
        }
        return off;
    
public static intfindDelim3(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;
    
public ServerCookiegetCookie(int idx)

        if( unprocessed ) {
            getCookieCount(); // will also update the cookies
        }
        return scookies[idx];
    
public intgetCookieCount()

        if( unprocessed ) {
            unprocessed=false;
            processCookies(headers);
        }
        return cookieCount;
    
public voidlog(java.lang.String s)

         
        if (log.isDebugEnabled())
            log.debug("Cookies: " + s);
    
voidprocessCookieHeader(byte[] bytes, int off, int len)
Process a byte[] header - allowing fast processing of the raw data

        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 );
            }
        }
    
private voidprocessCookieHeader(java.lang.String cookieString)

        if( dbg>0 ) log( "Parsing cookie header " + 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);
                if( dbg > 0 ) log( "Add cookie " + name + "=" + value);
            } else {
                // we have a bad cookie.... just let it go
            }
        }
    
public voidprocessCookies(MimeHeaders headers)
Add all Cookie found in the headers of a request.

        if( headers==null )
            return;// nothing to process
        // process each "cookie" header
        int pos=0;
        while( pos>=0 ) {
            // Cookie2: version ? not needed
            pos=headers.findHeader( "Cookie", pos );
            // no more cookie headers headers
            if( pos<0 ) break;

            MessageBytes cookieValue=headers.getValue( pos );
            if( cookieValue==null || cookieValue.isNull() ) {
                pos++;
                continue;
            }

            // Uncomment to test the new parsing code
            if( cookieValue.getType() == MessageBytes.T_BYTES ) {
                if( dbg>0 ) log( "Parsing b[]: " + cookieValue.toString());
                ByteChunk bc=cookieValue.getByteChunk();
                processCookieHeader( bc.getBytes(),
                                     bc.getOffset(),
                                     bc.getLength());
            } else {
                if( dbg>0 ) log( "Parsing S: " + cookieValue.toString());
                processCookieHeader( cookieValue.toString() );
            }
            pos++;// search from the next position
        }
    
public voidrecycle()
Recycle.

            for( int i=0; i< cookieCount; i++ ) {
            if( scookies[i]!=null )
                scookies[i].recycle();
        }
        cookieCount=0;
        unprocessed=true;
    
public voidsetHeaders(MimeHeaders headers)
Set the headers from which cookies will be pulled. This has the side effect of recycling the object.

param
headers Cookies are lazy-evaluated and will extract the information from the provided headers.

        recycle();
        this.headers=headers;
    
public static intskipSpaces(byte[] bytes, int off, int end)

        while( off < end ) {
            byte b=bytes[off];
            if( b!= ' " ) return off;
            off ++;
        }
        return off;
    
private static java.lang.StringstripQuote(java.lang.String value)
Strips quotes from the start and end of the cookie string This conforms to RFC 2965

param
value a String specifying the cookie value (possibly quoted).
see
#setValue

        //        log("Strip quote from " + value );
        if (value.startsWith("\"") && value.endsWith("\"")) {
            try {
                return value.substring(1,value.length()-1);
            } catch (Exception ex) { 
            }
        }
        return value;
    
public java.lang.StringtoString()
EXPENSIVE!!! only for debugging.

        StringWriter sw = new StringWriter();
        PrintWriter pw = new PrintWriter(sw);
        pw.println("=== Cookies ===");
        int count = getCookieCount();
        for (int i = 0; i < count; ++i) {
            pw.println(getCookie(i).toString());
        }
        return sw.toString();