Recently I was considering the technology to use for the GUI of a windows desktop client application I’m working on in my spare time. The standard picks are obviously WinForms or the more modern WPF. However, I have some problems with them in this case:

  • the technologies above are (windows) platform dependent, while for instance HTML5 isn’t.
  • the application I’m working on is a debugger. Perhaps details about that will follow in a future post, but I would like to be able to run it remotely from an arbitrary machine (and OS).
  • I really appreciate the power of modern javascript libraries such as knockout.js, and I can’t use those with WPF or WinForms.

Now, for Windows 8, the store applications can be developed in HTML5 already, while desktop apps can’t. Of course I could resort to creating a real web application (hosted in IIS), but that would require the debugger host machine to have IIS installed. The same is true for the rehostable web core.

There is an open source library – NancyFX – which can be self-hosted, and I was tempted to use that but it had some unknowns coming from the MVC experience: controllers are modules in Nancy, and I couldn’t quite find what existing functionality in MVC was or wasn’t available in the Nancy library.

So with all other options out of the window, I set out to self-host ASP.NET MVC4.

Surprisingly, much of the stuff that’s needed to do this is undocumented: while the MVC pipeline is well known, the internals of what happens between the TCP port and the first entry point in ASP.NET aren’t. However, logic dictates:

1. there should be an open TCP/IP port listening somewhere
2. there should be a dedicated app domain for the web application
3. somehow the request received on the port should be transferred to the web application http runtime

1. Receiving requests

The traditional way of listening for web requests was to simply open a socket on port 80 using the Windows Socket API (WinSocks), and run the HTTP protocol stack on top of this in your app in user mode. However, this had severe drawbacks, which led to the creation of a dedicated kernel HTTP protocol stack – http.sys – which does all the low level HTTP work, and lies directly on top of the TCP/IP protocol stack.

In the user mode space, applications communicate with http.sys through the Windows HTTP Server API. IIS uses this API, and if we want to self-host ASP.NET, we will have to as well. Fortunately, the .NET framework includes a wrapper class around this API: System.Net.HttpListener

Using HttpListener to process requests should then be easy. I started by implementing a listener on top of HttpListener, and using Reactive Extensions to push the incoming request context to clients:

internal class Listener : IDisposable
{
    private HttpListener _listener = null;
    public IObservable IncomingRequests { get; private set; }

    internal Listener(int port)
    {
        _listener = new HttpListener();
        _listener.Prefixes.Add(string.Format("http://localhost:{0}/", port));
        _listener.AuthenticationSchemes = AuthenticationSchemes.Anonymous;
        _listener.Start();

        IncomingRequests = _listener.GetContextsAsObservable().ObserveOn(NewThreadScheduler.Default);
    }

    public void Dispose()
    {
        try
        {
            if (_listener == null) return;
            _listener.Stop();
            _listener = null;
        }
        catch (ObjectDisposedException)
        {
        }
    }
}

internal static class ListenerExtensions
{
    private static IEnumerable<IObservable> Listen(this HttpListener listener)
    {
        while (true)
        {
            yield return listener.GetContextAsync().ToObservable();
        }
    }
    internal static IObservable GetContextsAsObservable(this HttpListener listener)
    {
        return listener.Listen().Concat();
    }
}

Now all client code has to do is create a new listener, and subscribe to the requestcontext stream:

private Listener _listener;

private void SetupListener(int port)
{
    _log.InfoFormat("Setting up new httplistener on port {0}", port);
    _listener = new Listener(port);

    _log.InfoFormat("Start forwarding incoming requests to ASP.NET pipeline");
    _listener.IncomingRequests.Subscribe(
    (c) =>
    {
        try
        {
            ProcessRequest(c);
        }
        catch (Exception ex)
        {
            _log.Error("Exception processing request", ex);
        }
    },
    (ex) => _log.Error("Exception in request sequence", ex),
    () => _log.Info("HttpListener completed"));
    _log.Info("Completed httplistener setup");
}

The real magic happens in the yet to be implemented ProcessRequest method. However, before feeding requests to the ASP.NET pipeline, we first have to set that up in its own AppDomain, which will be the topic of the next post.

Ruurd Keizer

Author Ruurd Keizer

Quantumphysics PhD disguised as software architect, developer, and cloud native platform greasemonkey. Analytic, pragmatic, result oriented, never forgetting the bottom line. Interested in the whole picture: from businessvalue down to the bare metal.

More posts by Ruurd Keizer
22 May 2013

Leave a Reply