CLre/CLre_server/WebStatus/WebServer.cs

162 lines
No EOL
5 KiB
C#

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Text;
using HarmonyLib;
using Svelto.DataStructures;
namespace CLre_server.WebStatus
{
public class WebServer
{
private const uint DEFAULT_PORT = 5030;
private const string DEFAULT_IP = "localhost";
private readonly HttpListener _httpListener;
public delegate void RequestHandler(HttpListenerContext ctx);
private Dictionary<string, RequestHandler> _handlers = new Dictionary<string, RequestHandler>();
public bool IsRunning
{
get => _httpListener.IsListening;
}
private string _ip_addr;
private uint _port;
public static WebServer MainInstance { get; internal set; }
internal static List<Assembly> _assembliesToCheck = new List<Assembly>(new []{typeof(CLre).Assembly});
public WebServer() : this(DEFAULT_IP, DEFAULT_PORT)
{
}
public WebServer(string ip, uint port)
{
if (!HttpListener.IsSupported)
{
API.Utility.Logging.LogWarning("HTTP Server is unsupported on earlier Windows versions. It will fail to start.");
}
_httpListener = new HttpListener();
_httpListener.Prefixes.Add($"http://{ip}:{port}/");
_ip_addr = ip;
_port = port;
}
internal static void Init()
{
if (Environment.GetCommandLineArgs().Contains("-web"))
{
API.Utility.Logging.Log("Starting status web server");
StatusEndpoints.Init();
MainInstance = new WebServer();
MainInstance.Start();
}
else
{
API.Utility.Logging.Log("Not starting web server (use CLI argument -web to enable)");
}
}
internal static void Deinit()
{
if (MainInstance != null) MainInstance.Stop();
MainInstance = null;
}
public void Start()
{
LoadHandlers();
try
{
_httpListener.Start();
}
catch (Exception e)
{
API.Utility.Logging.LogWarning(e);
return;
}
HandleAllRequests().Run();
}
public void Stop()
{
try
{
_httpListener.Stop();
_httpListener.Close();
}
catch (Exception e)
{
API.Utility.Logging.LogWarning(e);
return;
}
}
private IEnumerator HandleAllRequests()
{
API.Utility.Logging.MetaLog($"Started HTTP web server at http://{_ip_addr}:{_port}/");
while (_httpListener.IsListening)
{
var awaiter = _httpListener.GetContextAsync();
awaiter.GetAwaiter().OnCompleted(() => DoRequest(awaiter.Result));
yield return null;
}
API.Utility.Logging.MetaLog("Terminated HTTP web server");
}
private void DoRequest(HttpListenerContext ctx)
{
string endpoint = ctx.Request.Url.LocalPath.ToLower();
#if DEBUG
API.Utility.Logging.LogWarning($"Handling HTTP request {endpoint}");
#endif
bool handled = false;
foreach (string path in _handlers.Keys)
{
if (endpoint == path)
{
handled = true;
_handlers[path](ctx);
break;
}
}
if (!handled)
{
AssetEndpoints.Asset404(ctx);
}
//byte[] output = Encoding.UTF8.GetBytes(endpoint);
//ctx.Response.OutputStream.Write(output, 0, output.Length);
ctx.Response.Close();
}
private void LoadHandlers()
{
_handlers = new Dictionary<string, RequestHandler>();
foreach (Assembly asm in _assembliesToCheck.ToArray())
{
foreach (Type t in asm.GetTypes())
{
foreach (MethodInfo m in t.GetMethods(AccessTools.all))
{
WebEndpointAttribute attr = m.GetCustomAttribute<WebEndpointAttribute>();
if (attr != null)
{
// TODO validate that method signature matches that of RequestHandler
string key = attr.GetPath().ToLower();
API.Utility.Logging.MetaLog($"{t.FullName}:{m.Name} is handling {key}");
_handlers.Add(key, (RequestHandler) Delegate.CreateDelegate(typeof(RequestHandler), m));
}
}
}
}
}
}
}