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);
  }
}

1 kommentar:

  1. This is a very good starting point. However, I see a little problem with this. When you write to the output stream, you assume the stream is still open. That may not be always the case because the client might close the connection while you are generating the output. I do not know how to fix this beside a messy try ... catch. How can we tell if the output stream is still open?

    SvaraRadera