You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

229 lines
7.1 KiB

  1. // MIT License - Copyright (c) 2016 Can Güney Aksakalli
  2. // https://aksakalli.github.io/2014/02/24/simple-http-server-with-csparp.html
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Linq;
  6. using System.Text;
  7. using System.Net.Sockets;
  8. using System.Net;
  9. using System.IO;
  10. using System.Threading;
  11. using System.Diagnostics;
  12. class SimpleHTTPServer
  13. {
  14. private readonly string[] _indexFiles = {
  15. "index.html",
  16. "index.htm",
  17. "default.html",
  18. "default.htm"
  19. };
  20. private static IDictionary<string, string> _mimeTypeMappings = new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase) {
  21. #region extension to MIME type list
  22. {".asf", "video/x-ms-asf"},
  23. {".asx", "video/x-ms-asf"},
  24. {".avi", "video/x-msvideo"},
  25. {".bin", "application/octet-stream"},
  26. {".cco", "application/x-cocoa"},
  27. {".crt", "application/x-x509-ca-cert"},
  28. {".css", "text/css"},
  29. {".deb", "application/octet-stream"},
  30. {".der", "application/x-x509-ca-cert"},
  31. {".dll", "application/octet-stream"},
  32. {".dmg", "application/octet-stream"},
  33. {".ear", "application/java-archive"},
  34. {".eot", "application/octet-stream"},
  35. {".exe", "application/octet-stream"},
  36. {".flv", "video/x-flv"},
  37. {".gif", "image/gif"},
  38. {".hqx", "application/mac-binhex40"},
  39. {".htc", "text/x-component"},
  40. {".htm", "text/html"},
  41. {".html", "text/html"},
  42. {".ico", "image/x-icon"},
  43. {".img", "application/octet-stream"},
  44. {".iso", "application/octet-stream"},
  45. {".jar", "application/java-archive"},
  46. {".jardiff", "application/x-java-archive-diff"},
  47. {".jng", "image/x-jng"},
  48. {".jnlp", "application/x-java-jnlp-file"},
  49. {".jpeg", "image/jpeg"},
  50. {".jpg", "image/jpeg"},
  51. {".js", "application/x-javascript"},
  52. {".mml", "text/mathml"},
  53. {".mng", "video/x-mng"},
  54. {".mov", "video/quicktime"},
  55. {".mp3", "audio/mpeg"},
  56. {".mpeg", "video/mpeg"},
  57. {".mpg", "video/mpeg"},
  58. {".msi", "application/octet-stream"},
  59. {".msm", "application/octet-stream"},
  60. {".msp", "application/octet-stream"},
  61. {".pdb", "application/x-pilot"},
  62. {".pdf", "application/pdf"},
  63. {".pem", "application/x-x509-ca-cert"},
  64. {".pl", "application/x-perl"},
  65. {".pm", "application/x-perl"},
  66. {".png", "image/png"},
  67. {".prc", "application/x-pilot"},
  68. {".ra", "audio/x-realaudio"},
  69. {".rar", "application/x-rar-compressed"},
  70. {".rpm", "application/x-redhat-package-manager"},
  71. {".rss", "text/xml"},
  72. {".run", "application/x-makeself"},
  73. {".sea", "application/x-sea"},
  74. {".shtml", "text/html"},
  75. {".sit", "application/x-stuffit"},
  76. {".swf", "application/x-shockwave-flash"},
  77. {".tcl", "application/x-tcl"},
  78. {".tk", "application/x-tcl"},
  79. {".txt", "text/plain"},
  80. {".war", "application/java-archive"},
  81. {".wbmp", "image/vnd.wap.wbmp"},
  82. {".wmv", "video/x-ms-wmv"},
  83. {".xml", "text/xml"},
  84. {".xpi", "application/x-xpinstall"},
  85. {".zip", "application/zip"},
  86. #endregion
  87. };
  88. private Thread _serverThread;
  89. private string _rootDirectory;
  90. private HttpListener _listener;
  91. private int _port;
  92. public int Port {
  93. get { return _port; }
  94. private set { }
  95. }
  96. /// <summary>
  97. /// Construct server with given port.
  98. /// </summary>
  99. /// <param name="path">Directory path to serve.</param>
  100. /// <param name="port">Port of the server.</param>
  101. public SimpleHTTPServer(string path, int port)
  102. {
  103. this.Initialize(path, port);
  104. }
  105. /// <summary>
  106. /// Construct server with suitable port.
  107. /// </summary>
  108. /// <param name="path">Directory path to serve.</param>
  109. public SimpleHTTPServer(string path)
  110. {
  111. //get an empty port
  112. TcpListener l = new TcpListener(IPAddress.Loopback, 0);
  113. l.Start();
  114. int port = ((IPEndPoint)l.LocalEndpoint).Port;
  115. l.Stop();
  116. this.Initialize(path, port);
  117. }
  118. /// <summary>
  119. /// Stop server and dispose all functions.
  120. /// </summary>
  121. public void Stop()
  122. {
  123. _serverThread.Abort();
  124. _listener.Stop();
  125. }
  126. private void Listen()
  127. {
  128. _listener = new HttpListener();
  129. _listener.Prefixes.Add("http://*:" + _port.ToString() + "/");
  130. _listener.Start();
  131. while (true)
  132. {
  133. try
  134. {
  135. HttpListenerContext context = _listener.GetContext();
  136. Process(context);
  137. }
  138. catch (Exception ex)
  139. {
  140. }
  141. }
  142. }
  143. private void Process(HttpListenerContext context)
  144. {
  145. string filename = context.Request.Url.AbsolutePath;
  146. Console.WriteLine(filename);
  147. filename = filename.Substring(1);
  148. if (string.IsNullOrEmpty(filename))
  149. {
  150. foreach (string indexFile in _indexFiles)
  151. {
  152. if (File.Exists(Path.Combine(_rootDirectory, indexFile)))
  153. {
  154. filename = indexFile;
  155. break;
  156. }
  157. }
  158. }
  159. filename = Path.Combine(_rootDirectory, filename);
  160. //UnityEngine.Debug.Log("Client wants:" + filename);
  161. if (File.Exists(filename))
  162. {
  163. try
  164. {
  165. Stream input = new FileStream(filename, FileMode.Open);
  166. //Adding permanent http response headers
  167. string mime;
  168. context.Response.ContentType = _mimeTypeMappings.TryGetValue(Path.GetExtension(filename), out mime) ? mime : "application/octet-stream";
  169. context.Response.AddHeader("Date", DateTime.Now.ToString("r"));
  170. context.Response.AddHeader("Last-Modified", System.IO.File.GetLastWriteTime(filename).ToString("r"));
  171. if (Path.GetExtension(filename) == ".unityweb")
  172. context.Response.AddHeader("Content-Encoding", "gzip");
  173. var bytes = default(byte[]);
  174. using (var memstream = new MemoryStream())
  175. {
  176. input.CopyTo(memstream);
  177. bytes = memstream.ToArray();
  178. }
  179. context.Response.StatusCode = (int)HttpStatusCode.OK;
  180. context.Response.ContentLength64 = bytes.Length;
  181. Stream st = context.Response.OutputStream;
  182. st.Write(bytes, 0, bytes.Length);
  183. //UnityEngine.Debug.Log("Sent");
  184. }
  185. catch (Exception ex)
  186. {
  187. context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
  188. }
  189. }
  190. else
  191. {
  192. context.Response.StatusCode = (int)HttpStatusCode.NotFound;
  193. }
  194. context.Response.OutputStream.Close();
  195. }
  196. private void Initialize(string path, int port)
  197. {
  198. this._rootDirectory = path;
  199. this._port = port;
  200. _serverThread = new Thread(this.Listen);
  201. _serverThread.Start();
  202. }
  203. }