using System; using System.Collections.Generic; using System.IO; using System.Net; using System.Net.Http; using System.Reflection; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; namespace WelsonJS.Launcher { public class ResourceServer { private readonly HttpListener _listener; private CancellationTokenSource _cts; private Task _serverTask; private bool _isRunning; private string _prefix; private string _resourceName; private List _tools = new List(); private const int _blobTimeout = 5000; public ResourceServer(string prefix, string resourceName) { _prefix = prefix; _listener = new HttpListener(); _listener.Prefixes.Add(prefix); _resourceName = resourceName; // Add resource tools _tools.Add(new ResourceTools.Completion(this)); _tools.Add(new ResourceTools.Config(this)); _tools.Add(new ResourceTools.DevTools(this)); _tools.Add(new ResourceTools.DnsQuery(this)); _tools.Add(new ResourceTools.Tfa(this)); _tools.Add(new ResourceTools.Whois(this)); } public string GetPrefix() { return _prefix; } public void Start() { if (_isRunning) return; _isRunning = true; _cts = new CancellationTokenSource(); _listener.Start(); // Open the web browser Program.OpenWebBrowser(_prefix); // Run a task with cancellation token _serverTask = Task.Run(() => ListenLoop(_cts.Token)); } public void Stop() { _isRunning = false; _cts.Cancel(); _listener.Stop(); MessageBox.Show("Server stopped."); } public bool IsRunning() { return _isRunning; } private async Task ListenLoop(CancellationToken token) { while (!token.IsCancellationRequested && _isRunning) { try { await ProcessRequest(await _listener.GetContextAsync()); } catch (Exception ex) { if (token.IsCancellationRequested || !_isRunning) break; MessageBox.Show($"Error: {ex.Message}"); } } } private async Task ProcessRequest(HttpListenerContext context) { string path = context.Request.Url.AbsolutePath.TrimStart('/'); if (!String.IsNullOrEmpty(path)) { // Serve the favicon.ico file if ("favicon.ico".Equals(path, StringComparison.OrdinalIgnoreCase)) { ServeResource(context, GetResource("favicon"), "image/x-icon"); return; } // Serve from a resource tool foreach (var tool in _tools) { if (tool.CanHandle(path)) { await tool.HandleAsync(context, path); return; } } // Serve from the blob server if (await ServeBlob(context, path)) return; } // Serve from a resource name ServeResource(context, GetResource(_resourceName), "text/html"); } private async Task ServeBlob(HttpListenerContext context, string path) { try { using (HttpClient client = new HttpClient()) { client.Timeout = TimeSpan.FromMilliseconds(_blobTimeout); string blobServerPrefix = Program.GetAppConfig("BlobServerPrefix"); string url = $"{blobServerPrefix}{path}"; string userAgent = Program.GetAppConfig("DefaultUserAgent"); HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, url); request.Headers.UserAgent.ParseAdd(context.Request.UserAgent); HttpResponseMessage response = await client.SendAsync(request); if (!response.IsSuccessStatusCode) { return false; } byte[] data = await response.Content.ReadAsByteArrayAsync(); string mimeType = response.Content.Headers.ContentType?.MediaType ?? "application/octet-stream"; ServeResource(context, data, mimeType); return true; } } catch (Exception) { return false; } } public void ServeResource(HttpListenerContext context) { ServeResource(context, "Not Found", "application/xml", 404); } public void ServeResource(HttpListenerContext context, byte[] data, string mimeType = "text/html", int statusCode = 200) { string xmlHeader = ""; if (data == null) { data = Encoding.UTF8.GetBytes(xmlHeader + "\r\nCould not find the resource."); mimeType = "application/xml"; statusCode = 404; } context.Response.StatusCode = statusCode; context.Response.ContentType = mimeType; context.Response.ContentLength64 = data.Length; using (Stream outputStream = context.Response.OutputStream) { outputStream.Write(data, 0, data.Length); } } public void ServeResource(HttpListenerContext context, string data, string mimeType = "text/html", int statusCode = 200) { string xmlHeader = ""; if (data == null) { data = xmlHeader + "\r\nCould not find the resource."; mimeType = "application/xml"; statusCode = 404; } else if (mimeType == "application/xml" && !data.StartsWith("