I had a question on the forums about creating a PC based widget to display the callerID on an incoming call by maintaining a persistent connection to the sipsorcery telnet monitoring server. One problem with the design is that sipsorcery no longer uses telnet the end of telnet for sipsorcery, so the design would need to change to use SSH instead. The question mentioned using Adobe’s AIR to build the application and while I don’t know a lot about AIR it’s likely that a telnet client would be easy to implement, since it’s not much more than a TCP socket, but SSH would be a lot harder as there’s a lot of extra work to negotiate and set up the encrypted channel.

As it happens at the same time the SSH change was made I changed the way the Siverlight client retrieves the notifications messages from the server from using a telnet connection to a HTTPS one. Part of the motivation was exactly the same as the problem in the previous paragraph, it was going to be tricky to implement an SSH client in Silverlight. Another reason was that the Silverlight client can only establish TCP connections to destination ports 4302 to 4332 and I had begun to find that a bit frustrating, I couldn’t use the console from a previous workplace, some internet cafes etc.

The HTTPS service that the Silverlight client uses to get notifications is . The “pull” at the end of the service URL is pertinent and the notifications mechanism requires that clients poll the sipsorcery web server to pull notifications down. Such a pull mechanism is not ideal as it means unneccessary traffic and load but after spending the best part of two weeks fighting with Microsoft’s PollingDuplexHttpBinding only to conclude that it’s completely broken with IIS6 and switch back to a traditional pull mechansim. Maybe at some point I’ll revisit it, the sipsorcery web site has since moved to Windows Azure which uses IIS7 and on another front HTML5 has introduced Web Sockets which achieve the same as the PollingDuplexHttpBinding.

The ability to pull notifications from the sipsorcery web server is something that can be used right now to build an application like the callerID widget. The Silverlight client consumes the service using a WCF SOAP endpoint. Connecting to WCF SOAP endpoints is ok if you’re going to be writing a client in .Net (C#, VB.Net etc) but it can get tricky from non .Net languages especially when the service involves authorisations which the sipsorcery one does. One great thing about WCF is that with very little effort a service endpoint can be added to support an alternative interface. In this case REST and JSON (I’m not deliberately trying to set a record for acronyms) are a lot more universally understood compared to SOAP and .Net’s XML serialisation. I’ve spent a few hours adding the REST and JSON interface to the notifications service and have deployed it to the sipsorcery Windows Azure web site, the URL is https://www.sipsorcery.com/notificationspull.svc/rest/. The service interface is:

[ServiceContract(Namespace = "http://www.sipsorcery.com/notifications/pull")]
    public interface INotifications
    {
        [OperationContract]
        [WebGet(UriTemplate = "isalive", ResponseFormat = WebMessageFormat.Json)]
        bool IsAlive();

        [OperationContract]
        [WebGet(UriTemplate = "login?username={username}&password={password}", ResponseFormat = WebMessageFormat.Json)]
        string Login(string username, string password);

        [OperationContract]
        [WebGet(UriTemplate = "logout")]
        void Logout();

        [OperationContract]
        [WebGet(UriTemplate = "getpollperiod", ResponseFormat = WebMessageFormat.Json)]
        int GetPollPeriod();

        [OperationContract]
        [WebGet(UriTemplate = "subscribeforaddress?subject={subject}&filter={filter}&addressid={addressid}", ResponseFormat = WebMessageFormat.Json)]
        string SubscribeForAddress(string subject, string filter, string addressID);
        
        [OperationContract]
        [WebGet(UriTemplate = "getnotificationsforaddress?addressid={addressid}", ResponseFormat = WebMessageFormat.Json)]
        Dictionary<string, list=""><string>> GetNotificationsForAddress(string addressID);

        [OperationContract]
        [WebGet(UriTemplate = "closeconnectionforaddress?addressid={addressid}")]
        void CloseConnectionForAddress(string addressID);
    }

I’ve put together a Ruby sample that hooks up to the service and pulls down the notifications. I’ve put the sample in github so that it can be updated if needs be.

require 'httpclient'
require 'json'
require 'UUIDTools'
 
puts "sipsorcery get notifications sample"
 
notificationsURL = "https://www.sipsorcery.com/notificationspull.svc/rest/"
myUsername = "username"
myPassword = "password"
addressID = UUIDTools::UUID.random_create
filter = "event%2053"
 
client = HTTPClient.new
resp = client.get_content("#{notificationsURL}login?username=#{myUsername}&password=#{myPassword}")
authID = resp.delete('"')
puts "authID=#{authID}"
 
resp = client.get_content("#{notificationsURL}subscribeforaddress?subject=console&filter=#{filter}&addressid=#{addressID}", nil, "authID" => authID)
puts "Notifications subscribe response=#{resp}"
 
30.times do
  resp = client.get_content("#{notificationsURL}getnotificationsforaddress?addressid=#{addressID}", nil, "authID" => authID)
  if !resp.empty?
    notifications = JSON.parse(resp.to_s)
    notifications[0]["Value"].each do |notification|
      puts notification.chomp
    end
  end
  sleep(1)
end
 
# Close the notifications connection.
client.get_content("#{notificationsURL}closeconnectionforaddress?addressid=#{addressID}", nil, "authID" => authID)
 
puts "finished"

I will explain each of the service methods in my next post.

Advertisements