Package org.jvnet.glassfish.comms.clb.core.sip

Source Code of org.jvnet.glassfish.comms.clb.core.sip.SipLoadBalancerIncomingHandler

/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 1997-2009 Sun Microsystems, Inc. All rights reserved.
* Copyright (c) Ericsson AB, 2004-2008. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, 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/CDDL+GPL.html
* or glassfish/bootstrap/legal/LICENSE.txt.  See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
* Sun designates this particular file as subject to the "Classpath" exception
* as provided by Sun in the GPL Version 2 section of the License file that
* accompanied this code.  If applicable, add the following below the License
* Header, with the fields enclosed by brackets [] replaced by your own
* identifying information: "Portions Copyrighted [year]
* [name of copyright owner]"
*
* Contributor(s):
*
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license."  If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above.  However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
package org.jvnet.glassfish.comms.clb.core.sip;

import com.ericsson.ssa.sip.Header;
import com.ericsson.ssa.sip.SipServletMessageImpl;
import com.ericsson.ssa.sip.SipServletRequestImpl;
import com.ericsson.ssa.sip.SipServletResponseImpl;
import com.ericsson.ssa.sip.ViaImpl;
import com.ericsson.ssa.sip.dns.SipTransports;
import com.ericsson.ssa.sip.dns.TargetResolver;
import com.ericsson.ssa.sip.dns.TargetTuple;

import org.jvnet.glassfish.comms.clb.core.CLBConstants;
import org.jvnet.glassfish.comms.clb.core.ConsistentHashRequest;
import org.jvnet.glassfish.comms.clb.core.EndPoint;
import org.jvnet.glassfish.comms.clb.core.Router;
import org.jvnet.glassfish.comms.clb.core.ServerInstance;
import org.jvnet.glassfish.comms.clb.core.util.LoadbalancerUtil;
import org.jvnet.glassfish.comms.util.LogUtil;

import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;

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

import org.jvnet.glassfish.comms.clb.core.Controller;
import org.jvnet.glassfish.comms.clb.core.monitor.CLBMonitoringManager;



/**
* This class implements the SIP load balancer routing logic. It implements both
* the front-end logic (incoming requests and responses) and back-end logic
* (both incoming and outgoing requests and responses)
*/
class SipLoadBalancerIncomingHandler {
    private static final Logger logger = LogUtil.CLB_LOGGER.getLogger();
    private static final CLBMonitoringManager clbMonitoringManager =
            CLBMonitoringManager.getInstance();
    private Controller controller;
    private Socket localSipTcpSocket; // Socket for traffic between F-E to B-E and responses
    private ServerInstance localInstance;

    /**
     * Creates an instance and associates it with the specified hash key
     * extractor and server instance lookup.
     *
     */
    public SipLoadBalancerIncomingHandler(Controller controller, Socket localSipTcpSocket) {
        this.controller = controller;
        this.localSipTcpSocket = localSipTcpSocket;
        localInstance = null;
    }

    public SipLoadBalancerIncomingHandler(Controller controller) {
        this.controller = controller;    
        localInstance = controller.getLocalInstance();
        localSipTcpSocket = null;
    }

    /**
     * Handle a request that as been received by this instance. It shall either
     * be served by this instance, or be proxied to another instance.
     *
     * @param req the request; the request may be modified (headers added or
     *                changed)
     * @param serverSelector the server selector
     * @return a possible new connection; null means continue on this server
     * @throws SipRoutingException thrown in case request was malformed
     */
    public Connection handleIncomingRequest(ConsistentHashRequest req,
        Router router) throws SipRoutingException {
        if (logger.isLoggable(Level.FINER)) {
            logger.log(Level.FINER,
                "Handle incoming request");
        }

        SipServletRequestImpl request = (SipServletRequestImpl) req.getSipRequest();
       
        if(clbMonitoringManager.isCLBMonitoringEnabled()){
            clbMonitoringManager.getCLBStatsUpdater().
                    incrementTotalIncomingSipRequestsFE();
        }
       
        ServerInstance serverInstance = router.selectInstance(req);
        if (serverInstance == null) {
            throw new SipRoutingException("Could not find a server");
        }

        String hashkey = req.getHashKey();

        request.setBeKey(hashkey);

        if (logger.isLoggable(Level.FINER)) {
            logger.log(Level.FINER,
                "Hash key: " + hashkey + "; server: " +
                serverInstance.getName()+"; isLocal: "+serverInstance.isLocalInstance());
        }

        if (serverInstance.isLocalInstance()) {
            if(clbMonitoringManager.isCLBMonitoringEnabled()){
                clbMonitoringManager.getCLBStatsUpdater().
                        incrementTotalLocalSipRequestsFE();
            }
            // Continue on this instance
            return null;
        } else {
            if(clbMonitoringManager.isCLBMonitoringEnabled()){
                clbMonitoringManager.getCLBStatsUpdater().
                        incrementTotalProxiedSipRequestsFE();
            }
            // Proxy to the other instance
            Socket serverAddress = getServerAddress(
                    serverInstance.getEndPoint(CLBConstants.SIP_PROTOCOL));
            Socket outgoingSocket =  null;
            //update via with proper param
            if(controller.getLocalInstance() != null){
                EndPoint endPoint = controller.getLocalInstance().getEndPoint(
                        CLBConstants.SIP_PROTOCOL);                             
                pushVia(request,
                        endPoint.getHost(),
                        endPoint.getPort(),
                        true);
                outgoingSocket = getServerAddress(endPoint);
            }else{
                pushVia(request, localSipTcpSocket.getHostName(),
                        localSipTcpSocket.getPort(), true);
                outgoingSocket = localSipTcpSocket;
            }
            encodeRemote(request);
            encodeClientCert(request);
            encodeBeKeyHeader(request, hashkey);

            if (logger.isLoggable(Level.FINER)) {
                logger.log(Level.FINER,
                        "Proxy to other instance (" + serverAddress +
                        "); request: " + request);
            }

            Connection connection =  new Connection(SipTransports.TCP_PROT,
                    null, serverAddress);
            //set outgoing socket
            connection.setOutgoingEndpoint(outgoingSocket);
            return connection;
        }
    }

   
    private Socket getServerAddress(EndPoint serverAddress) {
        return new Socket(serverAddress.getIPAddress(), serverAddress.getPort());
    }

    /**
     * Handle a response that as been received by this instance. It shall either
     * be served by this instance, or be re-routed to another instance.
     *
     * @param response the response; the response may be modified (headers added
     *                or changed)
     * @return a possible list of new connection which shall be tried in the
     *         order specified in the list; if null continue on the current
     *         server.
     * @throws SipRoutingException thrown in case response was corrupt, the
     *                 caller shall do no further processing but just drop the
     *                 response.
     */
    public Connection handleIncomingResponse(SipServletResponseImpl response)
        throws SipRoutingException {
        if (logger.isLoggable(Level.FINER)) {
            logger.log(Level.FINER, "Handle incoming response.");
        }

        // Get topmost header
        Header viaHeader = response.getRawHeader(Header.VIA);

        if (viaHeader != null) {
            ListIterator<String> viaIterator;
            viaIterator = viaHeader.getValues();

            if (viaIterator.hasNext()) {
                if (clbMonitoringManager.isCLBMonitoringEnabled()) {
                    clbMonitoringManager.getCLBStatsUpdater().
                            incrementTotalOutgoingSipResponsesFE();
                }
                ViaImpl via = new ViaImpl(viaIterator.next());

                // Extract 'felb' flag
                String frontEndLb = via.getParameter(CLBConstants.FE_LB_PARAM);

                if (frontEndLb != null) {
                    // This response was received from the back-end,
                    // pop Via, extract connection and forward it to the client.
                    viaHeader.setReadOnly(false);
                    viaIterator.remove();
                    viaHeader.setReadOnly(true);

                    // Extract connection
                    Connection connection = LoadbalancerUtil.getConnection(via);
                    if (connection != null && connection == Connection.UDP) {
                            // The origin request was sent via UDP.
                            // There will thus *not* exist a connection between the client and this instance.
                            // Resolve the Via and use the resolved address as destination.
                            try {
                                TargetTuple tt = TargetResolver.getInstance().resolveResponse(response);
                                connection = new Connection(tt.getProtocol(), null, new Socket(tt.getIP(), tt.getPort()));
                            } catch (Exception e) {
                                logger.log(Level.WARNING, "clb.sip.warning.resolve_failed", e.getMessage());
                                throw new SipRoutingException("Unable to resolve address", e);
                            }
                    }
                    if(connection == null){
                        // This is plain wrong: connection information shall always exist!
                        throw new SipRoutingException(
                                "Was about to direct incoming response back to client, " +
                                "but could not find connection information to client in response.");
                    }
                    if (logger.isLoggable(Level.FINER)) {
                        logger.log(Level.FINER,
                            "Response was from from back-end forward it to the client via the connection: " +
                            connection);
                    }

                    return connection;
                } else {
                    if(clbMonitoringManager.isCLBMonitoringEnabled()){
                        clbMonitoringManager.getCLBStatsUpdater().
                                incrementTotalIncomingSipResponsesFE();
                    }
                    // This response was received from an external party and
                    // shall possibly be forwarded to a back-end
                    ServerInstance serverInstance =
                            getServerInstanceForResponse(response);

                    return handleResponseFromExternalParty(response,
                        serverInstance);
                }
            }
        }

        // No Via? The response must be corrupt, drop it!
        throw new SipRoutingException(
            "No Via on response, shall never happen; drop the response! : " + response.toString());
    }

