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

test.tck.msgflow.InviteServerTransactionsStateMachineTest 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;

import junit.framework.*;

import javax.sip.*;
import javax.sip.message.*;
import javax.sip.header.*;
import java.util.*;
import java.text.*;
import test.tck.*;

/**
 *
 *  * The test tries to verify that Invite Server Transactions correctly change
 * states as specified by the rfc3261. The Reference Implementation is used
 * to send requests and a Tested Implementation ServerTransaction's states are
 * queried and compared to those in the state machine described in
 * section 17.2.1 of rfc3261
 *
 *
 *
 *                              |INVITE
 *                              |pass INV to TU
 *            INVITE             V send 100 if TU won't in 200ms
 *            send response+-----------+
 *                +--------|           |--------+101-199 from TU
 *                |        | Proceeding|        |send response
 *                +------->|           |<-------+
 *                         |           |          Transport Err.
 *                         |           |          Inform TU
 *                         |           |--------------->+
 *                         +-----------+                |
 *            300-699 from TU |     |2xx from TU        |
 *            send response   |     |send response      |
 *                            |     +------------------>+
 *                            |                         |
 *            INVITE          V          Timer G fires  |
 *            send response+-----------+ send response  |
 *                +--------|           |--------+       |
 *                |        | Completed |        |       |
 *                +------->|           |<-------+       |
 *                         +-----------+                |
 *                            |     |                   |
 *                        ACK |     |                   |
 *                        -   |     +------------------>+
 *                            |        Timer H fires    |
 *                            V        or Transport Err.|
 *                         +-----------+  Inform TU     |
 *                         |           |                |
 *                         | Confirmed |                |
 *                         |           |                |
 *                         +-----------+                |
 *                               |                      |
 *                               |Timer I fires         |
 *                               |-                     |
 *                               |                      |
 *                               V                      |
 *                         +-----------+                |
 *                         |           |                |
 *                         | Terminated|<---------------+
 *                         |           |
 *                         +-----------+
 *
 *              Figure 7: INVITE server transaction
 *
 *
* * @author Emil Ivov * Network Research Team, Louis Pasteur University, Strasbourg, France. * This code is in the public domain. * @version 1.0 */ public class InviteServerTransactionsStateMachineTest extends MessageFlowHarness { public InviteServerTransactionsStateMachineTest(String name) { super(name); } //==================== tests ============================== /** * Tries to steer a TI server transaction through the following scenario * Proceeding-->Completed-->Confirmed-->Terminated. Apart from state * transitions, we also test, retransmissions and proper hiding/passing * of messages to the TU. */ public void testProceedingCompletedConfirmedScenario() { try { Request invite = createRiInviteRequest(null, null, null); SipEventCollector responseCollector = new SipEventCollector(); //Before Sending the request we should first register a listener with the //RI that would catch the TRYING response try { responseCollector.collectResponseEvent(riSipProvider); } catch (TooManyListenersException ex) { throw new TckInternalError( "Failed to register a SipListener with an RI SipProvider", ex); } //Send the initial request try { eventCollector.collectRequestEvent(tiSipProvider); riSipProvider.sendRequest(invite); } catch (SipException ex) { throw new TckInternalError( "A SipExceptionOccurred while trying to send request!", ex); } catch (TooManyListenersException ex) { throw new TiUnexpectedError( "Failed to register a SipListener with a TI SipProvider", ex); } waitForMessage(); RequestEvent inviteReceivedEvent = eventCollector.extractCollectedRequestEvent(); if (inviteReceivedEvent == null || inviteReceivedEvent.getRequest() == null) throw new TiUnexpectedError("The initial invite request was not received by the TI!"); //Let's create the transaction ServerTransaction tran = null; try { tran = tiSipProvider.getNewServerTransaction( inviteReceivedEvent.getRequest()); } catch (Exception ex) { ex.printStackTrace(); fail( ex.getClass().getName() + "was thrown while trying to " + "create the server transaction"); } assertNotNull( "tiSipProvider.getNewServerTransaction() returned null", tran); //Check whether a TRYING response has been sent. //wait for the trying response waitForMessage(); // At this point state must be PROCEEDING assertEquals(TransactionState.PROCEEDING, tran.getState()); ResponseEvent responseEvent = responseCollector.extractCollectedResponseEvent(); assertNotNull( "No TRYING response has been sent by the TI upon reception " + "of an INVITE request", responseEvent); assertTrue( "A response different from 100 was sent by the TI upon " + "reception of INVITE", Response.TRYING == responseEvent.getResponse().getStatusCode()); //Resend the invite and see that a TRYING response is resent try { //listen for the Trying response responseCollector.collectResponseEvent(riSipProvider); } catch (TooManyListenersException ex) { throw new TckInternalError( "Failed to register a SipListener with an RI SipProvider", ex); } try { eventCollector.collectRequestEvent(tiSipProvider); riSipProvider.sendRequest(invite); } catch (SipException ex) { throw new TckInternalError( "A SipExceptionOccurred while trying to send request!", ex); } catch (TooManyListenersException ex) { throw new TiUnexpectedError( "Failed to register a SipListener with a TI SipProvider", ex); } //Wait for the INVITE waitForMessage(); inviteReceivedEvent = eventCollector.extractCollectedRequestEvent(); assertNull( "Retransmitted INVITEs should not be passed to the TU", inviteReceivedEvent); //Wait for a retransmitted TRYING response waitForMessage(); //Verify whether there was a TRYING response responseEvent = responseCollector.extractCollectedResponseEvent(); assertNotNull( "No TRYING response has been sent by the TI upon reception " + "of an INVITE request", responseEvent); assertTrue( "A response different from 100 was sent by the TI upon " + "reception of INVITE", Response.TRYING == responseEvent.getResponse().getStatusCode()); Response ringing = null; try { ringing = tiMessageFactory.createResponse( Response.RINGING, tran.getRequest()); ((ToHeader) ringing.getHeader(ToHeader.NAME)).setTag( Integer.toString(hashCode())); addStatus(tran.getRequest(), ringing); // BUG report from Ben Evans: // set contact header on dialog-creating response ringing.setHeader(createTiContact()); } catch (ParseException ex) { throw new TiUnexpectedError( "A ParseException was thrown while trying to create a ringing " + "response using TI", ex); } //Create & send RINGING. See that it is properly sent //and that tran state doesn't change try { //listen for the RINGING response responseCollector.collectResponseEvent(riSipProvider); } catch (TooManyListenersException ex) { throw new TckInternalError( "Failed to register a SipListener with an RI SipProvider", ex); } try { tran.sendResponse(ringing); } catch (SipException ex) { ex.printStackTrace(); fail("The TI failed to send a RINGING response"); } //The Transaction should still be PROCEEDING assertEquals( "The Transaction did not remain PROCEEDING after transmitting a RINGING response", TransactionState.PROCEEDING, tran.getState()); //Check whether the RINGING is received by the RI. waitForMessage(); responseEvent = responseCollector.extractCollectedResponseEvent(); assertNotNull( "The RINGING response was not received by the RI", responseEvent); assertTrue( "A response different from RINGING was sent by the TI", Response.RINGING == responseEvent.getResponse().getStatusCode()); //Resend the INVITE, see that it is hidden from the TU and see that //the _RINGING_ response is resent (and not the TRYING) try { //listen for the Trying response responseCollector.collectResponseEvent(riSipProvider); } catch (TooManyListenersException ex) { throw new TckInternalError( "Failed to register a SipListener with an RI SipProvider", ex); } try { eventCollector.collectRequestEvent(tiSipProvider); riSipProvider.sendRequest(invite); } catch (SipException ex) { throw new TckInternalError( "A SipExceptionOccurred while trying to send request!", ex); } catch (TooManyListenersException ex) { throw new TiUnexpectedError( "Failed to register a SipListener with a TI SipProvider", ex); } //Wait for the INVITE waitForMessage(); inviteReceivedEvent = eventCollector.extractCollectedRequestEvent(); assertNull( "Retransmitted INVITEs should not be passed to the TU", inviteReceivedEvent); //Wait for a retransmitted RINGING response waitForMessage(); //Verify whether there was a RINGING response responseEvent = responseCollector.extractCollectedResponseEvent(); assertNotNull( "No RINGING response has been sent by the TI upon reception " + "of an INVITE request", responseEvent); assertTrue( "A response different from RINGING was sent by the TI upon " + "reception of a retransmitted invite INVITE", Response.RINGING == responseEvent.getResponse().getStatusCode()); //We should still be proceeding assertEquals( "The server transaction left the PROCEEDING state.", TransactionState.PROCEEDING, tran.getState()); //Send 300 - 699 from TU and see the tran goes COMPLETED Response busy = null; try { busy = tiMessageFactory.createResponse( Response.BUSY_HERE, tran.getRequest()); addStatus(tran.getRequest(), busy); } catch (ParseException ex) { throw new TiUnexpectedError( "A ParseException was thrown while trying to create a busy_here " + "response using TI", ex); } try { //listen for the BUSY_HERE response responseCollector.collectResponseEvent(riSipProvider); } catch (TooManyListenersException ex) { throw new TckInternalError( "Failed to register a SipListener with an RI SipProvider", ex); } try { tran.sendResponse(busy); } catch (SipException ex) { ex.printStackTrace(); fail("The TI failed to send a BUSY_HERE response"); } //The Transaction should now be COMPLETED assertEquals( "The Transaction did not remain COMPLETED after transmitting a BUSY_HERE response", TransactionState.COMPLETED, tran.getState()); //Check whether the BUSY_HERE is received by the RI. waitForMessage(); responseEvent = responseCollector.extractCollectedResponseEvent(); assertNotNull( "The BUSY_HERE response was not received by the RI", responseEvent); assertTrue( "A response different from BUSY_HERE was sent by the TI", Response.BUSY_HERE == responseEvent.getResponse().getStatusCode()); //Resend the initial from INVITE from the RI and see that TI //resends the 300 - 699 (see that tran state remains COMPLETED) try { //listen for the Trying response responseCollector.collectResponseEvent(riSipProvider); } catch (TooManyListenersException ex) { throw new TckInternalError( "Failed to register a SipListener with an RI SipProvider", ex); } try { eventCollector.collectRequestEvent(tiSipProvider); riSipProvider.sendRequest(invite); } catch (SipException ex) { throw new TckInternalError( "A SipExceptionOccurred while trying to send request!", ex); } catch (TooManyListenersException ex) { throw new TiUnexpectedError( "Failed to register a SipListener with a TI SipProvider", ex); } //Wait for the INVITE waitForMessage(); inviteReceivedEvent = eventCollector.extractCollectedRequestEvent(); assertNull( "Retransmitted INVITEs should not be passed to the TU", inviteReceivedEvent); //Wait for a retransmitted BUSY_HERE response waitForMessage(); //Verify whether there was a BUSY_HERE response responseEvent = responseCollector.extractCollectedResponseEvent(); assertNotNull( "No BUSY_HERE response has been sent by the TI upon reception " + "of a retransmitted INVITE request", responseEvent); assertTrue( "A response different from BUSY_HERE was sent by the TI upon " + "reception of a retransmitted invite INVITE", Response.BUSY_HERE == responseEvent.getResponse().getStatusCode()); //We should still be COMPLETED assertEquals( "The server transaction left the COMPLETED state.", TransactionState.COMPLETED, tran.getState()); //Send an ack from the RI and see that the tran goes CONFIRMED //and that response retransmissions cease Request ack = (Request) invite.clone(); try { eventCollector.collectRequestEvent(tiSipProvider); ack.setMethod(Request.ACK); // JvB: to tag must match response! String toTag = ((ToHeader) responseEvent.getResponse().getHeader("to")).getTag(); if (toTag!=null) { ((ToHeader) ack.getHeader("to")).setTag( toTag ); } riSipProvider.sendRequest(ack); } catch (TooManyListenersException ex) { throw new TiUnexpectedError( "Failed to register a SipListener with the TI provider", ex); } catch (Exception ex) { throw new TckInternalError( "Failed to create an ack request", ex); } waitForMessage(); RequestEvent ackEvent = eventCollector.extractCollectedRequestEvent(); assertNull( "ACKs in ServerInviteTransactions shouldn't be passed to the TU.", ackEvent); assertEquals( "The ServerTransaction did not pas into the confirmed state" + "after receiving an ACK.", TransactionState.CONFIRMED, tran.getState()); } catch (Throwable exc) { exc.printStackTrace(); fail(exc.getClass().getName() + ": " + exc.getMessage()); } assertTrue(new Exception().getStackTrace()[0].toString(), true); } /** * JvB: tests CANCEL for an INVITE ST */ public void testCanceledInvite() { try { Request invite = createRiInviteRequest(null, null, null); SipEventCollector responseCollector = new SipEventCollector(); //Before Sending the request we should first register a listener with the //RI that would catch the TRYING response try { responseCollector.collectResponseEvent(riSipProvider); } catch (TooManyListenersException ex) { throw new TckInternalError( "Failed to register a SipListener with an RI SipProvider", ex); } //Send the initial request (JvB: using a CT) ClientTransaction riInviteCt; try { eventCollector.collectRequestEvent(tiSipProvider); riInviteCt = riSipProvider.getNewClientTransaction( invite ); riInviteCt.sendRequest(); } catch (SipException ex) { throw new TckInternalError( "A SipExceptionOccurred while trying to send request!", ex); } catch (TooManyListenersException ex) { throw new TiUnexpectedError( "Failed to register a SipListener with a TI SipProvider", ex); } waitForMessage(); RequestEvent inviteReceivedEvent = eventCollector.extractCollectedRequestEvent(); if (inviteReceivedEvent == null || inviteReceivedEvent.getRequest() == null) throw new TiUnexpectedError("The initial invite request was not received by the TI!"); //Let's create the transaction ServerTransaction tran = null; try { tran = tiSipProvider.getNewServerTransaction( inviteReceivedEvent.getRequest()); } catch (Exception ex) { ex.printStackTrace(); fail( ex.getClass().getName() + "was thrown while trying to " + "create the server transaction"); } assertNotNull( "tiSipProvider.getNewServerTransaction() returned null", tran); //Check whether a TRYING response has been sent. //wait for the trying response waitForMessage(); // At this point state must be PROCEEDING assertEquals(TransactionState.PROCEEDING, tran.getState()); ResponseEvent responseEvent = responseCollector.extractCollectedResponseEvent(); assertNotNull( "No TRYING response has been sent by the TI upon reception " + "of an INVITE request", responseEvent); assertTrue( "A response different from 100 was sent by the TI upon " + "reception of INVITE", Response.TRYING == responseEvent.getResponse().getStatusCode()); //Create & send RINGING. See that it is properly sent //and that tran state doesn't change Response ringing = null; try { ringing = tiMessageFactory.createResponse( Response.RINGING, tran.getRequest()); ((ToHeader) ringing.getHeader(ToHeader.NAME)).setTag( Integer.toString(hashCode())); addStatus(tran.getRequest(), ringing); // BUG report from Ben Evans: // set contact header on dialog-creating response ringing.setHeader(createTiContact()); } catch (ParseException ex) { throw new TiUnexpectedError( "A ParseException was thrown while trying to create a ringing " + "response using TI", ex); } try { //listen for the RINGING response responseCollector.collectResponseEvent(riSipProvider); } catch (TooManyListenersException ex) { throw new TckInternalError( "Failed to register a SipListener with an RI SipProvider", ex); } try { tran.sendResponse(ringing); } catch (SipException ex) { ex.printStackTrace(); fail("The TI failed to send a RINGING response"); } //The Transaction should still be PROCEEDING assertEquals( "The Transaction did not remain PROCEEDING after transmitting a RINGING response", TransactionState.PROCEEDING, tran.getState()); //Check whether the RINGING is received by the RI. waitForMessage(); responseEvent = responseCollector.extractCollectedResponseEvent(); assertNotNull( "The RINGING response was not received by the RI", responseEvent); assertTrue( "A response different from RINGING was sent by the TI", Response.RINGING == responseEvent.getResponse().getStatusCode()); // JvB: *new* : send CANCEL here Request riCancel = riInviteCt.createCancel(); // Send the CANCEL request try { eventCollector.collectRequestEvent(tiSipProvider); riSipProvider.sendRequest(riCancel); } catch (SipException ex) { throw new TckInternalError( "A SipExceptionOccurred while trying to send CANCEL request!", ex); } catch (TooManyListenersException ex) { throw new TiUnexpectedError( "Failed to register a SipListener with a TI SipProvider", ex); } waitForMessage(); RequestEvent cancelReceivedEvent = eventCollector.extractCollectedRequestEvent(); if (cancelReceivedEvent == null || cancelReceivedEvent.getRequest() == null) throw new TiUnexpectedError("The CANCEL request was not received by the TI!"); // Check that a ST was matched, and that it is equal to the INVITE ST branch. assertEquals( tran.getBranchId(), cancelReceivedEvent.getServerTransaction().getBranchId() ); // Send an OK to the CANCEL Response cancelOK; try { cancelOK = tiMessageFactory.createResponse( Response.OK, cancelReceivedEvent.getRequest() ); addStatus( cancelReceivedEvent.getRequest(), cancelOK ); } catch (ParseException ex) { throw new TiUnexpectedError( "A ParseException was thrown while trying to create a OK " + "response using TI", ex); } try { //listen for the OK response responseCollector.collectResponseEvent(riSipProvider); } catch (TooManyListenersException ex) { throw new TckInternalError( "Failed to register a SipListener with an RI SipProvider", ex); } try { cancelReceivedEvent.getServerTransaction().sendResponse(cancelOK); } catch (SipException ex) { ex.printStackTrace(); fail("The TI failed to send a CANCEL OK response"); } // Check whether the OK is received by the RI. waitForMessage(); responseEvent = responseCollector.extractCollectedResponseEvent(); assertNotNull( "The CANCEL OK response was not received by the RI", responseEvent); assertTrue( "A response different from OK was sent by the TI", Response.OK == responseEvent.getResponse().getStatusCode()); //Send 487 from TU and see the tran goes COMPLETED Response reqTerminated = null; try { reqTerminated = tiMessageFactory.createResponse( Response.REQUEST_TERMINATED, tran.getRequest()); addStatus(tran.getRequest(), reqTerminated); } catch (ParseException ex) { throw new TiUnexpectedError( "A ParseException was thrown while trying to create a req_terminated " + "response using TI", ex); } try { //listen for the BUSY_HERE response responseCollector.collectResponseEvent(riSipProvider); } catch (TooManyListenersException ex) { throw new TckInternalError( "Failed to register a SipListener with an RI SipProvider", ex); } try { tran.sendResponse( reqTerminated ); } catch (SipException ex) { ex.printStackTrace(); fail("The TI failed to send a REQUEST_TERMINATED response"); } //The Transaction should now be COMPLETED or CONFIRMED. Could be CONFIRMED if it gets an ACK very fast. assertTrue( "The Transaction did not remain COMPLETED after transmitting a REQUEST_TERMINATED response", tran.getState() == TransactionState.COMPLETED || tran.getState() == TransactionState.CONFIRMED); //Check whether the BUSY_HERE is received by the RI. waitForMessage(); responseEvent = responseCollector.extractCollectedResponseEvent(); assertNotNull( "The REQUEST_TERMINATED response was not received by the RI", responseEvent); assertEquals( "A response different from REQUEST_TERMINATED was sent by the TI", Response.REQUEST_TERMINATED, responseEvent.getResponse().getStatusCode() ); // RI CT should have sent ACK, tran should be in CONFIRMED // and response retransmissions should cease assertEquals( "The ServerTransaction did not pas into the confirmed state" + "after receiving an ACK.", TransactionState.CONFIRMED, tran.getState()); } catch (Throwable exc) { exc.printStackTrace(); fail(exc.getClass().getName() + ": " + exc.getMessage()); } assertTrue(new Exception().getStackTrace()[0].toString(), true); } /** * JvB: tests CANCEL for an INVITE ST, from an non-RFC3261 client * which uses a Via branch not starting with the magic cookie */ public void testNonRFC3261CanceledInvite() { try { Request invite = createRiInviteRequest(null, null, null); // JvB: Pretend it is sent by an older client ViaHeader topVia = (ViaHeader) invite.getHeader( "Via" ); topVia.setBranch( "non-rfc3261" ); SipEventCollector responseCollector = new SipEventCollector(); //Before Sending the request we should first register a listener with the //RI that would catch the TRYING response try { responseCollector.collectResponseEvent(riSipProvider); } catch (TooManyListenersException ex) { throw new TckInternalError( "Failed to register a SipListener with an RI SipProvider", ex); } //Send the initial request (JvB: using a CT) ClientTransaction riInviteCt; try { eventCollector.collectRequestEvent(tiSipProvider); riInviteCt = riSipProvider.getNewClientTransaction( invite ); riInviteCt.sendRequest(); } catch (SipException ex) { throw new TckInternalError( "A SipExceptionOccurred while trying to send request!", ex); } catch (TooManyListenersException ex) { throw new TiUnexpectedError( "Failed to register a SipListener with a TI SipProvider", ex); } waitForMessage(); RequestEvent inviteReceivedEvent = eventCollector.extractCollectedRequestEvent(); if (inviteReceivedEvent == null || inviteReceivedEvent.getRequest() == null) throw new TiUnexpectedError("The initial invite request was not received by the TI!"); //Let's create the transaction ServerTransaction tran = null; try { tran = tiSipProvider.getNewServerTransaction( inviteReceivedEvent.getRequest()); } catch (Exception ex) { ex.printStackTrace(); fail( ex.getClass().getName() + "was thrown while trying to " + "create the server transaction"); } assertNotNull( "tiSipProvider.getNewServerTransaction() returned null", tran); //Check whether a TRYING response has been sent. //wait for the trying response waitForMessage(); // At this point state must be PROCEEDING assertEquals(TransactionState.PROCEEDING, tran.getState()); ResponseEvent responseEvent = responseCollector.extractCollectedResponseEvent(); assertNotNull( "No TRYING response has been sent by the TI upon reception " + "of an INVITE request", responseEvent); assertTrue( "A response different from 100 was sent by the TI upon " + "reception of INVITE", Response.TRYING == responseEvent.getResponse().getStatusCode()); //Create & send RINGING. See that it is properly sent //and that tran state doesn't change Response ringing = null; try { ringing = tiMessageFactory.createResponse( Response.RINGING, tran.getRequest()); ((ToHeader) ringing.getHeader(ToHeader.NAME)).setTag( Integer.toString(hashCode())); addStatus(tran.getRequest(), ringing); // BUG report from Ben Evans: // set contact header on dialog-creating response ringing.setHeader(createTiContact()); } catch (ParseException ex) { throw new TiUnexpectedError( "A ParseException was thrown while trying to create a ringing " + "response using TI", ex); } try { //listen for the RINGING response responseCollector.collectResponseEvent(riSipProvider); } catch (TooManyListenersException ex) { throw new TckInternalError( "Failed to register a SipListener with an RI SipProvider", ex); } try { tran.sendResponse(ringing); } catch (SipException ex) { ex.printStackTrace(); fail("The TI failed to send a RINGING response"); } //The Transaction should still be PROCEEDING assertEquals( "The Transaction did not remain PROCEEDING after transmitting a RINGING response", TransactionState.PROCEEDING, tran.getState()); //Check whether the RINGING is received by the RI. waitForMessage(); responseEvent = responseCollector.extractCollectedResponseEvent(); assertNotNull( "The RINGING response was not received by the RI", responseEvent); assertTrue( "A response different from RINGING was sent by the TI", Response.RINGING == responseEvent.getResponse().getStatusCode()); // JvB: *new* : send CANCEL here Request riCancel = riInviteCt.createCancel(); // Send the CANCEL request try { eventCollector.collectRequestEvent(tiSipProvider); riSipProvider.sendRequest(riCancel); } catch (SipException ex) { throw new TckInternalError( "A SipExceptionOccurred while trying to send CANCEL request!", ex); } catch (TooManyListenersException ex) { throw new TiUnexpectedError( "Failed to register a SipListener with a TI SipProvider", ex); } waitForMessage(); RequestEvent cancelReceivedEvent = eventCollector.extractCollectedRequestEvent(); if (cancelReceivedEvent == null || cancelReceivedEvent.getRequest() == null) throw new TiUnexpectedError("The CANCEL request was not received by the TI!"); // Check that a ST was matched, and that it is equal to the INVITE ST assertEquals( tran.getBranchId(), cancelReceivedEvent.getServerTransaction().getBranchId() ); // Send an OK to the CANCEL Response cancelOK; try { cancelOK = tiMessageFactory.createResponse( Response.OK, cancelReceivedEvent.getRequest() ); addStatus( cancelReceivedEvent.getRequest(), cancelOK ); } catch (ParseException ex) { throw new TiUnexpectedError( "A ParseException was thrown while trying to create a OK " + "response using TI", ex); } try { //listen for the OK response responseCollector.collectResponseEvent(riSipProvider); } catch (TooManyListenersException ex) { throw new TckInternalError( "Failed to register a SipListener with an RI SipProvider", ex); } try { // send it statelessly cancelReceivedEvent.getServerTransaction().sendResponse(cancelOK); } catch (SipException ex) { ex.printStackTrace(); fail("The TI failed to send a CANCEL OK response"); } // Check whether the OK is received by the RI. waitForMessage(); responseEvent = responseCollector.extractCollectedResponseEvent(); assertNotNull( "The CANCEL OK response was not received by the RI", responseEvent); assertTrue( "A response different from OK was sent by the TI", Response.OK == responseEvent.getResponse().getStatusCode()); //Send 487 from TU and see the tran goes COMPLETED Response reqTerminated = null; try { reqTerminated = tiMessageFactory.createResponse( Response.REQUEST_TERMINATED, tran.getRequest()); addStatus(tran.getRequest(), reqTerminated); } catch (ParseException ex) { throw new TiUnexpectedError( "A ParseException was thrown while trying to create a req_terminated " + "response using TI", ex); } try { //listen for the BUSY_HERE response responseCollector.collectResponseEvent(riSipProvider); } catch (TooManyListenersException ex) { throw new TckInternalError( "Failed to register a SipListener with an RI SipProvider", ex); } try { tran.sendResponse( reqTerminated ); } catch (SipException ex) { ex.printStackTrace(); fail("The TI failed to send a REQUEST_TERMINATED response"); } //The Transaction should now be COMPLETED assertEquals( "The Transaction did not remain COMPLETED after transmitting a REQUEST_TERMINATED response", TransactionState.COMPLETED, tran.getState()); //Check whether the BUSY_HERE is received by the RI. waitForMessage(); responseEvent = responseCollector.extractCollectedResponseEvent(); assertNotNull( "The REQUEST_TERMINATED response was not received by the RI", responseEvent); assertTrue( "A response different from REQUEST_TERMINATED was sent by the TI", Response.REQUEST_TERMINATED == responseEvent.getResponse().getStatusCode()); // RI CT should have sent ACK, tran should be in CONFIRMED // and response retransmissions should cease assertEquals( "The ServerTransaction did not pas into the confirmed state" + "after receiving an ACK.", TransactionState.CONFIRMED, tran.getState()); } catch (Throwable exc) { exc.printStackTrace(); fail(exc.getClass().getName() + ": " + exc.getMessage()); } assertTrue(new Exception().getStackTrace()[0].toString(), true); } /** * JvB: tests CANCEL for an INVITE ST, in case the client changes * the case of the branch id to all lowercvase (NIST used to do this) */ public void testCaseInsensitiveCanceledInvite() { try { Request invite = createRiInviteRequest(null, null, null); SipEventCollector responseCollector = new SipEventCollector(); //Before Sending the request we should first register a listener with the //RI that would catch the TRYING response try { responseCollector.collectResponseEvent(riSipProvider); } catch (TooManyListenersException ex) { throw new TckInternalError( "Failed to register a SipListener with an RI SipProvider", ex); } //Send the initial request (JvB: using a CT) ClientTransaction riInviteCt; try { eventCollector.collectRequestEvent(tiSipProvider); riInviteCt = riSipProvider.getNewClientTransaction( invite ); riInviteCt.sendRequest(); } catch (SipException ex) { throw new TckInternalError( "A SipExceptionOccurred while trying to send request!", ex); } catch (TooManyListenersException ex) { throw new TiUnexpectedError( "Failed to register a SipListener with a TI SipProvider", ex); } waitForMessage(); RequestEvent inviteReceivedEvent = eventCollector.extractCollectedRequestEvent(); if (inviteReceivedEvent == null || inviteReceivedEvent.getRequest() == null) throw new TiUnexpectedError("The initial invite request was not received by the TI!"); //Let's create the transaction ServerTransaction tran = null; try { tran = tiSipProvider.getNewServerTransaction( inviteReceivedEvent.getRequest()); } catch (Exception ex) { ex.printStackTrace(); fail( ex.getClass().getName() + "was thrown while trying to " + "create the server transaction"); } assertNotNull( "tiSipProvider.getNewServerTransaction() returned null", tran); //Check whether a TRYING response has been sent. //wait for the trying response waitForMessage(); // At this point state must be PROCEEDING assertEquals(TransactionState.PROCEEDING, tran.getState()); ResponseEvent responseEvent = responseCollector.extractCollectedResponseEvent(); assertNotNull( "No TRYING response has been sent by the TI upon reception " + "of an INVITE request", responseEvent); assertTrue( "A response different from 100 was sent by the TI upon " + "reception of INVITE", Response.TRYING == responseEvent.getResponse().getStatusCode()); //Create & send RINGING. See that it is properly sent //and that tran state doesn't change Response ringing = null; try { ringing = tiMessageFactory.createResponse( Response.RINGING, tran.getRequest()); ((ToHeader) ringing.getHeader(ToHeader.NAME)).setTag( Integer.toString(hashCode())); addStatus(tran.getRequest(), ringing); // BUG report from Ben Evans: // set contact header on dialog-creating response ringing.setHeader(createTiContact()); } catch (ParseException ex) { throw new TiUnexpectedError( "A ParseException was thrown while trying to create a ringing " + "response using TI", ex); } try { //listen for the RINGING response responseCollector.collectResponseEvent(riSipProvider); } catch (TooManyListenersException ex) { throw new TckInternalError( "Failed to register a SipListener with an RI SipProvider", ex); } try { tran.sendResponse(ringing); } catch (SipException ex) { ex.printStackTrace(); fail("The TI failed to send a RINGING response"); } //The Transaction should still be PROCEEDING assertEquals( "The Transaction did not remain PROCEEDING after transmitting a RINGING response", TransactionState.PROCEEDING, tran.getState()); //Check whether the RINGING is received by the RI. waitForMessage(); responseEvent = responseCollector.extractCollectedResponseEvent(); assertNotNull( "The RINGING response was not received by the RI", responseEvent); assertTrue( "A response different from RINGING was sent by the TI", Response.RINGING == responseEvent.getResponse().getStatusCode()); // JvB: *new* : send CANCEL here Request riCancel = riInviteCt.createCancel(); // Change Via branch to all lower case, clients SHOULD NOT // do this but stack should be able to handle this ViaHeader topVia = (ViaHeader) riCancel.getHeader("Via"); topVia.setBranch( topVia.getBranch().toLowerCase() ); // Send the CANCEL request try { eventCollector.collectRequestEvent(tiSipProvider); riSipProvider.sendRequest(riCancel); } catch (SipException ex) { throw new TckInternalError( "A SipExceptionOccurred while trying to send CANCEL request!", ex); } catch (TooManyListenersException ex) { throw new TiUnexpectedError( "Failed to register a SipListener with a TI SipProvider", ex); } waitForMessage(); RequestEvent cancelReceivedEvent = eventCollector.extractCollectedRequestEvent(); if (cancelReceivedEvent == null || cancelReceivedEvent.getRequest() == null) throw new TiUnexpectedError("The CANCEL request was not received by the TI!"); // Check that a ST was matched, and that it is equal to the INVITE ST // mranga -- I dont know if its valid to do things this way! //assertSame( tran, cancelReceivedEvent.getServerTransaction() ); // Send an OK to the CANCEL Response cancelOK; try { cancelOK = tiMessageFactory.createResponse( Response.OK, cancelReceivedEvent.getRequest() ); addStatus( cancelReceivedEvent.getRequest(), cancelOK ); } catch (ParseException ex) { throw new TiUnexpectedError( "A ParseException was thrown while trying to create a OK " + "response using TI", ex); } try { //listen for the OK response responseCollector.collectResponseEvent(riSipProvider); } catch (TooManyListenersException ex) { throw new TckInternalError( "Failed to register a SipListener with an RI SipProvider", ex); } try { cancelReceivedEvent.getServerTransaction().sendResponse(cancelOK); } catch (SipException ex) { ex.printStackTrace(); fail("The TI failed to send a CANCEL OK response"); } // Check whether the OK is received by the RI. waitForMessage(); responseEvent = responseCollector.extractCollectedResponseEvent(); assertNotNull( "The CANCEL OK response was not received by the RI", responseEvent); assertTrue( "A response different from OK was sent by the TI", Response.OK == responseEvent.getResponse().getStatusCode()); //Send 487 from TU and see the tran goes COMPLETED Response reqTerminated = null; try { reqTerminated = tiMessageFactory.createResponse( Response.REQUEST_TERMINATED, tran.getRequest()); addStatus(tran.getRequest(), reqTerminated); } catch (ParseException ex) { throw new TiUnexpectedError( "A ParseException was thrown while trying to create a req_terminated " + "response using TI", ex); } try { //listen for the BUSY_HERE response responseCollector.collectResponseEvent(riSipProvider); } catch (TooManyListenersException ex) { throw new TckInternalError( "Failed to register a SipListener with an RI SipProvider", ex); } try { tran.sendResponse( reqTerminated ); } catch (SipException ex) { ex.printStackTrace(); fail("The TI failed to send a REQUEST_TERMINATED response"); } //The Transaction should now be COMPLETED assertEquals( "The Transaction did not remain COMPLETED after transmitting a REQUEST_TERMINATED response", TransactionState.COMPLETED, tran.getState()); //Check whether the BUSY_HERE is received by the RI. waitShortForMessage(); responseEvent = responseCollector.extractCollectedResponseEvent(); assertNotNull( "The REQUEST_TERMINATED response was not received by the RI", responseEvent); assertTrue( "A response different from REQUEST_TERMINATED was sent by the TI", Response.REQUEST_TERMINATED == responseEvent.getResponse().getStatusCode()); // RI CT should have sent ACK, tran should be in CONFIRMED // and response retransmissions should cease assertEquals( "The ServerTransaction did not pas into the confirmed state" + "after receiving an ACK.", TransactionState.CONFIRMED, tran.getState()); } catch (Throwable exc) { exc.printStackTrace(); fail(exc.getClass().getName() + ": " + exc.getMessage()); } assertTrue(new Exception().getStackTrace()[0].toString(), true); } /** * Tries to steer a TI server transaction through the following scenario * Proceeding-->Terminated. Apart from state transitions, we also test, * retransmissions and proper hiding/passing of messages to the TU. */ public void testProceedingTerminatedScenario() { try { Request invite = createRiInviteRequest(null, null, null); SipEventCollector responseCollector = new SipEventCollector(); //Before Sending the request we should first register a listener with the //RI that would catch the TRYING response try { responseCollector.collectResponseEvent(riSipProvider); } catch (TooManyListenersException ex) { throw new TckInternalError( "Failed to register a SipListener with an RI SipProvider", ex); } //Send the initial request //riSipProvider.setAutomaticDialogSupportEnabled(false); try { eventCollector.collectRequestEvent(tiSipProvider); riSipProvider.sendRequest(invite); } catch (SipException ex) { throw new TckInternalError( "A SipExceptionOccurred while trying to send request!", ex); } catch (TooManyListenersException ex) { throw new TiUnexpectedError( "Failed to register a SipListener with a TI SipProvider", ex); } waitForMessage(); RequestEvent inviteReceivedEvent = eventCollector.extractCollectedRequestEvent(); if (inviteReceivedEvent == null || inviteReceivedEvent.getRequest() == null) throw new TiUnexpectedError("The initial invite request was not received by the TI!"); //Let's create the transaction ServerTransaction tran = null; try { tran = tiSipProvider.getNewServerTransaction( inviteReceivedEvent.getRequest()); } catch (Exception ex) { ex.printStackTrace(); fail( ex.getClass().getName() + "was thrown while trying to " + "create the server transaction"); } assertNotNull( "tiSipProvider.getNewServerTransaction() returned null", tran); //Check whether a TRYING response has been sent. //wait for the trying response waitForMessage(); // State must be proceeding After Response has been sent. assertEquals(TransactionState.PROCEEDING, tran.getState()); ResponseEvent responseEvent = responseCollector.extractCollectedResponseEvent(); assertNotNull( "No TRYING response has been sent by the TI upon reception " + "of an INVITE request", responseEvent); assertTrue( "A response different from 100 was sent by the TI upon " + "reception of INVITE", Response.TRYING == responseEvent.getResponse().getStatusCode()); //Resend the invite and see that a TRYING response is resent try { //listen for the Trying response responseCollector.collectResponseEvent(riSipProvider); } catch (TooManyListenersException ex) { throw new TckInternalError( "Failed to register a SipListener with an RI SipProvider", ex); } try { eventCollector.collectRequestEvent(tiSipProvider); riSipProvider.sendRequest(invite); } catch (SipException ex) { throw new TckInternalError( "A SipExceptionOccurred while trying to send request!", ex); } catch (TooManyListenersException ex) { throw new TiUnexpectedError( "Failed to register a SipListener with a TI SipProvider", ex); } //Wait for the INVITE waitForMessage(); inviteReceivedEvent = eventCollector.extractCollectedRequestEvent(); assertNull( "Retransmitted INVITEs should not be passed to the TU", inviteReceivedEvent); //Wait for a retransmitted TRYING response waitForMessage(); //Verify whether there was a TRYING response responseEvent = responseCollector.extractCollectedResponseEvent(); assertNotNull( "No TRYING response has been sent by the TI upon reception " + "of an INVITE request", responseEvent); assertTrue( "A response different from 100 was sent by the TI upon " + "reception of INVITE", Response.TRYING == responseEvent.getResponse().getStatusCode()); //Create & send RINGING. See that it is properly sent //and that tran state doesn't change Response ringing = null; try { ringing = tiMessageFactory.createResponse( Response.RINGING, tran.getRequest()); ((ToHeader) ringing.getHeader(ToHeader.NAME)).setTag( Integer.toString(hashCode())); addStatus(tran.getRequest(), ringing); // BUG report from Ben Evans: // set contact header on dialog-creating response ringing.setHeader(createTiContact()); } catch (ParseException ex) { throw new TiUnexpectedError( "A ParseException was thrown while trying to create a ringing " + "response using TI", ex); } try { //listen for the RINGING response responseCollector.collectResponseEvent(riSipProvider); } catch (TooManyListenersException ex) { throw new TckInternalError( "Failed to register a SipListener with an RI SipProvider", ex); } try { tran.sendResponse(ringing); } catch (SipException ex) { ex.printStackTrace(); fail("The TI failed to send a RINGING response"); } //The Transaction should still be PROCEEDING assertEquals( "The Transaction did not remain PROCEEDING after transmitting a RINGING response", TransactionState.PROCEEDING, tran.getState()); //Check whether the RINGING is received by the RI. waitForMessage(); responseEvent = responseCollector.extractCollectedResponseEvent(); assertNotNull( "The RINGING response was not received by the RI", responseEvent); assertTrue( "A response different from RINGING was sent by the TI", Response.RINGING == responseEvent.getResponse().getStatusCode()); //Resend the INVITE, see that it is hidden from the TU and see that //the _RINGING_ response is resent (and not the TRYING) try { //listen for the Trying response responseCollector.collectResponseEvent(riSipProvider); } catch (TooManyListenersException ex) { throw new TckInternalError( "Failed to register a SipListener with an RI SipProvider", ex); } try { eventCollector.collectRequestEvent(tiSipProvider); riSipProvider.sendRequest(invite); } catch (SipException ex) { throw new TckInternalError( "A SipExceptionOccurred while trying to send request!", ex); } catch (TooManyListenersException ex) { throw new TiUnexpectedError( "Failed to register a SipListener with a TI SipProvider", ex); } //Wait for the INVITE waitForMessage(); inviteReceivedEvent = eventCollector.extractCollectedRequestEvent(); assertNull( "Retransmitted INVITEs should not be passed to the TU", inviteReceivedEvent); //Wait for a retransmitted RINGING response waitLongForMessage(); //Verify whether there was a RINGING response responseEvent = responseCollector.extractCollectedResponseEvent(); assertNotNull( "No RINGING response has been sent by the TI upon reception " + "of an INVITE request", responseEvent); assertTrue( "A response different from RINGING was sent by the TI upon " + "reception of a retransmitted invite INVITE", Response.RINGING == responseEvent.getResponse().getStatusCode()); //We should still be proceeding assertEquals( "The server transaction left the PROCEEDING state.", TransactionState.PROCEEDING, tran.getState()); //Create a 2xx final response and test transaction termination Response ok = null; try { ok = tiMessageFactory.createResponse( Response.OK, tran.getRequest()); ContactHeader contact = this.createTiContact(); ok.addHeader(contact); ((ToHeader) ok.getHeader(ToHeader.NAME)).setTag( Integer.toString(hashCode())); addStatus(tran.getRequest(), ok); } catch (ParseException ex) { throw new TiUnexpectedError( "A ParseException was thrown while trying to create an ok " + "response using TI", ex); } try { //listen for the OK response responseCollector.collectResponseEvent(riSipProvider); } catch (TooManyListenersException ex) { throw new TckInternalError( "Failed to register a SipListener with an RI SipProvider", ex); } try { tran.sendResponse(ok); } catch (SipException ex) { ex.printStackTrace(); fail("The TI failed to send a OK response"); } //The Transaction should now be TERMINATED assertEquals( "The Transaction did not move to the TERMINATED state " + "after transmitting an OK response", TransactionState.TERMINATED, tran.getState()); //Check whether the OK is received by the RI. waitForMessage(); responseEvent = responseCollector.extractCollectedResponseEvent(); assertNotNull( "The OK response was not received by the RI", responseEvent); assertTrue( "A response different from OK was sent by the TI", Response.OK == responseEvent.getResponse().getStatusCode()); // Send an ACK to the other side. try { Dialog dialog = responseEvent.getDialog(); Request ack = dialog.createAck(((CSeqHeader)ok.getHeader(CSeqHeader.NAME)).getSeqNumber()); dialog.sendAck(ack); } catch (SipException ex) { fail("error sending ack ",ex); } } catch (Throwable exc) { exc.printStackTrace(); fail(exc.getClass().getName() + ": " + exc.getMessage()); } assertTrue(new Exception().getStackTrace()[0].toString(), true); } //==================== end of tests //====== STATIC JUNIT ========== public static Test suite() { return new TestSuite(InviteServerTransactionsStateMachineTest.class); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy