r37 - in regression_tests/common: . ua

erik at minisip.org erik at minisip.org
Sun Nov 19 14:45:26 CET 2006


Author: erik
Date: 2006-11-19 14:45:25 +0100 (Sun, 19 Nov 2006)
New Revision: 37

Added:
   regression_tests/common/ua/
   regression_tests/common/ua/Makefile
   regression_tests/common/ua/ua.cxx
Log:

 * Added a UA implementation using libmsip to set up and tear down
   sessions.

   This is suitable to do regression testing of libmsip. It enables
   output of packets that are sent and received.

   It supports the following commands:
    call <uri>
    hangup [<call-Id>]
    sleep <nsec> (don't read commands from stdin for nsec seconds)
    -d <out-drop-emul-spec>  
    -D <in-drop-emul-spec>  
    (enter without command prints stack state)

    Example1: set up a session for five seconds and then tear down
      echo sleep 60 | ./ua -u erik at domain.com -p 5000 &   # set up server at port 5000
      echo -e "call erik at localhost:5000\nsleep 5\nhangup" | ./ua -u erik2 at domain.com -p 6000
  
    Example2: set up a session for five seconds and then tear down. Drop
              first and third outgoing UDP packets.
      echo sleep 60 | ./ua -u erik at domain.com -p 5000 &   # set up server at port 5000
      echo -e "call erik at localhost:5000\nsleep 5\nhangup" | ./ua -u erik2 at domain.com -p 6000 -d 010

      Note that this application requires that libmsip has been configured
      with "--enable-dropemul".

   Example3: Test how what happens when unexpected response is received
	   over TCP:
      echo sleep 60 < ./ua -u e at e.e -p 5000 > output &
      cat responsepacket.txt | nc localhost 5000

 



Added: regression_tests/common/ua/Makefile
===================================================================
--- regression_tests/common/ua/Makefile	2006-11-19 13:21:36 UTC (rev 36)
+++ regression_tests/common/ua/Makefile	2006-11-19 13:45:25 UTC (rev 37)
@@ -0,0 +1,5 @@
+all: ua
+ua: ua.cxx
+	g++ -Wall -g -rdynamic -o ua ua.cxx -lminisip -lmsip -lpthread -lssl
+clean:
+	rm -f ua

Added: regression_tests/common/ua/ua.cxx
===================================================================
--- regression_tests/common/ua/ua.cxx	2006-11-19 13:21:36 UTC (rev 36)
+++ regression_tests/common/ua/ua.cxx	2006-11-19 13:45:25 UTC (rev 37)
@@ -0,0 +1,448 @@
+/**
+
+(C) 2006 Erik Eliasson
+Application supporting multiple simultaneous sessions
+using the libmsip SIP stack. It sets ut a session, but there is no sdp or
+other session description.
+
+It makes the SIP stack listen to a port that is specified
+via the command line.
+
+Usage: ./ua -u <uri> -p <port> [-d <dropvector_udp_out>] [-D <dropvector_udp_in>]
+
+ <uri> is the local users SIP URI
+ <port> is the local UDP/TCP port numbers that will be opened
+ <dropvector_udp_out> (optional) is a vector that specifies how to 
+   	simulate packet loss (Example: 1101 means drop third packet, allow
+	first, second, fourth, fifth, ...)
+ <dropvector_udp_in> same as previous, except it is applied to incoming
+ 	packets
+
+The application registers a default handler called "MyApp" to 
+libmsip.
+
+A class called MyCall represents a call. The default handler 
+creates a MyCall when ever the user wants to make a call or 
+it receives an "INVITE" request from the remote user.
+
+MyCall call answers the call with a "200" response.
+
+Press enter to see a list of ongoing calls.
+
+Type "call <someuri>" to initiate a call.
+
+Type "hangup [<callid>]" to end a call. If no callid is given,
+the last initiated call will be closed.
+
+Example:
+ Console 1:
+   ./client -p 5000 -u erik at mydomain.com
+
+ Console2:
+   ./client -p 6000 -u erik2 at mydomain.com
+   call erik at localhost:5000	<-- initiates a session to the client above
+*/
+
+
+#include<libmsip/SipStack.h>
+#include<libmsip/SipHeaderSubject.h>
+#include<libmsip/SipHeaderFrom.h>
+#include<libmsip/SipHeaderTo.h>
+#include<libmsip/SipHeaderMaxForwards.h>
+#include<libmsip/SipHeaderCallID.h>
+#include<libmsip/SipHeaderCSeq.h>
+#include<libmsip/SipHeaderContact.h>
+#include<libmsip/SipMessageContentIM.h>
+#include<libmsip/SipDialog.h>
+#include<libmsip/SipTransitionUtils.h>
+#include<libminisip/gui/ConsoleDebugger.h>
+#include<libmutil/Thread.h>
+#include<libmutil/Thread.h>
+#include<libmnetutil/NetworkFunctions.h>
+
+/*
+
+State machine implemented by MyCall.
+
+                      +----------+
+       +--------------|  start   |
+       |              +----------+
+       |a1:call <uri>      | 
+       v   send INVITE     | a2: INVITE
+  +----------+             |     200 ok
+  |  trying  |             |
+  +----------+             |
+       |                   |
+       | a2: 2xx           v
+       |     ACK      +----------+
+       +------------->| in_call  |
+                      +----------+
+		         |    |
+	     a4:hangup   |    | a5: BYE
+                send BYE |    |     send 200 ok, call_terminated
+		         v    |
+              +-------------+ |
+              | waitbyeresp | |    
+              +-------------+ |
+                         |    |
+             a6: 1XX-6XX |    |
+             call_termina|    |
+		         |    |
+		 	 v    v
+                      +----------+
+                      |terminated|
+                      +----------+
+
+*/
+
+string lastCallId;
+int port;
+
+class MyCall : public SipDialog{
+	public:
+		MyCall(MRef<SipStack*> stack, MRef<SipIdentity*> ident):
+				SipDialog(stack, ident),
+				myIdentity(ident)
+		{
+				
+			State<SipSMCommand,string> *s_start=new State<SipSMCommand,string>(this,"start");
+			addState(s_start);
+
+			State<SipSMCommand,string> *s_trying=new State<SipSMCommand,string>(this,"trying");
+			addState(s_trying);
+
+			State<SipSMCommand,string> *s_incall=new State<SipSMCommand,string>(this,"incall");
+			addState(s_incall);
+			
+			State<SipSMCommand,string> *s_waitbyeresp=new State<SipSMCommand,string>(this,"waitbyeresp");
+			addState(s_waitbyeresp);
+
+			State<SipSMCommand,string> *s_terminated=new State<SipSMCommand,string>(this,"terminated");
+			addState(s_terminated);
+
+
+			new StateTransition<SipSMCommand,string>(this,
+					"transition_start_trying_call",
+					(bool (StateMachine<SipSMCommand,string>::*)(const SipSMCommand&)) &MyCall::a1_start_trying_call,
+					s_start,
+					s_trying);
+
+			new StateTransition<SipSMCommand,string>(this,
+					"transition_start_incall_INVITE",
+					(bool (StateMachine<SipSMCommand,string>::*)(const SipSMCommand&)) &MyCall::a2_start_incall_INVITE,
+					s_start,
+					s_incall);
+
+			new StateTransition<SipSMCommand,string>(this,
+					"transition_trying_incall_2XX",
+					(bool (StateMachine<SipSMCommand,string>::*)(const SipSMCommand&)) &MyCall::a3_trying_incall_2XX,
+					s_trying,
+					s_incall);
+
+			new StateTransition<SipSMCommand,string>(this,
+					"transition_incall_waitbyeresp_hangup",
+					(bool (StateMachine<SipSMCommand,string>::*)(const SipSMCommand&)) &MyCall::a4_incall_waitbyeresp_hangup,
+					s_incall,
+					s_waitbyeresp);
+				
+			new StateTransition<SipSMCommand,string>(this,
+					"transition_incall_terminated_BYE",
+					(bool (StateMachine<SipSMCommand,string>::*)(const SipSMCommand&)) &MyCall::a5_incall_terminated_BYE,
+					s_incall,
+					s_terminated);
+
+			new StateTransition<SipSMCommand,string>(this,
+					"transition_waitbyeresp_terminated_RESP",
+					(bool (StateMachine<SipSMCommand,string>::*)(const SipSMCommand&)) &MyCall::a6_waitbyeresp_terminated_RESP,
+					s_waitbyeresp,
+					s_terminated);
+		}
+
+		string getName(){return "MyCall";}
+
+		bool a1_start_trying_call(const SipSMCommand &cmd)
+		{
+			if (transitionMatch(cmd,
+					"call",
+					SipSMCommand::dialog_layer,
+					SipSMCommand::dialog_layer ) )
+			{
+				string toUser = cmd.getCommandString().getParam();
+				myInvite = createInvite(toUser);
+				SipSMCommand invitecmd(
+						*myInvite,
+						SipSMCommand::dialog_layer,
+				SipSMCommand::transaction_layer );
+				sipStack->enqueueCommand(invitecmd);
+				return true;
+			}else
+				return false;
+		}
+		
+		bool a2_start_incall_INVITE(const SipSMCommand &cmd)
+		{
+			if (transitionMatch("INVITE", cmd,
+						SipSMCommand::transaction_layer,
+						SipSMCommand::dialog_layer))
+			{
+				this->dialogState.updateState((SipRequest*) *cmd.getCommandPacket());
+				MRef<SipMessage*> resp = new SipResponse(cmd.getCommandPacket()->getFirstViaBranch(), 
+				 		200, "ok", cmd.getCommandPacket() ); 
+				sipStack->enqueueCommand( SipSMCommand(resp, 
+						SipSMCommand::dialog_layer, 
+						SipSMCommand::transaction_layer));
+				return true;
+			}else
+				return false;
+		}
+		
+		bool a3_trying_incall_2XX(const SipSMCommand &cmd)
+		{
+			if (transitionMatchSipResponse("INVITE", cmd,
+					SipSMCommand::transaction_layer,
+					SipSMCommand::dialog_layer, "2**"))
+			{
+				this->dialogState.updateState((SipResponse*) *cmd.getCommandPacket());
+
+				MRef<SipResponse*> resp = (SipResponse*)*cmd.getCommandPacket();
+				MRef<SipRequest*> ack = SipRequest::createSipMessageAck("",myInvite,resp);
+
+				SipSMCommand sipcmd( *ack,
+						SipSMCommand::dialog_layer,
+						SipSMCommand::transport_layer
+						);
+				sipStack->enqueueCommand(sipcmd);
+
+				return true;
+			}else
+				return false;
+		}
+
+		bool a4_incall_waitbyeresp_hangup(const SipSMCommand &cmd){
+			if (transitionMatch(cmd,
+					"hangup",
+					SipSMCommand::dialog_layer,
+					SipSMCommand::dialog_layer ) )
+			{
+				MRef<SipRequest*> myBye = this->createSipMessageBye();
+				SipSMCommand sipcmd( *myBye,
+						SipSMCommand::dialog_layer,     // from layer (this class)
+						SipSMCommand::transaction_layer // to layer (down the stack)
+						);
+				sipStack->enqueueCommand(sipcmd);
+				return true;
+			}else
+				return false;
+		}
+
+		bool a5_incall_terminated_BYE(const SipSMCommand &cmd){
+			if (transitionMatch("BYE", cmd,
+						SipSMCommand::transaction_layer,
+						SipSMCommand::dialog_layer))
+			{
+				MRef<SipMessage*> resp = new SipResponse(cmd.getCommandPacket()->getFirstViaBranch(), 
+				 		200, "ok", cmd.getCommandPacket() ); 
+				sipStack->enqueueCommand( SipSMCommand(resp, 
+						SipSMCommand::dialog_layer, 
+						SipSMCommand::transaction_layer));
+
+				SipSMCommand sipcmd(
+						CommandString (dialogState.callId,"call_terminated"),
+						SipSMCommand::dialog_layer,     // from layer (this class)
+						SipSMCommand::dispatcher        // to layer
+						);
+				sipStack->enqueueCommand(sipcmd);
+
+				return true;
+			}else
+				return false;
+		}
+
+		bool a6_waitbyeresp_terminated_RESP(const SipSMCommand &cmd)
+		{
+			if (transitionMatchSipResponse("BYE", cmd,
+					SipSMCommand::transaction_layer,
+					SipSMCommand::dialog_layer,
+					"***"))
+			{
+				SipSMCommand sipcmd( CommandString (dialogState.callId,"call_terminated"),
+						SipSMCommand::dialog_layer,
+						SipSMCommand::dispatcher
+						);
+				sipStack->enqueueCommand(sipcmd);
+				return true;		
+			}else{
+				return false;
+			}
+		}
+
+
+
+	private:
+		MRef<SipRequest*> createInvite(string user){
+			MRef<SipRequest*> req = new SipRequest(itoa(rand()), "INVITE"); 
+			req->setUri(user);
+
+			req->addHeader(new SipHeader(new SipHeaderValueCallID(itoa(rand()))));
+			req->addHeader(new SipHeader(new SipHeaderValueMaxForwards(50)));
+
+			req->addHeader(new SipHeader(new SipHeaderValueFrom(myIdentity->getSipUri()))); 
+
+			req->addHeader(new SipHeader(new SipHeaderValueTo(user)));
+			req->addHeader(new SipHeader(new SipHeaderValueCSeq("INVITE",1)));
+
+			string contactstr = myIdentity->getSipUri().getUserName() + "@" +
+				getDialogConfig()->inherited->localIpString +
+				":" + itoa(getDialogConfig()->inherited->localUdpPort);
+			req->addHeader(new SipHeader(new SipHeaderValueContact(SipUri(contactstr))));
+
+			return req;
+		}
+
+
+		MRef<SipRequest*> myInvite;
+		MRef<SipIdentity*> myIdentity;
+};
+
+
+
+
+
+class MyApp : public SipSMCommandReceiver , public Runnable{
+	public:
+		MyApp(MRef<SipIdentity*> ident):myIdentity(ident){}
+
+		bool handleCommand(const SipSMCommand& cmd){
+			if (cmd.getType()==SipSMCommand::COMMAND_STRING){
+
+				if (cmd.getCommandString().getOp()=="call"){	//Act as a client
+					MRef<SipDialog*> myCall = new MyCall(sipStack, myIdentity);
+					sipStack->addDialog(myCall);
+					//cout << "INFO: Started call with id "<< myCall->getCallId()<<endl;
+					return myCall->handleCommand(cmd);
+				}else{
+					cerr << "MyApp: I don't know what to do with: "<<
+						cmd.getCommandString().getOp()<< " "<< cmd.getCommandString().getParam() <<endl;
+				}
+
+			}else{
+				if (cmd.getCommandPacket()->getType()=="INVITE"){
+					MRef<SipDialog*> myCall = new MyCall(sipStack, myIdentity);
+					lastCallId = myCall->getCallId();
+					sipStack->addDialog(myCall);
+					return myCall->handleCommand(cmd);
+				}else{
+					cerr << "MyApp: I don't know how to handle packet "<<cmd.getCommandPacket()->getType()<<endl;
+				}
+			}
+			return true;
+		}
+
+
+		void run(){
+			MRef<SipStackConfig*> config = new SipStackConfig;
+			config->localUdpPort=port;
+			config->localTcpPort=port;
+			config->localIpString=NetworkFunctions::getInterfaceIPStr("lo"); // localhost: 127.0.0.1
+			//config->localIpString=NetworkFunctions::getInterfaceIPStr("eth0");
+			sipStack = new SipStack( config );
+			sipStack->setDebugPrintPackets(true);
+			sipStack->setDefaultDialogCommandHandler( this );
+			sipStack->startTcpServer();
+			sipStack->run();
+		}
+
+		MRef<SipStack*> sipStack;
+		MRef<SipIdentity*> myIdentity;
+};
+
+
+void usage(string progname){
+	cerr << "Usage: "<< progname<<" -u <uri> -p <port> [-d <dropvector_udp_out>] [-D <dropvector_udp_in>]"<<endl;
+	exit(1);
+}
+
+extern void setDropFilterOut(string s);
+extern void setDropFilterIn(string s);
+
+int ua_main(int argc, char **argv){
+	if (argc <5 || (argc%2) != 1 ){ // must be an even number of args and at least uri and port
+		usage(argv[0]);
+	}
+
+	string uri;
+	string portstr;
+	
+	for (int i=1; i<argc; i+=2){
+		if (string("-u")==argv[i])
+			uri=argv[i+1];
+		if (string("-p")==argv[i])
+			portstr=argv[i+1];
+		if (string("-d")==argv[i])
+			setDropFilterOut(argv[i+1]);
+		if (string("-D")==argv[i])
+			setDropFilterIn(argv[i+1]);
+	}
+
+	if (uri=="" || portstr==""){
+		usage(argv[0]);
+	}
+
+	//mdbg.setEnabled(true);
+
+	MRef<SipIdentity*> myIdentity= new SipIdentity(SipUri(uri));
+
+	MRef<MyApp*> myApp = new MyApp(myIdentity);
+
+	port=atoi(portstr.c_str());
+
+	Thread t(*myApp);
+
+	while (true){
+		string line;
+		getline(cin,line);
+		if (!cin){
+			return 0;
+		}
+		line = trim(line);
+		if (line==""){
+			cout << myApp->sipStack->getStackStatusDebugString();
+		}else{
+			if (line.substr(0,4)=="call"){
+				string user = trim(line.substr(4));
+				if (user.size()>1){
+					CommandString cmd("", //no call-id
+							"call", //command to MyApp 
+							user);   //text to send
+					myApp->sipStack->handleCommand(cmd);
+				}else{
+					cerr << "Usage: call <sipuri>"<<endl;	
+				}
+			}else if (line.substr(0,6)=="hangup"){
+				string callId = trim(line.substr(6));
+				if (callId.size()==0)
+					callId = lastCallId;
+				CommandString cmd("", //no call-id
+						"hangup", //command to MyApp 
+						callId); //argument to hangup - which call
+				myApp->sipStack->handleCommand(cmd);
+			}else if (line.substr(0,5)=="sleep"){
+				string ss = trim(line.substr(5));
+				if (ss.size()>0){
+					int s= atoi( ss.c_str() );
+					Thread::msleep(s*1000);
+				}else{
+					cerr << "Usage: sleep <nseconds>"<<endl;	
+				}
+
+			}else if (line.substr(0,4)=="help"){
+				cerr << "Commands:\n\thelp\n\tcall <sipuri>\n\thangup [<callid>] (if empty, hang up most recent call)\n\tsleep <nsec>"<<endl;
+			}		
+		}
+	}
+	return 0;
+}
+
+int main(int argc, char **argv){
+	return ua_main(argc, argv);
+}



More information about the Minisip-devel mailing list