    // ---------------- Internal methods --------------------
    private ServerInstance getServerInstanceForResponse(
            SipServletResponseImpl response) throws SipRoutingException {
        // Get topmost Via
        Header viaHeader = response.getRawHeader(Header.VIA);

        if (viaHeader != null) {
            ListIterator<String> viaIterator;
            viaIterator = viaHeader.getValues();

            if (viaIterator.hasNext()) {
                ViaImpl via = new ViaImpl(viaIterator.next());

                String instanceID = via.getParameter(
                        CLBConstants.BE_ROUTE_PARAM);
               
                if(instanceID == null || instanceID.length() <=2){
                    // Should never happen, drop response
                    throw new SipRoutingException(
                        "No BERoute found in response, shall never happen; drop the response! : " + response.toString());
                }

                //remove the quotes around the beroute value
                instanceID = instanceID.substring(
                        1, (instanceID.length() - 1));
               
                ServerInstance serverInstance =
                    controller.getGlobalInstanceMap().
                        getServerInstance(instanceID);

                if (serverInstance == null) {
                    // Should never happen, drop response
                    throw new SipRoutingException(
                        "No matching instance found for BERoute \""
                        + instanceID + "\" found in response, shall"
                        + " never happen; drop the response! : " + response.toString());
                }

                return serverInstance;
            }
        }

        throw new SipRoutingException(
            "No Via on response, shall never happen; drop the response! : " + response.toString());
    }

    private Connection handleResponseFromExternalParty(
        SipServletResponseImpl response,
        ServerInstance serverInstance) throws SipRoutingException {
       
        if (!serverInstance.isHealthy()) {
            throw new SipRoutingException("Unable to route response as " +
                    serverInstance.getName() + " is unhealthy");
        }
        if (!serverInstance.isEnabled()) {
            throw new SipRoutingException("Unable to route response as " +
                    serverInstance.getName() + " is disabled");
        }
       
        // Note, it does not matter that the response actually arrived on another socket.
        // We just want to match it against the socket encoded in 'beroute' to see that
        // this is the instance that sent the request.
        if (serverInstance.isLocalInstance()) {
            if (clbMonitoringManager.isCLBMonitoringEnabled()) {
                clbMonitoringManager.getCLBStatsUpdater().
                        incrementTotalLocalSipResponsesFE();
            }
            // On the right server, continue on it
            if (logger.isLoggable(Level.FINER)) {
                logger.log(Level.FINER,
                    "The response came from the outside but we are on the right server, continue here.");
            }
            return null;
        } else {
            if (clbMonitoringManager.isCLBMonitoringEnabled()) {
                clbMonitoringManager.getCLBStatsUpdater().
                        incrementTotalProxiedSipResponsesFE();
            }
            // The response shall be re-routed to another server
            encodeRemote(response);
            encodeClientCert(response); // TODO Can a response ever have a client cert?

            Connection connection = new Connection(SipTransports.TCP_PROT, null,
                    getServerAddress(serverInstance.getEndPoint(
                    CLBConstants.SIP_PROTOCOL)));
           
            //set outgoing socket
            Socket outgoingSocket =  null;
            if(controller.getLocalInstance() != null){
                EndPoint endPoint = controller.getLocalInstance().getEndPoint(
                        CLBConstants.SIP_PROTOCOL);
                outgoingSocket = getServerAddress(endPoint);
            }else{
                outgoingSocket = localSipTcpSocket;
            }
            connection.setOutgoingEndpoint(outgoingSocket);
           
            if (logger.isLoggable(Level.FINER)) {
                logger.log(Level.FINER,
                    "The response came from the outside but we are on wrong server, " +
                    "re-route to : " + serverInstance.getName() +
                    " via the connection: " + connection);
            }
            return connection;
        }
    }

    // TODO move this method to LoadbalancerUtil.
    static void pushVia(SipServletRequestImpl request, String hostName,
            int port, boolean frontend) throws SipRoutingException {
        // This Via is used to route an incoming response if the request is being proxied and the
        // response goes via UDP or if the TCP connection via which the request was sent
        // is broken.
        Header viaHeader = request.getRawHeader(Header.VIA);
       
        ViaImpl topVia = null;
        String branchId;
        if (viaHeader == null) {
            throw new SipRoutingException("No Via header on incoming request!");
        } else {
            topVia = new ViaImpl(viaHeader.getValue());
            branchId = LoadbalancerUtil.createBranchId(request, topVia);
        }

        ViaImpl via = new ViaImpl("SIP", SipTransports.TCP_PROT.name(),
                hostName, port);
        if (frontend) {
            // Set flag indicating that this Via was pushed by the front-end
            via.setParameter(CLBConstants.FE_LB_PARAM, null);
            // Save connection between client and front-end
            Connection connection;
            if (request.getRemote().getProtocol() != SipTransports.UDP_PROT) {
                // Encode information about the connection between the client and the
                // front-end
                connection = new Connection(request.getRemote().getProtocol(),
                    new Socket(request.getLocal().getAddress().getHostAddress(),
                        request.getLocal().getPort()),
                    new Socket(request.getRemote().getIP(),
                        request.getRemote().getPort()));
            } else {
                // In case of UDP, only save a marker
                connection = Connection.UDP;
            }
            via.setParameter(CLBConstants.CONNID_PARAM, connection.getEncodedValue());
        } else {
            // Fix for issue#1418
            // This via is pushed by the CLB in back-end instance.
            // Set flag indicating that this Via was pushed by the back-end
            via.setParameter(CLBConstants.BE_LB_PARAM, null);
        }
        via.setParameter(ViaImpl.PARAM_BRANCH, branchId);
        viaHeader.setValue(via.toString(), true);
    }

    private void encodeClientCert(SipServletMessageImpl message)
        throws SipRoutingException {
        // Encode a possible client certificate
        X509Certificate[] clientCerts = message.getCertificate();

        if (clientCerts != null) {
            for (X509Certificate clientCert : clientCerts) {
                try {
                    message.addHeader(Header.PROXY_AUTH_CERT_HEADER,
                        LoadbalancerUtil.encodeParameter(
                            clientCert.getEncoded(), true));
                } catch (CertificateEncodingException e) {
                    throw new SipRoutingException("Could not encode client certificate.",
                        e);
                }
            }
        }
    }

    private void encodeRemote(SipServletMessageImpl msg) {
        msg.setHeader(Header.PROXY_REMOTE_HEADER,
            msg.getRemote().getProtocol().name() + ":" +
            msg.getRemote().getIP() + ":" + msg.getRemote().getPort());
    }
   
    private void encodeBeKeyHeader(SipServletMessageImpl msg, String bekey) {
        msg.setHeader(Header.PROXY_BEKEY_HEADER, bekey);
    }
}
TOP

Related Classes of org.jvnet.glassfish.comms.clb.core.sip.SipLoadBalancerIncomingHandler

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.