Created
April 5, 2025 12:26
-
-
Save deanebarker/d5cd2b8841c634642e817c82946ee9c2 to your computer and use it in GitHub Desktop.
A very simple web server for C#, suitable for testing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // Doc here: https://deanebarker.net/tech/code/webserver/ | |
| using System; | |
| using System.Collections.Generic; | |
| using System.IO; | |
| using System.Net; | |
| using System.Text; | |
| using System.Threading.Tasks; | |
| namespace DeaneBarker.Utils | |
| { | |
| public class WebServer | |
| { | |
| private readonly HttpListener listener = new HttpListener(); | |
| public Action<HttpListenerContext> Handler { get; set; } | |
| public string BaseDirectory { get; set; } | |
| public Action<string> Log { get; set; } = Console.WriteLine; | |
| public void Start(int port, Action<HttpListenerContext> handler) | |
| { | |
| Handler = handler; | |
| listener.Prefixes.Add($"http://*:{port}/"); | |
| listener.Start(); | |
| Log($"WebServer listening on port {port}"); | |
| while (true) | |
| { | |
| var ctx = listener.GetContext(); // It will block here until it gets a request | |
| Log(ctx.Request.Url.LocalPath); | |
| try | |
| { | |
| if (!string.IsNullOrWhiteSpace(BaseDirectory)) | |
| { | |
| if (ctx.HandleLocalFile(BaseDirectory)) | |
| { | |
| ctx.Response.Close(); | |
| continue; | |
| } | |
| } | |
| Handler(ctx); | |
| } | |
| catch (Exception ex) | |
| { | |
| Log(ex.ToString()); | |
| ctx.Response.StatusCode = 500; | |
| ctx.Response.Close(); | |
| } | |
| finally | |
| { | |
| ctx.Response.Close(); | |
| } | |
| } | |
| } | |
| public void Stop() | |
| { | |
| listener.Stop(); | |
| listener.Close(); | |
| Console.WriteLine("WebServer stopped"); | |
| } | |
| public static WebServer Launch(Action<HttpListenerContext> handler, int port = 8080) | |
| { | |
| var server = new WebServer(); | |
| Task.Run(() => server.Start(port, handler)); | |
| return server; | |
| } | |
| public static Dictionary<string, string> MimeTypes { get; internal set; } = new() | |
| { | |
| { ".css", "text/css" }, | |
| { ".js", "text/javascript" }, | |
| { ".html", "text/html" }, | |
| { ".json", "application/json" }, | |
| { ".xml", "application/xml" }, | |
| { ".txt", "text/plain" }, | |
| { ".jpg", "image/jpeg" }, | |
| { ".jpeg", "image/jpeg" }, | |
| { ".png", "image/png" }, | |
| { ".gif", "image/gif" }, | |
| { ".bmp", "image/bmp" }, | |
| { ".webp", "image/webp" } | |
| }; | |
| } | |
| public static class WebServerExtentionMethods | |
| { | |
| public static void Write(this HttpListenerResponse response, byte[] data) | |
| { | |
| response.ContentLength64 = response.ContentLength64 + data.Length; | |
| response.OutputStream.Write(data, 0, data.Length); | |
| } | |
| public static void Write(this HttpListenerResponse response, string text) | |
| { | |
| var buffer = Encoding.UTF8.GetBytes(text); | |
| Write(response, buffer); | |
| } | |
| public static void Close(this HttpListenerResponse response) | |
| { | |
| response.OutputStream.Close(); | |
| } | |
| public static bool HandleLocalFile(this HttpListenerContext context, string basePath) | |
| { | |
| var localPath = context.Request.Url.LocalPath; | |
| var extension = Path.GetExtension(localPath); | |
| if (WebServer.MimeTypes.GetValueOrDefault(extension) != null) | |
| { | |
| var fullPath = Path.Combine(basePath, localPath.TrimStart('/')); | |
| if (!File.Exists(fullPath)) | |
| { | |
| context.Response.StatusCode = 404; | |
| } | |
| else | |
| { | |
| context.Response.ContentType = WebServer.MimeTypes.GetValueOrDefault(extension); | |
| context.Response.Write(File.ReadAllBytes(fullPath)); | |
| } | |
| return true; | |
| } | |
| return false; | |
| } | |
| } | |
| } | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment