December 30, 2005
@ 11:08 PM

My little “TV anywhere“ project makes a bit more sense now, does it? Because Sabine and I will more than likely find ourselves somewhere in the Seattle area by mid-2006 (I’ll be telecommuting and “long-haul shuttling” for a while) and we are both big time German football (as in “soccer”) fans, we just need to fix a problem.

And I am doing it just because I can, of course. The whole Indigo REST/POX series here on the blog does nothing more than describe all the WCF extensions I wrote to build my personal TV server. The first screenshot here on the right shows the current state of things as it shows up in Media Player. The UI is a Windows Media Player hosted AJAX app with EPG data in tooltips as I hover over the channel icons, I can schedule recordings with a single mouse click and I have access to all my recordings on a separate channel or just on the channel that’s currently being watched. But that’s just the “Clemens on the road” version of this.

The other front-end is the “far away at home” version of it and that looks like the screen shots on the left. Meanwhile I have an AJAX front-end that’s built for Windows XP Media Center Edition (MCE) and snaps right into the MCE experience. My dad will get the PC that sits here in my living room and host it at his house behind an upgraded DSL line and my (to be bought) MCE machine over in Seattle will connect to it and give me remote (control) access to the 35 cable channels over here. That’s the plan.

The backend is and remains Beyond TV, because it’s a very flexible, programmable and – most importantly – streaming enabled TV engine. It’s an unlikely couple, but I decided that Beyond TV and MCE should have a wedding on my boxes. Works well.

The immediate question is of course still “Looks cool, when can I have it?” The issue is that this is a lot more of a tricky story than DasBlog, because I am depending on an existing backend software with its own, a bit limiting EULA, I am relying on a foundation that isn’t even released (WCF) and I am a lot more worried about the support situation, because setting this up correctly with Beyond TV alongside MCE and my service is … ummm… only something for folks who have no fear of the gates of Mordor and beyond. We’ll see what I can do…

Categories:

Part 1, Part 2, Part 3, Part 4

POX means “plain old XML” and I’ve also heard a definition saying that “POX is REST without the dogma”, but that’s not really correct. POX is not really well defined, but it’s clear that the “plain” is the focus and that means typically that folks who talk about “POX web services” explicitly mean that those services don’t use SOAP. You could see POX as an antonym for SOAP.

The design of Indigo (WCF) assumes that all messages that go onto the wire and come from the wire have a shape that is aligned with SOAP. That means that they have a “body” containing the user-defined message payload and a “header” section that contains the out-of-band metadata that helps getting the message from one place to the next, possibly through a chain of intermediaries. Most of the Indigo binding elements and their implementations also assume that those metadata elements (headers) conform to their respective WS-* standard that they are dealing with.

However, Indigo isn’t hard-wired to a specific envelope format. The default “encoders” that are responsible for turning a message into data stream (or a data package) that a transport can throw down a TCP socket or into a message queue (or whatever else) and which are likewise responsible for picking up the data from the wire to turn them into Message objects have two envelope formats baked in: SOAP 1.1 and SOAP 1.2. But that doesn’t mean that you have to use those. If your envelope format were different (there seem to be thousands, I’ll name AdsML [spec] as an example) and that’s what you want to use on the wire, you can assemble a binding that will compose an Indigo transport with your encoder. Moving away from SOAP means, though, that you can’t use the standard implementations of capabilities such as message-level security, reliable delivery, and transaction flow, because all of these are built on the assumption that you are exchanging WS-* headers with the other party and all of these specs depend on the SOAP information model. But if there are comparable specifications that come with your envelope format you can of course write Indigo extensions that you can configure into a binding just like you can compose the default binding elements. It’d be a lot of work to do that, but you’d still benefit greatly from the Indigo architecture per-se.

When we want to use a REST/POX model, our envelope format is quite simple: We don’t really have an envelope.

The idea of POX is that there’s only payload and that out-of-band metadata is unnecessary fluff. The idea of REST is that there is already and appropriate place for out-of-band metadata and that’s the HTTP headers.

In order to make REST/POX work, we therefore need to replace the Indigo default encoder with an encoder that fulfills these requirements:

1.      Extract the message body XML content of any outbound message and format it for the wire as-is and without a SOAP envelope around it and

2.      Accept an arbitrary inbound XML data and wrap it into a Message-derived class so that Indigo can handle it.

Since the use-case in whose context I’ve developed these extensions is a bit more far reaching than POX, but I indeed want to support RESTful access to any data including multi-GByte unencapsulated MPEG recordings I make on my Media PC, I’ve broadened these two requirements a bit and left out the “XML” constraint:

1.      Extract the message body XML content of any outbound message and format it for the wire as-is and without a SOAP envelope around it and

2.      Accept an arbitrary inbound XML data and wrap it into a Message-derived class so that Indigo can handle it.

XML aka POX is an interesting content-type to throw around, but it’s by no means the only one and therefore let’s not restrict ourselves too much here. Any content is good.

But then again, Indigo is assuming that all messages flowing through its channels contain XML payloads and therefore we’ve got a bit of a nut to crack when we want to use Indigo for arbitrary, non-XML payloads of arbitrary size. Luckily, XML is just an illusion.

The Indigo Message holds the message body content inside an XmlDictionaryReader (which is an optimized derivation of the well-known XmlReader). To construct a message, you can walk up to the static Message.CreateMessage(string action, XmlDictionaryReader reader) factory method and pass the readily formatted body content as a reader object and the message will happily adopt it. But can we use the XmlReader to smuggle arbitrary binary content into the message so that our own encoder can later unwrap it and put it onto the wire in whatever raw binary format we like? Sure we can! The class below may look a bit like an evil hack, but it’s a perfectly legal construct:

using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.IO;
using System.Xml.Schema;

namespace newtelligence.ServiceModelExtensions
{
   public class PoxBase64XmlStreamReader :
XmlTextReader
   {
      private const string xmlEnvelopeString =
           "<base64Binary xmlns:xsi=\"" + XmlSchema.InstanceNamespace + "\" " +
           "xmlns:xsd=\"" + XmlSchema.Namespace + "\" " +
           "xsi:type=\"xsd:base64Binary\">placeholder</base64Binary>";
      Stream innerStream;

      ///
<summary>
      /// Initializes a new instance of the <see cref="T:PoxBase64XmlStreamReader"/>
class.
      ///
</summary>
      /// <param name="stream">The stream.
</param>
      public PoxBase64XmlStreamReader(Stream stream)
         : base(new StringReader(xmlEnvelopeString))
      {
         innerStream = stream;
      }

      ///
<summary>
      ///
Gets The Common Language Runtime (CLR) type for the current node.
      ///
</summary>
      ///
<value></value>
      /// <returns>The CLR type that corresponds to the typed value of the node. The default is System.String.
</returns>
      public override Type ValueType
      {
         
get
         {
            if (NodeType == XmlNodeType.Text && base.Value == "placeholder")
            {
               return typeof(Byte[]);
            }
            
else
            {
               return base.ValueType;
            }
         }
      }
   
      ///
<summary>
      ///
Gets the text value of the current node.
      ///
</summary>
      public override string Value
      {
         
get
         {
            if (NodeType == XmlNodeType.Text && base.Value == "placeholder")
            {
               BinaryReader reader = new BinaryReader(innerStream);
               return Convert.ToBase64String(reader.ReadBytes((int)(reader.BaseStream.Length - reader.BaseStream.Position)));
            }
            return base.Value;
         }
      }

      ///
<summary>
      ///
Reads the content and returns the Base64 decoded binary bytes.
      ///
</summary>
      /// <param name="buffer">The buffer into which to copy the resulting text. This value cannot be null.
</param>
      /// <param name="index">The offset into the buffer where to start copying the result.
</param>
      /// <param name="count">The maximum number of bytes to copy into the buffer. The actual number of bytes copied is returned from this method.
</param>
      ///
<returns>
      ///
The number of bytes written to the buffer.
      ///
</returns>
      public override int ReadContentAsBase64(byte[] buffer, int index, int count)
      {
         if (NodeType == XmlNodeType.Text && base.Value == "placeholder")
         {
            return innerStream.Read(buffer, index, count);
         }
         
else
         {
            return base.ReadContentAsBase64(buffer, index, count);
         }
      }
   }
}

The PoxBase64XmlStreamReader is a specialized XML reader reading a fixed info-set constructed from a string that has a “placeholder” in whose place the content of a wrapped data stream is returned “as base64 encoded content”. Of course that latter statement is hogwash. The data is never encoded in base64 anywhere. But the consumer of the reader thinks that it is and that’s really good enough for us here. The XmlReader creates the illusion that the wrapped data stream were the “text” node of a base64Binary typed element and if that’s what the client wants to believe, we’re happy.  The implementation trick here is of course very simple. As long as the reader isn’t hitting the text node with the “placeholder” all work is being delegated to the base class. Once we arrive at that particular node, we change tactics and return the data type (byte[]) and the content of the wrapped stream instead of the “placeholder” string. After that we continue delegating to the base class. If the client asks for the Value of the text node, we are returning a base64 encoded string representation of the wrapped stream which might end up being pretty big. However, if the client is a bit less naïve about the content, it will figure that the data type is byte[] and therefore retrieve the data in binary chunks through the ReadContentAsBase64() method. Let’s assume that the client will be that clever.

It doesn’t take too much imagination talent to do so, because I’ve got the client right here. I used Doug Purdy’s PoxEncoder that he showed at PDC05 as a basis for this and extended it (quite) a bit:

using System;
using System.IO;
using System.Xml;
using System.Text;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Design;
using System.Runtime.CompilerServices;
using System.ServiceModel.Configuration;
using System.Configuration;
using System.Globalization;
using System.Xml.Schema;
using System.Diagnostics;

namespace newtelligence.ServiceModelExtensions
{
    /// <summary>
    /// This class is a wire-format encoder for System.ServiceModel that renders
    /// only the content (body) of a <see cref="T:Message"/> onto the wire, but not
    /// the surrounding SOAP message elements such as the enevlope, the headers or
    /// the body element. Likewise, the encoder expects input to be in 'raw', unwrapped
    /// form and will wrap it into a message for processing by the System.ServiceModel
    /// infrastructure.
    /// </summary>
    public class PoxEncoder : MessageEncoder
   {
      string contentType;
      string mediaType;
      Encoding textEncoding;
      MessageVersion messageVersion;

      /// <summary>
      /// Creates a new instance of PoxEncoder
      /// </summary>
      public PoxEncoder()
      {
          messageVersion = MessageVersion.Soap11Addressing1;
          textEncoding = Encoding.UTF8;
         Initialize();
      }

      /// <summary>
      /// Creates a new instance of PoxEncoder
      /// </summary>
      /// <param name="messageVersion"></param>
      public PoxEncoder(MessageVersion messageVersion)
      {
         this. messageVersion = messageVersion;
          textEncoding = Encoding.UTF8;
         Initialize();
      }


      /// <summary>
      /// Creates a new instance of PoxEncoder
      /// </summary>
      /// <param name="textEncoding"></param>
      /// <param name="messageVersion"></param>
      public PoxEncoder(Encoding textEncoding, MessageVersion messageVersion)
      {
         this. textEncoding = textEncoding;
         this. messageVersion = messageVersion;
         Initialize();
      }

        /// <summary>
        /// Initializes common properties of the encoder.
        /// </summary>
      private void Initialize()
      {
         if (this.MessageVersion.Envelope == EnvelopeVersion.Soap12)
         {
                // set the aprorpiate media type for SOAP 1.2
            this. mediaType = "application/soap+xml";
         }
         else if (this.MessageVersion.Envelope == EnvelopeVersion.Soap11)
         {
                // set the appropriate media type for SOAP 1.1
            this. mediaType = "text/xml";
         }
            // compose the content type from charset and media type
         this. contentType = string.Format(CultureInfo.InvariantCulture, "{0}; charset={1}", mediaType, textEncoding.WebName);
      }

        /// <summary>
        /// Gets the content type for the encoder instance
        /// </summary>
      public override string ContentType
      {
         get
         {
            return contentType;
         }
      }

        /// <summary>
        /// Gets the media type for the encoder instance
        /// </summary>
      public override string MediaType
      {
         get
         {
            return mediaType;
         }
      }

        /// <summary>
        /// Gets an indicator for whether a given input content type is
        /// supported.
        /// </summary>
        /// <param name="contentType">ContentType</param>
        /// <returns>Indicates whether the content type is supported</returns>
        /// <remarks>
        /// TODO: This currently returns 'true' for all content types because the
        /// encoder isn't locked down in features yet and this easier to debug.
        /// The plan is to support at least: application/x-www-form-urlencoded,
        /// text/xml, application/soap+xml
        /// </remarks>
      public override bool IsContentTypeSupported(string contentType)
      {
         return true;
      }

        /// <summary>
        /// Gets the supported message version of this instance
        /// </summary>
      public override MessageVersion MessageVersion
      {
         get
         {
            return messageVersion;
         }
      }

        /// <summary>
        /// Reads an incoming array segment containing a message and
        /// wraps it with a buffered message. The assumption is that the incoming
        /// data stream is <i>not</i> a SOAP envelope, but rather an unencapsulated
        /// data item, may it be some raw binary, an XML document or HTML form
        /// postback data. This method is called if the inbound transfer mode of the
        /// transport is "buffered".
        /// </summary>
        /// <param name="buffer">Buffer to wrap</param>
        /// <param name="bufferManager">Buffer manager to help with allocating a copy</param>
        /// <returns>Buffered message</returns>
        public override Message ReadMessage(ArraySegment<byte> buffer, BufferManager bufferManager)
      {
         return new PoxBufferedMessage(buffer, bufferManager);
      }

        /// <summary>
        /// Transforms an incoming message into a raw byte array that a transport can
        /// literally put on the wire as it is returned. This method is called if the outbound
        /// transfer mode of the transport is "buffered".
        /// </summary>
        /// <param name="msg">Input message</param>
        /// <param name="maxMessageSize">Maximum message size to be rendered</param>
        /// <param name="bufferManager">Buffer manager to optimize buffer allocation</param>
        /// <param name="messageOffset">Offset into the message to render.</param>
        /// <returns>Array segment containing the binary data to be put onto the wire by the transport.</returns>
        /// <remarks>
        /// <para>This method is the "secret sauce" of the the PoxEncoder. Instead of encoding the
        /// message in its entirety, this encoder will unwrap the message body and toss out
        /// the envelope and all headers. The resulting "raw" message body (everything inside
        /// and not including soap:Body) will be written out to the transport.</para>
        /// <para>The encoder has an optional, "out of band" argument that is flowing into it
        /// as part of the message's Properties. By adding a <see cref="T:PoxEncoderMessageProperty"/>
        /// to the <see cref="Message.Properties"/> and setting its <see cref="PoxEncoderMessageProperty.RawBinary"/>
        /// property to 'true', you can switch the encoder into its 'raw binary' mode.</para>
        /// <para> In 'raw binary' mode, the encoder expects that the only child of the message
        /// body element is an element with a local name of "base64Binary" containing base64 encoded
        /// binary data. If that is the case, the encoder will read the content of that element
        /// and return it (not the XML wrapper) to the transport in binary form. If the content does
        /// not comply with this requirement, an empty array is returned.
        /// </para>
        /// </remarks>
      public override ArraySegment<byte> WriteMessage(Message msg, int maxMessageSize, BufferManager bufferManager, int messageOffset)
      {
         if (msg.IsEmpty)
         {
            // if the message is empty (no body defined) the result is an empty
            // byte array.
            byte[] buffer = bufferManager.TakeBuffer(maxMessageSize);
            return new ArraySegment<byte>(buffer, 0, 0);
         }
         else
         {
            // check RawBinary bit in the message property
                bool rawBinary = false;
                if (msg.Properties.ContainsKey(PoxEncoderMessageProperty.Name))
            {
               rawBinary = ((PoxEncoderMessageProperty)msg.Properties[PoxEncoderMessageProperty.Name]).RawBinary;
            }

            ArraySegment<byte> retval = new ArraySegment<byte>();
            byte[] buffer = bufferManager.TakeBuffer(maxMessageSize);
            if (!rawBinary)
            {
               // If we're rendering XML data, we construct a memory stream
               // over the output buffer, layer an XMLDictionaryWriter on top of it
               // and have the message write the body content into the buffer as XML.
               // The buffer is then wrapped into an array segment and returned.
               MemoryStream stream = new MemoryStream(buffer);
               XmlWriterSettings settings = new XmlWriterSettings();
               settings.OmitXmlDeclaration = true;
               settings.Indent = true;
               settings.Encoding = this. textEncoding;
               XmlWriter innerWriter = XmlWriter.Create(stream, settings);
               XmlDictionaryWriter writer = XmlDictionaryWriter.CreateDictionaryWriter(innerWriter, false);
               msg.WriteBodyContents(writer);
               writer.Flush();
               retval = new ArraySegment<byte>(buffer, 0, (int)stream.Position);
            }
            else
            {
               // If we're rendering raw binary data, we grab at most 'buffer.Length'
               // bytes from the binary content of the base64Binary element (if that
               // exists) and return the result wrapped into an array segment.
               XmlDictionaryReader dictReader = msg.GetReaderAtBodyContents();
               if (dictReader.NodeType == XmlNodeType.Element &&
                  dictReader.LocalName == "base64Binary")
               {
                  if (dictReader.Read() && dictReader.NodeType == XmlNodeType.Text)
                  {
                     int size = dictReader.ReadContentAsBase64(buffer, 0, buffer.Length);
                     retval = new ArraySegment<byte>(buffer, 0, size);
                  }
               }
            }
            return retval;
         }
      }

        /// <summary>
        /// Reads an incoming stream containing a message and
        /// wraps it with a streamed message. The assumption is that the incoming
        /// data stream is <i>not</i> a SOAP envelope, but rather an unencapsulated
        /// data item, may it be some raw binary, an XML document or HTML form
        /// postback data. This method is called if the inbound transfer mode of the
        /// transport is "streamed".
        /// </summary>
        /// <param name="stream">Input stream</param>
        /// <param name="maxSizeOfHeaders">Maximum size of headers in bytes</param>
        /// <returns>Stream message</returns>
      public override Message ReadMessage(System.IO.Stream stream, int maxSizeOfHeaders)
      {
         return new PoxStreamedMessage(stream, maxSizeOfHeaders);
      }

        /// <summary>
        /// Transforms an incoming message into a stream that a transport can
        /// literally put on the wire as it is filled. This method is called if the outbound
        /// transfer mode of the transport is "streamed".
        /// </summary>
        /// <param name="msg">Input message</param>
        /// <param name="stream">Stream to write to</param>
        /// /// <remarks>
        /// <para>This method is the "secret sauce" of the the PoxEncoder. Instead of encoding the
        /// message in its entirety, this encoder will unwrap the message body and toss out
        /// the envelope and all headers. The resulting "raw" message body (everything inside
        /// and not including soap:Body) will be written out to the transport.</para>
        /// <para>The encoder has an optional, "out of band" argument that is flowing into it
        /// as part of the message's Properties. By adding a <see cref="PoxEncoderMessageProperty"/>
        /// to the <see cref="Message.Properties"/> and setting its <see cref="PoxEncoderMessageProperty.RawBinary"/>
        /// property to 'true', you can switch the encoder into its 'raw binary' mode.</para>
        /// <para> In 'raw binary' mode, the encoder expects that the only child of the message
        /// body element is an element with a local name of "base64Binary" containing base64 encoded
        /// binary data. If that is the case, the encoder will read the content of that element
        /// and write it (not the XML wrapper) onto the stream in binary form and in at most
        /// 1MByte large chunks. If the content does not comply with this requirement, nothing is written.
        /// </para>
        /// </remarks>
        public override void WriteMessage(Message msg, System.IO.Stream stream)
        {
            try
            {
                if (!msg.IsEmpty)
                {
                    // check RawBinary bit in the message property
                    bool rawBinary = false;
                    if (msg.Properties.ContainsKey(PoxEncoderMessageProperty.Name))
                    {
                        rawBinary = ((PoxEncoderMessageProperty)msg.Properties[PoxEncoderMessageProperty.Name]).RawBinary;
                    }

                    if (!rawBinary)
                    {
                        // If we're rendering XML, we layer an XMLDictionaryWriter over the
                        // output stream and have the message render its body content into
                        // that writer and therefore onto the stream.
                        XmlWriterSettings settings = new XmlWriterSettings();
                        settings.OmitXmlDeclaration = true;
                        settings.Indent = true;
                        settings.Encoding = this. textEncoding;
                        XmlWriter innerWriter = XmlWriter.Create(stream, settings);
                        XmlDictionaryWriter writer = XmlDictionaryWriter.CreateDictionaryWriter(innerWriter, false);
                        msg.WriteBodyContents(writer);
                        writer.Flush();
                    }
                    else
                    {
                        // If we're rendering raw binary data, we grab chunks of at most 1MByte
                        // from the 'base64Binary' content element (if that exists) and write them
                        // out as binary data to the output stream. Chunking is done, because we
                        // have to assume that the body content is arbitrarily large. To optimize the
                        // behavior for large streams, we read and write concurrently and swap buffers.
                        XmlDictionaryReader dictReader = msg.GetReaderAtBodyContents();
                        if (dictReader.NodeType == XmlNodeType.Element && dictReader.LocalName == "base64Binary")
                        {
                            if (dictReader.Read() && dictReader.NodeType == XmlNodeType.Text)
                            {
                                byte[] buffer1 = new byte[1024*1024], buffer2 = new byte[1024*1024];
                                byte[] readBuffer = buffer1, writeBuffer = buffer2;
                               
                                int bytesRead = 0;
                                // read the first chunk into the read buffer
                                bytesRead = dictReader.ReadContentAsBase64(readBuffer, 0, readBuffer.Length);
                                do
                                {
                                    // the abort condition for the loop is that we can't read
                                    // any more bytes from the input because the base64Binary element is
                                    // exhausted.
                                    if (bytesRead > 0 )
                                    {
                                        // make the last read buffer the write buffer
                                        writeBuffer = readBuffer;
                                        // write the write buffer to the output stream asynchronously
                                        IAsyncResult result = stream.BeginWrite(writeBuffer, 0, bytesRead,null,null);
                                        // swap the read buffer
                                        readBuffer = (readBuffer == buffer1) ? buffer2 : buffer1;
                                        // read a new chunk into the 'other' buffer synchronously
                                        bytesRead = dictReader.ReadContentAsBase64(readBuffer, 0, readBuffer.Length);
                                        // wait for the write operation to complete
                                        result.AsyncWaitHandle.WaitOne();
                                        stream.EndWrite(result);
                                    }
                                }
                                while (bytesRead > 0);
                            }
                        }
                    }
                }
            }
            catch
            {
                // the client may disconnect at any time, so that's an expected exception and absorbed.
            }
        }
   }
}


The encoder shown above fulfills my two requirements and it is aware of the PoxBase64XmlReader trickery. It renders unencapsulated data onto the wire and accepts and wraps unencapsulated data from the wire. Furthernore, it supports buffered messages and it supports Indigo’s streaming mode, which allows sending messages of arbitrary size. What’s still missing in the picture is how we hook the encoder into the binding and how we can control whether the encoder works in “POX mode” rending XML or in “Raw Binary” mode rendering arbitrary data content. I might also have to explain what a PoxStreamedMessage is. I might also have to explain a bit better what the encoder does to begin with ;-)

Well, at least you have the code already, Part 6 comes with the prose. 

Categories: Indigo

Part 1, Part 2, Part 3

The SuffixFilter that I have shown in Part 3 of this little series interacts with the Indigo dispatch internals to figure out which endpoint shall receive an incoming request. If the filter reports true from it’s Match() method, the service endpoint that owns the particular filter is being picked and its channel gets the message. But at that point we still don’t know which of the operations on the endpoint’s contract shall be selected to handle the request.

We’ll take a step back and recap what we have by citing one of the contract declarations from Part 1:

[ServiceContract, HttpMethodOperationSelector]
interface IMyApp
{
    [OperationContract, HttpMethod("GET",UriSuffix="/customers/*")]
    CustomerInfo GetCustomerInfo();
    [OperationContract, HttpMethod("PUT", UriSuffix = "/customers/*")]
    void UpdateCustomerInfo(CustomerInfo info);
    [OperationContract, HttpMethod("DELETE", UriSuffix = "/customers/*")]
    void DeleteCustomerInfo();
}

If we implement this contract on a class and host the service endpoint for it at, say, http://www.example.com/myapp this particular endpoint will only accept requests on http://www.example.com/myapp/customers/* (whereby ‘*’ can really be any string) because our suffix filter that’s being hooked in my the HttpMethodOperationSelectorAttribute and populated with “/customers/*” suffix won’t let any other request pass. Only those requests for which a pattern match can be found when combining an operation’s suffix pattern with the endpoint URI are positively matched by the suffix filter. For a more complex example I’ll let you peek at a (shortened) snippet of one the contracts of the TV server I am working on:

/// <summary>
///
Contract for the channel service
/// </summary>
[ServiceContract(Namespace = Runtime.ChannelServiceNamespaceURI), HttpMethodOperationSelector]
public interface IChannelService
{
    /// <summary>
    /// Gets the default RSS for this channel.
    /// </summary>
    /// <param name="message">Input message.</param>
    /// <returns>Reply message with 'text/xml' RSS content</returns>
    [OperationContract, HttpMethod("GET")]
    Message GetRss(Message message);
    /// <summary>
    /// Gets the channel logo as a raw binary image with appropriate
    /// media type, typically image/gif, image/jpeg or image/png
    /// </summary>
    /// <param name="message">Input message.</param>
    /// <returns>Reply message with 'image/*' binary content</returns>
    [OperationContract, HttpMethod("GET", UriSuffix = "/logo")]
    Message GetLogo(Message message);
    /// <summary>
    /// Gets the RSS for "now", which is typically including
    /// the next 12 hours of guide data from the current time
    /// onward and including currently running shows.
    /// </summary>
    /// <param name="message">Input message.</param>
    /// <returns>Reply message with 'text/xml' <a href="http://blogs.law.harvard.edu/tech/rss">
    /// RSS 2.0</a> content</returns>
    [OperationContract, HttpMethod("GET", UriSuffix = "/now")]
    Message GetRssForNow(Message message);
   
    ...

    /// <summary>
    /// Gets an ASX media metadata document containing a reference to
    /// the live TV stream for this channel and a reference to the
    /// HTMLView that provides the UI inside Windows Media Player.
    /// </summary>
    /// <param name="message">Input message.</param>
    /// <returns>Reply message with 'video/x-ms-asf' <a href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/wmplay10/mmp_sdk/asxelement.asp">
    /// ASX 3.0</a> content.</returns>
    [OperationContract, HttpMethod("GET", UriSuffix = "/media")]
    Message GetMedia(Message message);
    /// <summary>
    /// Gets information about the current media session hosted by the provider.
    /// </summary>
    /// <param name="message">Input message.</param>
    /// <returns>Reply message with 'text/xml' content</returns>
    [OperationContract, HttpMethod("GET", UriSuffix = "/media/session")]
    Message GetMediaSession(Message message);
    /// <summary>
    /// Gets the "media display envelope". This is an HTML stream that is loaded
    /// by Windows Media Player to render an AJAX UI for accessing this service.
    /// </summary>
    /// <param name="message">Input message.</param>
    /// <returns>Reply message with 'text/html' content</returns>
    [OperationContract, HttpMethod("GET", UriSuffix = "/media/envelope")]
    Message GetMediaDisplayEnvelope(Message message);
    /// <summary>
    /// Gets a media display envelope collateral data element. This method
    /// acts as a web-server and serves up binary files or text files referenced
    /// by the media display envelope. Requests to this endpoint are HTTP GET
    /// requests to the service base URL with the suffix '/media/envelope' with an
    /// appended '/' and the file name of the file that is being requested from the
    /// service runtime's 'envelope' directory.
    /// </summary>
    /// <param name="message">Input message.</param>
    /// <returns>Reply message containing a raw binary file with appropriate media type</returns>
    [OperationContract, HttpMethod("GET", UriSuffix = "/media/envelope/*")]
    Message GetMediaDisplayEnvelopeCollateral(Message message);
    /// <summary>
    /// Gets the detail information for a particular episode
    /// in the EPG guide data (linked from RSS) or for a given
    /// recording.
    /// </summary>
    /// <param name="message">Input message.</param>
    /// <returns>Reply message containing 'text/xml' with detail information.</returns>
    [OperationContract, HttpMethod("GET", UriSuffix = "/item/?")]
    Message GetItemDetail(Message message);
    /// <summary>
    /// Adds detail information for a particular episode. Concretely this
    /// allows adding a recoding job to the episode data that will cause this
    /// show to be recorded.
    /// </summary>
    /// <param name="message">Input message.</param>
    /// <returns>Reply message with HTTP 200 OK status code</returns>
    [OperationContract, HttpMethod("POST", UriSuffix = "/item/?")]
    Message PostItemDetail(Message message);
    /// <summary>
    /// Deletes some of the item detail information for a particular episode.
    /// This is used to cacnel a recording for the episode.
    /// </summary>
    /// <param name="message">Input message.</param>
    /// <returns>Reply message with HTTP 200 OK status code.</returns>
    [OperationContract, HttpMethod("DELETE", UriSuffix = "/item/?")]
    Message DeleteItemDetail(Message message);
    /// <summary>
    /// Method receiving all unknown messages sent to this endpoint
    /// </summary>
    /// <param name="message">The message</param>
    /// <returns></returns>
    [OperationContract(Action = "*")]
    Message HandleUnknownMessage(Message message);
}

If you look at the individual operations in the above contract, you’ll see that the suffix filter would – given a base address of http://www.example.com/TV – match requests made on the URIs  http://www.example.com/TV/logo,  http://www.example.com/TV/now, and http://www.example.com/TV/media to name just a few. A special case is the GetRss() operation, which does not have an explicit suffix defined and therefore causes the suffix filter to match on the base address. An important aspect of the suffix filter is that it does not consider the HTTP method (GET, POST). Matching the HTTP method to an operation is the job of the HttpMethodOperationSelectorBehavior, which acts higher up on the endpoint level and picks out the exact method that the call is being dispatched to. The filter is only deciding whether the message is “ours” with respect to the namespace it is targeting.

The HttpMethodOperationSelectorBehavior is hooked into the service endpoint by the HttpMethodOperationSelectorAttribute’s implementation of IContractBehavior that you can look up in Part 3. In BindDispatch(), the dispatcher’s OperationSelector property is set to a new instance of our specialized operation selector. An “operation selector” is a class that takes an incoing request on an endpoint and figures out the proper operation to dispatch to. The default operation selector in Indigo acts according to the SOAP dispatch rules that I explained in Part 1 (see “Figuring out a programming model”).

However, in our REST/POX world that we’re building here we do not have a concept of “SOAP action”, but rather URIs and HTTP methods and therefore the default dispatch mechanism doesn’t take us very far. Hence, we need to replace the operation selection algorithm with our own and we do that by implementing IDispatchOperationSelector:

using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.ServiceModel;
using System.ServiceModel.Configuration;
using System.ServiceModel.Channels;

namespace newtelligence.ServiceModelExtensions
{
    /// <summary>
    ///
    /// </summary>
   public class HttpMethodOperationSelectorBehavior : IDispatchOperationSelector
   {
      ContractDescription description;
      IDispatchOperationSelector defaultSelector;

        /// <summary>
        /// Initializes a new instance of the <see cref="T:HttpMethodOperationSelectorBehavior"/> class.
        /// </summary>
        /// <param name="description">The description.</param>
        /// <param name="defaultSelector">The default selector.</param>
      public HttpMethodOperationSelectorBehavior(ContractDescription description, IDispatchOperationSelector defaultSelector)
      {
         this.description = description;
         this.defaultSelector = defaultSelector;
      }

        /// <summary>
        /// Selects the operation.
        /// </summary>
        /// <param name="message">The message.</param>
        /// <returns></returns>
      public string SelectOperation(ref Message message)
      {
         if (message.Properties.ContainsKey(HttpRequestMessageProperty.Name))
         {
             HttpRequestMessageProperty msgProp =
                 message.Properties[HttpRequestMessageProperty.Name] as HttpRequestMessageProperty;
             string baseUriPath = message.Headers.To.AbsolutePath;
             List<OperationDescription> operationsWithSuffix = new List<OperationDescription>();

             /* Check methods with UriSuffix first. For that we first add
              * operation descriptions that have the correct http method into
              * a list and then sort that list by the processing order */
             foreach (OperationDescription opDesc in description.Operations)
             {
                 HttpMethodAttribute methodAttribute = opDesc.Behaviors.Find<HttpMethodAttribute>();
                 if (methodAttribute != null &&
                      String.Compare(methodAttribute.Method, msgProp.Method, true) == 0 &&
                      methodAttribute.UriSuffix != null)
                 {
                     operationsWithSuffix.Add(opDesc);
                 }
             }

             /*
              * We are sorting the list based on two criteria:
              * a) ProcessingPriority value, and if that's equal:
              * b) Length of the UriSuffix expression
              */
             operationsWithSuffix.Sort(
                 delegate(OperationDescription descA, OperationDescription descB)
                 {
                     HttpMethodAttribute descAAttr = descA.Behaviors.Find<HttpMethodAttribute>();
                     HttpMethodAttribute descBAttr = descB.Behaviors.Find<HttpMethodAttribute>();
                     int result = descAAttr.Priority.CompareTo(descBAttr.Priority);
                     if (result == 0)
                     {
                         result = Math.Sign(descAAttr.UriSuffix.Length - descBAttr.UriSuffix.Length);
                     }
                     return result;
                 }
             );

             for (int i = operationsWithSuffix.Count-1; i >= 0; i--)
             {
                 OperationDescription opDesc = operationsWithSuffix[i];
                 HttpMethodAttribute methodAttribute = opDesc.Behaviors.Find<HttpMethodAttribute>();
                 // we have a method attribute, the attribute's method value matches
                 // the incoming http request and we do have a regex.
                 Match match = methodAttribute.UriSuffixRegex.Match(baseUriPath);
                 if (match != null && match.Success)
                 {
                     return opDesc.Name;
                 }
             }
            

             /* now check the rest */
             foreach (OperationDescription opDesc in description.Operations)
             {
                 HttpMethodAttribute methodAttribute = opDesc.Behaviors.Find<HttpMethodAttribute>();
                 if (methodAttribute != null && methodAttribute.UriSuffixRegex == null)
                 {
                     // we have a http method attribute and the method macthes the request
                     // method: match
                     if (String.Compare(methodAttribute.Method, msgProp.Method, true) == 0)
                     {
                         return opDesc.Name;
                     }
                 }
                 else if (String.Compare(opDesc.Name, msgProp.Method, true) == 0)
                 {
                     // we do not have a http method attribute, but the method name
                     // equals the http method.
                     return opDesc.Name;
                 }
             }

             // No match so far. Now lets find a wildcard method.
             foreach (OperationDescription opDesc in description.Operations)
             {
                 if (opDesc.Messages.Count > 0 &&
                     opDesc.Messages[0].Action == "*" &&
                     opDesc.Messages[0].Direction == TransferDirection.Incoming)
                 {
                     return opDesc.Name;
                 }
             }
         }

            // No match so far, delegate to the default selector if one is present
         if (defaultSelector != null)
         {
            return defaultSelector.SelectOperation(ref message);
         }
         return "";
      }
   }
}

As you can see, there is only one method: SelectOperation. The method will only do work on its own if the incoming request is an HTTP request received by Indigo’s HTTP transport. We can figure this out by looking into the message properties and looking for the presence of a property with the name HttpRequestMessageProperty.Name. The presence of this property is required, because that’s the vehicle through which Indigo gives us access to the HTTP method that was used for the request. What we’re looking for sits as an instance string property on HttpRequestMessageProperty.Method.

The algorithm itself is fairly straightforward:

1.      We grab all operations whose HttpMethodAttribute.Method property matches (case-insensitively) the incoming HTTP method string and which have a suffix expression and throw them into a list.

