torsdag 23 maj 2013

A very simple webServer using .Net's HttpListener class

The other day a colleague and I ended up in the office kitchen discussing tiny web servers. Ha was writing some SDK code and wanted lightweight web server functionality in his code sample. I suggested using the HttpListener class which makes for great - and simple - web server implementations. I also had a vague memory of building something similar a year or so ago and I had a look in my personal code vault. Sure enough, there was indeed a lightweight web server implementation, though it had been somewhat de-simplified due to the nature of the app in which in resided. I decided to clean it up and hand it to him and after having failed to find any HttpListener sample code that was as clean as this I quickly decided to post it here as well.

Just a few short notes on the code.. It consists of an asynchronous and multithreaded webserver that can handle multiple simultaneous connections. The web server class (SimpleWebServer) has a Start() and a Stop() method that facilitates HttpListener- and thread management. It also exposes an event that is raised for each incoming request. The code itself is pretty self explanatory but here is a link to the MSDN documentation on the HttpListener Class.


class Program
{
  static void Main(string[] args)
  {
    var ws = new SimpleWebServer("http://localhost:666/Simple/");
    ws.RequestReceived += (sender, context) =>
      {
        var buffer = System.Text.Encoding.Default.GetBytes("Scattered I walk towards the fractured light. " + DateTime.Now);
        context.Response.OutputStream.Write(buffer, 0, buffer.Length);
        context.Response.StatusCode = (int)HttpStatusCode.OK;
        context.Response.OutputStream.Close();
      };
      ws.Start();
      Console.WriteLine("Listening to http://localhost:666/Simple/");
      Console.WriteLine("Press any key to stop...");
      Console.ReadLine();
      ws.Stop();
  }
}
class SimpleWebServer
{
  public delegate void RequestReceivedHandler(object sender, HttpListenerContext context);
  public event RequestReceivedHandler RequestReceived;

  private readonly HttpListener _listener;
  private bool _running;
  private readonly Thread _connthread;
  
  public SimpleWebServer(string prefix)
  {
    _listener = new HttpListener();
    _listener.Prefixes.Add(prefix);
    _connthread = new Thread(ConnectionThread);
  }
  
  public void Start()
  {
    _connthread.Start();
  }
  
  public void Stop()
  {
    _running = false;
    _listener.Stop();
  }
  
  private void ConnectionThread()
  {
    try
    {
      _running = true;
      _listener.Start();
      while (_running)
        ProcessRequest();
    }
    catch (HttpListenerException) { }
    catch (ThreadAbortException) { }
    catch (Exception) { }
  }

  private void ProcessRequest()
  {
    IAsyncResult result = _listener.BeginGetContext(ListenerCallback, _listener);
    result.AsyncWaitHandle.WaitOne();
  }

  protected void ListenerCallback(IAsyncResult result)
  {
    if (_listener == null || !_listener.IsListening) return;
    var context = _listener.EndGetContext(result);
    OnRequestReceived(context);
  }

  private void OnRequestReceived(HttpListenerContext context)
  {
    if (RequestReceived != null) RequestReceived(this, context);
  }
}