All Downloads are FREE. Search and download functionalities are using the official Maven repository.

test.tck.msgflow.callflows.subsnotify.Forker Maven / Gradle / Ivy

There is a newer version: 1.3.0-91
Show newest version
/*
* Conditions Of Use
*
* This software was developed by employees of the National Institute of
* Standards and Technology (NIST), and others.
* This software is has been contributed to the public domain.
* As a result, a formal license is not needed to use the software.
*
* This software is provided "AS IS."
* NIST MAKES NO WARRANTY OF ANY KIND, EXPRESS, IMPLIED
* OR STATUTORY, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTY OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT
* AND DATA ACCURACY.  NIST does not warrant or make any representations
* regarding the use of the software or the results thereof, including but
* not limited to the correctness, accuracy, reliability or usefulness of
* the software.
*
*
*/
package test.tck.msgflow.callflows.subsnotify;

import javax.sip.*;
import javax.sip.address.*;
import javax.sip.header.*;
import javax.sip.message.*;

import org.apache.log4j.ConsoleAppender;
import org.apache.log4j.FileAppender;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.SimpleLayout;

import test.tck.msgflow.callflows.ProtocolObjects;

import java.text.ParseException;

import java.util.*;

/**
 * This implements a simple forking proxy to test proper handling of multiple
 * NOTIFYs. An initial SUBSCRIBE request (i.e. without to-tag) is forked two
 * times to the same destination. Each response should have a different to-tag;
 * this proxy only passes the first 2xx response through, and discards the
 * second
 *
 * NOTIFYs should go directly to the Contact announced in the SUBSCRIBE, hence
 * this proxy won't see them
 *
 *
 * @author Jeroen van Bemmel
 */

public class Forker implements SipListener {

    private static AddressFactory addressFactory;

    private static MessageFactory messageFactory;

    private static HeaderFactory headerFactory;

    private static SipStack sipStack;

    private SipProvider sipProvider;

    private Hashtable serverTransactionTable = new Hashtable();

    /**
     * Flag to test UAC behavior for non-RFC3261 proxies. In particular, they
     * dont set the 'lr' flag and perform strict routing, ie replace the request
     * URI with the topmost Route header
     */
    private static boolean nonRFC3261Proxy;

    private static Logger logger = Logger.getLogger(Forker.class);
    static {
        try {
            logger.addAppender(new FileAppender(new SimpleLayout(),
                    "logs/forkeroutputlog.txt"));
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    }

    /**
     * Adds a suitable Record-Route header to the given request or response
     *
     * @param r
     * @throws ParseException
     * @throws SipException
     * @throws
     */
    private void recordRoute(Message m, String uniqueId) throws ParseException,
            SipException {
        Address me = addressFactory.createAddress(" forking or forwarding");

            // Check if it is in-dialog or not
            ToHeader to = (ToHeader) request.getHeader(ToHeader.NAME);
            if (to.getTag() == null) {
                logger
                        .info("forker: got a dialog-creating Subscribe forking twice");

                if (st == null) {
                    st = ((SipProvider) re.getSource())
                            .getNewServerTransaction(request);
                }

                AbstractSubsnotifyTestCase.assertTrue(
                        "Proxy: dialog stateless operation expected", st
                                .getDialog() == null);
                // Subscriber added a Route to us; remove it could check its
                // 'id' here)
                Request newRequest = (Request) request.clone();
                newRequest.removeFirst(RouteHeader.NAME);

                doFork(newRequest, st, 5070);
                doFork(newRequest, st, 5071);
            } else {
                logger
                        .info("forker: got a mid-dialog Subscribe, forwarding statelessly...");

                // Forward it statelessly
                Request newRequest = (Request) request.clone();
                doForwardStateless(newRequest, st);
            }
        } catch (Exception ex) {
            ex.printStackTrace();
            logger.error("unexpected exception", ex);
            AbstractSubsnotifyTestCase.fail("unexpected exception");
        }
    }

    /**
     * Mapping of Via branch IDs to the corresponding ServerTransaction, used
     * for forwarding responses
     */
    private final Map CTtoST = new HashMap();

    private String transport;

    private int port;

    public Forker(ProtocolObjects protObjects) {
        addressFactory = protObjects.addressFactory;
        messageFactory = protObjects.messageFactory;
        headerFactory = protObjects.headerFactory;
        sipStack = protObjects.sipStack;
        transport = protObjects.transport;
    }

    private void doFork(Request orig, ServerTransaction st, int port)
            throws Exception {
        ViaHeader myVia = headerFactory.createViaHeader("127.0.0.1",
                sipProvider.getListeningPoint("udp").getPort(), "udp", null);
        Request forked = (Request) orig.clone();
        forked.addHeader(myVia);

        // Note: BIG Gotcha: Need to do this before creating the
        // ClientTransaction!
        if (nonRFC3261Proxy) {
            SipURI suri = addressFactory.createSipURI(null, "127.0.0.1");
            suri.setPort(port);
            forked.setRequestURI(suri);
        } else {
            RouteHeader route = headerFactory.createRouteHeader(addressFactory
                    .createAddress(""));
            ((SipURI) route.getAddress().getURI()).setPort(port);
            forked.addHeader(route);
        }

        // Add a Record-Route header, to test that separate dialog instances are
        // correctly created at the subscriber
        // This causes us to receive NOTIFYs too
        recordRoute(forked, Integer.toString(port));

        ClientTransaction ct = sipProvider.getNewClientTransaction(forked);
        AbstractSubsnotifyTestCase.assertTrue("Stateless operation -- should not create a dialog ", ct.getDialog() == null );
        CTtoST.put(ct, st);
        ct.sendRequest(); // gets sent to the outbound proxy == Notifier
    }

