A Web Server In Less Than 100 Lines of Code

Last night at the Lincoln .NET User’s Group, we had our second laptop meeting. A few people brought some code, a dev tip, or a fully working program to demo. I choose to show my "100 Line Web Server" I created to perform some unit testing on an app that was HTTP-centric, lately.

So, here is the entire program. I hope it’s useful to some and at least interesting to most.



using System;
using System.IO;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Collections.Generic;

namespace MicroWebServer
  /// <summary>
  /// A web server in a console app; implemented in less than 100 lines of C# code
  /// </summary>
  /// <remarks>
  /// Note: See the application settings for port number and physical file
  /// path properties.
  /// Limitations: 
  /// Only files listed in the WebServer._MimeTypes dictionary will be served properly
  /// Only files with extensions will be served (not a bad thing, just take note)
  /// Only files within the root or first sub-folder will be served
  /// </remarks>
  class Program
    private interface IWebServerContract
      [WebInvoke(UriTemplate = "{file}.{extension}", Method="GET")]
      Stream FileRequest(string file, string extension);
      [WebInvoke(UriTemplate = "{folder}/{file}.{extension}", Method = "GET")]
      Stream SubFolderFileRequest(string folder, string file, string extension);

    private class WebServer : IWebServerContract
      private Dictionary<string, string> _MimeTypes;

      public WebServer()
        this._MimeTypes = new Dictionary<string, string>();
        this._MimeTypes.Add(string.Empty, "text/plain");
        this._MimeTypes.Add("htm", "text/html");
        this._MimeTypes.Add("txt", "text/plain");
        this._MimeTypes.Add("gif", "image/gif");
        this._MimeTypes.Add("jpg", "image/jpeg");
      public Stream FileRequest(string file, string extension)
        string filePath = file;
        if (!string.IsNullOrEmpty(extension))
        filePath += "." + extension;

        Log(string.Format("Request for {0} received", filePath));
        string fullPath = Path.Combine(Properties.Settings.Default.WebRoot, filePath);
        if (File.Exists(fullPath) && !filePath.Contains(".."))
          string mimeType = GetMimeType(extension);
          Log(string.Format("Serving {0} as {1}", fullPath, mimeType));
          WebOperationContext.Current.OutgoingResponse.ContentType = mimeType;
          return File.OpenRead(fullPath);
          Log("File does not exist or access denied.");
          return null;
      public Stream SubFolderFileRequest(string folder, string file, string extension)
        return FileRequest(string.Format("{0}\\{1}", folder, file), extension);
      private string GetMimeType(string extension)
        return (this._MimeTypes.ContainsKey(extension)) ? this._MimeTypes[extension] : this._MimeTypes[string.Empty];

    static void Main(string[] args)
      WebServiceHost host = new WebServiceHost(typeof(WebServer), new Uri(string.Format("http://localhost:{0}/", Properties.Settings.Default.PortNumber)));
      WebHttpBinding binding = new WebHttpBinding();
      host.AddServiceEndpoint(typeof(IWebServerContract), binding, string.Empty);

      Log(string.Format("Starting web server for {0}", Properties.Settings.Default.WebRoot));
      Log(string.Format("Web server started at {0}", host.BaseAddresses[0]));

      Console.WriteLine("You may press [Enter] to close this window");
    private static void Log(string message)
      Console.WriteLine(string.Format("{0}\t{1}", DateTime.Now, message));


<?xml version="1.0" encoding="utf-8" ?>
        <sectionGroup name="userSettings" type="System.Configuration.UserSettingsGroup, System, Version=, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
            <section name="MicroWebServer.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=, Culture=neutral, PublicKeyToken=b77a5c561934e089" allowExeDefinition="MachineToLocalUser" requirePermission="false" />
            <setting name="WebRoot" serializeAs="String">
            <setting name="PortNumber" serializeAs="String">