Multithreaded WebServer Class

December 15th, 2008 Leave a comment Go to comments

Our world now a days wouldn’t be posible without the Internet. More and more devices get a web interface. Some available to the end user as a webpage the other as webservices (SOAP eg.). To aid in creating this web interfaces I created a multithreaded webserver class for .NET Micro Framework. 

Introduction:

I tried making it as universal as posible and at the same time trying to make it very simple to use. The implementation consists of 4 classes (5 actualy but the last is only used internaly).

  • HTTPServer Class
    This class contains the actual webserver. Create a new instance of this class to start listening
     
  • IRequestHandler Interface
    Create your own class implementing this interface. When a request comes in the HandleRequest function is called with all data available in the request (URI requested, GET and POST Value’s and Request Header).
     
  • HTTPHeader Class
    Used for passing Request and Response headers from and to the HandleRequest function. Provides easy Set and Get functions to modify header items.
     
  • RequestParameter Class
    Used for passing GET and POST values to the HandleRequest function. Provides easy Get function to retrieve a value. The value can automaticly be url decoded.

 

The 5th class is HTTPServerWorker. This class is spawned by the HTTPServer class when a request comes in. This class processes this request, calss the HandleRequest function and returns the result back to the client. The HTTPServer automaticly limit the number of concurrent HTTPServerWorkers to 5 (This value is adjustable). When the 6th request comes in the server waits until a slot comes free.

Changelog:

  • 14 December 2008: Initial version
     
  • 17 December 2008: Some source breaking updates:
    • Update in error checking, all values are checked for null. Error’s don’t crash the HTTP worker.
    • URL parameter fixed when parameters are added.
    • Return body is now a byte[] array instead of String so you can send binary data (images)
    • Automaticly add Content-Length to response header.
       
  • 22 December 2008: Minor updates and Keep-Alive:
    • There are some minor updates in the basic code, the code required the POST data to be in
      application/x-www-form-urlencoded. Some clients add an encoding to this string.. wich is valid. I changed the ruleset from Content-Type = "application/x-www-form-urlencoded" to
      Content-Type has "application/x-www-form-urlencoded".
    • HTTPHeader.Get and HTTPHeader.Set Field name parameter isn’t case sensitive anymore.
    • Experimental Keep-Alive support. It doesn’t follow the HTTP 1.1 specifications yet but I can’t find a client that fails. The HTTP 1.1 specs allows clients to queue HTTP requests. This requires that the server searches for request end. The server doesn’t do that yet. At this moment there are no clients implementing request enqueing.

       

Example usage:
To give you an idea how you can use the class the sourcecode of the example program is displayed below: 

/// <summary>
/// Demo program showing the usage of the Webserver implementation
/// </summary>
public class Program
{
  /// <summary>
  /// Here we create our own Request Handler implementation
  /// </summary>
  public class RequestHandler : IWebRequestHandler
  {
   
    /// <summary>
    /// This value is retrieved trough an AJAX call
    /// </summary>
        private static Int32 RequestCount = 0;

   /// <summary>
   /// Function called when HTTP gets in
   /// </summary>
   /// <param name="Method">HTTP Method (GET or POST)</param>
   /// <param name="URL">URL requested</param>
   /// <param name="RequestHeaders">Request headers</param>
   /// <param name="GET">GET Values</param>
   /// <param name="POST">POST Values</param>
   /// <param name="Status">Status string to return (eg. HTTP/1.0 200 Ok)</param>
   /// <param name="ResponseHeaders">Response headers to return</param>
   /// <param name="ResponseBody">Response body</param>
   /// <returns></returns>
   public void HandleRequest(
     String Method,
     String URL,
     String POSTBody,
     HTTPHeader[] RequestHeaders,
     RequestParameter[] GET,
     RequestParameter[] POST,
     ref String Status,
     ref HTTPHeader[] ResponseHeaders,
     ref byte[] ResponseBody)
   {
      // Send logo
      if ((URL == "/logo.png") & (Method == "GET"))
      {
        Status = "HTTP/1.0 200 OK";
        HTTPHeader.Set(ref ResponseHeaders, "Content-Type", "image/png");
        HTTPHeader.Set(ref ResponseHeaders, "Cache-Control", "no-cache");
        ResponseBody = Resources.GetBytes(Resources.BinaryResources.logo);
      }

      // Sample Ajax request
      if ((URL == "/ajax.txt") & (Method == "GET"))
      {
        Status = "HTTP/1.0 200 OK";
        HTTPHeader.Set(ref ResponseHeaders, "Content-Type", "text/plain");
        HTTPHeader.Set(ref ResponseHeaders, "Cache-Control", "no-cache");
        ResponseBody = Encoding.UTF8.GetBytes(RequestCount.ToString() + "th request");
        RequestCount++;
      }

      // Load prototype
      if (URL == "/prototype.js")
      {
        Status = "HTTP/1.0 200 OK";
        HTTPHeader.Set(ref ResponseHeaders, "Content-Type", "text/javascript");
        ResponseBody = Resources.GetBytes(Resources.BinaryResources.prototype);
      }

      // Main page with form and post result
      if (URL == "/")
      {
         if (Method == "GET")
         {
            // Set Status Ok
            Status = "HTTP/1.0 200 OK";

            // Set correct content type
            HTTPHeader.Set(ref ResponseHeaders, "Content-Type", "text/html");

            // Get Form
            ResponseBody = Resources.GetBytes(Resources.BinaryResources.posttest);
        }

        if (Method == "POST")
        {
           // Set Status Ok
           Status = "HTTP/1.0 200 OK";

           // Set correct content type
           HTTPHeader.Set(ref ResponseHeaders, "Content-Type", "text/plain");

           // Output request headers
           String ResponseBodyTxt = "";
           ResponseBodyTxt = "Request header: \r\n";
           ResponseBodyTxt += HTTPHeader.OutputResponseHeader(RequestHeaders);
           ResponseBodyTxt += "\r\n";

           // Output post values
           ResponseBodyTxt += "Value 1: \r\n";
           ResponseBodyTxt += RequestParameter.Get(ref POST, "value1", true) + "\r\n";
           ResponseBodyTxt += "Value 2: \r\n";
           ResponseBodyTxt += RequestParameter.Get(ref POST, "value2", true) + "\r\n";

           // Convert to byte[] array
           ResponseBody = Encoding.UTF8.GetBytes(ResponseBodyTxt);
        }
      }
   }
}

public static void Main()
{
  // Create our Webserver
  HTTPServer WebServer = new HTTPServer(80, new RequestHandler());

  // Infinite sleep
  System.Threading.Thread.Sleep(-1);
}
 

Download:
To start using the class you can download the full Visual Studio 2008 solution here: MFWebserverTest.zip
(The example program is included)

 

Share and Enjoy:
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • NuJIJ
  • Technorati
  • Yahoo! Bookmarks
  1. Dje
    April 14th, 2009 at 15:59 | #1

    Hi, thanks for your example, i tryed it and it worked fine on the emulator. However, when i deploy it to my tahoe II board with the last firmware version. I experience very long delay when loading the main page from the webserver. Any idea of what be causing this? thanks cheers Dje

  2. April 16th, 2009 at 10:43 | #2

    I developed the webserver project on a GHI board. I don’t know the reason. Michael Schwarz also made a webserver implementation. It is part of the Micro Framework toolkit that you can download from CodePlex:

    http://www.codeplex.com/mftoolkit
    http://netmicroframework.blogspot.com/

    Good luck!

  3. Christian Cordes
    April 17th, 2009 at 20:07 | #3

    I have also the Problem and i not found the reason. I found out that in Mozilla Firefox it works better and on Internet Explorer sometimes the main page doesnt display. I use the GHI Board. I think something must wrong in the script. i want to found the error when i back from holiday.

  4. Dje
    April 27th, 2009 at 11:26 | #4

    Hi Elze,
    thanks for your answrer, i tried on a GHI board too and it worked fine, cant understand whats wrong on the Tahoe board but I’ll try your links to codeplex. Cheers

  5. October 25th, 2009 at 08:37 | #5

    Hi

    I greatly appreciate your sample code, in minutes your WebServer was running on an Embeded Master (GHI Card) loaded with an RF Power meter.
    But I have a question, I am unable to alter the binary resources files, when I includes new one they are considered as txt. I suppose i have missed something in vs2008 ?

    regards
    Loïc

  6. October 27th, 2009 at 09:05 | #6

    @Loic
    That is fixed, i have removed the .js and .html extention, and now it is considered as binary files and the server is working fine with my new files.

    Loïc

  1. No trackbacks yet.