Package com.meterware.httpunit.cookies

Source Code of com.meterware.httpunit.cookies.CookieJar$RFC2109CookieRecipe

package com.meterware.httpunit.cookies;
/********************************************************************************************************************
* $Id: CookieJar.java,v 1.11 2004/09/29 17:15:26 russgold Exp $
*
* Copyright (c) 2002-2004, Russell Gold
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
* documentation files (the "Software"), to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and
* to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions
* of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
* THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
*******************************************************************************************************************/
import java.io.IOException;
import java.io.StreamTokenizer;
import java.io.StringReader;
import java.net.URL;
import java.util.*;


/**
* A collection of HTTP cookies, which can interact with cookie and set-cookie header values.
*
* @author <a href="mailto:russgold@httpunit.org">Russell Gold</a>
* @author <a href="mailto:drew.varner@oracle.com">Drew Varner</a>
**/
public class CookieJar {

    private static final int DEFAULT_HEADER_SIZE = 80;

    private ArrayList _cookies = new ArrayList();
    private ArrayList _globalCookies = new ArrayList();
    private CookiePress _press;


    /**
     * Creates an empty cookie jar.
     */
    public CookieJar() {
        _press = new CookiePress( null );
    }


    /**
     * Creates a cookie jar which is initially populated with cookies parsed from the <code>Set-Cookie</code> and
     * <code>Set-Cookie2</code> header fields.
     * <p>
     * Note that the parsing does not strictly follow the specifications, but
     * attempts to imitate the behavior of popular browsers. Specifically,
     * it allows cookie values to contain commas, which the
     * Netscape standard does not allow for, but which is required by some servers.
     * </p>
     */
    public CookieJar( CookieSource source ) {
        _press = new CookiePress( source.getURL() );
        findCookies( source.getHeaderFields( "Set-Cookie" ), new RFC2109CookieRecipe() );
        findCookies( source.getHeaderFields( "Set-Cookie2" ), new RFC2965CookieRecipe() );
    }


    private void findCookies( String cookieHeader[], CookieRecipe recipe ) {
        for (int i = 0; i < cookieHeader.length; i++) {
            recipe.findCookies( cookieHeader[i] );
        }
    }


    /**
     * Empties this cookie jar of all contents.
     */
    public void clear() {
        _cookies.clear();
        _globalCookies.clear();
    }


    /**
     * Defines a cookie to be sent to the server on every request. This bypasses the normal mechanism by which only
     * certain cookies are sent based on their host and path.
     * @deprecated as of 1.6, use #putCookie
     **/
    public void addCookie( String name, String value ) {
        _globalCookies.add( new Cookie( name, value ) );
    }


    /**
     * Defines a cookie to be sent to the server on every request. This bypasses the normal mechanism by which only
     * certain cookies are sent based on their host and path.
     * @since 1.6
     **/
    public void putCookie( String name, String value ) {
        for (Iterator iterator = _globalCookies.iterator(); iterator.hasNext();) {
            Cookie cookie = (Cookie) iterator.next();
            if (name.equals( cookie.getName() )) iterator.remove();
        }
        _globalCookies.add( new Cookie( name, value ) );
    }


    /**
     * Returns the name of all the active cookies in this cookie jar.
     **/
    public String[] getCookieNames() {
        final int numGlobalCookies = _globalCookies.size();
        String[] names = new String[ _cookies.size() + numGlobalCookies ];
        for (int i = 0; i < numGlobalCookies; i++) {
            names[i] = ((Cookie) _globalCookies.get(i)).getName();
        }
        for (int i = numGlobalCookies; i < names.length; i++) {
            names[i] = ((Cookie) _cookies.get( i-numGlobalCookies )).getName();
        }
        return names;
    }


    /**
     * Returns a collection containing all of the cookies in this jar.
     */
    public Collection getCookies() {
        final Collection collection = (Collection) _cookies.clone();
        collection.addAll( _globalCookies );
        return collection;
    }


    /**
     * Returns the value of the specified cookie.
     **/
    public String getCookieValue( String name ) {
        Cookie cookie = getCookie( name );
        return cookie == null ? null : cookie.getValue();
    }


    /**
     * Returns the value of the specified cookie.
     **/
    public Cookie getCookie( String name ) {
        if (name == null) throw new IllegalArgumentException( "getCookieValue: no name specified" );
        for (Iterator iterator = _cookies.iterator(); iterator.hasNext();) {
            Cookie cookie = (Cookie) iterator.next();
            if (name.equals( cookie.getName() )) return cookie;
        }
        for (Iterator iterator = _globalCookies.iterator(); iterator.hasNext();) {
            Cookie cookie = (Cookie) iterator.next();
            if (name.equals( cookie.getName() )) return cookie;
        }
        return null;
    }


    /**
     * Returns the value of the cookie header to be sent to the specified URL.
     * Will return null if no compatible cookie is defined.
     **/
    public String getCookieHeaderField( URL targetURL ) {
        if (_cookies.isEmpty() && _globalCookies.isEmpty()) return null;
        StringBuffer sb = new StringBuffer( DEFAULT_HEADER_SIZE );
        HashSet restrictedCookies = new HashSet();
        for (Iterator i = _cookies.iterator(); i.hasNext();) {
            Cookie cookie = (Cookie) i.next();
            if (!cookie.mayBeSentTo( targetURL )) continue;
            restrictedCookies.add( cookie.getName() );
            if (sb.length() != 0) sb.append( "; " );
            sb.append( cookie.getName() ).append( '=' ).append( cookie.getValue() );
        }
        for (Iterator i = _globalCookies.iterator(); i.hasNext();) {
            Cookie cookie = (Cookie) i.next();
            if (restrictedCookies.contains( cookie.getName() )) continue;
            if (sb.length() != 0) sb.append( "; " );
            sb.append( cookie.getName() ).append( '=' ).append( cookie.getValue() );
        }
        return sb.length() == 0 ? null : sb.toString();
    }


    /**
     * Updates the cookies maintained in this cookie jar with those in another cookie jar. Any duplicate cookies in
     * the new jar will replace those in this jar.
     **/
    public void updateCookies( CookieJar newJar ) {
        for (Iterator i = newJar._cookies.iterator(); i.hasNext();) {
            addUniqueCookie( (Cookie) i.next() );
        }
    }


    /**
     * Add the cookie to this jar, replacing any previous matching cookie.
     */
    void addUniqueCookie( Cookie cookie ) {
        _cookies.remove( cookie );
        _cookies.add( cookie );
    }


    abstract class CookieRecipe {

        /**
         * Extracts cookies from a cookie header. Works in conjunction with a cookie press class, which actually creates
         * the cookies and adds them to the jar as appropriate.
         *
         * 1. Parse the header into tokens, separated by ',' and ';' (respecting single and double quotes)
         * 2. Process tokens from the end:
         *    a. if the token contains an '=' we have a name/value pair. Add them to the cookie press, which
         *       will decide if it is a cookie name or an attribute name.
         *    b. if the token is a reserved word, flush the cookie press and continue.
         *    c. otherwise, add the token to the cookie press, passing along the last character of the previous token.
         */
        void findCookies( String cookieHeader ) {
            Vector tokens = getCookieTokens( cookieHeader );

            for (int i = tokens.size() - 1; i >= 0; i--) {
                String token = (String) tokens.elementAt( i );

                int equalsIndex = getEqualsIndex( token );
                if (equalsIndex != -1) {
                    _press.addTokenWithEqualsSign( this, token, equalsIndex );
                } else if (isCookieReservedWord( token )) {
                    _press.clear();
                } else {
                    _press.addToken( token, lastCharOf( (i == 0) ? "" : (String) tokens.elementAt( i - 1 ) ) );
                }
            }
        }


        private char lastCharOf( String string ) {
            return (string.length() == 0) ? ' ' : string.charAt( string.length()-1 );
        }


        /**
         * Returns the index (if any) of the equals sign separating a cookie name from the its value.
         * Equals signs at the end of the token are ignored in this calculation, since they may be
         * part of a Base64-encoded value.
         */
        private int getEqualsIndex( String token ) {
            if (!token.endsWith( "==" )) {
                return token.indexOf( '=' );
            } else {
                return getEqualsIndex( token.substring( 0, token.length()-2 ) );
            }
        }


        /**
         * Tokenizes a cookie header and returns the tokens in a
         * <code>Vector</code>.
         **/
        private Vector getCookieTokens(String cookieHeader) {
            StringReader sr = new StringReader(cookieHeader);
            StreamTokenizer st = new StreamTokenizer(sr);
            Vector tokens = new Vector();

            // clear syntax tables of the StreamTokenizer
            st.resetSyntax();

            // set all characters as word characters
            st.wordChars(0,Character.MAX_VALUE);

            // set up characters for quoting
            st.quoteChar( '"' ); //double quotes
            st.quoteChar( '\'' ); //single quotes

            // set up characters to separate tokens
            st.whitespaceChars(59,59); //semicolon
            st.whitespaceChars(44,44); //comma

            try {
                while (st.nextToken() != StreamTokenizer.TT_EOF) {
                    tokens.addElement( st.sval.trim() );
                }
            }
            catch (IOException ioe) {
                // this will never happen with a StringReader
            }
            sr.close();
            return tokens;
        }


        abstract protected boolean isCookieAttribute( String stringLowercase );


        abstract protected boolean isCookieReservedWord( String token );

    }


    class CookiePress {

        private StringBuffer _value = new StringBuffer();
        private HashMap _attributes = new HashMap();
        private URL     _sourceURL;


        public CookiePress( URL sourceURL ) {
            _sourceURL = sourceURL;
        }


        void clear() {
            _value.setLength(0);
            _attributes.clear();
        }


        void addToken( String token, char lastChar ) {
            _value.insert( 0, token );
            if (lastChar != '=') _value.insert( 0, ',' );
        }


        void addTokenWithEqualsSign( CookieRecipe recipe, String token, int equalsIndex ) {
            String name = token.substring( 0, equalsIndex ).trim();
            _value.insert( 0, token.substring( equalsIndex + 1 ).trim() );
            if (recipe.isCookieAttribute( name.toLowerCase() )) {
                _attributes.put( name.toLowerCase(), _value.toString() );
            } else {
                addCookieIfValid( new Cookie( name, _value.toString(), _attributes ) );
                _attributes.clear();
            }
            _value.setLength(0);
        }


        private void addCookieIfValid( Cookie cookie ) {
            if (acceptCookie( cookie )) addUniqueCookie( cookie );
        }


        private boolean acceptCookie( Cookie cookie ) {
            if (cookie.getPath() == null) {
                cookie.setPath( getParentPath( _sourceURL.getPath() ) );
            } else {
                int status = getPathAttributeStatus( cookie.getPath(), _sourceURL.getPath() );
                if (status != CookieListener.ACCEPTED) {
                    reportCookieRejected( status, cookie.getPath(), cookie.getName() );
                    return false;
                }
            }

            if (cookie.getDomain() == null) {
                cookie.setDomain( _sourceURL.getHost() );
            } else if (!CookieProperties.isDomainMatchingStrict() && cookie.getDomain().equalsIgnoreCase( _sourceURL.getHost() )) {
                cookie.setDomain( _sourceURL.getHost() );
            } else {
                int status = getDomainAttributeStatus( cookie.getDomain(), _sourceURL.getHost() );
                if (status != CookieListener.ACCEPTED) {
                    reportCookieRejected( status, cookie.getDomain(), cookie.getName() );
                    return false;
                }
            }

            return true;
        }


        private String getParentPath( String path ) {
            int rightmostSlashIndex = path.lastIndexOf( '/' );
            return rightmostSlashIndex < 0 ? "/" : path.substring( 0, rightmostSlashIndex );
        }


        private int getPathAttributeStatus( String pathAttribute, String sourcePath ) {
            if (!CookieProperties.isPathMatchingStrict() || sourcePath.length() == 0 || sourcePath.startsWith( pathAttribute )) {
                return CookieListener.ACCEPTED;
            } else {
                return CookieListener.PATH_NOT_PREFIX;
            }
        }


        private int getDomainAttributeStatus( String domainAttribute, String sourceHost ) {
            if (!domainAttribute.startsWith(".")) domainAttribute = '.' + domainAttribute;

            if (domainAttribute.lastIndexOf('.') == 0) {
                return CookieListener.DOMAIN_ONE_DOT;
            } else if (!sourceHost.endsWith( domainAttribute )) {
                return CookieListener.DOMAIN_NOT_SOURCE_SUFFIX;
            } else if (CookieProperties.isDomainMatchingStrict() &&
                       sourceHost.lastIndexOf( domainAttribute ) > sourceHost.indexOf( '.' )) {
                return CookieListener.DOMAIN_TOO_MANY_LEVELS;
            } else {
                return CookieListener.ACCEPTED;
            }
        }


        private boolean reportCookieRejected( int reason, String attribute, String source ) {
            CookieProperties.reportCookieRejected( reason, attribute, source );
            return false;
        }

    }


    /**
     * Parses cookies according to
     * <a href="http://www.ietf.org/rfc/rfc2109.txt">RFC 2109</a>
     *
     * <br />
     * These cookies come from the <code>Set-Cookie:</code> header
     **/
    class RFC2109CookieRecipe extends CookieRecipe {

        protected boolean isCookieAttribute( String stringLowercase ) {
            return stringLowercase.equals("path") ||
                   stringLowercase.equals("domain") ||
                   stringLowercase.equals("expires") ||
                   stringLowercase.equals("comment") ||
                   stringLowercase.equals("max-age") ||
                   stringLowercase.equals("version");
        }


        protected boolean isCookieReservedWord( String token ) {
            return token.equalsIgnoreCase( "secure" );
        }
    }


    /**
     * Parses cookies according to
     * <a href="http://www.ietf.org/rfc/rfc2965.txt">RFC 2965</a>
     *
     * <br />
     * These cookies come from the <code>Set-Cookie2:</code> header
     **/
    class RFC2965CookieRecipe extends CookieRecipe {

        protected boolean isCookieAttribute( String stringLowercase ) {
            return stringLowercase.equals("path") ||
                   stringLowercase.equals("domain") ||
                   stringLowercase.equals("comment") ||
                   stringLowercase.equals("commenturl") ||
                   stringLowercase.equals("max-age") ||
                   stringLowercase.equals("version") ||
                   stringLowercase.equals("$version") ||
                   stringLowercase.equals("port");
        }


        protected boolean isCookieReservedWord( String token ) {
            return token.equalsIgnoreCase( "discard" ) || token.equalsIgnoreCase( "secure" );
        }
    }


}
TOP

Related Classes of com.meterware.httpunit.cookies.CookieJar$RFC2109CookieRecipe

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.