HTTP Bridge





HTTP Bridge

The MSMQ binding is designed to be employed on the intranet. It cannot go through firewalls by default, and more importantly, it uses a Microsoft-specific encoding and message format. Even if you could tunnel though the firewall, you would need the other party to use WCF. While requiring WCF at both ends is a reasonable assumption on the intranet, it is unrealistic to demand that from Internet-facing clients and services, and it violates a core service-oriented principle that service boundaries are explicit and that the implementation technology used by the service is immaterial to its clients. That said, Internet services may benefit from queued calls just like intranet clients and services, and yet the lack of an industry standard for such queued interoperability (and the lack of support in WCF) prevents such interaction. The solution to that is a technique I call the HTTP bridge. Unlike most of my other techniques shown in this book, the HTTP bridge is a configuration pattern rather than a set of helper classes in a small framework. The HTTP bridge, as its name implies, is designed to provide queued calls support when going over the Internet to a service or a client. The bridge requires the use of WSHttpBinding because it is a transactional binding. The bridge has two parts to it. The bridge enables WCF clients to queue up calls to an Internet service that uses the WS binding. The bridge also enables a WCF service that exposes an HTTP endpoint over WS binding to queue up calls from its Internet clients. You can use each part of the bridge separately, or you can use them in conjunction. The bridge can only be used if the remote service contract can be queued (that is, the contract has only one-way operations), but that is usually the case; otherwise the client would not have been interested in the bridge in the first place.

Designing the Bridge

Since you cannot really queue up calls with the WS binding, you would facilitate that instead using an intermediary bridging client and service. When the client wishes to queue up a call against an Internet-based service, the client would queue up a call against a local (that is, intranet-based) queued service called MyClientHttpBridge. The client-side queued bridge service in its processing of the queued call will use the WS binding to call the remote Internet-based service. When an Internet-based service wishes to receive queued calls, it will use a queue. Because that queue cannot be accessed by non-WCF clients over the Internet, the service will use a façadea dedicated connected service called MyServiceHttpBridge that exposes a WS-binding endpoint. In its processing of the Internet call, MyServiceHttpBridge simply makes a queued call against the local service. Figure shows the HTTP bridge architecture.

The HTTP bridge


Transaction Configuration

It is important to use transactions between MyClientHttpBridge, the client side of the bridge, and the remote service, and it is important to configure the service-side bridge (MyServiceHttpBridge) to use the Client transaction mode of Chapter 7. The rationale is that by using a single transaction from the playback of the client call to the MyClientHttpBridge to the MyServiceHttpBridge (if present) you will approximate the transactional delivery semantic of a normal queued call, as shown in Figure.

The HTTP bridge and transactions


Compare Figure with Figure. If the delivery transaction in the bridge aborts for whatever reason, the message will roll back to the MyClientHttpBridge queue for another retry. To maximize the chances for successful delivery, you should also turn on reliability for the call between the MyClientHttpBridge and the remote service.

Service-Side Configuration

MyServiceHttpBridge converts a regular connected call over the WS binding into a queued call and posts it to the service queue. MyServiceHttpBridge implements a similar, but not identical, contract to the queued service. The reason is that the service-side bridge should be able to participate in the incoming transaction, but transactions cannot flow over one-way operations. The solution is to modify the contract to support and even mandate transactions. For example, if this is the original service contract:

[ServiceContract]
public interface IMyContract
{
   [OperationContract(IsOneWay = true)]
   void MyMethod( );
}

then MyServiceHttpBridge should expose this contract instead:

[ServiceContract]
public interface IMyContractHttpBridge
{
   [OperationContract]
   [TransactionFlow(TransactionFlowOption.Mandatory)]
   void MyMethod( );
}

In essence, you need to set IsOneWay to false and use transactionFlowOption.Mandatory. For readability's sake, I recommend you also rename the interface by suffixing HttpBridge to it. The MyServiceHttpBridge can be hosted anywhere in the service's intranet, including the service's own process. Figure shows the required configuration of the service and its HTTP bridge.

Service-side configuration of the HTTP bridge

////////////////////// MyService Config File //////////////////////////
<services>
   <service name = "MyService">
      <endpoint
         address  = "net.msmq://localhost/private/MyServiceQueue"
         binding  = "netMsmqBinding"
         contract = "IMyContract"
      />
   </service>
</services>
////////////////////// MyServiceHttpBridge Config File ////////////////
<services>
   <service name  = "MyServiceHttpBridge">
      <endpoint
         address  = "http://localhost:8001/MyServiceHttpBridge"
         binding  = "wsHttpBinding"
         bindingConfiguration = "ReliableTransactedHTTP"
         contract = "IMyContractHttpBridge"
      />
   </service>
</services>

<client>
   <endpoint
      address  = "net.msmq://localhost/private/MyServiceQueue"
      binding  = "netMsmqBinding"
      contract = "IMyContract"
   />
</client>

<bindings>
   <wsHttpBinding>
      <binding name = "ReliableTransactedHTTP" transactionFlow = "true">
         <reliableSession enabled = "true"/>
      </binding>
   </wsHttpBinding>
</bindings>

The service MyService exposes a simple queued endpoint with IMyContract. The service MyServiceHttpBridge exposes an endpoint with WSHttpBinding and the IMyContractHttpBridge contract. MyServiceHttpBridge is also a client of the queued endpoint defined by the service. Figure shows the corresponding implementation. Note that MyServiceHttpBridge is configured for Client transaction mode.

Service-side implementation of the HTTP bridge

[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
class MyService : IMyContract
{
   //This call comes in over MSMQ
   [OperationBehavior(TransactionScopeRequired = true)]
   public void MyMethod( )
   {...}
}

[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
class MyServiceHttpBridge : IMyContractHttpBridge
{
   //This call comes in over HTTP
   [OperationBehavior(TransactionScopeRequired = true)]
   public void MyMethod( )
   {
      MyContractClient proxy = new MyContractClient( );

      //This call goes out over MSMQ
      proxy.MyMethod( );

      proxy.Close( );
   }
}

Client-Side Configuration

The client uses queued calls against the local MyClientHttpBridge service. The MyClientHttpBridge can even be hosted in the same process as the client, or it can be on a separate machine on the client's intranet. MyClientHttpBridge uses WSHttpBinding to call the remote service. The client needs to retrieve the metadata of the remote Internet service (such as the definition of IMyContractHttpBridge) and convert it to a queued contract (such as IMyContract). Figure shows the required configuration of the client and its HTTP bridge.

Client-side configuration of the HTTP bridge

//////////////////////// Client Config File //////////////////////////
<client>
   <endpoint
      address  = "net.msmq://localhost/private/MyClientHttpBridgeQueue"
      binding  = "netMsmqBinding"
      contract = "IMyContract"
   />
</client>
////////////////////// MyClientHttpBridge Config File ////////////////
<services>
   <service name  = "MyClientHttpBridge">
      <endpoint
         address  = "net.msmq://localhost/private/MyClientHttpBridgeQueue"
         binding  = "netMsmqBinding"
         contract = "IMyContract"
      />
   </service>
</services>
<client>
   <endpoint
      address  = "http://localhost:8001/MyServiceHttpBridge"
      binding  = "wsHttpBinding"
      bindingConfiguration = "ReliableTransactedHTTP"
      contract = "IMyContractHttpBridge"
   />
</client>
<bindings>
   <wsHttpBinding>
      <binding name = "ReliableTransactedHTTP" transactionFlow = "true">
         <reliableSession enabled = "true"/>
      </binding>
   </wsHttpBinding>
</bindings>

MyClientHttpBridge exposes a simple queued endpoint with IMyContract. MyClientHttpBridge is also a client of the connected WS-binding endpoint defined by the service. Figure shows the corresponding implementation.

Client-side implementation of the HTTP bridge

MyContractClient proxy = new MyContractClient( );

//This call goes out over MSMQ
proxy.MyMethod( );

proxy.Close( );

////////////////  Client-side bridge implementation ////////////
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
class MyClientHttpBridge : IMyContract
{
   //This call comes in over MSQM
   [OperationBehavior(TransactionScopeRequired = true)]
   public void MyMethod( )
   {
      MyContractHttpBridgeClient proxy = new MyContractHttpBridgeClient( );

      //This call goes out over HTTP
      proxy.MyMethod( );

      proxy.Close( );
   }
}



 Python   SQL   Java   php   Perl 
 game development   web development   internet   *nix   graphics   hardware 
 telecommunications   C++ 
 Flash   Active Directory   Windows