Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

MiniHttpd: an HTTP web server library

4.86/5 (54 votes)
30 Dec 200514 min read 39   9.9K  
A portable and flexible HTTP web server library written in 100% managed C#.

Introduction

MiniHttpd is a standalone HTTP and HTTP web server library. It can be used either as a web server, or as an HTTP listener in situations where deploying a full-blown web server such as IIS or Apache would not be feasible.

One of the original motives for making MiniHttpd was to make a web interface for the Windows Media Player that I could use to control Media Player from my phone or Pocket PC and download tracks onto. One such program, called PlayerPal, as of version 3.0 uses the Cassini library which is similar in functionality but is no longer under development.

Another motive was to make an alternative file server to use between my friends. It doesn't seem to matter how many ways there are to send files - MSN, IRC, FTP, email... sometimes none of them work!

MiniHttpd is not complete, and additional features and improvements are sure to come. If you have any suggestions or problems, I'd be glad to hear them. If you're using MiniHttpd in the wild, I recommend isolating it in a separate OS account with limited privileges. As always, I appreciate bug finds - this is a one-man project for now, and there's only so much that I can do alone. Many thanks to those who've looked at the code.

Download

To demonstrate the server, I've set up a MiniHttpd server on my friend's computer hosting the demo application binary. You can download the MiniHttpdApp here.

Compared to other web servers

Here is a feature comparison of MiniHttpd and other common web servers. This list is exhaustive only to the limit of my knowledge, so let me know if I'm missing anything important.

ApacheIISXSPCassiniMiniHttpd
Windows compatible     
*NIX/BSD/OS X compatible     
.NET Compact framework compatible    Possibly in the future
Standalone     
Portable library     
ASPX capableWith Mod-Mono   As of version 1.1
PHP capable     
Commercially suitable     
ProgrammabilityPHP/CGIASP/ASPXASPXASPXASPX, IFile/IDirectory
Supports resuming     
SSL (HTTPS) support    Hopefully soon

Using the code

Basic setup

At minimum, setting up a web server requires only a few lines of code. This example initializes a web server that uses your C drive as the root directory:

C#
// Create a server object on the
// default HTTP web port (80)
HttpWebServer server = new HttpWebServer();

// Set the root directory to C (don't try this at home)
server.Root = new DriveDirectory(@"c:\");

// Start the server
server.Start();

Console.ReadLine();

// Stop the server (note that you must stop the listener
// explicitly either by calling Stop or Dispose, or by
// wrapping the server in a 'using' block)
server.Stop();

And that's it! You can browse to your local address in your favorite browser and it should show you a directory listing or an index.htm/index.html page if there is one. You can specify a different listener port in the constructor or by setting the Port property.

Virtual directories

Virtual directories are directories that you can populate programmatically rather than by reflecting a physical directory. By default, an HttpWebServer instance sets root to an empty virtual directory upon creation. You can choose either to work with this one or create your own. This example creates a virtual directory, adds a few files and folders, and deletes a few.

C#
// Create a new virtual directory
// and set the root to it
VirtualDirectory root = new VirtualDirectory();
server.Root = root;

// Add a few files
root.AddFile("notepad.exe");
root.AddFile("autoexec.bat");

// Add a directory
root.AddDirectory(@"c:\windows");

// Remove a file
root.Remove(@"autoexec.bat");

// Add a virtual subdirectory
root.AddDirectory(new VirtualDirectory("virtual", root));

ASP.NET support

ASPX is implemented as extensions of DriveDirectory and HttpWebServer, and AspxAppDir and AspxWebServer respectively. Both have the same constructors as their base classes, so setting up is as easy as replacing all instances of the two.

Unfortunately, there is no virtual ASPX directory because ASP.NET requires files to have absolute paths on-disk. I will keep an eye out for workarounds -- it'd be really nice to be able to store ASPX applications in assemblies.

The ASPX code is contained in the MiniHttpd assembly to avoid cluttering distributions. If for any reason you don't want the ASPX at all, you can just exclude the Aspx folder from the project and recompile.

Certain features are still not implemented in version 1.1; the two that I know are not yet implemented are HTTP handlers and HTTPS.

Note that MiniHttpd leaves Bin folders with MiniHttpd.dll in whichever folder you use as an ASPX directory. MiniHttpd 1.2.0 onwards will remove the file upon disposal as well as the folder if it is empty.

PHP support

PHP is implemented similar to ASPX, with PhpAppDirectory and PhpWebServer inheriting DriveDirectory and HttpWebServer. PHP support is even more primitive than ASPX for the time being, accepting only scripts with text output and no request/response headers.

There are three assemblies you need to use PHP with MiniHttpd:

  • MiniHttpd.Php.dll - you can download this along with its source from the link at the top of the article.
  • php4app.dll - this is included in the project, but you can also download it from here.
  • php5ts.dll - because this is a rather big file (4 MB), I've hosted it here. You can also find it in the PHP Windows binaries, which you can download here.

Thanks to Daaron Dwyer for demonstrating PHP on MiniHttpd to me and doing the bulk of the work on MiniHttpd.Php!

Index Page

The index page currently built into the library is a very boring list of files available in the folder. You can override this by replacing the HttpWebServer.IndexPage property. Optionally, you can override the IndexPage class, which provides a BuildPath method that you can use to determine the relative path of a directory.

Additionally, as of version 1.2.0, there's a slightly fancier index page called IndexPageEx. You can use it by creating a new IndexPageEx object and assigning it to HttpWebServer.IndexPage. You can specify the columns and order of columns to display, by setting an array of ResourceColumn values in the IndexPageEx.Columns property. The default is Modified, Size, Name. IndexPageEx can be slow for large directories, so decide carefully whether or not to use it. (My suspicion is that this will change in a few months when MSH and/or Vista is released.)

HttpServer

HttpServer, along with its satellite classes, implement the essential workings of the HTTP/1.1 protocol. HttpWebServer is derived from this class. You can use this class to handle HTTP transactions manually without using the directory structure that the HttpWebServer provides. However, a similar class, System.Net.HttpListener, is introduced in the .NET 2.0 Framework, so you may want to use that instead if all you need is an HTTP listener.

Logging

The server writes log messages to the TextWriter specified by HttpServer.Log. By default, this writes to the console, unless there is no console available (such as on Pocket PC). You can disable this by setting LogConnections and LogRequests to false, and you can redirect the output to other TextWriters by setting the Log property.

Authentication

As of version 1.2.0, MiniHttpd supports a very simple authentication (logins and passwords) through the IAuthenticator interface. There are two implemented IAuthenticators in MiniHttpd: BasicAuthenticator and LdapAuthenticator. You can enable it by setting HttpServer.RequireAuthentication to true. The default is BasicAuthenticator, but you can enable LdapAuthenticator just as easily by assigning a new LdapAuthenticator to HttpServer.Authenticator.

BasicAuthenticator just maintains a Hashtable containing user/password pairs. Passwords are stored as MD5 hashes to be ready for serialization and on-disk storage. LdapAuthenticator authenticates against an Active Directory server with the username and password given by the client.

Authentication is primitive for the time being; access is server-wide. Also note that this is not HTTPS, so passwords and transactions are not encrypted.

Thanks again to Daaron Dwyer for demonstrating LDAP and authentication on MiniHttpd.

IDirectory and IFile

All objects in the HttpWebServer file tree are described by implementations of IDirectory and IFile. Both IDirectory and IFile inherit IResource, which provides properties to identify a file or directory. Internally, the IDirectory is implemented by DriveDirectory to reference physical directories, and VirtualDirectory for memory-resident directories containing other IDirectorys and IFiles. IFile is implemented by DriveFile to reference files on disk.

IDirectory and IFile can be used to make dynamic content. Any tree-file structure can be represented by an IDirectory object; for example, the Media Library in Windows Media Player. IFile can be used to send anything from redirects to streaming video. The IDirectory/IFile interfaces have been used for some cool projects so far:

  • "What's my IP" image generator
  • Webcam feed that automatically turns on the garage lights before snapping
  • Video stream to web-enabled TV devices

There isn't much ASPX couldn't do that IFile/IDirectory could, so they shine most in situations where ASPX is not available. But at this point, MiniHttpd isn't compatible with any OS that doesn't have ASPX. If there's any interest, I'll try to restore the Compact Framework compatibility so that it can be used as a programmable web server for Pocket PC and SmartPhones. I'd love to see it running on SPOT OS (used for SPOT watches and Windows Vista's Auxiliary Displays) if they ever make it available to the public for hobby electronics and robotics. Let's hope they do!

Implementing IDirectory

Implementing IDirectory is pretty straightforward. An IDirectory object typically holds a collection of subdirectories and subfiles. There are just three required steps:

  • Create a constructor that takes in the name and the parent directory of the directory, and stores them for the Name and Parent properties to return (see IFile example below).
  • Implement the IResource.Name and IResource.Parent properties by returning the values passed in from the constructor.
  • Implement all of the IDirectory.Get...() functions. Their usages are described in the XML documentation in FileSystem/IDirectory.cs.

Be sure to call Dispose() on all subdirectories and files when they are removed, and in the Dispose function.

Implementing IFile

Implementing IFile is slightly more complicated, although from a programmer's perspective, I find it easier to grasp conceptually than an ASPX webpage (but that's probably just me).

Like IDirectory, the first step is to create a constructor that takes the name and the parent directory as arguments, and stores them for the Name and Parent properties.

Then you implement ContentType. Content Types (also called MIME types) are the Web's way of describing the type of a resource, much like the way Windows identifies file types by their filename extensions. .txt files are called "text/plain", .jpg files are called "image/jpeg", and .html/.htm files are called "text/html". In general, you can use ContentTypes.GetExtensionType() to retrieve the MIME equivalents of common file extensions.

Last but not least, implement the OnFileRequested method. request provides all of the properties and methods to manipulate the request, while directory provides the parent directory of the file. In most cases, the Response property is the only one that matters, and specifically, the ResponseContent property of the Response property. You can assign any sort of stream to the ResponseContent stream, so long as it supports reading, seeking, and has a definite length.

An IFile implementation that sends the text "Hello, world!" to the client might look like this:

C#
public class HelloWorldFile : IFile
{
   public HelloWorldFile(string name, IDirectory parent)
   {
      this.name = name;
      this.parent = parent;
   }

   string name;
   IDirectory parent;

   public void OnFileRequested(HttpRequest request, 
                               IDirectory directory)
   {
      // Assign a MemoryStream to hold the response content.
      request.Response.ResponseContent = new MemoryStream();

      // Create a StreamWriter to which we
      // can write some text, and write to it.
      StreamWriter writer = 
        new StreamWriter(request.Response.ResponseContent);

      writer.WriteLine("Hello, world!");

      // Don't forget to flush!
      writer.Flush();
   }

   public string ContentType
   {
      get { return ContentTypes.GetExtensionType(".txt"); }
   }
   public string Name
   {
      get { return name; }
   }

   public IDirectory Parent
   {
      get { return parent; }
   }

   public void Dispose()
   {
   }
}

Chunked Responses

Because HTTP normally requires that you provide the size of a resource in the header, MiniHttpd uses the Length property of the stream assigned to HttpResponse.ResponseStream to retrieve this information. However, there are some forms of data that don't have a definite size - dynamic web pages and video/audio streams are examples of such. The HTTP/1.1 protocol provides a mechanism to allow for this, called Chunked transfer encoding (HTTP/1.0 implicitly signals the end of a transmission by disconnecting after each transmission). You can enable it by calling BeginChunkedOutput() instead of assigning a stream to HttpResponse.ResponseStream (the function does this for you).

MiniHttpdApp demo projects

MiniHttpdApp

MiniHttpdApp is a web server sample application that demonstrates the MiniHttpd library. Although the GUI looks nearly the same, I've rewritten it from scratch for version 1.2.0 to address several crashing issues that were part of its crude design and lack of thread-safety consideration. It should now be stable enough to be a permanent resident of your Windows tray.

Specify a port and optionally the host name if you are behind a router or other NAT device, and then click Server -> Start. There are more options in the property grid on the left, most of them are self-explanatory.

With RootType set to Drive in the Directories section of the left pane, you can specify a folder to share in the RootFolder box. With RootType set to Virtual, you can drag and drop files and folders to share, and right click or press Delete to remove. Settings are saved to MiniHttpdAppSettings.xml.

MiniHttpdConsole

MiniHttpdConsole is a command-line demo project I made primarily to try MiniHttpd on Linux. The interface is primitive, with a somewhat nostalgic text-based menu system (maybe not so to Unix users) and command-line argument input. Entering "?" brings up a list of available menu items, and entering "h" brings up a list of available command-line arguments.

Aside the VirtualDirectory format (which unfortunately is a base-64 encoded binary serialization of the root VirtualDirectory due to .NET 1.1's incomplete XML serialization implementation), you can use the same config file for MiniHttpdApp with MiniHttpdConsole. Once you've set all your settings, you can run MiniHttpdConsole on Mono in the background doing the following:

mono MiniHttpdConsole.exe -s -l &

-s specifies silent mode (no output, no menu); -l turns on file logging to MiniHttpd.log.

Note that MiniHttpdConsole requires .NET 2.0 or Mono 1.1.10.

MSH is awesome

Microsoft is working on a new command-line shell for the Longhorn Server (and possibly Vista) called Microsoft Shell (Monad), a .NET-powered shell with type-safe pipes and a syntax borrowed from functional languages, SQL and Unix shells. I couldn't resist sharing the fact that I tried MiniHttpd through Monad. Note that I did this right in the shell, but this could almost as easily be made into a function (called a cmdlet) and saved to a cmdlet file:

> [Reflection.Assembly]::LoadFile($(combine-path 
                         $(get-location) "minihttpd.dll"))

GAC    Version        Location
---    -------        --------
False  v1.1.4322      C:\Documents and Settings\Rei Miyasaka\
                      My Documents\Visual Studio Proj...


>$server = new-object MiniHttpd.HttpWebServer
>$server.Root = new-object MiniHttpd.DriveDirectory($(get-location))
>$server.Start()
Server: MiniHttpd/1.2.0.92
CLR: 2.0.50727.42
Server running at http://rei/

Isn't that cool?!

Pseudo-legal stuff

I will bind MiniHttpd by the Creative Commons Attribution 2.5 license, which in a word says that you may use MiniHttpd for anything you want, however you want (including commercially, but with no warranty), so long as you credit me for the original work.

On the other hand, I'm a college student, and programming work here in Vancouver is not very well-paying despite being scarce. In my position, it's hard to justify working on anything that won't earn me at least something. So if you ever do find commercial use for MiniHttpd, a small donation would be appreciated to give me encouragement.

Also, please let me know if you use MiniHttpd for a project. It'd look good on my resume :)

Future updates

These are ideas for future versions, in no particular order, and with no particular deadline. I will increment a minor version number (1.x) each time I make a breaking change.

  • Register HTTP handlers from web.config.
  • Repair .NET Compact Framework compatibility.
  • More debugging, especially ASPX.
  • HTTPS support.
  • Permissions and passwords.
  • Per-directory MIME types and other settings.
  • Upload cap/quota control.
  • Chunked POST data.
  • Improve PHP support.
  • Try multiplexing instead of asynchronous IO (see article by Alexey Popov).
  • Windows service.
  • Custom error pages.
  • Request handlers for better ASPX and PHP.
  • Think of a better name.
  • Use XML serialization for MiniHttpdApp in .NET 2.0.
  • Use W3C Extended Log Format.
  • Remote admin page.
  • Rewrite the whole thing in Spec# for .NET 2.0.

Links

History

  • 2004.12.20 - Beginning of project.
  • 2005.08.18 - First release - version 1.0.0.
  • 2005.08.18 - version 1.0.1 - I left some debug code in from last night... it would dump all of the headers from requests! Fixed.
  • 2005.08.18 - version 1.0.2 - Server now listens on all addresses (including localhost) unless otherwise specified in the constructor or the LocalAddress property.
  • 2005.08.20 - version 1.0.4 - MiniHttpdApp now starts in Virtual mode by default. Really bad bug - I forgot to implement the maximum POST size limit! Sorry. I checked all the other upload limits; everything else should be fine.
  • 2005.08.22 - version 1.0.5 - Fixed a severe flaw where a user could enter a URL-encoded backslash to get access to the root folder, among other similar abuses. Please download this version if you are using anything prior. Made a few small improvements.
  • 2005.09.17 - version 1.1.1 - Added ASPX support, fixed several small glitches, and made minor improvements. Added a transfer monitor to MiniHttpdApp.
  • 2005.12.12 - version 1.2.0
    • Several bug fixes.
    • IResource now inherits IDisposable; all IDirectorys should call Dispose on their containing resources.
    • Advanced index page.
    • MiniHttpdApp sample app rewritten with bug fixes and some improvements.
    • Log to file support.
    • Added MiniHttpdConsole sample app.
    • All future versions will be ported to Visual Studio 2005 and .NET 2.0, with the exception of crucial bug fixes.
    • Basic authentication.
    • Basic PHP support.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here