📄 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.buf.MessageBytes;import org.apache.tomcat.util.buf.CharChunk;import org.apache.tomcat.util.buf.ByteChunk;import java.io.*;import java.util.*;import java.text.*;/** * 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 */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=0; boolean unprocessed=true; 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. */ public Cookies(MimeHeaders headers) { this.headers=headers; } /** * Construct a new uninitialized cookie collection. * Use {@link #setHeaders} to initialize. */ // [seguin] added so that an empty Cookies object could be // created, have headers set, then recycled. public Cookies() { } /** * 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. */ // [seguin] added so that an empty Cookies object could be // created, have headers set, then recycled. public void setHeaders(MimeHeaders headers) { recycle(); this.headers=headers; } /** * Recycle. */ public void recycle() { for( int i=0; i< cookieCount; i++ ) { if( scookies[i]!=null ) scookies[i].recycle(); } cookieCount=0; unprocessed=true; } /** * EXPENSIVE!!! only for debugging. */ public String toString() { 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(); } // -------------------- Indexed access -------------------- public ServerCookie getCookie( int idx ) { if( unprocessed ) { getCookieCount(); // will also update the cookies } return scookies[idx]; } public int getCookieCount() { if( unprocessed ) { unprocessed=false; processCookies(headers); } return cookieCount; } // -------------------- Adding cookies -------------------- /** 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 */ 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; } // code from CookieTools /** Add all Cookie found in the headers of a request. */ public void processCookies( MimeHeaders headers ) { 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() ) 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 } } /** 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==',' ) { 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; // quote is valid only in version=1 cookies cc=bytes[pos]; if( version==1 && ( cc== '\'' || cc=='"' ) ) { startValue++; endValue=indexOf( bytes, startValue, end, cc ); 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; } 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; } // XXX will be refactored soon! public static boolean equals( 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; } // --------------------------------------------------------- // -------------------- DEPRECATED, OLD -------------------- private void processCookieHeader( 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 } } } /** * * 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; } // log static final int dbg=0; public void log(String s ) { System.out.println("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;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"); } 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 )); } } */}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -