/*
 * The contents of this file are subject to the terms
 * of the Common Development and Distribution License
 * (the License).  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the license at
 * https://glassfish.dev.java.net/public/CDDLv1.0.html or
 * glassfish/bootstrap/legal/CDDLv1.0.txt.
 * See the License for the specific language governing
 * permissions and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL
 * Header Notice in each file and include the License file
 * at glassfish/bootstrap/legal/CDDLv1.0.txt.
 * If applicable, add the following below the CDDL Header,
 * with the fields enclosed by brackets [] replaced by
 * you own identifying information:
 * "Portions Copyrighted [year] [name of copyright owner]"
 *
 * Copyright (c) Ericsson AB, 2004-2007. All rights reserved.
 */
package org.jvnet.glassfish.comms.clb.core.common.chr;

import com.ericsson.ssa.sip.Header;
import com.ericsson.ssa.sip.SipServletMessageImpl;
import com.ericsson.ssa.sip.UriUtil;

import com.sun.grizzly.util.http.Parameters;

import org.jvnet.glassfish.comms.clb.core.CLBConstants;
import org.jvnet.glassfish.comms.clb.proxy.http.util.HttpRequest;
import org.jvnet.glassfish.comms.util.LogUtil;

import java.util.ListIterator;
import java.util.logging.Level;

import javax.servlet.sip.Address;
import javax.servlet.sip.ServletParseException;
import javax.servlet.sip.SipServletRequest;


/**
 * This hash key extractor extracts hash key encoded in the BEkey cookie of a
 * request.
 */
public class StickyHashKeyExtractor implements HashKeyExtractor {
    private static LogUtil logger = new LogUtil(LogUtil.CLB_LOG_DOMAIN);

    /** The URI parameter name for the hash key to the Back-End. */
    public static final String BE_KEY_PARAM = "bekey";

    /** The header used to transfer BEKey between front-end and back-end. */
    public static final String PROXY_BEKEY_HEADER = Header.PROXY_BEKEY_HEADER;

    /**
     * @see org.jvnet.glassfish.comms.clb.core.common.chr.HashKeyExtractor#getHashKey(javax.servlet.http.HttpServletRequest)
     */
    public String getHashKey(HttpRequest request) {
        //check parameters for the key
        Parameters parameters = request.getParameters();
        String value = parameters.getParameter(CLBConstants.BEKEY_KEY_NAME);

        if (value != null) {
            return value;
        }

        //search the cookies
        String cookieString = request.getHeader(CLBConstants.COOKIE_HEADER);

        if ((cookieString == null) || (cookieString.length() == 0)) {
            return null;
        }

        int index = cookieString.indexOf(CLBConstants.BEKEY_KEY_NAME);

        if (index == -1) {
            return null;
        }

        int valueStartIndex = cookieString.indexOf("=", index + 1);

        if (valueStartIndex == -1) {
            return null;
        }

        int valueEndIndex = cookieString.indexOf(";", valueStartIndex + 1);

        if (valueEndIndex == -1) {
            valueEndIndex = cookieString.indexOf(",", valueStartIndex + 1);
        }

        if (valueEndIndex == -1) {
            return cookieString.substring(valueStartIndex + 1);
        }

        return cookieString.substring(valueStartIndex + 1, valueEndIndex);
    }

    /**
     * @see org.jvnet.glassfish.comms.clb.core.common.chr.HashKeyExtractor#getHashKey(javax.servlet.sip.SipServletRequest)
     */
    public String getHashKey(SipServletRequest req) {
        return extractHashKey(req);
    }

    private String extractHashKey(SipServletRequest request) {
        // First check if it is in proxy-bekey (used for transfer between F-E and B-E)
        String bekey = request.getHeader(PROXY_BEKEY_HEADER);
        request.removeHeader(PROXY_BEKEY_HEADER);

        if (logger.isLoggable(Level.FINE)) {
            if (bekey != null) {
                logger.logMsg(Level.FINE,
                    "bekey: " + bekey + " was found in the Proxy-Bekey header");
            }
        }

        if (bekey == null) {
            // Try to extract BEKey from request-URI
            bekey = UriUtil.getParameter(request.getRequestURI(), BE_KEY_PARAM);

            if (logger.isLoggable(Level.FINE)) {
                if (bekey != null) {
                    logger.logMsg(Level.FINE,
                        "bekey: " + bekey + " was found in the request-URI");
                }
            }
        }

        if (bekey == null) {
            // OK, no BEKey in request-URI, check topmost Route
            Header routeHeader = ((SipServletMessageImpl) request).getRawHeader(Header.ROUTE);

            if (routeHeader != null) {
                if (logger.isLoggable(Level.FINE)) {
                    logger.logMsg(Level.FINE,
                        "The request contains a Route header: " + routeHeader);
                }

                ListIterator<Address> routeIterator;

                try {
                    routeIterator = routeHeader.getAddressValues();
                } catch (ServletParseException e) {
                    logger.warning(e,
                        StickyHashKeyExtractor.class.getName() +
                        ".address_parse_error");

                    return null;
                }

                if (routeIterator.hasNext()) {
                    Address topRoute = routeIterator.next();
                    bekey = UriUtil.getParameter(topRoute.getURI(), BE_KEY_PARAM);

                    if (logger.isLoggable(Level.FINE)) {
                        logger.logMsg(Level.FINE,
                            "The bekey: " + bekey +
                            " was found in the topmost Route: " + topRoute);
                    }
                }
            }
        }

        return bekey;
    }
}
