January 31, 2004

Channels

In a previous entry I mentioned that we had added a new, low-level programming model to the WSE 2.0 release, this entry gives some early, overview, information on what that programming model is and how it works.

public interface ISoapChannel
{
    void Close();
    bool Closed { get; }
    EndpointReference Endpoint { get; }
}

public interface ISoapInputChannel : ISoapChannel
{
    IAsyncResult BeginReceive(...);
    SoapEnvelope EndReceive(IAsyncResult ar);
    SoapEnvelope Receive();
}

public interface ISoapOutputChannel : ISoapChannel
{
    IAsyncResult BeginSend(SoapEnvelope msg, ...);
    void EndSend(IAsyncResult ar);
    void Send(SoapEnvelope msg);
}

public interface ISoapTransport
{
    ISoapInputChannel GetInputChannel(EndpointReference epr, ...);
    ISoapOutputChannel GetOutputChannel(EndpointReference epr, ...);
}

As you can see from the above code, the new model is exclusively channel and EndpointReference based. Sending a message involves retrieving an ISoapOutputChannel to the target endpoint and then calling the Send method. Receiving a message involves retrieving an ISoapInputChannel for a local endpoint and then calling Receive. There will be a single place to obtain a channel from, but this is presently in flux; expect something like a SoapChannelManager class.

As with the Tech Preview release, we've continued the approach of trying to avoid leaking transport-level semantics up into even this level of the programming model; there's no notion of the TCP-level listen / accept model in the channels layer. Further, a channel can be used to either send or receive, but not both, and there's no access to the network layer itself - you cannot retrieve a socket handle or an MSMQ handle from a channel.

We learned a lot in this area from experimenting with the Tech Preview and from Kevin's work implementing a transport for MSMQ. In particular we realised that the bi-directional capabilities of HTTP and TCP that we'd leaked up into the Tech Preview programming model caused problems when the transport wasn't capable of providing such a connection[1].

Channels are, however, directly bound to transports via the TransportAddress of the EndpointReference object that they relate to. This has two effects: (1) to receive or send over different transports you need two channels (2) transport cross-over cannot occur. The second point is important in that we don't want allow messages to be dispatched to an input channel when that message didn't arrive on the transport associated with the channel.

Following on from the above is the fact that the fundamental message dispatching takes place at the transport level in current builds, not in the old SoapReceiver.DispatchMessage method of the Tech Preview. Each transport has a collection of input channels ordered by EndpointReference; incoming messages are deserialized and the addressing headers are loaded. The transport then selects the correct input channel to deliver the message to. A common base class, SoapTransport, provides helper methods for individual transports to perform the dispatch.

You may also have noticed that channels do not have Pipelines associated with them. Channels are presently an abstraction of the network transport for sending and receiving messages - only limited WS-Addressing processing occurs as part of SoapEnvelope deserialization to allow for dispatching, all other filters presently remain as part of the Pipeline. Looking forward to the future, I can see a model where the current filters are replaced by "filter channels" and a message is passed through a set of channels rather than the Pipeline, this migration could be simplified with a simple adapter class.

The current Pipeline, along with channels, is used by the SoapSender / SoapReceiver layer to process the headers in messages. SoapSender / SoapReceiver have been re-plumbed to use the new channel model. Adding a SoapReceiver to the SoapReceivers collection triggers the creation of an ISoapInputChannel that the collection then posts a BeginReceive on. When a message appears on the channel it is dispatched to the appropriate SoapReceiver.

As you can see, this is a fairly fundamental change to the WSE 2.0 Messaging plumbing - it provides a stronger foundation for the current feature set and for several items that we have planned for the next release. There's also a little more synergy, at least conceptually, with the low-level Indigo programming model.

[1] You can, however, imagine a channel implementation that implements both ISoapInputChannel and ISoapOutputChannel to provide an ISoapDuplexChannel. We're still debating how useful this would be in practice given differences in transport semantics.

Posted by herveyw at January 31, 2004 02:10 PM
Comments

It seems like a bad thing to implement filters as channels, i.e. it feels convenient (this method takes a envelope, the filter needs a method that takes an envelope, bingo) rather than good. It seems like a channel should be something over which you transmit or receive a message, aka the pipe in "Pipes and Filters." I could see chaining together channels conceptually as a routing construct, but not as an "allow this message through if this and that condition are true."

Posted by: Jef Newsom at March 24, 2004 10:46 AM