Unofficial CardLife revival project, pronounced like "celery"
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

162 lines
5.0KB

  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Net;
  6. using System.Reflection;
  7. using System.Text;
  8. using HarmonyLib;
  9. using Svelto.DataStructures;
  10. namespace CLre_server.WebStatus
  11. {
  12. public class WebServer
  13. {
  14. private const uint DEFAULT_PORT = 5030;
  15. private const string DEFAULT_IP = "localhost";
  16. private readonly HttpListener _httpListener;
  17. public delegate void RequestHandler(HttpListenerContext ctx);
  18. private Dictionary<string, RequestHandler> _handlers = new Dictionary<string, RequestHandler>();
  19. public bool IsRunning
  20. {
  21. get => _httpListener.IsListening;
  22. }
  23. private string _ip_addr;
  24. private uint _port;
  25. public static WebServer MainInstance { get; internal set; }
  26. internal static List<Assembly> _assembliesToCheck = new List<Assembly>(new []{typeof(CLre).Assembly});
  27. public WebServer() : this(DEFAULT_IP, DEFAULT_PORT)
  28. {
  29. }
  30. public WebServer(string ip, uint port)
  31. {
  32. if (!HttpListener.IsSupported)
  33. {
  34. API.Utility.Logging.LogWarning("HTTP Server is unsupported on earlier Windows versions. It will fail to start.");
  35. }
  36. _httpListener = new HttpListener();
  37. _httpListener.Prefixes.Add($"http://{ip}:{port}/");
  38. _ip_addr = ip;
  39. _port = port;
  40. }
  41. internal static void Init()
  42. {
  43. if (Environment.GetCommandLineArgs().Contains("-web"))
  44. {
  45. API.Utility.Logging.Log("Starting status web server");
  46. StatusEndpoints.Init();
  47. MainInstance = new WebServer();
  48. MainInstance.Start();
  49. }
  50. else
  51. {
  52. API.Utility.Logging.Log("Not starting web server (use CLI argument -web to enable)");
  53. }
  54. }
  55. internal static void Deinit()
  56. {
  57. if (MainInstance != null) MainInstance.Stop();
  58. MainInstance = null;
  59. }
  60. public void Start()
  61. {
  62. LoadHandlers();
  63. try
  64. {
  65. _httpListener.Start();
  66. }
  67. catch (Exception e)
  68. {
  69. API.Utility.Logging.LogWarning(e);
  70. return;
  71. }
  72. HandleAllRequests().Run();
  73. }
  74. public void Stop()
  75. {
  76. try
  77. {
  78. _httpListener.Stop();
  79. _httpListener.Close();
  80. }
  81. catch (Exception e)
  82. {
  83. API.Utility.Logging.LogWarning(e);
  84. return;
  85. }
  86. }
  87. private IEnumerator HandleAllRequests()
  88. {
  89. API.Utility.Logging.MetaLog($"Started HTTP web server at http://{_ip_addr}:{_port}/");
  90. while (_httpListener.IsListening)
  91. {
  92. var awaiter = _httpListener.GetContextAsync();
  93. awaiter.GetAwaiter().OnCompleted(() => DoRequest(awaiter.Result));
  94. yield return null;
  95. }
  96. API.Utility.Logging.MetaLog("Terminated HTTP web server");
  97. }
  98. private void DoRequest(HttpListenerContext ctx)
  99. {
  100. string endpoint = ctx.Request.Url.LocalPath.ToLower();
  101. #if DEBUG
  102. API.Utility.Logging.LogWarning($"Handling HTTP request {endpoint}");
  103. #endif
  104. bool handled = false;
  105. foreach (string path in _handlers.Keys)
  106. {
  107. if (endpoint == path)
  108. {
  109. handled = true;
  110. _handlers[path](ctx);
  111. break;
  112. }
  113. }
  114. if (!handled)
  115. {
  116. AssetEndpoints.Asset404(ctx);
  117. }
  118. //byte[] output = Encoding.UTF8.GetBytes(endpoint);
  119. //ctx.Response.OutputStream.Write(output, 0, output.Length);
  120. ctx.Response.Close();
  121. }
  122. private void LoadHandlers()
  123. {
  124. _handlers = new Dictionary<string, RequestHandler>();
  125. foreach (Assembly asm in _assembliesToCheck.ToArray())
  126. {
  127. foreach (Type t in asm.GetTypes())
  128. {
  129. foreach (MethodInfo m in t.GetMethods(AccessTools.all))
  130. {
  131. WebEndpointAttribute attr = m.GetCustomAttribute<WebEndpointAttribute>();
  132. if (attr != null)
  133. {
  134. // TODO validate that method signature matches that of RequestHandler
  135. string key = attr.GetPath().ToLower();
  136. API.Utility.Logging.MetaLog($"{t.FullName}:{m.Name} is handling {key}");
  137. _handlers.Add(key, (RequestHandler) Delegate.CreateDelegate(typeof(RequestHandler), m));
  138. }
  139. }
  140. }
  141. }
  142. }
  143. }
  144. }