Skip to content

Instantly share code, notes, and snippets.

@ichengzi
Created September 18, 2022 06:25
Show Gist options
  • Save ichengzi/50c8a3732d4174928d21efb78f97e4ba to your computer and use it in GitHub Desktop.
Save ichengzi/50c8a3732d4174928d21efb78f97e4ba to your computer and use it in GitHub Desktop.
c# simple http server
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace demo
{
public class MimeType
{
public static string GetOrDefault(string fileExtension, string defaultMime)
{
if (_mimeTypeMappings.ContainsKey(fileExtension))
return _mimeTypeMappings[fileExtension];
return defaultMime;
}
/// <summary>
/// Mime Type conversion table
/// </summary>
public static IDictionary<string, string> _mimeTypeMappings =
new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase)
{
{ ".asf", "video/x-ms-asf" },
{ ".asx", "video/x-ms-asf" },
{ ".avi", "video/x-msvideo" },
{ ".bin", "application/octet-stream" },
{ ".cco", "application/x-cocoa" },
{ ".crt", "application/x-x509-ca-cert" },
{ ".css", "text/css" },
{ ".deb", "application/octet-stream" },
{ ".der", "application/x-x509-ca-cert" },
{ ".dll", "application/octet-stream" },
{ ".dmg", "application/octet-stream" },
{ ".ear", "application/java-archive" },
{ ".eot", "application/octet-stream" },
{ ".exe", "application/octet-stream" },
{ ".flv", "video/x-flv" },
{ ".gif", "image/gif" },
{ ".hqx", "application/mac-binhex40" },
{ ".htc", "text/x-component" },
{ ".htm", "text/html" },
{ ".html", "text/html" },
{ ".ico", "image/x-icon" },
{ ".img", "application/octet-stream" },
{ ".iso", "application/octet-stream" },
{ ".jar", "application/java-archive" },
{ ".jardiff", "application/x-java-archive-diff" },
{ ".jng", "image/x-jng" },
{ ".jnlp", "application/x-java-jnlp-file" },
{ ".jpeg", "image/jpeg" },
{ ".jpg", "image/jpeg" },
{ ".js", "application/x-javascript" },
{ ".json", "application/json" },
{ ".mml", "text/mathml" },
{ ".mng", "video/x-mng" },
{ ".mov", "video/quicktime" },
{ ".mp3", "audio/mpeg" },
{ ".mp4", "video/mp4" },
{ ".mpeg", "video/mpeg" },
{ ".mpg", "video/mpeg" },
{ ".msi", "application/octet-stream" },
{ ".msm", "application/octet-stream" },
{ ".msp", "application/octet-stream" },
{ ".pdb", "application/x-pilot" },
{ ".pdf", "application/pdf" },
{ ".pem", "application/x-x509-ca-cert" },
{ ".pl", "application/x-perl" },
{ ".pm", "application/x-perl" },
{ ".png", "image/png" },
{ ".prc", "application/x-pilot" },
{ ".ra", "audio/x-realaudio" },
{ ".rar", "application/x-rar-compressed" },
{ ".rpm", "application/x-redhat-package-manager" },
{ ".rss", "text/xml" },
{ ".run", "application/x-makeself" },
{ ".sea", "application/x-sea" },
{ ".shtml", "text/html" },
{ ".sit", "application/x-stuffit" },
{ ".swf", "application/x-shockwave-flash" },
{ ".tcl", "application/x-tcl" },
{ ".tk", "application/x-tcl" },
{ ".txt", "text/plain" },
{ ".war", "application/java-archive" },
{ ".wbmp", "image/vnd.wap.wbmp" },
{ ".wmv", "video/x-ms-wmv" },
{ ".xml", "text/xml" },
{ ".xpi", "application/x-xpinstall" },
{ ".zip", "application/zip" },
};
}
}
using Ctrip.IO;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace demo
{
public static class MyHttpServer
{
private static List<SimpleHttpServer> _servers = new List<SimpleHttpServer>();
public static String Start(string path, int port = -1)
{
if (port == -1)
port = SimpleHttpServer.GetNotUsedPort();
var server = new SimpleHttpServer(path, port);
_servers.Add(server);
return server.Url;
}
public static void Stop()
{
foreach (var server in _servers)
{
try
{
log.Info("Stop_Http_Server", $"{server.Url} {server.HostDir}");
server.Stop();
}
catch (Exception ex)
{
log.Error("Stop_Http_Server", $"{server.Url} {server.HostDir}" + ex);
}
}
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Threading;
using System.IO;
using System.Net.Sockets;
using System.Threading.Tasks;
namespace demo
{
public class SimpleHttpServer
{
private volatile bool _stop = false;
private readonly string _rootDirectory;
private readonly int _port;
private HttpListener _listener;
public string Url { get; }
public string HostDir { get { return _rootDirectory; } }
public SimpleHttpServer(string path, int port)
{
_stop = false;
_rootDirectory = path;
_port = port;
// Url = "http://127.0.0.1:" + _port + "/";
Url = "http://localhost:" + _port + "/";
Start();
}
/// <summary>
/// Stop server and dispose all functions.
/// </summary>
public void Stop()
{
_stop = true;
_listener.Stop();
}
private void Start()
{
_listener = new HttpListener();
_listener.Prefixes.Add(Url);
_listener.Start();
// reactor 网络编程模型. 这里用 单reactor多线程模型
// https://www.cnblogs.com/xiaolincoding/p/14706824.html
// 新起一个线程 接受请求
new Thread(() =>
{
while (!_stop)
{
try
{
HttpListenerContext context = _listener.GetContext();
// 读取请求完成后,传给 线程池 完成后续的处理
Task.Factory.StartNew(() => { Process(context); });
}
catch (Exception ex) when (ex is InvalidOperationException || ex is ObjectDisposedException)
{
Console.WriteLine("http listener error," + ex.ToString());
break;
}
catch (Exception ex)
{
// log error
}
}
}).Start();
}
/// <summary>
/// Process an individual request. Handles only static file based requests
/// </summary>
/// <param name="context"></param>
private void Process(HttpListenerContext context)
{
HttpListenerResponse response = context.Response;
var request = context.Request;
string filename = context.Request.Url.AbsolutePath;
Console.WriteLine("request fileName :" + filename);
// 支持传入超时参数, 模拟接口响应时间太长
var delayStr = request.QueryString.Get("__delayMillisecond__");
int delay;
if (Int32.TryParse(delayStr, out delay) && delay > 0)
{
Console.WriteLine("url contain delay flag, delay milliSeconds: " + delay);
Thread.Sleep(delay);
}
// 中文文件名称url转码
filename = System.Web.HttpUtility.UrlDecode(filename.Substring(1), Encoding.UTF8);
if (string.IsNullOrEmpty(filename))
{
foreach (string indexFile in DefaultDocuments)
{
if (File.Exists(Path.Combine(_rootDirectory, indexFile)))
{
filename = indexFile;
break;
}
}
}
filename = Path.Combine(_rootDirectory, filename);
var fileExtension = Path.GetExtension(filename);
if (string.IsNullOrWhiteSpace(fileExtension))
{
// 文件扩展名默认处理
if (request.ContentType.Contains("application/json"))
fileExtension = ".json";
else
fileExtension = ".html";
filename += fileExtension;
}
if (!File.Exists(filename))
{
response.StatusCode = (int)HttpStatusCode.NotFound;
response.OutputStream.Close();
return;
}
try
{
Stream fileStream = new FileStream(filename, FileMode.Open);
//Adding permanent http response headers
string mime = MimeType.GetOrDefault(fileExtension, "application/octet-stream");
response.ContentType = mime;
if (fileStream.Length > 1024 * 16)
response.SendChunked = true;
else
response.ContentLength64 = fileStream.Length;
response.AddHeader("Date", DateTime.Now.ToString("r"));
response.AddHeader("Last-Modified", File.GetLastWriteTime(filename).ToString("r"));
response.StatusCode = (int)HttpStatusCode.OK;
fileStream.CopyTo(response.OutputStream);
fileStream.Close();
response.OutputStream.Flush();
}
catch (Exception ex)
{
response.StatusCode = (int)HttpStatusCode.InternalServerError;
}
response.OutputStream.Close();
}
public static int GetNotUsedPort()
{
//get an empty port
TcpListener listener = new TcpListener(IPAddress.Loopback, 0);
listener.Start();
int port = ((IPEndPoint)listener.LocalEndpoint).Port;
listener.Stop();
return port;
}
private string[] DefaultDocuments =
{
"index.html",
"index.htm",
"default.html",
"default.htm"
};
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment