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.
Program.cs:
[more]
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
{
[ServiceContract]
private interface IWebServerContract
{
[OperationContract]
[WebInvoke(UriTemplate = "{file}.{extension}", Method="GET")]
Stream FileRequest(string file, string extension);
[OperationContract]
[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);
}
else
{
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));
host.Open();
Log(string.Format("Web server started at {0}", host.BaseAddresses[0]));
Console.WriteLine("You may press [Enter] to close this window");
Console.ReadLine();
host.Close();
}
private static void Log(string message)
{
Console.WriteLine(string.Format("{0}\t{1}", DateTime.Now, message));
}
}
}
app.config:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<sectionGroup name="userSettings" type="System.Configuration.UserSettingsGroup, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
<section name="MicroWebServer.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" allowExeDefinition="MachineToLocalUser" requirePermission="false" />
</sectionGroup>
</configSections>
<userSettings>
<MicroWebServer.Properties.Settings>
<setting name="WebRoot" serializeAs="String">
<value>C:\WebRoot</value>
</setting>
<setting name="PortNumber" serializeAs="String">
<value>8888</value>
</setting>
</MicroWebServer.Properties.Settings>
</userSettings>
</configuration>