Package org.cafesip.jiplet.sip

Source Code of org.cafesip.jiplet.sip.RequestForwarding

/*
* RequestForwarding.java
*
* Created on April 16, 2003, 11:08 AM
*/

package org.cafesip.jiplet.sip;

import java.text.ParseException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.ListIterator;
import java.util.Vector;

import javax.sip.ClientTransaction;
import javax.sip.Dialog;
import javax.sip.DialogState;
import javax.sip.InvalidArgumentException;
import javax.sip.ListeningPoint;
import javax.sip.RequestEvent;
import javax.sip.ServerTransaction;
import javax.sip.SipException;
import javax.sip.SipProvider;
import javax.sip.Transaction;
import javax.sip.TransactionState;
import javax.sip.address.Address;
import javax.sip.address.AddressFactory;
import javax.sip.address.SipURI;
import javax.sip.address.URI;
import javax.sip.header.ContentTypeHeader;
import javax.sip.header.Header;
import javax.sip.header.HeaderFactory;
import javax.sip.header.MaxForwardsHeader;
import javax.sip.header.RecordRouteHeader;
import javax.sip.header.RouteHeader;
import javax.sip.header.ToHeader;
import javax.sip.header.ViaHeader;
import javax.sip.message.MessageFactory;
import javax.sip.message.Request;
import javax.sip.message.Response;

import org.cafesip.jiplet.Jiplet;
import org.cafesip.jiplet.JipletDialog;
import org.cafesip.jiplet.JipletException;
import org.cafesip.jiplet.JipletLogger;

/**
*
* @author Deruelle
*/
public class RequestForwarding
{
    private Jiplet jiplet;

    private RequestEvent requestEvent;

    private ServerTransaction serverTransaction;

    private boolean stateful;

    private boolean addRecordRoute;

    private SipCommunicator proxy;

    /** Creates a new instance of RequestForwarding */
    public RequestForwarding(Jiplet jiplet, SipCommunicator proxy,
            RequestEvent requestEvent,
            ServerTransaction serverTransaction, boolean stateful,
            boolean addRecordRoute)
    {
        this.jiplet = jiplet;
        this.proxy = proxy;
        this.requestEvent = requestEvent;
        this.serverTransaction = serverTransaction;
        this.stateful = stateful;
        this.addRecordRoute = addRecordRoute;
   }

    public void forwardRequest(ArrayList targetsURIList)
            throws InvalidArgumentException, JipletException, ParseException,
            SipException
    {
        /*
         * RFC 3261: 16.6. Request Forwarding For each target, the jiplet
         * forwards the request following these steps:
         *
         * 1. Make a copy of the received request
         *
         * 2. Update the Request-URI
         *
         * 3. Update the Max-Forwards header field
         *
         * 4. Optionally add a Record-route header field value
         *
         * 5. Optionally add additional header fields
         *
         * 6. Postprocess routing information
         *
         * 7. Determine the next-hop address, port, and transport
         *
         * 8. Add a Via header field value
         *
         * 9. Add a Content-Length header field if necessary
         *
         * 10. Forward the new request
         *
         * 11. Set timer C
         */

        HeaderFactory headerFactory = jiplet.getHeaderFactory();
        AddressFactory addressFactory = jiplet.getAddressFactory();
        // Get the parameters and the transport of the request URI
        URI requestURI = requestEvent.getRequest().getRequestURI();
        Iterator parametersNames = null;

        if (requestURI.isSipURI())
        {
            parametersNames = ((SipURI) requestURI).getParameterNames();

            // this is how the JAIN-SIP jiplet guys have done it but it seems
            // problematic. Why?
            // transport = ((SipURI) requestURI).getTransportParam();
        }

        for (int i = 0; i < targetsURIList.size(); i++)
        {
            URI targetURI = (URI) targetsURIList.get(i);

            // Copy the parameters and the transport in the new Request URI
            // of the cloned Request

            //           
            // 1. Make a clone of the received request
            //           
            //           

            Request clonedRequest = (Request) requestEvent.getRequest().clone();

            //           
            // 2. Update the Request-URI
            //         

            /*
             * The Request-URI in the copy's start line MUST be replaced with
             * the URI for this target. If the URI contains any parameters not
             * allowed in a Request-URI, they MUST be removed.
             *
             * This is the essence of a jiplet's role. This is the mechanism
             * through which a jiplet routes a request toward its destination.
             *
             * In some circumstances, the received Request-URI is placed into
             * the target set without being modified. For that target, the
             * replacement above is effectively a no-op.
             *
             */
            // All the targets URI are already canonicalized
            if (requestURI.isSipURI())
            {
                clonedRequest.setRequestURI(targetURI);
            }

            //           
            // 3. Max-Forwards
            //           
            /*
             * If the copy contains a Max-Forwards header field, the jiplet MUST
             * decrement its value by one (1). If the copy does not contain a
             * Max-Forwards header field, the jiplet MUST add one with a field
             * value, which SHOULD be 70.
             */
            MaxForwardsHeader mf = (MaxForwardsHeader) clonedRequest
                    .getHeader(MaxForwardsHeader.NAME);
            if (mf == null)
            {
                mf = headerFactory.createMaxForwardsHeader(70);
                clonedRequest.addHeader(mf);
            }
            else
            {
                int max = mf.getMaxForwards() - 1;
                mf.setMaxForwards(max);
            }

            //           
            // 4. Record-Route
            //           
            /*
             * The URI placed in the Record-Route header field value MUST be a
             * SIP or SIPS URI. This URI MUST contain an lr parameter (see
             * Section 19.1.1). This URI MAY be different for each destination
             * the request is forwarded to. The URI SHOULD NOT contain the
             * transport parameter.
             */

            ListeningPoint defaultLP = jiplet.getListeningPointDefault();
            SipProvider sipProvider = jiplet.getSipProvider(defaultLP);
           
            if (addRecordRoute)
            {
                // Only in stateful forwarding
                // We add our jiplet RecordRoute header to the top of the
                // list - use the following recommended mechanism to handle multi-homing

                /*
                 * If the URI placed in the Record-Route header field needs to
                 * be rewritten when it passes back through in a response, the
                 * URI MUST be distinct enough to locate at that time. (The
                 * request may spiral through this proxy, resulting in more than
                 * one Record-Route header field value being added). Item 8 of
                 * Section 16.7 recommends a mechanism to make the URI
                 * sufficiently distinct.
                 *
                 * The proxy MAY include parameters in the Record-Route header
                 * field value. These will be echoed in some responses to the
                 * request such as the 200 (OK) responses to INVITE. Such
                 * parameters may be useful for keeping state in the message
                 * rather than the proxy.
                 */

                if (stateful)
                {
                    SipURI sipURI = addressFactory.createSipURI(null, defaultLP
                            .getIPAddress());

                    sipURI.setPort(defaultLP.getPort());
                    sipURI.setTransportParam(defaultLP.getTransport());
                    sipURI.setLrParam();

                    // save the IP address and port the request came in on, for
                    // rewriting the record route header later in the response
                    // put it in this forwarded message itself - user part of RR
                    SipProvider sourceProvider = (SipProvider) requestEvent
                            .getSource();
                    ListeningPoint sourceLp = sourceProvider
                            .getListeningPoints()[0];
                    sipURI.setUser(sourceLp.getIPAddress() + '-' + sourceLp.getPort());
                   
                    Address address = addressFactory
                            .createAddress(null, sipURI);
                    RecordRouteHeader recordRouteHeader = headerFactory
                            .createRecordRouteHeader(address);

                    ListIterator recordRouteHeaders = clonedRequest
                            .getHeaders(RecordRouteHeader.NAME);
                    clonedRequest.removeHeader(RecordRouteHeader.NAME);
                    ArrayList v = new ArrayList();
                    v.add(recordRouteHeader);

                    // add the other record route headers.
                    while (recordRouteHeaders != null
                            && recordRouteHeaders.hasNext())
                    {
                        recordRouteHeader = (RecordRouteHeader) recordRouteHeaders
                                .next();
                        v.add(recordRouteHeader);
                    }

                    for (int j = 0; j < v.size(); j++)
                    {
                        recordRouteHeader = (RecordRouteHeader) v.get(j);
                        clonedRequest.addHeader(recordRouteHeader);
                    }
                }
            }

            //           
            // 5. Add Additional Header Fields
            //

            // No Additional headers to add...

            //           
            // 6. Postprocess routing information
            //           
            /*
             * If the copy contains a Route header field, the jiplet MUST
             * inspect the URI in its first value. If that URI does not contain
             * an lr parameter, the jiplet MUST modify the copy as follows: -
             * The jiplet MUST place the Request-URI into the Route header field
             * as the last value. - The jiplet MUST then place the first Route
             * header field value into the Request-URI and remove that value
             * from the Route header field.
             */

            // Strip first route if it is the jiplet UIR adn lr parameter
            ListIterator routes = clonedRequest.getHeaders(RouteHeader.NAME);
            if (routes != null && routes.hasNext())
            {

                RouteHeader routeHeader = (RouteHeader) routes.next();
                Address routeAddress = routeHeader.getAddress();
                URI routeURI = routeAddress.getURI();

                if (routeURI.isSipURI() && ((SipURI) routeURI).hasLrParam())
                {
                    String host = ((SipURI) routeURI).getHost();
                    int port = ((SipURI) routeURI).getPort();

                    if (jiplet.hasAddress(host, port))
                    {
                        routes.remove();
                    }
                }
            }

            //           
            // 7. Determine Next-Hop Address, Port, and Transport
            //
            /*
             * the jiplet applies the procedures listed in [4] as follows to
             * determine where to send the request. If the jiplet has
             * reformatted the request to send to a strict-routing element as
             * described in step 6 above, the jiplet MUST apply those procedures
             * to the Request-URI of the request. Otherwise, the jiplet MUST
             * apply the procedures to the first value in the Route header
             * field, if present, else the Request-URI. The procedures will
             * produce an ordered set of (address, port, transport) tuples.
             * Independently of which URI is being used as input to the
             * procedures of [4], if the Request-URI specifies a SIPS resource,
             * the jiplet MUST follow the procedures of [4] as if the input URI
             * were a SIPS URI.
             */

            // Determine Next-Hop Address, Port, and Transport will be done by
            // the stack.
            //           
            // 8. Add a Via header field value
            //
            /*
             * The jiplet MUST insert a Via header field value into the copy
             * before the existing Via header field values.
             */

            ViaHeader viaHeader = null;
            String transport = defaultLP.getTransport();

            if (clonedRequest.getMethod().equals(Request.CANCEL))
            {
                // Branch Id will be assigned by the stack.
                viaHeader = headerFactory.createViaHeader(defaultLP.getIPAddress(),
                        defaultLP.getPort(), transport != null ? transport : "", null);

                // Cancel is hop by hop so remove all other via headers.
                clonedRequest.removeHeader(ViaHeader.NAME);
            }
            else
            {
                String branch = ProxyUtilities.generateBranchId();
                viaHeader = headerFactory.createViaHeader(defaultLP.getIPAddress(),
                        defaultLP.getPort(), transport != null ? transport : "", branch);
            }

            if (viaHeader != null)
                clonedRequest.addHeader(viaHeader);

            /*
             * Proxies choosing to detect loops have an additional constraint in
             * the value they use for construction of the branch parameter. A
             * jiplet choosing to detect loops SHOULD create a branch parameter
             * separable into two parts by the implementation. The first part
             * MUST satisfy the constraints of Section 8.1.1.7 as described
             * above. The second is used to perform loop detection and
             * distinguish loops from spirals.
             */

            // Not yet implemented
            //           
            // 9. Add a Content-Length header field if necessary
            //           
            ContentTypeHeader contentTypeHeader = (ContentTypeHeader) clonedRequest
                    .getHeader(ContentTypeHeader.NAME);
            if (contentTypeHeader != null)
            {
                contentTypeHeader.removeParameter("msgr");
            }

            // if the proxy has been reset, do not send the to tag
            if (proxy.isReset() == true)
            {
                ToHeader to = (ToHeader) clonedRequest.getHeader(ToHeader.NAME);
                to.removeParameter("tag");
            }

            //
            // 10. Forward Request
            //
            if (stateful)
            {
                forwardRequestStatefully(sipProvider, clonedRequest, requestEvent.getRequest(),
                        serverTransaction);
            }
            else
            {
                forwardRequestStatelessly(sipProvider, clonedRequest, requestEvent.getRequest(),
                        serverTransaction);
            }

            //
            // 11. Set timer C
            //           
            // Not Implemented....
        }
    }

    /**
     * Forward the request statefully.
     *
     * @param sipProvider --
     *            sip provider to forward request.
     * @param clonedRequest --
     *            cloned request to forward.
     * @param originalRequest --
     *            incoming request
     * @param serverTransaction --
     *            server transaction used to fwd the request.
     * @throws ParseException
     * @throws SipException
     * @throws JipletException
     */

    private void forwardRequestStatefully(SipProvider sipProvider,
            Request clonedRequest, Request originalRequest,
            ServerTransaction serverTransaction) throws ParseException,
            SipException, JipletException
    {
        MessageFactory messageFactory = jiplet.getMessageFactory();

        /*
         * A stateful jiplet MUST create a new client transaction for this
         * request as described in Section 17.1 and instructs the transaction to
         * send the request using the address, port and transport determined in
         * step 7
         */

        // SERVER TRANSACTION CHECK
        if (serverTransaction == null
                && !clonedRequest.getMethod().equals(Request.MESSAGE))
        {
            // dont create a server transaction for MESSAGE -
            // just forward the request statefully through a new
            // client transaction.
            return;
        }

        if (originalRequest.getMethod().equals(Request.CANCEL))
        {
            // reply to the canceled request and maybe to the received CANCEL
            // and also send CANCELs to pending client transactions
            JipletDialog jdialog = jiplet.getDialog(serverTransaction
                    .getDialog(), true);
            Transaction firstTransaction = (Transaction) jdialog
                    .getAttribute("firstTransaction");
            if (firstTransaction == null)
            {
                throw new JipletException(
                        "ERROR, RequestForwarding Cancel, the first transaction"
                                + " for the  dialog is null");
            }

            if (firstTransaction instanceof ClientTransaction)
            {
                return;
            }

            ServerTransaction firstServerTransaction = (ServerTransaction) firstTransaction;

            try
            {
                // send 487 Request Terminated reply to canceled request
                Response response = messageFactory.createResponse(
                        Response.REQUEST_TERMINATED, firstServerTransaction
                                .getRequest());
                firstServerTransaction.sendResponse(response);
            }
            catch (InvalidArgumentException e)
            {
                // inapplicable - it only happens if the Response was created
                // by Dialog.createReliableProvisionalResponse(int) and the
                // application calls ServerTransaction.sendResponse() to send it
                JipletLogger
                        .error("Cancel response sending failed  - invalid send method used.");
            }

            // find the response context
            TransactionsMapping transactionsMapping = jdialog
                    .getTransactionsMapping();
            Vector clientTransactions = transactionsMapping
                    .getClientTransactions(firstServerTransaction);
            if (clientTransactions == null || clientTransactions.isEmpty())
            {
                // RFC states to send the CANCEL statelessly in this case
                forwardRequestStatelessly(sipProvider, clonedRequest,
                        originalRequest, serverTransaction);
                return;
            }

            try
            {
                // send OK to CANCEL
                Response response = messageFactory.createResponse(Response.OK,
                        originalRequest);
                serverTransaction.sendResponse(response);
            }
            catch (InvalidArgumentException e)
            {
                JipletLogger
                        .error("Cancel response sending failed  - invalid send method used.");
            }

            for (Enumeration e = clientTransactions.elements(); e
                    .hasMoreElements();)
            {
                ClientTransaction ct = (ClientTransaction) e.nextElement();

                // check if the client transaction can be canceled.
                if (ct.getState().equals(TransactionState.COMPLETED)
                        || ct.getState().equals(TransactionState.TERMINATED))
                {
                    continue;
                }

                try
                {
                    JipletDialog clientJipletDialog = (JipletDialog) ct
                            .getDialog().getApplicationData();
                    if (clientJipletDialog != null)
                    {
                        ClientTransaction client = clientJipletDialog
                                .getSipProvider().getNewClientTransaction(
                                        ct.createCancel());
                        client.sendRequest();
                    }
                    else
                    {
                        ClientTransaction client = sipProvider
                                .getNewClientTransaction(ct.createCancel());
                        client.sendRequest();
                    }
                }
                catch (Exception ex)
                {
                    jiplet
                            .error("Couldn't statefully forward CANCEL : Exception "
                                    + ex.getClass().getName()
                                    + " : "
                                    + ex.getMessage()
                                    + "\n"
                                    + JipletLogger.getStackTrace(ex));
                }
            }
           
            clientTransactions.clear();

            return;

        }

        // FROM TAG UPDATE FOR METHODS DIALOG CREATOR

        // DIALOG CHECK

        // Note that the jiplet server is actually implemented as a back
        // to back User Agent.
        Dialog dialog = null;
        if (serverTransaction != null)
        {
            dialog = serverTransaction.getDialog();
        }

        DialogState dialogState = null;
        if (dialog != null)
            dialogState = dialog.getState();
        if ((dialogState == null) || (proxy.isReset() == true))
        {
            ClientTransaction clientTransaction = sipProvider
                    .getNewClientTransaction(clonedRequest);
            clientTransaction.sendRequest();

            // register for response
            jiplet.registerForResponse(clonedRequest, 60000L);

            if (dialog != null)
            {
                JipletDialog jdata = jiplet.getDialog(serverTransaction
                        .getDialog(), true);
                TransactionsMapping transactionsMapping = jdata
                        .getTransactionsMapping();
                transactionsMapping.addMapping(serverTransaction,
                        clientTransaction);

                if (clientTransaction.getDialog() != null)
                {
                    JipletDialog clientDialog = jiplet.getDialog(
                            clientTransaction.getDialog(), true);
                    if (clientDialog.getSipProvider() == null)
                    {
                        clientDialog.setSipProvider(sipProvider);
                    }
                }
            }

            return;
        }

        forwardRequestThroughDialog(sipProvider, serverTransaction,
                clonedRequest, dialog);
        return;
    }

    /**
     * Forward a request statefully through a dialog.
     *
     * @throws SipException
     * @throws ParseException
     */
    private void forwardRequestThroughDialog(SipProvider sipProvider,
            ServerTransaction serverTransaction, Request clonedRequest,
            Dialog dialog) throws JipletException, SipException, ParseException
    {
        JipletDialog jdata = jiplet.getDialog(serverTransaction.getDialog(),
                true);
        TransactionsMapping transactionsMapping = jdata
                .getTransactionsMapping();

        Dialog peerDialog = transactionsMapping
                .getPeerDialog(serverTransaction);

        if (peerDialog == null)
        {
            ClientTransaction ct = sipProvider
                    .getNewClientTransaction(clonedRequest);
            ct.sendRequest();
            transactionsMapping.addMapping(serverTransaction, ct);
           
            if (ct.getDialog() != null)
            {
                JipletDialog clientDialog = jiplet.getDialog(
                        ct.getDialog(), true);
                if (clientDialog.getSipProvider() == null)
                {
                    clientDialog.setSipProvider(sipProvider);
                }
            }
        }
        else if (clonedRequest.getMethod().equals(Request.ACK))
        {
            peerDialog.sendAck(clonedRequest);
        }
        else
        {

            Request dialogRequest = peerDialog.createRequest(clonedRequest
                    .getMethod());
            Object content = clonedRequest.getContent();
            if (content != null)
            {
                ContentTypeHeader contentTypeHeader = (ContentTypeHeader) clonedRequest
                        .getHeader(ContentTypeHeader.NAME);
                if (contentTypeHeader != null)
                    dialogRequest.setContent(content, contentTypeHeader);
            }

            // Copy all the headers from the original request to the
            // dialog created request:
            ListIterator l = clonedRequest.getHeaderNames();
            while (l.hasNext())
            {
                String name = (String) l.next();
                Header header = dialogRequest.getHeader(name);
                if (header == null)
                {
                    ListIterator li = clonedRequest.getHeaders(name);
                    if (li != null)
                    {
                        while (li.hasNext())
                        {
                            Header h = (Header) li.next();
                            dialogRequest.addHeader(h);
                        }
                    }
                }
                else
                {
                    if (header instanceof ViaHeader)
                    {
                        ListIterator li = clonedRequest.getHeaders(name);
                        if (li != null)
                        {
                            dialogRequest.removeHeader(name);
                            Vector v = new Vector();
                            while (li.hasNext())
                            {
                                Header h = (Header) li.next();
                                v.addElement(h);
                            }
                            for (int k = (v.size() - 1); k >= 0; k--)
                            {
                                Header h = (Header) v.elementAt(k);
                                dialogRequest.addHeader(h);
                            }
                        }
                    }
                }
            }

            JipletDialog clientDialog = jiplet.getDialog(peerDialog, false);
            ClientTransaction clientTransaction = clientDialog.getSipProvider()
                    .getNewClientTransaction(dialogRequest);
            peerDialog.sendRequest(clientTransaction);
            transactionsMapping
                    .addMapping(serverTransaction, clientTransaction);

            // register for response
            jiplet.registerForResponse(clonedRequest, 60000L);
        }
    }

    private void forwardRequestStatelessly(SipProvider sipProvider,
            Request clonedRequest, Request originalRequest,
            ServerTransaction serverTransaction) throws SipException
    {
        // We forward statelessly:
        // It means the Request does not create dialogs...
        sipProvider.sendRequest(clonedRequest);

        // register for response
        jiplet.registerForResponse(clonedRequest, 60000L);
    }

    /**
     * @return Returns the addRecordRoute.
     */
    public boolean isAddRecordRoute()
    {
        return addRecordRoute;
    }

    /**
     * @param addRecordRoute
     *            The addRecordRoute to set.
     */
    public void setAddRecordRoute(boolean addRecordRoute)
    {
        this.addRecordRoute = addRecordRoute;
    }

    /**
     * @return Returns the jiplet.
     */
    public Jiplet getJiplet()
    {
        return jiplet;
    }

    /**
     * @param jiplet
     *            The jiplet to set.
     */
    public void setJiplet(Jiplet proxy)
    {
        this.jiplet = proxy;
    }

    /**
     * @return Returns the requestEvent.
     */
    public RequestEvent getRequestEvent()
    {
        return requestEvent;
    }

    /**
     * @param requestEvent
     *            The requestEvent to set.
     */
    public void setRequestEvent(RequestEvent requestEvent)
    {
        this.requestEvent = requestEvent;
    }

    /**
     * @return Returns the serverTransaction.
     */
    public ServerTransaction getServerTransaction()
    {
        return serverTransaction;
    }

    /**
     * @param serverTransaction
     *            The serverTransaction to set.
     */
    public void setServerTransaction(ServerTransaction serverTransaction)
    {
        this.serverTransaction = serverTransaction;
    }

    /**
     * @return Returns the stackIPAddress.
     */
    public String getStackIPAddress()
    {
        return jiplet.getListeningPointDefault().getIPAddress();
    }

    /**
     * @return Returns the statefulForwarding.
     */
    public boolean isStateful()
    {
        return stateful;
    }

    /**
     * @param statefulForwarding
     *            The statefulForwarding to set.
     */
    public void setStateful(boolean statefulForwarding)
    {
        this.stateful = statefulForwarding;
    }
}
TOP

Related Classes of org.cafesip.jiplet.sip.RequestForwarding

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.