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