Skip to content

Instantly share code, notes, and snippets.

@deanebarker
Created April 5, 2025 12:26
Show Gist options
  • Select an option

  • Save deanebarker/d5cd2b8841c634642e817c82946ee9c2 to your computer and use it in GitHub Desktop.

Select an option

Save deanebarker/d5cd2b8841c634642e817c82946ee9c2 to your computer and use it in GitHub Desktop.
A very simple web server for C#, suitable for testing
// 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