moel@348: /* moel@348: moel@348: This Source Code Form is subject to the terms of the Mozilla Public moel@348: License, v. 2.0. If a copy of the MPL was not distributed with this moel@348: file, You can obtain one at http://mozilla.org/MPL/2.0/. moel@348: moel@348: Copyright (C) 2012 Prince Samuel moel@402: Copyright (C) 2012-2013 Michael Möller moel@348: moel@348: */ moel@348: moel@348: using System; moel@386: using System.Drawing; moel@386: using System.Drawing.Imaging; moel@386: using System.IO; moel@386: using System.Net; moel@386: using System.Reflection; moel@348: using System.Text; moel@348: using System.Threading; moel@348: using OpenHardwareMonitor.GUI; moel@348: using OpenHardwareMonitor.Hardware; moel@348: moel@386: namespace OpenHardwareMonitor.Utilities { moel@386: moel@386: public class HttpServer { moel@386: private HttpListener listener; moel@386: private int listenerPort, nodeCount; moel@386: private Thread listenerThread; moel@386: private Node root; moel@386: moel@387: public HttpServer(Node node, int port) { moel@387: root = node; moel@387: listenerPort = port; moel@386: moel@386: //JSON node count. moel@386: nodeCount = 0; moel@387: moel@387: try { moel@387: listener = new HttpListener(); moel@402: listener.IgnoreWriteExceptions = true; moel@387: } catch (PlatformNotSupportedException) { moel@387: listener = null; moel@387: } moel@387: } moel@387: moel@387: public bool PlatformNotSupported { moel@387: get { moel@387: return listener == null; moel@387: } moel@386: } moel@386: moel@386: public Boolean StartHTTPListener() { moel@387: if (PlatformNotSupported) moel@387: return false; moel@387: moel@386: try { moel@386: if (listener.IsListening) moel@386: return true; moel@386: moel@386: string prefix = "http://+:" + listenerPort + "/"; moel@386: listener.Prefixes.Clear(); moel@386: listener.Prefixes.Add(prefix); moel@386: listener.Start(); moel@386: moel@386: if (listenerThread == null) { moel@386: listenerThread = new Thread(HandleRequests); moel@386: listenerThread.Start(); moel@386: } moel@386: } catch (Exception) { moel@386: return false; moel@386: } moel@386: moel@386: return true; moel@386: } moel@386: moel@386: public Boolean StopHTTPListener() { moel@387: if (PlatformNotSupported) moel@387: return false; moel@387: moel@386: try { moel@386: listenerThread.Abort(); moel@386: listener.Stop(); moel@386: listenerThread = null; moel@386: } catch (HttpListenerException) { moel@386: } catch (ThreadAbortException) { moel@386: } catch (NullReferenceException) { moel@386: } catch (Exception) { moel@386: } moel@386: return true; moel@386: } moel@386: moel@387: private void HandleRequests() { moel@386: moel@386: while (listener.IsListening) { moel@386: var context = listener.BeginGetContext( moel@386: new AsyncCallback(ListenerCallback), listener); moel@386: context.AsyncWaitHandle.WaitOne(); moel@386: } moel@386: } moel@386: moel@387: private void ListenerCallback(IAsyncResult result) { moel@386: HttpListener listener = (HttpListener)result.AsyncState; moel@386: if (listener == null || !listener.IsListening) moel@386: return; moel@387: moel@386: // Call EndGetContext to complete the asynchronous operation. moel@387: HttpListenerContext context; moel@387: try { moel@387: context = listener.EndGetContext(result); moel@387: } catch (Exception) { moel@387: return; moel@387: } moel@387: moel@386: HttpListenerRequest request = context.Request; moel@386: moel@386: var requestedFile = request.RawUrl.Substring(1); moel@386: if (requestedFile == "data.json") { moel@387: SendJSON(context.Response); moel@386: return; moel@386: } moel@386: moel@386: if (requestedFile.Contains("images_icon")) { moel@387: ServeResourceImage(context.Response, moel@387: requestedFile.Replace("images_icon/", "")); moel@386: return; moel@386: } moel@386: moel@386: // default file to be served moel@386: if (string.IsNullOrEmpty(requestedFile)) moel@386: requestedFile = "index.html"; moel@386: moel@386: string[] splits = requestedFile.Split('.'); moel@386: string ext = splits[splits.Length - 1]; moel@387: ServeResourceFile(context.Response, moel@387: "Web." + requestedFile.Replace('/', '.'), ext); moel@386: } moel@386: moel@387: private void ServeResourceFile(HttpListenerResponse response, string name, moel@386: string ext) moel@348: { moel@386: // resource names do not support the hyphen moel@386: name = "OpenHardwareMonitor.Resources." + moel@386: name.Replace("custom-theme", "custom_theme"); moel@386: moel@386: string[] names = moel@386: Assembly.GetExecutingAssembly().GetManifestResourceNames(); moel@386: for (int i = 0; i < names.Length; i++) { moel@386: if (names[i].Replace('\\', '.') == name) { moel@386: using (Stream stream = Assembly.GetExecutingAssembly(). moel@386: GetManifestResourceStream(names[i])) { moel@387: response.ContentType = GetcontentType("." + ext); moel@387: response.ContentLength64 = stream.Length; moel@386: byte[] buffer = new byte[512 * 1024]; moel@386: int len; moel@387: try { moel@387: Stream output = response.OutputStream; moel@387: while ((len = stream.Read(buffer, 0, buffer.Length)) > 0) { moel@387: output.Write(buffer, 0, len); moel@387: } moel@402: output.Flush(); moel@402: output.Close(); moel@402: response.Close(); moel@402: } catch (HttpListenerException) { moel@402: } catch (InvalidOperationException) { moel@386: } moel@387: return; moel@387: } moel@348: } moel@386: } moel@387: moel@387: response.StatusCode = 404; moel@387: response.Close(); moel@386: } moel@348: moel@387: private void ServeResourceImage(HttpListenerResponse response, string name) { moel@386: name = "OpenHardwareMonitor.Resources." + name; moel@348: moel@386: string[] names = moel@386: Assembly.GetExecutingAssembly().GetManifestResourceNames(); moel@386: for (int i = 0; i < names.Length; i++) { moel@386: if (names[i].Replace('\\', '.') == name) { moel@386: using (Stream stream = Assembly.GetExecutingAssembly(). moel@386: GetManifestResourceStream(names[i])) { moel@348: moel@386: Image image = Image.FromStream(stream); moel@387: response.ContentType = "image/png"; moel@387: try { moel@387: Stream output = response.OutputStream; moel@387: using (MemoryStream ms = new MemoryStream()) { moel@387: image.Save(ms, ImageFormat.Png); moel@387: ms.WriteTo(output); moel@387: } moel@387: output.Close(); moel@387: } catch (HttpListenerException) { moel@348: } moel@386: image.Dispose(); moel@387: response.Close(); moel@386: return; moel@386: } moel@386: } moel@386: } moel@387: moel@387: response.StatusCode = 404; moel@387: response.Close(); moel@386: } moel@348: moel@387: private void SendJSON(HttpListenerResponse response) { moel@348: moel@386: string JSON = "{\"id\": 0, \"Text\": \"Sensor\", \"Children\": ["; moel@386: nodeCount = 1; moel@386: JSON += GenerateJSON(root); moel@386: JSON += "]"; moel@386: JSON += ", \"Min\": \"Min\""; moel@386: JSON += ", \"Value\": \"Value\""; moel@386: JSON += ", \"Max\": \"Max\""; moel@386: JSON += ", \"ImageURL\": \"\""; moel@386: JSON += "}"; moel@348: moel@386: var responseContent = JSON; moel@386: byte[] buffer = Encoding.UTF8.GetBytes(responseContent); moel@348: moel@387: response.AddHeader("Cache-Control", "no-cache"); moel@348: moel@387: response.ContentLength64 = buffer.Length; moel@387: response.ContentType = "application/json"; moel@348: moel@387: try { moel@387: Stream output = response.OutputStream; moel@387: output.Write(buffer, 0, buffer.Length); moel@387: output.Close(); moel@387: } catch (HttpListenerException) { moel@387: } moel@387: moel@387: response.Close(); moel@386: } moel@348: moel@386: private string GenerateJSON(Node n) { moel@386: string JSON = "{\"id\": " + nodeCount + ", \"Text\": \"" + n.Text moel@386: + "\", \"Children\": ["; moel@386: nodeCount++; moel@348: moel@386: foreach (Node child in n.Nodes) moel@386: JSON += GenerateJSON(child) + ", "; moel@386: if (JSON.EndsWith(", ")) moel@386: JSON = JSON.Remove(JSON.LastIndexOf(",")); moel@386: JSON += "]"; moel@348: moel@386: if (n is SensorNode) { moel@386: JSON += ", \"Min\": \"" + ((SensorNode)n).Min + "\""; moel@386: JSON += ", \"Value\": \"" + ((SensorNode)n).Value + "\""; moel@386: JSON += ", \"Max\": \"" + ((SensorNode)n).Max + "\""; moel@386: JSON += ", \"ImageURL\": \"images/transparent.png\""; moel@386: } else if (n is HardwareNode) { moel@386: JSON += ", \"Min\": \"\""; moel@386: JSON += ", \"Value\": \"\""; moel@386: JSON += ", \"Max\": \"\""; moel@386: JSON += ", \"ImageURL\": \"images_icon/" + moel@386: GetHardwareImageFile((HardwareNode)n) + "\""; moel@386: } else if (n is TypeNode) { moel@386: JSON += ", \"Min\": \"\""; moel@386: JSON += ", \"Value\": \"\""; moel@386: JSON += ", \"Max\": \"\""; moel@386: JSON += ", \"ImageURL\": \"images_icon/" + moel@386: GetTypeImageFile((TypeNode)n) + "\""; moel@386: } else { moel@386: JSON += ", \"Min\": \"\""; moel@386: JSON += ", \"Value\": \"\""; moel@386: JSON += ", \"Max\": \"\""; moel@386: JSON += ", \"ImageURL\": \"images_icon/computer.png\""; moel@386: } moel@348: moel@386: JSON += "}"; moel@386: return JSON; moel@386: } moel@348: moel@386: private static void ReturnFile(HttpListenerContext context, string filePath) moel@386: { moel@386: context.Response.ContentType = moel@386: GetcontentType(Path.GetExtension(filePath)); moel@386: const int bufferSize = 1024 * 512; //512KB moel@386: var buffer = new byte[bufferSize]; moel@386: using (var fs = File.OpenRead(filePath)) { moel@348: moel@386: context.Response.ContentLength64 = fs.Length; moel@386: int read; moel@386: while ((read = fs.Read(buffer, 0, buffer.Length)) > 0) moel@386: context.Response.OutputStream.Write(buffer, 0, read); moel@386: } moel@348: moel@386: context.Response.OutputStream.Close(); moel@386: } moel@348: moel@386: private static string GetcontentType(string extension) { moel@386: switch (extension) { moel@386: case ".avi": return "video/x-msvideo"; moel@386: case ".css": return "text/css"; moel@386: case ".doc": return "application/msword"; moel@386: case ".gif": return "image/gif"; moel@386: case ".htm": moel@386: case ".html": return "text/html"; moel@386: case ".jpg": moel@386: case ".jpeg": return "image/jpeg"; moel@386: case ".js": return "application/x-javascript"; moel@386: case ".mp3": return "audio/mpeg"; moel@386: case ".png": return "image/png"; moel@386: case ".pdf": return "application/pdf"; moel@386: case ".ppt": return "application/vnd.ms-powerpoint"; moel@386: case ".zip": return "application/zip"; moel@386: case ".txt": return "text/plain"; moel@386: default: return "application/octet-stream"; moel@386: } moel@386: } moel@348: moel@386: private static string GetHardwareImageFile(HardwareNode hn) { moel@348: moel@386: switch (hn.Hardware.HardwareType) { moel@386: case HardwareType.CPU: moel@386: return "cpu.png"; moel@386: case HardwareType.GpuNvidia: moel@386: return "nvidia.png"; moel@386: case HardwareType.GpuAti: moel@386: return "ati.png"; moel@386: case HardwareType.HDD: moel@386: return "hdd.png"; moel@386: case HardwareType.Heatmaster: moel@386: return "bigng.png"; moel@386: case HardwareType.Mainboard: moel@386: return "mainboard.png"; moel@386: case HardwareType.SuperIO: moel@386: return "chip.png"; moel@386: case HardwareType.TBalancer: moel@386: return "bigng.png"; moel@386: case HardwareType.RAM: moel@386: return "ram.png"; moel@386: default: moel@386: return "cpu.png"; moel@386: } moel@348: moel@386: } moel@348: moel@386: private static string GetTypeImageFile(TypeNode tn) { moel@348: moel@386: switch (tn.SensorType) { moel@386: case SensorType.Voltage: moel@386: return "voltage.png"; moel@386: case SensorType.Clock: moel@386: return "clock.png"; moel@386: case SensorType.Load: moel@386: return "load.png"; moel@386: case SensorType.Temperature: moel@386: return "temperature.png"; moel@386: case SensorType.Fan: moel@386: return "fan.png"; moel@386: case SensorType.Flow: moel@386: return "flow.png"; moel@386: case SensorType.Control: moel@386: return "control.png"; moel@386: case SensorType.Level: moel@386: return "level.png"; moel@386: case SensorType.Power: moel@386: return "power.png"; moel@386: default: moel@386: return "power.png"; moel@386: } moel@348: moel@386: } moel@348: moel@386: public int ListenerPort { moel@386: get { return listenerPort; } moel@386: set { listenerPort = value; } moel@386: } moel@348: moel@386: ~HttpServer() { moel@387: if (PlatformNotSupported) moel@387: return; moel@387: moel@386: StopHTTPListener(); moel@386: listener.Abort(); moel@386: } moel@348: moel@386: public void Quit() { moel@387: if (PlatformNotSupported) moel@387: return; moel@387: moel@386: StopHTTPListener(); moel@386: listener.Abort(); moel@348: } moel@386: } moel@348: }