Utilities/HttpServer.cs
author Stephane Lenclud
Sat, 30 Jan 2016 23:01:51 +0100
branchMiniDisplay
changeset 454 f84878f52cd9
parent 387 87093432c843
permissions -rw-r--r--
Disabling Nuvoton NCT6791D because of fan full speed bug on Asus Z97 WS.
moel@348
     1
/*
moel@348
     2
 
moel@348
     3
  This Source Code Form is subject to the terms of the Mozilla Public
moel@348
     4
  License, v. 2.0. If a copy of the MPL was not distributed with this
moel@348
     5
  file, You can obtain one at http://mozilla.org/MPL/2.0/.
moel@348
     6
 
moel@348
     7
	Copyright (C) 2012 Prince Samuel <prince.samuel@gmail.com>
moel@402
     8
  Copyright (C) 2012-2013 Michael Möller <mmoeller@openhardwaremonitor.org>
moel@348
     9
moel@348
    10
*/
moel@348
    11
moel@348
    12
using System;
moel@386
    13
using System.Drawing;
moel@386
    14
using System.Drawing.Imaging;
moel@386
    15
using System.IO;
moel@386
    16
using System.Net;
moel@386
    17
using System.Reflection;
moel@348
    18
using System.Text;
moel@348
    19
using System.Threading;
moel@348
    20
using OpenHardwareMonitor.GUI;
moel@348
    21
using OpenHardwareMonitor.Hardware;
moel@348
    22
moel@386
    23
namespace OpenHardwareMonitor.Utilities {
moel@386
    24
moel@386
    25
  public class HttpServer {
moel@386
    26
    private HttpListener listener;
moel@386
    27
    private int listenerPort, nodeCount;
moel@386
    28
    private Thread listenerThread;
moel@386
    29
    private Node root;
moel@386
    30
moel@387
    31
    public HttpServer(Node node, int port) {
moel@387
    32
      root = node;
moel@387
    33
      listenerPort = port;
moel@386
    34
moel@386
    35
      //JSON node count. 
moel@386
    36
      nodeCount = 0;
moel@387
    37
moel@387
    38
      try {
moel@387
    39
        listener = new HttpListener();
moel@402
    40
        listener.IgnoreWriteExceptions = true;
moel@387
    41
      } catch (PlatformNotSupportedException) {
moel@387
    42
        listener = null;
moel@387
    43
      }
moel@387
    44
    }
moel@387
    45
moel@387
    46
    public bool PlatformNotSupported {
moel@387
    47
      get {
moel@387
    48
        return listener == null;
moel@387
    49
      }
moel@386
    50
    }
moel@386
    51
moel@386
    52
    public Boolean StartHTTPListener() {
moel@387
    53
      if (PlatformNotSupported)
moel@387
    54
        return false;
moel@387
    55
moel@386
    56
      try {
moel@386
    57
        if (listener.IsListening)
moel@386
    58
          return true;
moel@386
    59
moel@386
    60
        string prefix = "http://+:" + listenerPort + "/";
moel@386
    61
        listener.Prefixes.Clear();
moel@386
    62
        listener.Prefixes.Add(prefix);
moel@386
    63
        listener.Start();
moel@386
    64
moel@386
    65
        if (listenerThread == null) {
moel@386
    66
          listenerThread = new Thread(HandleRequests);
moel@386
    67
          listenerThread.Start();
moel@386
    68
        }
moel@386
    69
      } catch (Exception) {
moel@386
    70
        return false;
moel@386
    71
      }
moel@386
    72
moel@386
    73
      return true;
moel@386
    74
    }
moel@386
    75
moel@386
    76
    public Boolean StopHTTPListener() {
moel@387
    77
      if (PlatformNotSupported)
moel@387
    78
        return false;
moel@387
    79
moel@386
    80
      try {
moel@386
    81
        listenerThread.Abort();
moel@386
    82
        listener.Stop();
moel@386
    83
        listenerThread = null;
moel@386
    84
      } catch (HttpListenerException) {
moel@386
    85
      } catch (ThreadAbortException) {
moel@386
    86
      } catch (NullReferenceException) {
moel@386
    87
      } catch (Exception) {
moel@386
    88
      }
moel@386
    89
      return true;
moel@386
    90
    }
moel@386
    91
moel@387
    92
    private void HandleRequests() {
moel@386
    93
moel@386
    94
      while (listener.IsListening) {
moel@386
    95
        var context = listener.BeginGetContext(
moel@386
    96
          new AsyncCallback(ListenerCallback), listener);
moel@386
    97
        context.AsyncWaitHandle.WaitOne();
moel@386
    98
      }
moel@386
    99
    }
moel@386
   100
moel@387
   101
    private void ListenerCallback(IAsyncResult result) {
moel@386
   102
      HttpListener listener = (HttpListener)result.AsyncState;
moel@386
   103
      if (listener == null || !listener.IsListening)
moel@386
   104
        return;
moel@387
   105
moel@386
   106
      // Call EndGetContext to complete the asynchronous operation.
moel@387
   107
      HttpListenerContext context;
moel@387
   108
      try {
moel@387
   109
        context = listener.EndGetContext(result);     
moel@387
   110
      } catch (Exception) {
moel@387
   111
        return;
moel@387
   112
      }
moel@387
   113
moel@386
   114
      HttpListenerRequest request = context.Request;
moel@386
   115
moel@386
   116
      var requestedFile = request.RawUrl.Substring(1);
moel@386
   117
      if (requestedFile == "data.json") {
moel@387
   118
        SendJSON(context.Response);
moel@386
   119
        return;
moel@386
   120
      }
moel@386
   121
moel@386
   122
      if (requestedFile.Contains("images_icon")) {
moel@387
   123
        ServeResourceImage(context.Response, 
moel@387
   124
          requestedFile.Replace("images_icon/", ""));
moel@386
   125
        return;
moel@386
   126
      }
moel@386
   127
moel@386
   128
      // default file to be served
moel@386
   129
      if (string.IsNullOrEmpty(requestedFile))
moel@386
   130
        requestedFile = "index.html";
moel@386
   131
moel@386
   132
      string[] splits = requestedFile.Split('.');
moel@386
   133
      string ext = splits[splits.Length - 1];
moel@387
   134
      ServeResourceFile(context.Response, 
moel@387
   135
        "Web." + requestedFile.Replace('/', '.'), ext);
moel@386
   136
    }
moel@386
   137
moel@387
   138
    private void ServeResourceFile(HttpListenerResponse response, string name, 
moel@386
   139
      string ext) 
moel@348
   140
    {
moel@386
   141
      // resource names do not support the hyphen
moel@386
   142
      name = "OpenHardwareMonitor.Resources." + 
moel@386
   143
        name.Replace("custom-theme", "custom_theme");
moel@386
   144
moel@386
   145
      string[] names =
moel@386
   146
        Assembly.GetExecutingAssembly().GetManifestResourceNames();
moel@386
   147
      for (int i = 0; i < names.Length; i++) {
moel@386
   148
        if (names[i].Replace('\\', '.') == name) {
moel@386
   149
          using (Stream stream = Assembly.GetExecutingAssembly().
moel@386
   150
            GetManifestResourceStream(names[i])) {
moel@387
   151
            response.ContentType = GetcontentType("." + ext);
moel@387
   152
            response.ContentLength64 = stream.Length;
moel@386
   153
            byte[] buffer = new byte[512 * 1024];
moel@386
   154
            int len;
moel@387
   155
            try {
moel@387
   156
              Stream output = response.OutputStream;
moel@387
   157
              while ((len = stream.Read(buffer, 0, buffer.Length)) > 0) {
moel@387
   158
                output.Write(buffer, 0, len);
moel@387
   159
              }
moel@402
   160
              output.Flush();
moel@402
   161
              output.Close();              
moel@402
   162
              response.Close();
moel@402
   163
            } catch (HttpListenerException) { 
moel@402
   164
            } catch (InvalidOperationException) { 
moel@386
   165
            }
moel@387
   166
            return;
moel@387
   167
          }          
moel@348
   168
        }
moel@386
   169
      }
moel@387
   170
moel@387
   171
      response.StatusCode = 404;
moel@387
   172
      response.Close();
moel@386
   173
    }
moel@348
   174
moel@387
   175
    private void ServeResourceImage(HttpListenerResponse response, string name) {
moel@386
   176
      name = "OpenHardwareMonitor.Resources." + name;
moel@348
   177
moel@386
   178
      string[] names =
moel@386
   179
        Assembly.GetExecutingAssembly().GetManifestResourceNames();
moel@386
   180
      for (int i = 0; i < names.Length; i++) {
moel@386
   181
        if (names[i].Replace('\\', '.') == name) {
moel@386
   182
          using (Stream stream = Assembly.GetExecutingAssembly().
moel@386
   183
            GetManifestResourceStream(names[i])) {
moel@348
   184
moel@386
   185
            Image image = Image.FromStream(stream);
moel@387
   186
            response.ContentType = "image/png";
moel@387
   187
            try {
moel@387
   188
              Stream output = response.OutputStream;
moel@387
   189
              using (MemoryStream ms = new MemoryStream()) {
moel@387
   190
                image.Save(ms, ImageFormat.Png);
moel@387
   191
                ms.WriteTo(output);
moel@387
   192
              }
moel@387
   193
              output.Close();
moel@387
   194
            } catch (HttpListenerException) {              
moel@348
   195
            }
moel@386
   196
            image.Dispose();
moel@387
   197
            response.Close();
moel@386
   198
            return;
moel@386
   199
          }
moel@386
   200
        }
moel@386
   201
      }
moel@387
   202
moel@387
   203
      response.StatusCode = 404;
moel@387
   204
      response.Close();
moel@386
   205
    }
moel@348
   206
moel@387
   207
    private void SendJSON(HttpListenerResponse response) {
moel@348
   208
moel@386
   209
      string JSON = "{\"id\": 0, \"Text\": \"Sensor\", \"Children\": [";
moel@386
   210
      nodeCount = 1;
moel@386
   211
      JSON += GenerateJSON(root);
moel@386
   212
      JSON += "]";
moel@386
   213
      JSON += ", \"Min\": \"Min\"";
moel@386
   214
      JSON += ", \"Value\": \"Value\"";
moel@386
   215
      JSON += ", \"Max\": \"Max\"";
moel@386
   216
      JSON += ", \"ImageURL\": \"\"";
moel@386
   217
      JSON += "}";
moel@348
   218
moel@386
   219
      var responseContent = JSON;
moel@386
   220
      byte[] buffer = Encoding.UTF8.GetBytes(responseContent);
moel@348
   221
moel@387
   222
      response.AddHeader("Cache-Control", "no-cache");
moel@348
   223
moel@387
   224
      response.ContentLength64 = buffer.Length;
moel@387
   225
      response.ContentType = "application/json";
moel@348
   226
moel@387
   227
      try {
moel@387
   228
        Stream output = response.OutputStream;
moel@387
   229
        output.Write(buffer, 0, buffer.Length);
moel@387
   230
        output.Close();
moel@387
   231
      } catch (HttpListenerException) {
moel@387
   232
      }
moel@387
   233
moel@387
   234
      response.Close();
moel@386
   235
    }
moel@348
   236
moel@386
   237
    private string GenerateJSON(Node n) {
moel@386
   238
      string JSON = "{\"id\": " + nodeCount + ", \"Text\": \"" + n.Text 
moel@386
   239
        + "\", \"Children\": [";
moel@386
   240
      nodeCount++;
moel@348
   241
moel@386
   242
      foreach (Node child in n.Nodes)
moel@386
   243
        JSON += GenerateJSON(child) + ", ";
moel@386
   244
      if (JSON.EndsWith(", "))
moel@386
   245
        JSON = JSON.Remove(JSON.LastIndexOf(","));
moel@386
   246
      JSON += "]";
moel@348
   247
moel@386
   248
      if (n is SensorNode) {
moel@386
   249
        JSON += ", \"Min\": \"" + ((SensorNode)n).Min + "\"";
moel@386
   250
        JSON += ", \"Value\": \"" + ((SensorNode)n).Value + "\"";
moel@386
   251
        JSON += ", \"Max\": \"" + ((SensorNode)n).Max + "\"";
moel@386
   252
        JSON += ", \"ImageURL\": \"images/transparent.png\"";
moel@386
   253
      } else if (n is HardwareNode) {
moel@386
   254
        JSON += ", \"Min\": \"\"";
moel@386
   255
        JSON += ", \"Value\": \"\"";
moel@386
   256
        JSON += ", \"Max\": \"\"";
moel@386
   257
        JSON += ", \"ImageURL\": \"images_icon/" + 
moel@386
   258
          GetHardwareImageFile((HardwareNode)n) + "\"";
moel@386
   259
      } else if (n is TypeNode) {
moel@386
   260
        JSON += ", \"Min\": \"\"";
moel@386
   261
        JSON += ", \"Value\": \"\"";
moel@386
   262
        JSON += ", \"Max\": \"\"";
moel@386
   263
        JSON += ", \"ImageURL\": \"images_icon/" + 
moel@386
   264
          GetTypeImageFile((TypeNode)n) + "\"";
moel@386
   265
      } else {
moel@386
   266
        JSON += ", \"Min\": \"\"";
moel@386
   267
        JSON += ", \"Value\": \"\"";
moel@386
   268
        JSON += ", \"Max\": \"\"";
moel@386
   269
        JSON += ", \"ImageURL\": \"images_icon/computer.png\"";
moel@386
   270
      }
moel@348
   271
moel@386
   272
      JSON += "}";
moel@386
   273
      return JSON;
moel@386
   274
    }
moel@348
   275
moel@386
   276
    private static void ReturnFile(HttpListenerContext context, string filePath) 
moel@386
   277
    {
moel@386
   278
      context.Response.ContentType = 
moel@386
   279
        GetcontentType(Path.GetExtension(filePath));
moel@386
   280
      const int bufferSize = 1024 * 512; //512KB
moel@386
   281
      var buffer = new byte[bufferSize];
moel@386
   282
      using (var fs = File.OpenRead(filePath)) {
moel@348
   283
moel@386
   284
        context.Response.ContentLength64 = fs.Length;
moel@386
   285
        int read;
moel@386
   286
        while ((read = fs.Read(buffer, 0, buffer.Length)) > 0)
moel@386
   287
          context.Response.OutputStream.Write(buffer, 0, read);
moel@386
   288
      }
moel@348
   289
moel@386
   290
      context.Response.OutputStream.Close();
moel@386
   291
    }
moel@348
   292
moel@386
   293
    private static string GetcontentType(string extension) {
moel@386
   294
      switch (extension) {
moel@386
   295
        case ".avi": return "video/x-msvideo";
moel@386
   296
        case ".css": return "text/css";
moel@386
   297
        case ".doc": return "application/msword";
moel@386
   298
        case ".gif": return "image/gif";
moel@386
   299
        case ".htm":
moel@386
   300
        case ".html": return "text/html";
moel@386
   301
        case ".jpg":
moel@386
   302
        case ".jpeg": return "image/jpeg";
moel@386
   303
        case ".js": return "application/x-javascript";
moel@386
   304
        case ".mp3": return "audio/mpeg";
moel@386
   305
        case ".png": return "image/png";
moel@386
   306
        case ".pdf": return "application/pdf";
moel@386
   307
        case ".ppt": return "application/vnd.ms-powerpoint";
moel@386
   308
        case ".zip": return "application/zip";
moel@386
   309
        case ".txt": return "text/plain";
moel@386
   310
        default: return "application/octet-stream";
moel@386
   311
      }
moel@386
   312
    }
moel@348
   313
moel@386
   314
    private static string GetHardwareImageFile(HardwareNode hn) {
moel@348
   315
moel@386
   316
      switch (hn.Hardware.HardwareType) {
moel@386
   317
        case HardwareType.CPU:
moel@386
   318
          return "cpu.png";
moel@386
   319
        case HardwareType.GpuNvidia:
moel@386
   320
          return "nvidia.png";
moel@386
   321
        case HardwareType.GpuAti:
moel@386
   322
          return "ati.png";
moel@386
   323
        case HardwareType.HDD:
moel@386
   324
          return "hdd.png";
moel@386
   325
        case HardwareType.Heatmaster:
moel@386
   326
          return "bigng.png";
moel@386
   327
        case HardwareType.Mainboard:
moel@386
   328
          return "mainboard.png";
moel@386
   329
        case HardwareType.SuperIO:
moel@386
   330
          return "chip.png";
moel@386
   331
        case HardwareType.TBalancer:
moel@386
   332
          return "bigng.png";
moel@386
   333
        case HardwareType.RAM:
moel@386
   334
          return "ram.png";
moel@386
   335
        default:
moel@386
   336
          return "cpu.png";
moel@386
   337
      }
moel@348
   338
moel@386
   339
    }
moel@348
   340
moel@386
   341
    private static string GetTypeImageFile(TypeNode tn) {
moel@348
   342
moel@386
   343
      switch (tn.SensorType) {
moel@386
   344
        case SensorType.Voltage:
moel@386
   345
          return "voltage.png";
moel@386
   346
        case SensorType.Clock:
moel@386
   347
          return "clock.png";
moel@386
   348
        case SensorType.Load:
moel@386
   349
          return "load.png";
moel@386
   350
        case SensorType.Temperature:
moel@386
   351
          return "temperature.png";
moel@386
   352
        case SensorType.Fan:
moel@386
   353
          return "fan.png";
moel@386
   354
        case SensorType.Flow:
moel@386
   355
          return "flow.png";
moel@386
   356
        case SensorType.Control:
moel@386
   357
          return "control.png";
moel@386
   358
        case SensorType.Level:
moel@386
   359
          return "level.png";
moel@386
   360
        case SensorType.Power:
moel@386
   361
          return "power.png";
moel@386
   362
        default:
moel@386
   363
          return "power.png";
moel@386
   364
      }
moel@348
   365
moel@386
   366
    }
moel@348
   367
moel@386
   368
    public int ListenerPort {
moel@386
   369
      get { return listenerPort; }
moel@386
   370
      set { listenerPort = value; }
moel@386
   371
    }
moel@348
   372
moel@386
   373
    ~HttpServer() {
moel@387
   374
      if (PlatformNotSupported)
moel@387
   375
        return;
moel@387
   376
moel@386
   377
      StopHTTPListener();
moel@386
   378
      listener.Abort();
moel@386
   379
    }
moel@348
   380
moel@386
   381
    public void Quit() {
moel@387
   382
      if (PlatformNotSupported)
moel@387
   383
        return;
moel@387
   384
moel@386
   385
      StopHTTPListener();
moel@386
   386
      listener.Abort();
moel@348
   387
    }
moel@386
   388
  }
moel@348
   389
}