This guide will take you through the process of creating a simple UAS application and will guide you through some of the more important concepts of sipstack.io. We will also look at some of the underlying libraries that sipstack.io is using and touch upon some of the reasons why sipstack.io exists to begin with.
Sipstack.io is a framework whose primary goal is to provide you with a light-weight, ready-to-go sipstack with the all the necessary functionality needed by a production-ready SIP application, but without the fuzz that larger application containers typically come with.
All code is opensource and hosted on github and contributions are encouraged and highly appreciated.
Any SIP stack needs a blazingly fast and reliable network stack and netty.io is just that. Netty also has a large install base, an active community and over 10 years of development behind it, making it an excellent base for sipstack.io. If you are familiar with Netty, then integrating raw SIP support to your own SIP enabled application should be a breeze.
Equally important to a fast and reliable network stack, is the layer responsible for parsing and framing the data coming off of the network. A poor parsing/framing implementation will cripple the performance of any stack so it is important that this library is written with CPU and memory consumption in mind. Do not copy data if it is avoidable and do everything lazily since many SIP applications rarely need to parse every part of a message anyway. Pkts.io is a library designed with these requirements in mind and is what sipstack.io is using to encode/decode SIP messages and will be the library your application will interact with the most.
The first application we are going to build is a very basic SIP User Agent Server (UAS). In SIP, a User Agent Client (UAC) generates requests and the UAS terminates the request by sending back a response. It is important to understand that these are roles a UA (User Agent) undertakes at different points in time. Hence, a SIP element can one moment act as a UAC and as a UAS the next. If you want to learn more about SIP, head over to aboutsip.com and go through those presentations.
sipstack.io is available through Maven Central and we will be using Maven in these examples. If you are new to Maven, then check out Maven: The Complete Reference, which should have you up and running in no time.
First, add a property to your POM with the current version of sipstack.io (which is 0.1.1):
<properties>
<sipstackio.version>0.1.1</sipstackio.version>
</properties>
Add the sipstack-netty-codec-sip library as a dependency:
<dependencies>
<dependency>
<groupId>io.sipstack</groupId>
<artifactId>sipstack-netty-codec-sip</artifactId>
<version>${sipstackio.version}</version>
</dependency>
</dependencies>
Sipstack.io requires Java 8 so you need to configure your Maven setup to use Java 8. To do this, you need to configure your compiler, which in Maven is accomplished by re-configuring the maven-compiler-plugin
like so:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<!-- compile for Java 1.8 -->
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
Your final pom.xml should look like something like the following gist
Now that we have Maven setup we are ready to start coding.
All applications using sipstack.io starts off the same way, you will have to define and implement a handler, which is handles all I/O events generated by Netty. However, sipstack.io will already have converted the incoming network data into Java objects representing the SIP message so your handler will simply receive a SipMessageEvent with which you can interact.
In Netty, you implement a ChannelInboundHandler and override the channelRead method to handle the incoming data. For this simple example, and probably for all your SIP applications depending on your needs, we will extend the SimpleChannelInboundHandler, which is handling some events we currently do not care about.
@Sharable // (1)
public final class UASHandler extends SimpleChannelInboundHandler<SipMessageEvent> { // (2)
@Override
protected void channelRead0(final ChannelHandlerContext ctx, // (3)
final SipMessageEvent event)
throws Exception {
final SipMessage msg = event.getMessage(); // (4)
if (msg.isAck()) { // (5)
return;
}
if (msg.isRequest()) { // (6)
final SipResponse response = msg.createResponse(200);
event.getConnection().send(response);
}
}
}
UasHandler
is marked as Sharable
, which is how we indicate to Netty that this handler can be shared between multiple concurrent network channels. As long as the handler you write is statless, you should use it this way.channelRead0
method instead of the channelRead
one. All SIP messages that is received by the stack will be delivered to this method and is also where you will put your application logic.That is all that is needed for creating your first basic SIP application. The only thing that is left is to wire up the actual network stack, specify if we want to listen on UDP, TCP (or both) and add our UASHandler
into the pipeline.
We need a main
method for our program and we also need to wire up our handler with the rest of the stack. In Netty, this is accomplished by inserting the handler into a pipeline, which we will see shortly. Also, we need to insert the handlers that encode and decodes SIP messages into that very same pipeline. Those encoders/decoders are provided by sipstack.io and therefore not something you have to worry about.
public final class UAS {
public static void main(final String[] args) throws Exception {
final UASHandler uas = new UASHandler(); // (1)
final EventLoopGroup udpGroup = new NioEventLoopGroup(); // (2)
final Bootstrap b = new Bootstrap(); // (3)
b.group(udpGroup)
.channel(NioDatagramChannel.class)
.handler(new ChannelInitializer<DatagramChannel>() {
@Override
protected void initChannel(final DatagramChannel ch) throws Exception {
final ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast("decoder", new SipMessageDatagramDecoder()); // (4)
pipeline.addLast("encoder", new SipMessageEncoder()); // (5)
pipeline.addLast("handler", uas); // (6)
}
});
final InetSocketAddress socketAddress = new InetSocketAddress("127.0.0.1", 5060); // (7)
final ChannelFuture f = b.bind(socketAddress).sync(); // (8)
f.channel().closeFuture().await();
}
}
UASHandler
.UASHandler
last into the pipeline. What we have done now is to configure a pipeline of handlers that will be invoked by Netty one at a time and along the way, the original raw data will be transformed into different representations. For incoming data, the decoder will transform the bytes into a SipMessageEvent, which our handler later can work with. When we write a SipMessage back to the Connection, the encoder will take care of the details around converting that message back to bytes, which is then handed off to the netty stack for transmission across the network.That is all there is to it and you have now created a simple UAS that is capable of handling several thousands of calls per second. Granted, the application really doesn't do much but it illustrates the basics of sipstack.io but let's finish everything off by building a jar out of our project, run the application and then send some test traffic to it.
We recommend that you build your sipstack.io applications as “fat” JAR files — single .jar files which contain all of the .class files required to run your application. This allows you to build a single deployable artifact which you can promote from your staging environment to your QA environment to your production environment without worrying about differences in installed libraries. To start building your UAS application as a fat JAR, we need to configure a Maven plugin called maven-shade
. In the <build><plugins>
section of your pom.xml file, add this:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>1.6</version>
<configuration>
<createDependencyReducedPom>true</createDependencyReducedPom>
<filters>
<filter>
<artifact>*:*</artifact>
</filter>
</filters>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>com.example.UAS</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
This configures Maven to do a couple of things during its package phase:
pom.xml
file which doesn’t include dependencies for the libraries whose contents are included in the fat JAR.META-INF/services entries
in the JARs instead of overwriting them.com.example.UAS
as the JAR’s MainClass
. This will allow you to run the JAR using java -jar
. Of course, change the main class to match that of your UAS. Most likely, you have a different packagename.To run your application, simply issue the following command in your project folder:
java -jar target/uas-0.0.1-SNAPSHOT.jar
and that is all there is to it. The stack is up and running, listening on localhost:5060 and ready to accept traffic. So let's send some traffic!
There are several ways in which you could send test traffic to your application. One of the more common tools for testing SIP related applications is SIPp, a sip performance testing tool and that is what we will use. Download and install SIPp for your local environment and then issue the following command:
sipp -sn uac 127.0.0.1:5060
This will start SIPp and will execute the built in UAC scenario (remember, we built a UAS) and will send traffic to port 5060 on localhost. By default, sipp will use UDP as the transport and will send 10 requests/second. If you want to send more traffic, simply press +
for increase the calls/second with 1 or *
to increase it with 10 so go ahead, hit *
a few times and you will be suprised how much traffic your simple UAS can take!
Congratulations! You just created a first small SIP application using sipstack.io. Even though the application itself was small, it illustrates how to cofigure the network stack and how to write a handler, which you must always do for every application you write. A good next step is to look at how to write a stateless proxy, something that is a little more useful than our UAS and then continue with writing a SIP registrar in order to allow SIP clients register with your service. You should also checkout how to configure other protocols, such as TCP, since all SIP applications must handle these two protocols at the very least.
You will find the answers to all of the above, and more, in the document section.