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>