2.      We sort the list by the priority of the attributes amongst each other. I introduced the priorities, because I am allowing wildcards here and I want to allow the suffixes /item/detail and /item/* (read: “anything except detail”) to coexist on the same endpoint, but I need a something other than method order to specify that the match on the concrete expression should be done before the wildcard expression. In absence of priorities and/or in the case of collisions, longer suffixes always trump shorter expressions for matching priority.

3.      We match the sorted list in reverse order (higher priority is better) and return the first operation in the list whose suffix expression matches the incoming messages “To” header (which is the same as the HTTP request URI).

4.      If we don’t have a match, we proceed to iterate over all operations that do not have a suffix and see whether we can find a match solely based on the  HttpMethodAttribute.Method value or, if the HttpMethodAttribute is absent, on the plain method name. (So if the method just named “Get” and there is no attribute, an HTTP GET request will still match).

5.      If we still don’t have a match, we look for the common “all messages without a proper home” method with an OperationContract.Action value of “*”.

6.      And as the very last resort we fall back to the default selector if we have been given one and else we fail out by returning an empty string, which means that there is no match at at all.

If we find a match, we return a string that’s the same as the name of the method we want to dispatch to and Indigo will them promptly do the right thing and call the respective method, either by passing the raw message outright (as in my TV app) or by breaking up the message body using the XmlFormatter or the XmlSerializer and passing a typed message or a set of parameters.

Step 4 is noteworthy insofar as that the [HttpMethod] attributes aren’t strictly necessary. If you name your methods exactly like the HTTP methods they should handle, the operation selector will figure this out. If that’s what you want, you don’t even need the [HttpMethodOperationSelector] attribute, if you choose to add that information in the configuration file instead. To enable that. I’ve built the required configuration class that you can register in the <behaviorExtensions> and map to the <behaviors> section of an endpoint’s configuration. The class is very, very simple:

using System;
using System.Collections.Generic;
using System.Text;
using System.ServiceModel.Configuration;

namespace newtelligence.ServiceModelExtensions
{
   public class HttpMethodOperationSelectorSection : BehaviorExtensionSection
   {
      public HttpMethodOperationSelectorSection()
      {
      }

      protected override object CreateBehavior()
      {
         return new HttpMethodOperationSelectorAttribute();
      }

      public override string ConfiguredSectionName
      {
         get
         {
            return "httpMethodOperationSelector";
         }
      }
   }
}

Alright, so where are we? We’ve got dispatch metadata, we’ve got an endpoint dispatch mechanism and we’ve got an operation dispatch mechanism. Furthermore we have a tool that conveniently grabs “parameter segments” from the URI and maps them to an out-of-band collection on the UriArgumentsMessageProperty from where we can conveniently fetch them inside the service implementation.

What we don’t have is POX. We’re still dealing with SOAP messages here. So the next step is to modify the wire encoding in a way that we unwrap the content and throw away the envelope on the way out and that we wrap incoming “raw” data into an envelope to make Indigo happy with incoming requests.

That’s plenty of material for Part 5 and beyond. Stay tuned.

Go to Part 5

Categories: Indigo

Part 1, Part 2

If you’ve read the first two parts of this series, you should know by now (if I’ve done a reasonable job explaining) about the fundamental concepts of how incoming web service messages (requests) are typically dispatched to their handler code and also understand how my Indigo REST/POX extensions are helping to associate the metadata required for dispatching plain, envelope-less HTTP requests with Indigo service operations using the HttpMethod attribute and how the HttpMethodParameterInspector breaks up the URI components into easily consumable, out-of-band parameters that flow into the service code the UriArgumentsMessageProperty.

What I have not explained is how the dispatching is actually done. There are two parts to that story: Dispatching to services on the listener level (which I will cover here) and dispatching to operations at the endpoint level (which I’ll cover in part 4).

When an HTTP request is received on a namespace that Indigo has registered with HTTP.SYS, the request is matched against a collection of “address filters”. “Registering a namespace” means that if you configure a service-endpoint to listen at the endpoint http://www.example.com/foo, the service-endpoint “owns” that URI.

What’s noteworthy is that if you have an Indigo/WCF application listening to endpoints at http://www.example.com/baz, http://www.example.com/foo and http://www.example.com/foo/bar, the demultiplexing (“demuxing” in short) of the requests is done by Indigo and not by the network stack. HTTP.SYS will push requests from any registered URI namespace of the particular application into the “shared” Indigo HTTP transport and leave it up to Indigo to figure out the right endpoint to dispatch to. And that turns out to be perfect for our purposes.

Whenever an incoming message needs to be dispatched to an endpoint, the message is matched against an address filter table. [For the very nosy: The place where it all happens is in the internal EndpointListenerTable class’s Lookup method, which you could probably look at if you had the right tools, but I didn’t say that.]

By default, the address filter that is used for any “regular” service is the EndpointAddressFilter, which reports a match if the incoming message’s “To” addressing header (which is constructed from the HTTP header information if it’s not immediately contained in the incoming message) is a match for the registered URI. Whether a match is found is dependent on the URI’s port and host-name (controllable by the HostNameComparisonMode in the HTTP binding configuration) and the URIs remaining path, which must be an exact match for the registered service endpoint URI. Since we want to introduce a slightly different dispatch scheme that is based on matching not only on the exact endpoint URI’s path but also on suffixes appended to that URI, we must put a hook into the dispatch mechanism and extend the default behavior. If a method marked up with [HttpMethod(“GET”,UriSuffix=”/bar”)] and the endpoint is hosted at http://www.example.com/foo, we want any HTTP GET request to http://www.example.com/foo/bar to be dispatched to that endpoint and, subsequently, to that exact method.

To infuse that behavior into Indigo, we need to tell it so. If you take a look at Part 2 and at the service contract declarations that I posted there, you will notice the HttpMethodOperationSelector attribute alongside the ServiceContract attribute. That attribute class does the trick:

using System;
using System.Collections.Generic;
using System.Text;
using System.ServiceModel;

namespace newtelligence.ServiceModelExtensions
{
   public class HttpMethodOperationSelectorAttribute :
                  Attribute, IContractBehavior, IEndpointBehavior
   {
      public void BindDispatch(
                ContractDescription description,
                IEnumerable<ServiceEndpoint> endpoints,
                DispatchBehavior dispatch,
                BindingParameterCollection parameters)
      {
         dispatch.OperationSelector =
              new HttpMethodOperationSelectorBehavior(description, dispatch.OperationSelector);
         foreach (ServiceEndpoint se in endpoints)
         {
            if (se.Behaviors.Find<HttpMethodOperationSelectorAttribute>() == null)
            {
               se.Behaviors.Add(this);
            }
         }
           
      }

      public void BindProxy(
                     ContractDescription description,
                     ServiceEndpoint endpoint,
                     ProxyBehavior proxy,
                     BindingParameterCollection parameters)
      {

      }

      public bool IsApplicableToContract(Type contractType)
      {
         return true;
      }

      public void BindServiceEndpoint(
                     ServiceEndpoint serviceEndpoint,
                     EndpointListener endpointListener,
                     BindingParameterCollection parameters)
      {
            SuffixFilter suffixFilter = null;

            if (endpointListener.AddressFilter == null ||
                !(endpointListener.AddressFilter is SuffixFilter))
            {
                suffixFilter = new SuffixFilter(endpointListener, endpointListener.AddressFilter);
                endpointListener.AddressFilter = suffixFilter;
                ((Dispatcher)endpointListener.Dispatcher).Filter = suffixFilter;
            }
            else
            {
                suffixFilter = endpointListener.AddressFilter as SuffixFilter;
            }

         foreach (OperationDescription opDesc in serviceEndpoint.Contract.Operations)
         {
            HttpMethodAttribute methodAttribute = opDesc.Behaviors.Find<HttpMethodAttribute>();
            if (methodAttribute != null)
            {
               if (methodAttribute.UriSuffixRegex != null)
               {
                  suffixFilter.AddSuffix(methodAttribute.UriSuffixRegex);
               }
            }
         }
      }
   }
}


In the attribute’s implementation of IEndpointBehavior.BindServiceEndpoint, which is invoked by Indigo as the endpoint is initialized (in response to ServiceHost.Open() ), we replace the service’s default endpoint filter with our own SuffixFilter class. Once we’ve done that, we iterate over the HttpMethodAttribute metadata elements that sit on the individual operations/methods in the contract description (this is the actual reason we put them there, see Part 2) and add any suffix we find to the filter’s suffix table. We’ll get back to this class in the next part while to investigate how the “operation selector” is hooked in; let’s investigate the suffix filter first.

using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.ServiceModel;
using System.ServiceModel.Configuration;
using System.ServiceModel.Channels;

namespace newtelligence.ServiceModelExtensions
{
    /// <summary>
    /// This class implements a specialized ServiceModel address filter
    /// that allows matching URL suffixes.
    /// </summary>
    /// <remarks>
    /// The class aggregates an EndpointAddressFilter to helpi with the matching logic.
    /// </remarks>
    public class SuffixFilter : Filter
    {
        /// <summary>
        /// List for the suffixes.
        /// </summary>
        List<Regex> suffixes;
        /// <summary>
        /// Original filter that we delegate to if we can't match with this
        /// one.
        /// </summary>
        Filter originalFilter;
        /// <summary>
        /// The endpoint listener that this filter is applied to
        /// </summary>
        EndpointListener endpointListener;
        /// <summary>
        /// The aggregated endpoint address filter
        /// </summary>
        EndpointAddressFilter addressFilter;

        /// <summary>
        /// Creates a new instance of SuffixFilter
        /// </summary>
        /// <param name="endpointListener">EndpointListener this filter is attached to</param>
        /// <param name="originalFilter">Original AddressFilter of the EndpointListener</param>
        public SuffixFilter(EndpointListener endpointListener, Filter originalFilter)
        {
            this. suffixes = new List<Regex>();
            this. originalFilter = originalFilter;
            this. endpointListener = endpointListener;
        }

        /// <summary>
        /// Implements the matching logic
        /// </summary>
        /// <param name="message">Message that shall be matched</param>
        /// <returns>Returns an indicator for whether the message is considered a match</returns>
        public override bool Match(Message message)
        {
            // Workaround for Nov2006 CTP bug. GetEndpointAddress() cannot be
            // called on an EndpointListener before the listener is running.
            if ( addressFilter == null)
            {
                addressFilter = new EndpointAddressFilter( endpointListener.GetEndpointAddress(), false);
            }

            // check whether we have an immediate match, which means that the message's
            // To Header is an excat match for the EndpointListener's address
            if ( addressFilter.Match(message))
            {
                return true;
            }
            else
            {
                // no direct match. Save the original header value and chop off the
                // query portion of the URI.
                Uri originalTo = message.Headers.To;
                string baseUriPath = originalTo.AbsolutePath;
                string baseUriRoot = originalTo.GetLeftPart(UriPartial.Authority);

                // match against the suffix list
                foreach (Regex suffixExpression in suffixes)
                {
                    Match match = suffixExpression.Match(baseUriPath);
                    if (match != null && match.Success)
                    {
                        string filterUri = baseUriRoot+baseUriPath.Remove(baseUriPath.LastIndexOf(match.Value));
                        message.Headers.To = new Uri(filterUri);
                        if ( addressFilter.Match(message))
                        {
                            message.Headers.To = originalTo;
                            return true;
                        }
                        message.Headers.To = originalTo;
                    }                       
                }
            }
            if ( originalFilter != null)
            {
                // of no match has been found up to here, we match against the
                // original filter if that was provided.
                return originalFilter.Match(message);
            }
            return false;
        }

        /// <summary>
        /// Implements the matching logic by constructing a Message over
        /// a MessageBuffer and delegating to the Match(Message) overload
        /// </summary>
        /// <param name="buffer"></param>
        /// <returns></returns>
        public override bool Match(MessageBuffer buffer)
        {
            Message msg = buffer.CreateMessage();
            bool result = Match(msg);
            msg.Close();
            return result;
        }

        /// <summary>
        /// Adds a new suffix to the suffix table
        /// </summary>
        /// <param name="suffix">Suffix value</param>
        public void AddSuffix(Regex suffix)
        {
            suffixes.Add(suffix);
        }

        /// <summary>
        /// Removes a suffix from the suffix table
        /// </summary>
        /// <param name="suffix"></param>
        public void RemoveSuffix(Regex suffix)
        {
            suffixes.Remove(suffix);
        }
    }
}


The filter’s filet piece is in the Match method. To finally figure out whether a message is a match, we employ the matching logic of the default EndpointAddressFilter, which deals with matching the host names and the “base URI” at which the service was registered. What the suffix filter does in addition is to match the suffix regex pattern against the incoming message’s “To” header and if that is a match, the suffix is stripped and the remaining URI is matched against the aggregated EndpointAddressFilter. Only if we get a match for the suffix and for the remainder URI, we’ll report a positive match back to the infrastructure by returning true. And in that case and only in that case the service endpoint for which “this” suffix filter was installed and populated gets the request.

For each incoming request, Indigo goes through all registered endpoint address filters and asks them whether they want to service it. And that really means “all”. Indigo will refuse to service the request if two or more filters report ownership of the respective request and will throw a traceable (using Indigo tracing) internal exception that will cause none of the services to be picked due to this ambiguity. In the case of overlapping dispatch namespaces, none is indeed better than “any random”.

Next part: HttpMethodOperationSelectorBehavior

Go to Part 4

Categories: Indigo

December 27, 2005
@ 01:25 PM

Before I get back to the POX/REST story, I promised a few folks to re-publish an updated version of my „JITA pool“ that I published over three years ago. Many people have used the technique that this utility class enables and have seen dramatic performance improvements with their Enterprise Services applications. And until Indigo ships, Enterprise Services (a.k.a. “COM+” or, shorter, “ES”) is still the most powerful application server technology on the Windows platform, so I think it deserves continued attention.

One of the common misconceptions around ES is that keep hearing is that it comes with such a significant overhead for each call that turns your server application into a fat, slow moving thing. And as things are, there are always two sides to every story and it turns out that if you use ES correctly, it is probably faster than most other cross-process technologies you can easily use from managed code. I write “probably”, because as with every distributed systems technology, “your mileage may vary” based on what your scenario is.

Before I show you the code, let’s take a quick dive into the ES/COM+ internals so that you understand what I mean with “use ES correctly”. To use technology in an optimal way you either need to understand how things work and how all technology pieces fit together or you need absolutely precise documentation that gives you the guidance you’d otherwise have to work out yourself. The Microsoft of the “COM+ era” (2000 and earlier) didn’t do so well on giving us either. And unfortunately it’s still very often the case that Microsoft’s technical marketing teams drop a technology like a hot potato once it has shipped and the launch events are over. And so ES still suffers from former negligence and in managed code land it’s clearly overshadowed by ASP.NET, Windows Forms and everything else that has design-time support and demos easily. Anyways, let’s dig in:   

Assume you had a ServicedComponent and you’d call it from a client application in the following way:

MyComponent myComp = new MyComponent();
myComp.MyMethod()

Surprising to many is that it turns out that the innocent looking “new” is sometimes the more costly of these two code lines. Invoking and executing the functionality you’ve been implementing might in some cases indeed be significantly quicker than constructing the component instance. Why is that so? Because constructing ES/COM+ component instances is a lot of work. The picture above is a simplified illustration of the moving parts involved in the process. (Mind that I am simplifying the explanation a bit. I’ve already written about the internals in 2003, but in a bit different context.)

The COM+ infrastructure is essentially an aspect engine that allows plugging interception sinks into the call chain that leads from the client proxy to the server component instance. Whenever a (any) COM object is constructed on Windows 2000 (or later), the CoCreateInstance(Ex) API function will route the request through a chain of “activators” that are registered in the COM+ catalog, which is a simple ISAM database with very fast read-capabilities that has been custom-built for COM+. If an component isn’t “configured” (registered in a proper COM+ application), the activation process shortcuts out of the activator chain and creates a “normal” COM object, otherwise the activator chain is fully processed. “Activators” are nothing but proper COM components implementing a special interface ISystemActivator whose CLSID is registered in the COM+ catalog. In the illustration about the activators are depicted as yellow circles. Whenever a new instance must be created, each activator is polled for whether it has interest in contributing a “policy” (blue rectangles) into the interceptor chain. It does so by checking the COM+ catalog for whether the component or the application has been configured to use the feature the activator is responsible for. Conceptually, each COM+ feature (Transactions, Security, JITA, Object Pooling) therefore has an activator even though one activator may (and in fact does) do the work for multiple features and install multiple policies.

A policy is also a COM component (or two) implementing the IPolicy and IPolicyMaker interfaces. The former interface has the interceptor methods that are being invoked before and after the method call and before and after leaving the context, the latter has methods for actually installing that interceptor into the call chain. The interceptor methods in IPolicy are the ones where the COM+ functionality actually sits. The transaction policy will open/close new transaction scopes here, the object pooling will pool/depool components, the JITA policy connects/disconnects instances, and the tracker starts and stops its stopwatch to measure the call times. The resulting policies form a sink-chain, which means that any policy can absorb a call or change the environment before it lets the call proceed. The policy objects have context-affinity, which means that they are created anew for each context.

What that all means is that whenever you create a new instance of your serviced component, you don’t create just one object, you create a whole lot and I haven’t really gone into the additional managed code object instances that are created for the proxy/dispatcher trickery of hooking it all up to the Remoting infrastructure. In addition to all that context setup work, a cross-process connection (on-machine and off-machine) does, of course, require a DCOM/MS-RPC connection to be established, which comes with additional cost.

Mind that while this mechanism is theoretically extensible using arbitrary plug-ins, it’s not practically possible to do so. Contrary to the open extensibility model of Indigo that was specifically designed for 3rd party extensibility, the COM+ model is so tightly coupled and there are so many interdependencies between the respective behaviors that you would practically need to sit on the same floor with the development team to do anything meaningful with the extensibility features even if you had the header and IDL files for it.

And looking at all that internal effort that’s hidden in the guts of COM+/ES, it is now not very surprising that that creating a component is pretty costly, is it?

The goal for optimizing a COM+/ES application must therefore be to eliminate as much of that cost as possible. And it turns out that two of the policies sitting in the server context stage are incredibly helpful in getting this done. The “Just In Time Activation” feature of COM+/ES is in fact a policy that will drop the reference to the object instance on its right (in the illustration above) once the completion-bit in the context properties is set. Before it does so, it calls the Deactivate() method if the component supports IObjectControl. The object-pooling policy will grab such a deactivated object and move it into a server-side object pool is the object agrees to that by returning true from CanBePooled(). Even though the policy chain (“context”) has no active instance sitting at its far end, it is alive and doing quite well. Whenever the next call is made on the proxy, the call comes through the chain, the “Just in Time Activation” policy will create a new instance and, if present, the object pooling policy will supply one from the pool. Otherwise a new instance is created. The key aspect here is that the entire context and all that context setup work is reused, even though there’s an entirely new or a recycled component acting on the server side.

This is quite similar to databases where you have connections (context) and statements (instance/call). A serviced component proxy is not just a proxy. It’s got the whole policy chain and the server connection behind it. And as we know, database connections are pretty expensive to establish as well and therefore the infrastructure you use to connect to them (ADO, System.Data, OLE DB) commonly gives you “connection pools” from which pre-established connections are recycled even though the API creates the illusion that you are creating new connections every time you call Connect() or Open().  

Consequently and learning from the database folks, what we’ll do for optimizing ES is to steal that idea and build a connection pool. The downloadable file below contains an implementation of such a pool. The JustInTimeActivationProxyPool<T> class implements a simple, lockless, SMP safe pool of static size that keeps references to existing proxies. To create a pool on the client side, you will typically add a static field like  JustInTimeActivationProxyPool<MyComponent> myComponentPool to your client implementation and initialize it appropriately. Once you’ve done that, you can obtain a proxy with Pop() and return the proxy after use with Push(). Because all serviced components you write in managed code are implicitly multi-threading aware (MTA), this works perfectly well in multi-threaded environments.

However, due to the connection-oriented nature of COM, there’s a bit of a complication in case the server application recycles (stops and restarts). In that case, all the proxies you have cached are stale and will throw an exception with the HRESULT 0x800706bf (E RPC CALL FAILED DNE), which means that the call couldn’t be made. In that case you need to create a new proxy and simply reissue the call. The two other special cases for COM+/ES (security errors and general network failures aside) are those where the target application has been paused or disabled, in which case the proxies are also invalid. The older versions of my pool class did not address these issues, but this new one does.

The example below shows the proper usage of the pool with the pool’s Using() method and an anonymous inline method. This technique allows me to call the user code in three different contexts that I can establish inside the implementation of Using(). The first is when the call were to be executed within a preexisting transaction context, which disallows the use of pooled proxies, the second is the normal Pop()/Push() bracket around the call and the third is the new/Push() exception case for when the proxy is detected to be stale.

 

[WebService(Name="CatalogQueriesWebService",Namespace=Namespaces.CatalogQueriesNamespaceUri)]
public class CatalogQueriesWebService : ICatalogQueries
{
    static JustInTimeActivationProxyPool<ES.CatalogQueries> jitaPool = new JustInTimeActivationProxyPool<ES.CatalogQueries>();
   
    public  GetAllCategoriesReplyMessage GetAllCategories()
    {
        GetAllCategoriesReplyMessage result = null;
       
        jitaPool.Using(
            delegate(ES.CatalogQueries service)
            {
                result = service.GetAllCategories();
            }
        );
        return result;
    }
   
    …
}


The “requirement” for the server component is that Just-In-Time Activation is enabled (the [JustInTimeActivation] attribute on the serviced-component class will do that and the [Transaction] attribute with the TransactionOption.Supported/Required/RequiresNew optional will implicitly do that, too) , even though stateful components could be pooled as well with this technique.

Download the code for the class below and try it out.

JustInTimeActivationProxyPool.cs.txt (10.03 KB)
Categories:

December 22, 2005
@ 05:35 AM

Yes, all the rumors are true. I am moving from the consumer side of things to the builder side of things. From February 1st, 2006 I will be a “blue badge” and work for Microsoft as a Program Manager on the Windows Communication Foundation. The guys convinced me that it would be a good idea to make the move.

I’ve been spending so much time talking about and writing extensions for the shipping and not-yet shipping Microsoft distributed systems technologies stuff that it became the logical next step for me to become part of the team that creates all of these things. I hope that I can make some contribution to the cause.

As for why I am leaving my baby newtelligence? It’s the urge to build stuff that matters in a big way. Bart and Achim (my partners) have given me all the support they could since January in pursuing this goal. We’ve worked hard to put the company into a position that allows me to make this move and we are all delighted with the way things are. newtelligence is expanding its consulting business, continues to deliver world-class developer education and will continue to be a strong partner for our customers, which makes it easier for me to let go.

I’ll tell you more about it in the upcoming weeks…

Categories:

In the first part of this series, I gave you a little introduction to REST/POX in contrast to SOAP and also explained some of the differences in how incoming requests are dispatched. Now I’ll start digging into how we can teach Indigo a RESTish dispatch mechanism that dispatches based on the HTTP method and by matching on the URI against a suffix pattern.

The idea here is that we have a service implementation that takes care of a certain resource namespace. To stick with the example from Part 1, we assume that the resources managed within this (URI) namespace are customers and data related to customers. Mind that this might not be all data of a respective customer, but that some data may very well be residing in completely different namespaces (and on different servers).

As a reminder: When I write “namespaces” I specifically mean that we’re creating hierarchical scopes for data. All customer representations managed by our imaginary service are subordinates of the namespace http://www.example.com/customers, the representation of the customer identified by customer-id 00212332 occupies the namespace http://www.example.com/customers/00212332, all communication (phone) numbers of that customer are subordinates of http://www.example.com/customers/00212332/comm and the home phone number might be identified by http://www.example.com/customers/00212332/comm/home-phone. However, all orders made by that respective customer might be found somewhere completely different; maybe here:  http://www.tempuri.org/ordersystem/customer-orders/00212332. The data representation of the customer would contain that link, but the customer service would not manage those resources (the orders), at all.

Purists might (and do) argue that plain HTTP handlers (or “servlets”, in Java terms) representing exactly one resource type are the best way to implement the processing logic for this URI/HTTP centric model, but since I am much more a pragmatist than a purist, I prefer using a infrastructure that maps incoming requests to a programming model that’s easy enough for most programmers to deal with. It turns out that a class with methods that deal with related stuff (a customer and his addresses and phone numbers) is something that most programmers can handle pretty well by now and there’s nothing wrong with co-locating related handlers for data from a given data source on one flat interface, even if the outside representation of that data suggests that the data and its “endpoints” are ordered hierarchically. In the end, the namespace organization is just a smokescreen that we put in front of our implementation. Just to make Mark happy, I’ll show a very HTTP and service-per-object aligned contract model and, later in the next part, also a more readable model for the rest of us to explain how the dispatch works. I’ll start with the model for idealists:

[ServiceContract, HttpMethodOperationSelector]
interface
ICustomerResource
{
    [OperationContract,
     HttpMethod("GET", UriSuffix = "/customers/?")]
    Message Get(Message msg);
    [OperationContract,
     HttpMethod("PUT", UriSuffix = "/customers/?")]
    Message Put(Message msg);
    [OperationContract,
     HttpMethod("POST", UriSuffix = "/customers/?")]
    Message Post(Message msg);
    [OperationContract,
     HttpMethod("DELETE", UriSuffix = "/customers/?")]
    Message Delete(Message msg);
}

[ServiceContract, HttpMethodOperationSelector]
interface
ICommunicationResource
{
    [OperationContract,
     HttpMethod("GET", UriSuffix = "/customers/?/comm/?")]
    Message Get(Message msg);
    [OperationContract,
     HttpMethod("PUT", UriSuffix = "/customers/?/comm/?")]
    Message Put(Message msg);
    [OperationContract,
     HttpMethod("POST", UriSuffix = "/customers/?/comm/?")]
    Message Post(Message msg);
    [OperationContract,
      HttpMethod("DELETE", UriSuffix = "/customers/?/comm/?")]
    Message Delete(Message msg);
 }

We have two different contracts here, one for the “customers” namespace and one for the “comm” sub-namespace, and the implementation of these two contracts could be sitting on the same implementation class or on two different classes whereby they could be co-located at the exact same root address or sitting on different machines. All of that doesn’t really matter, since the filtering/dispatch logic we’ll use here will figure out the right thing to do, meaning the right handler method to dispatch to. Also mind that there’s a difference to the examples I show in Part 1 in that I am now using messages.

The UriSuffix of the HttpMethodAttribute serves three purposes. First, it is used to construct a regular expression that is used to match incoming messages to the right endpoint using a custom endpoint Filter. Second, the same regular expression is used to figure out which method the message shall be dispatched on at that endpoint using a custom implementation of IDispatchOperation. Third, the regular expression is also used to isolate the URI-embedded parameters and make them easily accessible on a message property. So for the ICommunicationResource.Get() operation above, the handler implementation would start out as follows and would make the values occurring at the two “?” of the suffix available in the UriArgumentsMessageProperty.InUrlArgs collection that is shown further below:

Message ICommunicationResource.Get(Message msg)
{
   UriArgumentsMessageProperty uriArgs = UriArgumentsMessageProperty.FromOperationContext();
   string customerid = uriArgs.InUrlArgs[0];
   string commid = uriArgs.InUrlArgs[1];
   ...

The expressions for UriSuffix support two different wildcards. The “*”wildcard will match any character and the “?” wildcard will match any character except the forward slash. If you would, for instance, want to build an operation that behaves like a web server and might serve up data from a directory and its subdirectories, you’d use something as global as “/*” and any URI would match the respective endpoint/method. If you want to match/extract segments of a namespace path as we do here, you use the “?”.

And so here is the complete and rather straightforward implementation of the HttpMethodAttribute:

using System;
using System.Collections.Generic;
using System.Text;
using System.ServiceModel;
using System.Text.RegularExpressions;

namespace newtelligence.ServiceModelExtensions
{
    ///
<summary>
    ///
The HttpMethodAttribute is used to declare the HTTP method and
    ///
an optional suffix for the REST/POX extensions to dispatch on. In absence of
    ///
this attribute, the dispatch mechanism will attempt to dispatch on the
    ///
name of the operation and try matching by name it to the HTTP method used.
    ///
</summary>
   [AttributeUsage(AttributeTargets.Method)]
   public class HttpMethodAttribute : Attribute,
IOperationBehavior
   {
      ///
<summary>
      /// Initializes a new instance of the <see cref="T:HttpMethodAttribute"/>
class.
      ///
</summary>
      /// <param name="method">The method.
</param>
      public HttpMethodAttribute(string method)
      {
         _Method = method;
      }

      private string _Method;
      ///
<summary>
      ///
Gets the HTTP method. See http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html
      ///
</summary>
      /// <value>The method.
</value>
      public string Method
      {
         
get
         {
            return _Method;
         }
      }

      private string _UriSuffix;
      ///
<summary>
      ///
Sets or gets the Uri suffix. The Uri suffix is an expression used
      ///
to match an incoming request against the endpoint and this method.
      ///
The UriSuffix may contain two different wildcards: '*' matches the
      ///
shortest sequence of arbitrary character up to the next concrete character
      ///
in the suffix string. The '?' behaves similarly, but excludes the '/'
      ///
character from matching.
      ///
</summary>
      /// <value>The URI suffix.
</value>
      public string UriSuffix
      {
         
set
         {
             _UriSuffix = value;
             _UriSuffixRegex = new Regex(
                    Regex.Escape(_uriSuffix).Replace("\\*", "(.*?)")
                                            .Replace("\\?", "([^/]*?)") + "$",
                    RegexOptions.CultureInvariant |
                    RegexOptions.IgnoreCase |
                    RegexOptions.Singleline |
                    RegexOptions.Compiled);
         }
           
get
            {
                return _UriSuffix;
            }
      }

        private Regex _UriSuffixRegex;
        ///
<summary>
        ///
Gets the regular match expression constructed from
        ///
the UriSuffix.
        ///
</summary>
        /// <value>The URI suffix regex.
</value>
        public Regex UriSuffixRegex
        {
           
get
            {
                return _UriSuffixRegex;
            }
        }

        private int _Priority;
        ///
<summary>
        ///
Gets or sets the priority. The priority is used to control the
        ///
order in which the suffix expressions are processed when matching.
        ///
A higher priority causes the expression to be matched earlier.
        ///
</summary>
        /// <value>The priority.
</value>
        public int Priority
        {
           
get
            {
                return _Priority;
            }
           
set
            {
                _Priority = value;
            }
        }

        ///
<summary>
        ///
Applies the behavior.
        ///
</summary>
        /// <param name="description">Description.
</param>
        /// <param name="proxy">Proxy.
</param>
        /// <param name="parameters">Parameters.
</param>
      public void ApplyBehavior(OperationDescription description,
                ProxyOperation proxy, BindingParameterCollection parameters)
      {
           
// do nothing proxy-side
      }

        ///
<summary>
        ///
Applies the behavior.
        ///
</summary>
        /// <param name="description">Description.
</param>
        /// <param name="dispatch">Dispatch.
</param>
        /// <param name="parameters">Parameters.
</param>
      public void ApplyBehavior(OperationDescription description,
                    DispatchOperation dispatch, BindingParameterCollection parameters)
      {
           
// We're adding a parameter inspector that parses the Uri parameters into
           
// an UriArgumentsMessageProperty
            dispatch.ParameterInspectors.Add( new HttpMethodParameterInspector(this));
      }
   }
}


Of course this attribute is a bit special. You might have noticed that it implements the IOperationBehavior interface with its two method overloads of ApplyBehavior whereby one is for the proxy side of a channel (which we don’t care about in this case) and the other for the dispatcher (service-) side of an implementation. The presence of this interface causes the Indigo runtime to instantiate the attribute and add it to the contract metadata whenever the contract is built using reflection. You could also instantiate the attribute yourself and add it to any existing in-memory Indigo contract’s operation description if you liked. This is a convenient way to get the additional metadata into the contract description because we need it at several places later.

At the dispatch-side we’re also adding an implementation of IParameterInspector, whose job it is to extract the URI-embedded arguments and also parse “key=value” pairs of an optional query string, if one is present.

Parameter inspectors are called immediately before and after a method has been invoked and, as their name implies, are meant to be used to inspect a method’s parameters and/or output. However, their use is not restricted to that. Because the operation context is also available when the inspectors are called, you can also inspect headers, properties or any other context information at this point.

Even though this is largely a convenience feature not central to the dispatcher, I’ll show the class here, because I mentioned the UriArgumentsMessageProperty above. This is where it gets populated and set:

using System;
using System.Collections.Generic;
using System.Text;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.Text.RegularExpressions;
using System.Collections.Specialized;

namespace newtelligence.ServiceModelExtensions
{
    class HttpMethodParameterInspector :
IParameterInspector
    {
        HttpMethodAttribute _metadata;
        ///
<summary>
        ///
Creates a new instance of HttpMethodParameterInspector
        ///
</summary>
        public HttpMethodParameterInspector(HttpMethodAttribute metadata)
        {
            _metadata = metadata;
        }
       
        #region IParameterInspector Members

        public object BeforeCall(string operationName, object[] inputs)
        {
            OperationContext opCtx = OperationContext.Current;
            NameValueCollection queryArguments;
            StringCollection inUrlArgs = new StringCollection();
            Uri toHeader = opCtx.IncomingMessageHeaders.To;

           
// Parse query strings
            if (opCtx.IncomingMessageProperties.ContainsKey(HttpRequestMessageProperty.Name))
            {
                HttpRequestMessageProperty rqMsgProp =
                   opCtx.IncomingMessageProperties[HttpRequestMessageProperty.Name]
                      as HttpRequestMessageProperty;
                queryArguments = UriHelper.ParseQueryString(rqMsgProp.QueryString);
            }
           
else
            {
                queryArguments = UriHelper.ParseQueryString(toHeader.Query);
            }

           
// Get the in URL arguments from the regex captures
            Match match = _metadata.UriSuffixRegex.Match(toHeader.AbsolutePath);
            if (match.Success && match.Groups.Count > 0)
            {
               
// if we have more than 1 capture group (the first is always the
                
// full match expression), we store the next groups in the
               
// irUrlArgument collection in order of occurrence.
                for (int i = 1; i < match.Groups.Count; i++)
                {
                    inUrlArgs.Add(match.Groups[i].Value);
                }
            }

            opCtx.IncomingMessageProperties.Add(
                UriArgumentsMessageProperty.Name,
                new UriArgumentsMessageProperty(queryArguments,inUrlArgs)
            );

            return null;
        }

        public void AfterCall(string operationName, object[] outputs,
                      object returnValue, object correlationState)
        {
           
// do nothing
        }      

        #endregion
    }
}

The query string is parsed using a helper class which splits the query string at each ‘&’ first and then splits the segments at ‘=’ and throws the resulting pairs into a NameValueCollection. That is no big deal and I'll omit the helper here. The UriArgumentsMessageProperty that gets put into the mesage properties is just a class that holds the query arguments and the string collection with the regex matches. It is quite trivial:

using System;
using System.Collections.Specialized;
using System.Collections.Generic;
using System.Text;
using System.ServiceModel;

namespace newtelligence.ServiceModelExtensions
{

    ///
<summary>
    ///
Message property class holding arguments fro the request URI.
    ///
</summary>
    public class
UriArgumentsMessageProperty
    {
        NameValueCollection _queryArguments;
        StringCollection _inUrlArgs;

        ///
<summary>
        ///
Creates a new instance of UrlArgumentsMessageProperty
        ///
</summary>
        internal UriArgumentsMessageProperty(NameValueCollection queryArguments,
                                             StringCollection inUrlArgs)
        {
            _queryArguments = queryArguments;
            _inUrlArgs = inUrlArgs;
        }

        ///
<summary>
        ///
Gets the query arguments.
        ///
</summary>
        /// <value>The query arguments.
</value>
        public NameValueCollection QueryArguments
        {
           
get
            {
                return _queryArguments;
            }
        }

        ///
<summary>
        ///
Gets the arguments in the URL.
        ///
</summary>
        /// <value>The in URL args.
</value>
        public StringCollection InUrlArgs
        {
           
get
            {
                return _inUrlArgs;
            }
        }

        ///
<summary>
        ///
Gets the name of the property.
        ///
</summary>
        /// <value>The name.
</value>
        public static string Name
        {
           
get
            {
                return "urlArguments";
            }
        }

        ///
<summary>
        ///
Retrieves the message property from the current operation context.
        ///
</summary>
        ///
<returns></returns>
        static public UriArgumentsMessageProperty FromOperationContext()
        {
            return FromOperationContext(OperationContext.Current);
        }

        ///
<summary>
        ///
Retrieves the message property from the operation context.
        ///
</summary>
        /// <param name="operationContext">operation context
</param>
        ///
<returns></returns>
        static public UriArgumentsMessageProperty
                 FromOperationContext(OperationContext operationContext)
        {
            return operationContext.IncomingMessageProperties[Name]
                      as UriArgumentsMessageProperty;
        }
    }
}


Ok, fine, and how is that now really used for dispatching? You’ll see that when I explain the SuffixFilter and the HttpMethodOperationSelectorBehavior in the next parts.

(There will also be a downloadable version of the code later, but I am still tweaking some little things and don’t want to keep updating)

Go to Part 3

Categories: Indigo

A not so long time ago in a land far away…

A little bit more than half a year ago I got invited to a meeting at Microsoft in Redmond and discussed with Steve Swartz, Yasser Shohoud and Eugene Osovetsky how to implement POX and REST support for Indigo. You could also say that Steve dragged me into the meeting, since I happened to be on campus anyways and was burning some time in Steve’s office. I am not sure whether I made any good contribution to the cause in the meeting, but at least I witnessed the definition a special capability for the HTTP transport that I am exploiting with a set of Indigo extensions that I’ll present in this series of blog posts. The consensus in the meeting was that the requirements for building POX/REST support into the product weren’t generally clear enough in the sense that when you ask 100 people in the community you get 154 ever-changing opinions about how to write such apps. As a consequence it would not really be possible to define a complete programming model surface that everyone would be happy with, but that a simple set of hooks could be put into the product that people could use to build programming models rather easily.

And so they did, and so I did. This new capability of the HTTP transport first appeared in the September CTP of Indigo/WCF and surfaces to the developer as properties in the Message class Properties collection or the OperationContext.Incoming/OutgoingMessageProperties.

If you are using the Indigo HTTP transport on the server, the transport will always stick a HttpRequestMessageProperty instance into the incoming message properties, which provides access to the HTTP headers, the HTTP method (GET, POST, PUT, etc.) and the full query string. On the client, you can create an instance of this property class yourself and stick it into any outbound message’s properties collection  and, with that, control how the transport performs the request. For sending replies from the server, you can put a HttpResponseMessageProperty into the message properties (or, again, into the OperationContext) and set the HTTP status code and description and of course the HTTP reply headers.  

And since I have nothing better to do, I wanted to know whether this rather simple control feature for the HTTP transport would indeed be enough to build a POX/REST programming model and application when combined with the rest of the Indigo extensibility features. Executive Summary: Yes.

Now, the “yes” doesn’t mean that the required extensions write themselves in an hour. It’s a bit of work to get there, so it’ll take me a bit of writing (and a few days) to tell the whole story. There are two ways to approach the explanation: “Bottom-up” where I’d start at the wire level and show how to have Indigo send and accept any (and I mean any: 8GB+ video/mpeg files included) stuff that isn’t wrapped in SOAP envelopes or “Top-Down” where I start at the programming model surface and explain the application-level developer experience first. It’s a tough call, but I’ve decided to start with the latter.

REST and POX

In a nutshell – and I am sure that I’ll now get 153 corrections by comments and email from the other 99 people – the core ideas behind REST (representational state transfer) are that it builds on the pervasive HTTP web-architecture, that every item (“resource”) in the whole wide world can/could be identified by a HTTP URL and that HTTP has sufficient built-in methods to manipulate such resources. You can GET a data representation of the item, you can POST a new, related resource representation underneath an existing resource, you can PUT updates to resource representations and you can of course DELETE a resource representation. Every representation of a resource may have links to related resources that you can likewise access using these methods and therefore you get a web of information. REST is an architectural generalization of the HTML/HTTP web and the proponents of REST argue that the web works very obviously very well and that therefore REST is fit for any type of application. Of course, that’s a bit of an exaggeration, because there are plenty of scenarios where request/response isn’t the thing to do, but for a lot of applications REST may just be a good choice. Commonly, the REST architectural style is combined with XML data representations, even though this architectural style is really content-neutral. The place to find a “link” in a resource represented by a JPEG photo could very well be a URL on somewhere an advertising poster in the background behind your mother-in-law wearing a horrible hat at an English horse-racing event.    

POX means “plain old XML” and I’ve also heard a definition saying that “POX is REST without the dogma”, but that’s not really correct. POX is not really well defined, but it’s clear that the “plain” is the focus and that means typically that folks who talk about “POX web services” explicitly mean that those services don’t use SOAP. You could see POX as an antonym for SOAP, if you will.

Except for “this isn’t SOAP” the term POX means very little. POX isn’t about architecture, it’s only about content. But REST is about architecture and it is content neutral. So what you commonly find is the combination of REST/POX as an alternative model to SOAP and the WS-* web services stack.

SOAP vs. REST/POX

Indigo is a messaging system that deals with messages that are represented by an envelope with headers and a body: SOAP. Indigo is so soaked with SOAP that you might just be able to wash your hands with a WinFX SDK CD. Still, the fact that Indigo is using an information model that is aligned with SOAP means nothing for what goes on or comes from the wire as I will show you later.

Fundamentally, the reason for why anybody might be using SOAP is that it gives you two places to stick things: A header section for metadata and a body section for payload. The headers are important if you need to communicate addressing, security or other information independent from a transport and across multiple communication hops. HTTP does have an extensible headers model, but other messaging transport options don’t. So to make things consistent across all sorts of transports and to provide an abstraction away from the transport specifics, all metadata needed to establish communication is stuck into an envelope alongside the payload. If you look at SOAP and WS-Addressing combined, you can find that the information content is really not much more than that of an IP packet (yes, IP=Internet Protocol). SOAP/WS-Addressing provide an abstraction for routing packages over any sort of transport just as much as IP provided an abstraction over Ethernet, Token Ring, ArcNet and so forth The problematically-named WS-ReliableMessaging is the TCP equivalent, by the way.

But: If you’ve decided that HTTP is all you need and REST/POX is the way to go for your application, you are apparently happy with a request/response model, you don’t need routing or reliable delivery, transport/app-protocol level security is sufficient, and you don’t need to have an abstraction of the HTTP headers and HTTP methods. In that case, the features that SOAP and the WS-* stack give you could be considered redundant. Let’s assume that’s so and try to eliminate SOAP out of the equation.

Figuring out a programming model

Incoming SOAP messages can be dispatched to handlers, typically methods, in two (and a half) different ways. The first (and a half) and most common option is to dispatch based on the value of the SOAPAction: (SOAP 1.1) HTTP header or the “action” media type parameter (SOAP 1.2). Alternatively, some stacks dispatch on the value of the WS-Addressing wsa:Action header, if present. The second way to dispatch messages it to look at the immediate child element of the soap:Body and to associate that content with a handler. The mapping rules are also spelled out in WS-Addressing. Indigo, like ASP.NET web services, uses the first method of dispatching, because that doesn’t require touching the message content.

Now, if we want to do away with SOAP and only want to kick plain old XML documents or even raw binary data around, we have a bit of a problem. Since there are neither any of the HTTP header hints, nor do we have a WS-Addressing wsa:Action header that Indigo could at, its got no information that it could use to dispatch the incoming request on. Even worse: If the incoming request is just an HTTP GET with no entity-body, at all, there’s really nothing to look at except, well, the HTTP method and the URI.

But let’s take a step back and look at this Indigo service contract (I spare you the WSDL) that makes any REST/POX person cringe and shout “This is RPC!”:

[ServiceContract]
interface ICustomerInfo
{
    [OperationContract]
    CustomerInfo GetCustomerInfo(string customerKey);
    [OperationContract]
    void UpdateCustomerInfo(CustomerInfo info);
    [OperationContract]
    void DeleteCustomerInfo(string customerKey);
}

And, yes, this is very RPC like and I could use this exact contract with Enterprise Services or Remoting. It’s also perfectly fine for SOAP Web Services. The action values that Indigo uses for its dispatcher are, because we don’t specify any overrides in the attributes, derived from the method names combined with the port type name (here implicitly ICustomerInfo) and the contract namespace (here implicitly http://tempouri.org). So for this plain definition the action value for the operation GetCustomerInfo() is http://tempuri.org/ICustomerInfo/GetCustomerInfo. An incoming message with that exact URI as its action identifier is mapped to the operation implementation of the GetCustomerInfo() method.

However, without SOAP this dispatch strategy no longer works. Let’s assume from here on that the only thing that goes onto the wire are plain XML documents and not SOAP envelopes. We only send and receive XML payload with nothing around it. (You’ll get to see the “how to” later in this series).

Also, if you were a REST/POX person, you’d rightfully say that there is redundancy in this contract for two reasons. First, because HTTP is an application protocol (Mark Baker, one of the most visible REST proponents, keeps reminding the public about this) and it already defines methods for “Get”, “Update” (PUT), and “Delete”. With SOAP web services, everything is typically POSTed and so it’s effectively tunneling all sorts of semantics through what’s supposed to be the create semantics of HTTP and that makes purists fundamentally unhappy. Second, the “customerKey” for identifying the object is redundant because identifying the resource you want to modify or query is the job of the URL. (Note that I intentionally leave “Create” (POST) out of this for the moment. We’ll get back to that later.)

A more RESTish and HTTP aligned contract definition could look like this:

[ServiceContract]
interface ICustomerResource
{
    [OperationContract]
    CustomerInfo Get();
    [OperationContract]
    void Put(CustomerInfo info);
    [OperationContract]
    void Delete();
}

Now we assume, for a moment, that every customer in the system had its own HTTP service endpoint. If you have a million customers, you have a million endpoints, probably looking like this: http://www.example.com/myapp/customers/00212332. Each of these endpoints has an implementation of the shown interface, representing the resource.

Each of these million services already knows the customer key when a call reaches its endpoint, because each endpoint represents exactly one customer. Therfore we don’t have to pass any parameters to the service for Get or Delete. Get returns the data representation of the service endpoint’s very own, exclusively assigned customer. Following the same logic, the CustomerInfo record that we pass as an argument to Put() (Update) doesn’t need to contain an identifier. This new contract definition would also have sufficient metadata for a dispatcher extension for Indigo that we need to replace the SOAP action dispatcher, because we could map the incoming HTTP method directly to the respective handler by doing a (case-insensitive) string compare to the method name on the interface. HTTP GET maps to Get(), HTTP POST maps to Post(), and so forth.

I think that you probably even could create a million endpoints, but of course such an application would be a complete pig. I haven’t tried and I don’t think you should. So we should find a way to optimize the million endpoints down to one endpoint and from an implementation standpoint it would be rather useful if one service implementation could deal with multiple resource types. That means that we might want to have multiple GET or DELETE methods sitting on the same interface but handling different resources. That aside, “Post” and “Put” are not immediately intuitive names for “Create” and “Update” so that it’d be nice to decouple the application’s operation names from the HTTP method names.

To satisfy all of these requirements, I am adding a little additional metadata to this contract declaration:

[ServiceContract]
interface IMyApp
{
    [OperationContract, HttpMethod("GET",UriSuffix="/customers/*")]
    CustomerInfo GetCustomerInfo();
    [OperationContract, HttpMethod("PUT", UriSuffix = "/customers/*")]
    void UpdateCustomerInfo(CustomerInfo info);
    [OperationContract, HttpMethod("DELETE", UriSuffix = "/customers/*")]
    void DeleteCustomerInfo();
}

The HttpMethodAttribute that I have written which I’ll show and explain in more detail in the next post, has a mandatory argument method which is the HTTP method that the operation handles. Along with that I’ll show an Indigo address filter and an Indigo operation selector that plug into the Indigo dispatch engine and perform the mapping for the incoming request to the correct handler based on the comparison of the HTTP method value and the attribute’s method value.

The optional, named attribute property UriSuffix allows narrowing the match to a namespace. If an implementation of this contract were hosted at http://www.example.com/myapp, only HTTP GET requests made on http://www.example.com/myapp/customers/ or any sub-path of that URL (the ‘*’ acts as wildcard) would be dispatched to GetCustomers(). Inside the method it is then very easy (String.LastIndexOf(‘/’)) to parse out the customer identifier from the URL that can be retrieved from the http request message property or the incoming message’s header collection (Indigo maps a set of HTTP transport information items to message headers if you ask it to). So if we were extending this service to also manage a set of phone/fax/mobile numbers for the customer, we could do it this way:

[ServiceContract]
interface IMyApp
{
    [OperationContract, HttpMethod("POST", UriSuffix = "/customers/*/comm/*")]
    void CreateCommunicationNumber(CommNoInfo commNoInfo);
    [OperationContract, HttpMethod("GET", UriSuffix = "/customers/*/comm/*")]
    CommNoInfo GetCommunicationNumber();
    [OperationContract, HttpMethod("PUT", UriSuffix = "/customers/*/comm/*")]
    void UpdateCommunicationNumber(CommNoInfo info);
    [OperationContract, HttpMethod("DELETE", UriSuffix = "/customers/*/comm/*")]
    void DeleteCommunicationNumber();
    [OperationContract, HttpMethod("GET",UriSuffix="/customers/*")]
    CustomerInfo GetCustomerInfo();
    [OperationContract, HttpMethod("PUT", UriSuffix = "/customers/*")]
    void UpdateCustomerInfo(CustomerInfo info);
    [OperationContract, HttpMethod("DELETE", UriSuffix = "/customers/*")]
    void DeleteCustomerInfo();
}

With the support for multiple namespaces we can create a neat, hierarchical external representation of our data whereby each data element has its individual URL. The fun part is that the application-level implementation does not differ greatly from what you would do in a “normal” app. The “magic” sits in the infrastructure. Sticking with our example, the home phone number data bit for a customer might be retrievable or manipulated here: http://www.example.com/myapp/customers/00212332/comm/home-phone.

Mind that for all examples I’ve shown here, I made the implicit suggestion that we’d use XML serialization. That’s really only for illustrative purposes. It turns out that a lot of the REST/POX proponents are also defenders of the pure beauty of angle brackets and therefore I’ll switch to a pure XML message model with the next post, because that is indeed quite a bit more flexible for this particular application style as you’ll see.

Go to Part 2

Categories: Indigo

December 7, 2005
@ 11:22 PM

Back to blogland. Looking back at this year, i have hardly blogged at all. Partly because I wad too busy and partly because I just had better things to do with my free time. Anyways, in the upcoming weeks I'll write about the things that I've been quietly building in the past half year or so and also dig into and publish stuff from my code archive where I still have some gems laying around that should really be published before they get totally useless. I even have a very cool NETFX 2.0 update for this here.

Part of what I am going to blog about and explain in quite some detail is the (code-named) "Clemens TV" project, which I keep working on. As things stand right now, there are so many variables and configuration issues with getting this to work for everyone (or "anyone but myself" for starters) that it doesn't seem feasible from a support perspective to make all of it public in the same way as I did with dasBlog. Instead, I'll publish a framework that allows hooking in all sorts of (self-written) live TV providers into a common (Indigo/WCF) server app. I will publish a provider for public web streams.

However, if you happen to use SnapStream's Beyond TV and have an additional Beyond TV Link license (that's required for the Beyond TV provider for the app), you have at least one software encoding TV card (hardware encoding cards won't work for web streams), and you have a connection with at least 256 KBps upstream, drop me a line to clemensv@newtelligence.com and I'll put you on a short list for those folks who might get the provider for testing (and to keep) once I am happy with it. We'll see where we go from there.

That said, the application is only partially about TV. It's a showcase demonstrating that Indigo is not only about pushing SOAP envelopes around. I am sending RSS, ASX, OPML and multi-gigabyte, restartable MPEG downloads through Indigo channels and all the receiving application sees is a plain old data stream or plain old XML (nicknamed POX). And when I want to record a show I send an HTTP POST to an endpoint to update the episode details and add a recording and when I want to cancel the recording I send an HTTP DELETE to remove the recording job. That smells like REST. I am sure Mark Baker will dig Indigo once he sees my set of ServiceModel extensions ;-)

Anyways, this is just a "heads up" that it's probably worth looking this direction in the upcoming weeks, no matter whether you are checking out Indigo today or are doing stuff with shipping technologies such as Enterprise Services.

Categories: Indigo