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