| #region MIT License | 
| /** | 
|  * HttpServer.cs | 
|  * | 
|  * The MIT License | 
|  * | 
|  * Copyright (c) 2012 sta.blockhead | 
|  *  | 
|  * Permission is hereby granted, free of charge, to any person obtaining a copy | 
|  * of this software and associated documentation files (the "Software"), to deal | 
|  * in the Software without restriction, including without limitation the rights | 
|  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | 
|  * copies of the Software, and to permit persons to whom the Software is | 
|  * furnished to do so, subject to the following conditions: | 
|  * | 
|  * The above copyright notice and this permission notice shall be included in | 
|  * all copies or substantial portions of the Software. | 
|  *  | 
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | 
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | 
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | 
|  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | 
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | 
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | 
|  * THE SOFTWARE. | 
|  */ | 
| #endregion | 
|   | 
| using System; | 
| using System.Collections.Generic; | 
| using System.Configuration; | 
| using System.Diagnostics; | 
| using System.IO; | 
| using System.Threading; | 
| using WebSocketSharp.Net; | 
|   | 
| namespace WebSocketSharp.Server | 
| { | 
|     public class HttpServer | 
|     { | 
|         #region Fields | 
|   | 
|         private Thread _acceptRequestThread; | 
|         private bool _isWindows; | 
|         private HttpListener _listener; | 
|         private int _port; | 
|         private string _rootPath; | 
|         private ServiceManager _services; | 
|   | 
|         #endregion | 
|   | 
|         #region Constructors | 
|   | 
|         public HttpServer() | 
|             : this(80) | 
|         { | 
|         } | 
|   | 
|         public HttpServer(int port) | 
|         { | 
|             _port = port; | 
|             init(); | 
|         } | 
|   | 
|         #endregion | 
|   | 
|         #region Properties | 
|   | 
|         public int Port | 
|         { | 
|             get { return _port; } | 
|         } | 
|   | 
|         public IEnumerable<string> ServicePath | 
|         { | 
|             get | 
|             { | 
|                 return _services.Path; | 
|             } | 
|         } | 
|   | 
|         public bool Sweeped | 
|         { | 
|             get | 
|             { | 
|                 return _services.Sweeped; | 
|             } | 
|   | 
|             set | 
|             { | 
|                 _services.Sweeped = value; | 
|             } | 
|         } | 
|   | 
|         #endregion | 
|   | 
|         #region Events | 
|   | 
|         public event EventHandler<ResponseEventArgs> OnConnect; | 
|         public event EventHandler<ResponseEventArgs> OnDelete; | 
|         public event EventHandler<ErrorEventArgs> OnError; | 
|         public event EventHandler<ResponseEventArgs> OnGet; | 
|         public event EventHandler<ResponseEventArgs> OnHead; | 
|         public event EventHandler<ResponseEventArgs> OnOptions; | 
|         public event EventHandler<ResponseEventArgs> OnPatch; | 
|         public event EventHandler<ResponseEventArgs> OnPost; | 
|         public event EventHandler<ResponseEventArgs> OnPut; | 
|         public event EventHandler<ResponseEventArgs> OnTrace; | 
|   | 
|         #endregion | 
|   | 
|         #region Private Methods | 
|   | 
|         private void acceptRequest() | 
|         { | 
|             while (true) | 
|             { | 
|                 try | 
|                 { | 
|                     var context = _listener.GetContext(); | 
|                     respondAsync(context); | 
|                 } | 
|                 catch (HttpListenerException) | 
|                 { | 
|                     // HttpListener has been closed. | 
|                     break; | 
|                 } | 
|                 catch (Exception ex) | 
|                 { | 
|                     onError(ex.Message); | 
|                     break; | 
|                 } | 
|             } | 
|         } | 
|   | 
|         private void configureFromConfigFile() | 
|         { | 
|             _rootPath = ConfigurationManager.AppSettings["RootPath"]; | 
|         } | 
|   | 
|         private void init() | 
|         { | 
|             _isWindows = false; | 
|             _listener = new HttpListener(); | 
|             _services = new ServiceManager(); | 
|   | 
|             var os = Environment.OSVersion; | 
|             if (os.Platform != PlatformID.Unix && os.Platform != PlatformID.MacOSX) | 
|                 _isWindows = true; | 
|   | 
|             var prefix = String.Format( | 
|               "http{0}://*:{1}/", _port == 443 ? "s" : String.Empty, _port); | 
|             _listener.Prefixes.Add(prefix); | 
|   | 
|             configureFromConfigFile(); | 
|         } | 
|   | 
|         private bool isUpgrade(HttpListenerRequest request, string value) | 
|         { | 
|             if (!Ext.Exists(request.Headers, "Upgrade", value)) | 
|                 return false; | 
|   | 
|             if (!Ext.Exists(request.Headers, "Connection", "Upgrade")) | 
|                 return false; | 
|   | 
|             return true; | 
|         } | 
|   | 
|         private void onError(string message) | 
|         { | 
| #if DEBUG | 
|             var callerFrame = new StackFrame(1); | 
|             var caller = callerFrame.GetMethod(); | 
|             Console.WriteLine("HTTPSV: Error@{0}: {1}", caller.Name, message); | 
| #endif | 
|             Ext.Emit(OnError, this, new ErrorEventArgs(message)); | 
|         } | 
|   | 
|         private void respond(HttpListenerContext context) | 
|         { | 
|             var req = context.Request; | 
|             var res = context.Response; | 
|             var eventArgs = new ResponseEventArgs(context); | 
|   | 
|             if (req.HttpMethod == "GET" && OnGet != null) | 
|             { | 
|                 OnGet(this, eventArgs); | 
|                 return; | 
|             } | 
|   | 
|             if (req.HttpMethod == "HEAD" && OnHead != null) | 
|             { | 
|                 OnHead(this, eventArgs); | 
|                 return; | 
|             } | 
|   | 
|             if (req.HttpMethod == "POST" && OnPost != null) | 
|             { | 
|                 OnPost(this, eventArgs); | 
|                 return; | 
|             } | 
|   | 
|             if (req.HttpMethod == "PUT" && OnPut != null) | 
|             { | 
|                 OnPut(this, eventArgs); | 
|                 return; | 
|             } | 
|   | 
|             if (req.HttpMethod == "DELETE" && OnDelete != null) | 
|             { | 
|                 OnDelete(this, eventArgs); | 
|                 return; | 
|             } | 
|   | 
|             if (req.HttpMethod == "OPTIONS" && OnOptions != null) | 
|             { | 
|                 OnOptions(this, eventArgs); | 
|                 return; | 
|             } | 
|   | 
|             if (req.HttpMethod == "TRACE" && OnTrace != null) | 
|             { | 
|                 OnTrace(this, eventArgs); | 
|                 return; | 
|             } | 
|   | 
|             if (req.HttpMethod == "CONNECT" && OnConnect != null) | 
|             { | 
|                 OnConnect(this, eventArgs); | 
|                 return; | 
|             } | 
|   | 
|             if (req.HttpMethod == "PATCH" && OnPatch != null) | 
|             { | 
|                 OnPatch(this, eventArgs); | 
|                 return; | 
|             } | 
|   | 
|             res.StatusCode = (int)HttpStatusCode.NotImplemented; | 
|         } | 
|   | 
|         private void respondAsync(HttpListenerContext context) | 
|         { | 
|             WaitCallback respondCb = (state) => | 
|             { | 
|                 var req = context.Request; | 
|                 var res = context.Response; | 
|   | 
|                 try | 
|                 { | 
|                     if (isUpgrade(req, "websocket")) | 
|                     { | 
|                         if (upgradeToWebSocket(context)) | 
|                             return; | 
|                     } | 
|                     else | 
|                     { | 
|                         respond(context); | 
|                     } | 
|   | 
|                     res.Close(); | 
|                 } | 
|                 catch (Exception ex) | 
|                 { | 
|                     onError(ex.Message); | 
|                 } | 
|             }; | 
|   | 
|             ThreadPool.QueueUserWorkItem(respondCb); | 
|         } | 
|   | 
|         private void startAcceptRequestThread() | 
|         { | 
|             _acceptRequestThread = new Thread(new ThreadStart(acceptRequest)); | 
|             _acceptRequestThread.IsBackground = true; | 
|             _acceptRequestThread.Start(); | 
|         } | 
|   | 
|         private bool upgradeToWebSocket(HttpListenerContext context) | 
|         { | 
|             var res = context.Response; | 
|             var wsContext = context.AcceptWebSocket(); | 
|             var socket = wsContext.WebSocket; | 
|             var path = Ext.UrlDecode(wsContext.Path); | 
|   | 
|             IServiceHost svcHost; | 
|             if (!_services.TryGetServiceHost(path, out svcHost)) | 
|             { | 
|                 res.StatusCode = (int)HttpStatusCode.NotImplemented; | 
|                 return false; | 
|             } | 
|   | 
|             svcHost.BindWebSocket(socket); | 
|             return true; | 
|         } | 
|   | 
|         #endregion | 
|   | 
|         #region Public Methods | 
|   | 
|         public void AddService<T>(string absPath) | 
|           where T : WebSocketService, new() | 
|         { | 
|             string msg; | 
|             if (!Ext.IsValidAbsolutePath(absPath, out msg)) | 
|             { | 
|                 onError(msg); | 
|                 return; | 
|             } | 
|   | 
|             var svcHost = new WebSocketServiceHost<T>(); | 
|             svcHost.Uri = Ext.ToUri(absPath); | 
|             if (!Sweeped) | 
|                 svcHost.Sweeped = Sweeped; | 
|   | 
|             _services.Add(absPath, svcHost); | 
|         } | 
|   | 
|         public byte[] GetFile(string path) | 
|         { | 
|             var filePath = _rootPath + path; | 
|             if (_isWindows) | 
|                 filePath = filePath.Replace("/", "\\"); | 
|   | 
|             if (File.Exists(filePath)) | 
|                 return File.ReadAllBytes(filePath); | 
|   | 
|             return null; | 
|         } | 
|   | 
|         public void Start() | 
|         { | 
|             _listener.Start(); | 
|             startAcceptRequestThread(); | 
|         } | 
|   | 
|         public void Stop() | 
|         { | 
|             _listener.Close(); | 
|             _acceptRequestThread.Join(5 * 1000); | 
|             _services.Stop(); | 
|         } | 
|   | 
|         #endregion | 
|     } | 
| } |