2021-03-18 18:51:13 +00:00
|
|
|
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)
|
|
|
|
{
|
2021-08-14 02:26:48 +01:00
|
|
|
API.Utility.Logging.LogWarning("HTTP Server is unsupported on earlier Windows versions. CLre webserver will fail to start.");
|
2021-03-18 18:51:13 +00:00
|
|
|
}
|
|
|
|
_httpListener = new HttpListener();
|
|
|
|
_httpListener.Prefixes.Add($"http://{ip}:{port}/");
|
|
|
|
_ip_addr = ip;
|
|
|
|
_port = port;
|
|
|
|
}
|
|
|
|
|
|
|
|
internal static void Init()
|
|
|
|
{
|
2021-05-27 02:00:26 +01:00
|
|
|
if (CLre.Config.web_server || Environment.GetCommandLineArgs().Contains("-web"))
|
2021-03-18 18:51:13 +00:00
|
|
|
{
|
|
|
|
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));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|