    private void doForwardStateless(Request orig, ServerTransaction st)
            throws ParseException, InvalidArgumentException, SipException {
        // To forward statelessly, we need to keep the stack from
        // creating a ST for us.
        // Internally a dialog is created for the SUBSCRIBE, unless
        // dialog support
        // XXX bug: if I use branch==null here, the stack assigns a random int
        // without magic cookie
        //
        // Latest wisdom from RFC3261 says to simply copy branch from current
        // top via
        // when forwarding statelessly
        //
        ViaHeader top = (ViaHeader) orig.getHeader(ViaHeader.NAME);
        ViaHeader myVia = headerFactory.createViaHeader("127.0.0.1", port,
                "udp", top.getBranch());
        orig.addFirst(myVia);

        if (nonRFC3261Proxy) {
            RouteHeader route = (RouteHeader) orig.getHeader("Route");
            if (route != null) {
                orig.removeFirst("Route");
                orig.setRequestURI(route.getAddress().getURI());
            }
        } else {
            orig.removeFirst(RouteHeader.NAME); // points at us
        }

        // To forward statelessly, we need to keep the stack from creating a ST
        // for us.
        // Internally a dialog is created for the SUBSCRIBE, unless dialog
        // support
        // is switched off (see initialization)
        if (st != null) {
            logger
                    .info("Would like to forward statelessly, but ST!=null! Problem...");
            logger.info("st == " + st);

        }
        sipProvider.sendRequest(orig);
    }

    public void processResponse(ResponseEvent responseReceivedEvent) {
        logger.info("Got a response");
        Response response = (Response) responseReceivedEvent.getResponse().clone();
        ClientTransaction ct = responseReceivedEvent.getClientTransaction();

        logger.info("Dialog = " + responseReceivedEvent.getDialog());

        logger.info("Response received with client transaction id " + ct + ": "
                + response.getStatusCode());

        if (ct == null) {
            logger.info("Assuming NOTIFY response, forwarding...");
            // NOTIFYs are forwarded without transaction, do the same for their
            // responses
            response.removeFirst(ViaHeader.NAME);
            try {
                String branchId = ((ViaHeader) response.getHeader(ViaHeader.NAME)).getBranch();
                ServerTransaction st = this.serverTransactionTable.get(branchId);
                if ( st != null) {
                    st.sendResponse(response);
                    this.serverTransactionTable.remove(branchId);
                } else {
                    sipProvider.sendResponse(response);
                }
            } catch (SipException e) {
                AbstractSubsnotifyTestCase.fail("Unexpected exception seen",e);
            } catch (InvalidArgumentException ex) {
                AbstractSubsnotifyTestCase.fail("Unexpected exception seen",ex);

            }
        } else {
            ServerTransaction st = (ServerTransaction) CTtoST.get(ct);
            if (st != null) {
                // only forward the first response
                synchronized (st) {
                    if (st.getState() == TransactionState.TRYING) {
                        response.removeFirst(ViaHeader.NAME);
                        try {
                            st.sendResponse(response);
                        } catch (SipException e) {
                            e.printStackTrace();
                        } catch (InvalidArgumentException e) {
                            e.printStackTrace();
                        }
                    } else {
                        logger.info("Discarding second response");
                    }
                    CTtoST.remove(ct);
                }
            } else {
                logger.info("No ST found");
            }
        }
    }

    public void processTimeout(javax.sip.TimeoutEvent timeoutEvent) {
        Transaction transaction;
        if (timeoutEvent.isServerTransaction()) {
            transaction = timeoutEvent.getServerTransaction();
        } else {
            transaction = timeoutEvent.getClientTransaction();
        }
        logger.info("state = " + transaction.getState());
        logger.info("Transaction Time out");
    }

    public SipProvider createProvider(int newPort) {

        try {

            this.port = newPort;

            ListeningPoint lp = sipStack.createListeningPoint("127.0.0.1",
                    port, transport);

            this.sipProvider = sipStack.createSipProvider(lp);
            this.sipProvider.setAutomaticDialogSupportEnabled(false);
            logger.info("sip provider " + sipProvider);

        } catch (Exception ex) {
            logger.error(ex.getMessage());
            ex.printStackTrace();
            sipProvider = null;
        }

        return sipProvider;

    }

    public void processIOException(IOExceptionEvent exceptionEvent) {

    }

    public void processTransactionTerminated(
            TransactionTerminatedEvent transactionTerminatedEvent) {
        // TODO Auto-generated method stub

    }

    public void processDialogTerminated(
            DialogTerminatedEvent dialogTerminatedEvent) {
        // TODO Auto-generated method stub

    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy