Skip to main content

Indigo Duplex Bindings

Posted by arungupta on May 16, 2005 at 4:26 PM EDT

In part 1 of the series, I gave a short tutorial on Indigo bindings. In this entry, I'll describe Indigo Duplex bindings and related code sample.

Indigo Duplex Binding enables bi-directional communication between a service and client. The steps in Indigo documentation are scattered unless I look at the Duplex Contract sample. In this entry I'm listing all the steps and tried explaining my understanding at each step with certain questions I'm still trying to answer.

In a single direction communication, client can invoke a set of operations on a service and this interface is referred as "primary" interface in Duplex binding. The set of operations that service can invoke on client is called as "callback" interface. To enable bi-directional communication, here is what I need to do on the service side:

  1. Link the "primary" and "callback" interface by setting CallbackContract property in the "primary" interface to the type of the "callback" interface.
  2. Get the CallbackChannel from OperationContext specifying the "callback" interface

Here is the service endpoint interface showing the linking using CallbackContract in line (01):

(01) [ServiceContract(CallbackContract=typeof(ICalculatorCallback)]
(02) public interface ICalculator {
(03)   [OperationContract]
(04)   double add(double n1, double n2);
(05) }
(06) 
(07) public interface ICalculatorCallback {
(08)   [OperationContract]
(09)   void display(string line);
(10) }

Line (02) to (05) is the "primary" interface and line (07) to (10) is the "callback" interface. Here is the service endpoint implementation:

(01) public class CalculatorService : ICalculator {
(02)   CalculatorCallback callback = null;
(03)   public CalculatorService() {
(04)     callback = OperationContext.Current.GetCallbackChannel<ICalculatorCallback>();
(05)   }
(06) 
(07)   public double add(double n1, double n2) {
(08)     callback.display(“Adding “ + n1 + “ and “ + n2);
(09)     return n1 + n2;
(10)   }
(11) }
Line (03) to (05) is the constructor and retrieves the callback channel, passing the generic type ICalculatorCallback. Line (08) invokes the "callback" interface. The "callback" interface is implemented on the client side but the programming model requires it to be defined on the server side explicitly. Here is the binding configuration file:
(01) <configuration>
(02)   <system.serviceModel>
(03)     <services>
(04)       <service serviceType=”Calculator”>
(05)         <endpoint
(06)          address=””
(07)           contractType=”ICalculator”
(08)          bindingType=”wsProfileDualHttpBinding”/>
(09)       </service>
(10)     </services>
(11)   </system.serviceModel>
(12) </configuration>

Line (08) shows the WsProfileDualHttpBinding is used and that's what enables duplex binding on the service. Once the service is deployed in IIS, here is an Indigo generated wsdl:portType:

(01) <portType>
(02)   <wsdl:operation name="Add">
(03)     <wsdl:input wsa:Action="..." message="tns:ICalculator_Add_InputMessage"/>
(04)     <wsdl:output wsa:Action="..." message="tns:ICalculator_Add_OutputMessage"/>
(05)   </wsdl:operation>
(06) 
(07)   <wsdl:operation name="Display">
(08)     <wsdl:output wsa:Action="..." message="tns:ICalculator_Display_OutputCallbackMessage"/>
(09)     <wsdl:input wsa:Action="..." message="tns:ICalculator_Display_InputCallbackMessage"/>
(10)   </wsdl:operation>
(11) </portType>
The methods on "callback" interface, Display in our case, are described in WSDL 1.1 using the following patterns:
  1. wsdl:operation on line (07) resembles WSDL 1.1 solicit-response operation, i.e. the sequence is wsdl:output and wsdl:input instead of wsdl:input and wsdl:output in the conventional request-response operation.
  2. The input message, on line (08), is suffixed with _InputCallbackMessage and output message, on line (09), with _OutputCallbackMessage.

wsdl:port within wsdl:service looks like:

(01) <wsdl:port name="WSProfileDualHttpBinding_ICalculator_port" binding="i0:WSProfileDualHttpBinding_ICalculator">
(02)   <soap:address location="http://wsstar/duplex/service.svc" /> 
(03)     <EndpointReference xmlns="http://schemas.xmlsoap.org/ws/2004/08/addressing"> 
(04)       <Address>http://wsstar/duplex/service.svc</Address> 
(05)     </EndpointReference>
(06) </wsdl:port>

Line (03) to (05) shows an EndpointReference within wsdl:port as an extensibility element. Now, lets take a look at the client code:

(01) public class CalculatorClient {
(02)   static void Main() {
(03)     ServiceSite site = new ServiceSite(CalculatorCallback);
(04)     CalculatorProxy proxy = new CalculatorProxy(site, "CalculatorEndpoint");
(05)     double result = proxy.add(10.00D, 20.00D);
(06)     Console.writeLine("Result is: " + result);
(07)   }
(08) }
(09) 
(10) public class CalculatorCallback : ICalculatorCallback {
(11)   public void Display(string line) {
(12)     Console.writeLine(line);
(13)   }
(14) }

Line (01) to (08) is the client class and line (10) to (14) is the implementation of the "callback" interface defined on the service. Most of the client code is simple except ServiceSite class, in line (03), which seems to perform the magic of enabling the callback. ServiceSite is initialized by passing a reference to the implementation of "callback" interface and then it is passed to the proxy constructor. Service endpoint is invoked, via proxy, on line (05). I could not find much documentation on how ServiceSite enables duplex contract but here is the console output on the client-side:

(01) Adding 10.00 and 20.00
(02) Result is: 30.00

So the request goes from client to the service endpoint, line (08) in service endpoint implementation above is invoke that displays line (01) in the console, response comes back to client and then line (02) from the client code, in line (06), is displayed.

Here is the SOAP header of the request message from client to service endpoint:

<s:Header>
  <a:Action s:mustUnderstand="1">http://schemas.xmlsoap.org/ws/2005/02/rm/CreateSequence</a:Action>
  <a:ReplyTo>
    <a:Address>http://wsstar:8000/Temporary_Indigo_Addresses/...698064</a:Address>
  </a:ReplyTo>
  <a:MessageID>uuid:1adf8d20-bad9-47a1-90aa-8510c3b0bfec;id=0</a:MessageID>
  <a:To s:mustUnderstand="1">http://localhost/duplex/service.svc</a:To>
</s:Header>
The particular header element to notice is <a:ReplyTo> that sends a HTTP address to service endpoint and thus enabling callback. ServiceSite extends from CommunicationObject, but unfortunately it is not a hyperlink (how I miss Javadoc style documentation) and I could not find any other related documentation so I still need to understand the magic behind how this header is generated. So far my understanding is that specifying ServiceSite seems to generate an HTTP endpoint, hosted on IIS, on port 8000 in the context root Temporary_Indigo_Addresses. Is this supposed to work on a non-Windows platform ?

Anyway, here is the SOAP header of the response message from service to client endpoint:

<s:Header>
  <a:Action s:mustUnderstand="1">http://schemas.xmlsoap.org/ws/2005/02/rm/CreateSequenceResponse</a:Action>
  <a:RelatesTo>uuid:1adf8d20-bad9-47a1-90aa-8510c3b0bfec;id=0</a:RelatesTo>
  <a:To s:mustUnderstand="1">http://wsstar:8000/Temporary_Indigo_Addresses/...698064</a:To>
  <RedirectTo s:mustUnderstand="1" xmlns="http://schemas.xmlsoap.org/ws/2004/06/addressingex">
    <a:EndpointReference>
      <a:Address>http://wsstar/duplex/service.svc</a:Address>
      <Via>http://wsstar:8000/Temporary_Indigo_Addresses/...26df92</Via>
    </a:EndpointReference>
  </RedirectTo>
</s:Header>

Note that the <a:To> header uses the <a:ReplyTo> header from the SOAP request message and thus enabling callback.

Related Topics >>