162 lines
5 KiB
C#
162 lines
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));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|