Building a Basic Web Server Using WCF

In my last post, I talked about how WCF actually can handle a lot more than basic web service communication. Today, I’m going to take that a little further, by showing you how you could build a very basic web server on top of WCF.

If you are familiar with ASP.NET, you have probably heard of a little class called IRequestHandler. In ASP.NET, if you wanted to create a handler that simply streamed a file from disk, you might write a little code like this:

    public class FileRequestHandler : IHttpHandler
    {
        #region IHttpHandler Members

        public bool IsReusable
        {
            get { return false;  }
        }

        public void ProcessRequest(HttpContext context)
        {
            context.Response.WriteFile(context.Request.PhysicalPath);
        }

        #endregion
    }

This should not be scary at all. Now… what if we wanted to host a simple web server inside our own application and we wanted to be able to use WCF to call into the interface? It takes a little WCF ninjitsu to make it work, but that’s what this blog is for.

The first step is creating a binding that will allow raw HTTP communication. To do this, we will create an instance of the CustomBinding class. Most of the bindings that you might be familiar with, such as basicHttpBinding or netTcpBinding hide the internals of WCF and don’t let you have any clue what is going on under the covers. CustomBinding is not one of those classes. The CustomBinding class allows you to pick and choose transports, security, message encoding (known in WCF terms as “BindingElements”) and combine them to do things that the standard bindings won’t let you do. In this case, we want to tell WCF to combine the HttpTransportBindingElement and the WebMessageEncodingBindingElement. In .NET 4.0 we have a better option… but most people aren’t using .NET 4.0 today so I’ll stick with .NET 3.5 for now. Another possibility would be to write a custom message encoder, but I’ll try to reuse as much of the built-in classes as possible. One little quirk about the WebMessageEncodingBindingElement is that it defaults to POX support, not raw HTTP. We can change that by adding a custom ContentTypeMapper implementation. To facilitate this process, I’ve created a BindingFactory class:

 public static class BindingFactory
    {
        public static Binding CreateRawHttpBinding()
        {
            HttpTransportBindingElement transport = new HttpTransportBindingElement();
            return new CustomBinding(CreateEncoder(), transport);
        }

        public static WebMessageEncodingBindingElement CreateEncoder()
        {
            WebMessageEncodingBindingElement encoding = new WebMessageEncodingBindingElement();
            encoding.ContentTypeMapper = new RawContentTypeMapper();
            return encoding;
        }

        class RawContentTypeMapper : WebContentTypeMapper
        {
            public override WebContentFormat GetMessageFormatForContentType(string contentType)
            {
                return WebContentFormat.Raw;
            }
        }
    }

The second thing we are going to need is a ServiceContract. If you read just about any documentation on WCF REST support, you will be lead to believe that you need to add special service behaviors for Url templates and HTTP verbs. Personally, I think all of that nonsense is crap, so we won’t do that. Instead, we’ll create a generic contract that can receive any message. Note that this contract is not REST specific. You could actually use the contract to do raw SOAP messaging as well. The way we tell WCF to allow any message to be handled by our contract is to add a method that accept a Message, returns a Message, and has it’s action and reply action set to “*”. This is what it looks like:

 [ServiceContract]
    public interface IWebRequestHandler
    {
        [OperationContract(Action="*", ReplyAction="*")]
        Message Request(Message message);
    }

When a request comes into our service, we’ll want the HTTP specific properties from the request to obtain things like the HTTP method and query string. To obtain the HTTP specific information about the request, we can get an HttpRequestMessageProperty from the properties collection of the message. This is pretty simple:

  HttpRequestMessageProperty property = (HttpRequestMessageProperty)message.Properties[HttpRequestMessageProperty.Name];

When we create a response message, we’ll add an HttpResponseMessageProperty for HTTP specific information. To write the response we’ll create a simple custom message class that can be used to represent our raw binary data. There are two tricks here. The first is that we are adding a WebBodyFormatMessageProperty to the message. This is required because of our choice of MessageEncoder and instructs the message encoder that we will be writing raw binary data. The second trick is that we write out the content as Base64 data. The XmlDictionaryWriter implementation that will be created by the MessageEncoder as a result of the property we have added will write out the Base64 data as raw binary data instead of encoding it. This might seem odd, but there is a very good reason that it works this way. I’ll save that for a later date.

        class RawMessage : Message
        {
            public RawMessage(byte[] data)
            {
                _data = data;

                WebBodyFormatMessageProperty property = new WebBodyFormatMessageProperty(WebContentFormat.Raw);
                Properties.Add(WebBodyFormatMessageProperty.Name, property);
            }

            byte[] _data;

            MessageHeaders _headers = new MessageHeaders(MessageVersion.None);
            public override MessageHeaders Headers
            {
                get { return _headers; }
            }

            protected override void OnWriteBodyContents(System.Xml.XmlDictionaryWriter writer)
            {
                writer.WriteBase64(_data, 0, _data.Length);
            }

            MessageProperties _properties = new MessageProperties();
            public override MessageProperties Properties
            {
                get { return _properties; }
            }

            public override MessageVersion Version
            {
                get { return MessageVersion.None;  }
            }
        }

Now we are ready to move on to our simple server implementation. Note that we’ve marked up the implementation of our interface with an attribute to tell WCF to accept requests to any child path below our endpoint address.

 [ServiceBehavior(AddressFilterMode=AddressFilterMode.Prefix, InstanceContextMode=InstanceContextMode.Single, ConcurrencyMode=ConcurrencyMode.Multiple)]
    public abstract class WebRequestHandler : IWebRequestHandler
    {
        public WebRequestHandler(Uri address)
        {
            // Ensure there is a trailing slash to our URL to so that MakeRelativeUri will work when we map the URL to a relative path.
            if (!address.ToString().EndsWith("/"))
            {
                address = new Uri(address + "/");
            }
            _address = address;
        }

        Uri _address;        

        MessageEncoder _webEncoder = BindingFactory.CreateEncoder().CreateMessageEncoderFactory().Encoder;

        protected abstract void ProcessRequest(Uri address, HttpRequestMessageProperty requestProperty, TextReader requestBody, HttpResponseMessageProperty responseProperty, TextWriter responseBody);        

        protected string GetRelativePath(Uri address)
        {
            string relativePath = address.LocalPath;
            relativePath = _address.MakeRelativeUri(address).ToString();
            int qIndex = relativePath.IndexOf('?');
            if (qIndex != -1)
            {
                relativePath = relativePath.Substring(0, qIndex);
            }
            return relativePath;
        }

        public System.ServiceModel.Channels.Message Request(System.ServiceModel.Channels.Message message)
        {
            using (MemoryStream responseStream = new MemoryStream())
            {
                using (TextWriter writer = new StreamWriter(responseStream))
                {
                    using (MemoryStream requestStream = new MemoryStream())
                    {
                        _webEncoder.WriteMessage(message, requestStream);
                         byte[] requestData = requestStream.ToArray();

                        using (TextReader reader = new StreamReader(new MemoryStream(requestData, false)))
                        {
                            HttpRequestMessageProperty requestProperty = (HttpRequestMessageProperty)message.Properties[HttpRequestMessageProperty.Name];

                            HttpResponseMessageProperty responseProperty = new HttpResponseMessageProperty();

                            ProcessRequest(message.Headers.To, requestProperty, reader, responseProperty, writer);

                            writer.Flush();
                            Message responseMessage = new RawMessage(responseStream.ToArray());
                            responseMessage.Properties[HttpResponseMessageProperty.Name] = responseProperty;
                            return responseMessage;
                        }
                    }
                }
            }
        }
    }

So now we have a basic web server class that we can extend. Hooking into the ASP.NET side of things is actually fairly messy and complex in comparison. I’ve included a very simple implementation in the sample project which contains enough logic to execute the simple file handler that this post started with. So there you have it. A raw HTTP host that can respond to web requests and serve up content using WCF.

[Download Sample Project: WebServer.zip]

Tags: ,

This website uses IntenseDebate comments, but they are not currently loaded because either your browser doesn't support JavaScript, or they didn't load fast enough.

5 Responses to “Building a Basic Web Server Using WCF”

  1. Larry Wall 24 January 2010 at 3:51 am #

    amazing. lots of progress in your world son. what are you 14? http://www.floodgap.com/httpi/. i hope some day you kids actually produce something new instead of the same old ideas wrapped in MSFT fan boi crap.

  2. Darrel Miller 24 January 2010 at 9:10 pm #

    I think I know where and why you are going with this approach because I took a similar route previously. However, using the HttpListener class you can do this much easier and throw WCF away completely. WCF uses the HttpListener class under the covers itself to provide HTTP endpoints and then it wraps it all up with generic WCF goop. HttpListener is really quite easy to use and gives you direct access to the HTTP request and response information.

  3. jezell 25 January 2010 at 1:02 am #

    .NET 4.0 includes a new message encoder for raw byte streams that doesn't require as many tricks because it isn't coupled to the REST support that the WebMessageEncoder was designed for.

  4. jezell 24 January 2010 at 9:21 pm #

    Don't disagree. If all you want is basic raw HTTP support with no frills, you are probably better off just using HttpListener. WCF itself uses HttpListener under the covers. However, WCF isn't just about doing the same thing in a different way. WCF is about a uniform communication model. The nice thing about this approach is that the same interface and concepts also work with TCP, MSMQ, and any other WCF binding with very minimal effort. WCF also adds a standard extensibility model for things like tracing, performance counters, instancing, security, etc. With HttpListener, you just get the basics.


Trackbacks/Pingbacks.

  1. Building an embedded MVC server « Simon says… architecture! - 03. Feb, 2010

    [...] yesterday’s blog reading session I came across two interesting posts. First one was about building a web server using WCF. The other one was about testing ASP.NET MVC as a whole, [...]

Leave a Reply