#if !NET20 using System; using System.Collections.Generic; using System.Net; using System.Net.Sockets; using System.Security.Authentication; using System.Security.Cryptography.X509Certificates; namespace Apewer.WebSocket { internal class WebSocketServer : IDisposable { private readonly string _scheme; private readonly IPAddress _locationIP; private Action _config; /// /// public WebSocketServer(string location, bool supportDualStack = true) { var uri = new Uri(location); Port = uri.Port; Location = location; SupportDualStack = supportDualStack; _locationIP = ParseIPAddress(uri); _scheme = uri.Scheme; var socket = new Socket(_locationIP.AddressFamily, SocketType.Stream, ProtocolType.IP); if (SupportDualStack) { if (!Runtime.IsRunningOnMono() && Runtime.IsRunningOnWindows()) { socket.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.IPv6Only, false); socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, 1); } } ListenerSocket = new SocketWrapper(socket); SupportedSubProtocols = new string[0]; } public SocketWrapper ListenerSocket { get; set; } public string Location { get; private set; } public bool SupportDualStack { get; } public int Port { get; private set; } public X509Certificate2 Certificate { get; set; } public SslProtocols EnabledSslProtocols { get; set; } public IEnumerable SupportedSubProtocols { get; set; } public bool RestartAfterListenError { get; set; } public bool IsSecure { get { return _scheme == "wss" && Certificate != null; } } public void Dispose() { ListenerSocket.Dispose(); } /// /// private IPAddress ParseIPAddress(Uri uri) { string ipStr = uri.Host; if (ipStr == "0.0.0.0") { return IPAddress.Any; } else if (ipStr == "[0000:0000:0000:0000:0000:0000:0000:0000]") { return IPAddress.IPv6Any; } else { try { return IPAddress.Parse(ipStr); } catch (Exception ex) { throw new FormatException("Failed to parse the IP address part of the location. Please make sure you specify a valid IP address. Use 0.0.0.0 or [::] to listen on all interfaces.", ex); } } } public void Start(Action config) { var ipLocal = new IPEndPoint(_locationIP, Port); ListenerSocket.Bind(ipLocal); ListenerSocket.Listen(100); Port = ((IPEndPoint)ListenerSocket.LocalEndPoint).Port; WebSocketLog.Info(string.Format("Server started at {0} (actual port {1})", Location, Port)); if (_scheme == "wss") { if (Certificate == null) { WebSocketLog.Error("Scheme cannot be 'wss' without a Certificate"); return; } if (EnabledSslProtocols == SslProtocols.None) { EnabledSslProtocols = SslProtocols.Tls; WebSocketLog.Debug("Using default TLS 1.0 security protocol."); } } ListenForClients(); _config = config; } private void ListenForClients() { ListenerSocket.Accept(OnClientConnect, e => { WebSocketLog.Error("Listener socket is closed", e); if (RestartAfterListenError) { WebSocketLog.Info("Listener socket restarting"); try { ListenerSocket.Dispose(); var socket = new Socket(_locationIP.AddressFamily, SocketType.Stream, ProtocolType.IP); ListenerSocket = new SocketWrapper(socket); Start(_config); WebSocketLog.Info("Listener socket restarted"); } catch (Exception ex) { WebSocketLog.Error("Listener could not be restarted", ex); } } }); } private void OnClientConnect(SocketWrapper clientSocket) { if (clientSocket == null) return; // socket closed WebSocketLog.Debug(String.Format("Client connected from {0}:{1}", clientSocket.RemoteIpAddress, clientSocket.RemotePort.ToString())); ListenForClients(); Connection connection = null; connection = new Connection( clientSocket, _config, bytes => RequestParser.Parse(bytes, _scheme), r => HandlerFactory.BuildHandler(r, s => connection.OnMessage(s), connection.Close, b => connection.OnBytes(b), b => connection.OnPing(b), b => connection.OnPong(b)), s => SubProtocolNegotiator.Negotiate(SupportedSubProtocols, s)); if (IsSecure) { WebSocketLog.Debug("Authenticating Secure Connection"); clientSocket .Authenticate(Certificate, EnabledSslProtocols, connection.StartReceiving, e => WebSocketLog.Warn("Failed to Authenticate", e)); } else { connection.StartReceiving(); } } } } #endif