using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Net; using System.Reflection; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; using System.Xml.Linq; 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 ExecutablesCollector _executablesCollector; public ResourceServer(string prefix, string resourceName) { _prefix = prefix; _listener = new HttpListener(); _listener.Prefixes.Add(prefix); _resourceName = resourceName; _executablesCollector = new ExecutablesCollector(); } public string GetPrefix() { return _prefix; } public void Start() { if (_isRunning) return; _isRunning = true; _cts = new CancellationTokenSource(); _listener.Start(); // Open the web browser Process.Start(_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 { ProcessRequest(await _listener.GetContextAsync()); } catch (Exception ex) { if (token.IsCancellationRequested || !_isRunning) break; MessageBox.Show($"Error: {ex.Message}"); } } } private void ProcessRequest(HttpListenerContext context) { string path = context.Request.Url.AbsolutePath.TrimStart('/'); // Serve the favicon.ico file if ("favicon.ico".Equals(path, StringComparison.OrdinalIgnoreCase)) { ServeResource(context, GetResource("favicon"), "image/x-icon"); return; } // Serve the code completion (word suggestion) if (path.StartsWith("completion/", StringComparison.OrdinalIgnoreCase)) { ServeCompletion(context, path.Substring("completion/".Length)); return; } // Serve a resource ServeResource(context, GetResource(_resourceName), "text/html"); } private void ServeCompletion(HttpListenerContext context, string word) { int statusCode = 200; List executables = _executablesCollector.GetExecutables(); CompletionItem[] completionItems = executables .Where(exec => exec.IndexOf(word, 0, StringComparison.OrdinalIgnoreCase) > -1) .Select(exec => new CompletionItem { label = Path.GetFileName(exec), kind = "Text", documentation = "An executable file", insertText = exec }) .ToArray(); XElement response = new XElement("suggestions", completionItems.Select(item => new XElement("item", new XElement("label", item.label), new XElement("kind", item.kind), new XElement("documentation", item.documentation), new XElement("insertText", item.insertText) )) ); byte[] data = Encoding.UTF8.GetBytes( "\r\n" + response.ToString() ); context.Response.StatusCode = statusCode; context.Response.ContentType = "application/xml"; context.Response.ContentLength64 = data.Length; context.Response.OutputStream.Write(data, 0, data.Length); context.Response.OutputStream.Close(); } private void ServeResource(HttpListenerContext context, byte[] data, string mimeType = "text/html") { int statusCode = 200; if (data == null) { data = "text/html".Equals(mimeType, StringComparison.OrdinalIgnoreCase) ? Encoding.UTF8.GetBytes("

Could not find the resource.

") : Encoding.UTF8.GetBytes("Could not find the resource.") ; statusCode = 404; } context.Response.StatusCode = statusCode; context.Response.ContentType = mimeType; context.Response.ContentLength64 = data.Length; context.Response.OutputStream.Write(data, 0, data.Length); context.Response.OutputStream.Close(); } private byte[] GetResource(string resourceName) { // Try to fetch embedded resource. byte[] data = GetEmbeddedResource(typeof(ResourceServer).Namespace + "." + resourceName); if (data != null) return data; // Fallback: Try to fetch resource from ResourceManager. return GetResourceFromManager(resourceName); } private byte[] GetEmbeddedResource(string fullResourceName) { Assembly assembly = Assembly.GetExecutingAssembly(); using (Stream stream = assembly.GetManifestResourceStream(fullResourceName)) { if (stream != null) { using (MemoryStream memoryStream = new MemoryStream()) { stream.CopyTo(memoryStream); return memoryStream.ToArray(); } } } return null; } private byte[] GetResourceFromManager(string resourceName) { object resourceObject = Properties.Resources.ResourceManager.GetObject(resourceName); switch (resourceObject) { case byte[] resourceBytes: return resourceBytes; case System.Drawing.Icon icon: return ConvertIconToBytes(icon); default: return null; } } private byte[] ConvertIconToBytes(System.Drawing.Icon icon) { using (MemoryStream memoryStream = new MemoryStream()) { icon.Save(memoryStream); return memoryStream.ToArray(); } } } public class CompletionItem { public string label { get; set; } public string kind { get; set; } public string documentation { get; set; } public string insertText { get; set; } } }