private bool ShakeHands() { _Header["Version"] = "0"; try { // Peek first byte for 22, 128 (indicates ssl) // Don't use class methods for peek/read since they eat data into the input buffer, and AuthenticateAsServer needs that data if (_Socket.Poll(5 * 1000 * 1000, SelectMode.SelectRead)) { byte[] FirstByte = new byte[1]; _Socket.Receive(FirstByte, 0, 1, SocketFlags.Peek); if ((FirstByte[0] == 22) || (FirstByte[0] == 128)) { if (_Certificate == null) { throw new Exception("wss:// requires a certificate"); } else { var SSL = new SslStream(_Stream, false); _Stream = SSL; SSL.AuthenticateAsServer(_Certificate, false, SslProtocols.Tls, false); Protocol = "wss"; } } } else { RMLog.Error("Timeout exceeded while waiting for complete handshake"); return(false); } // Keep reading header data until we get all the data we want while (true) { // Read another line, and abort if we don't get one within 5 seconds string InLine = ReadLn(new string[] { "\r\n", "\0" }, false, '\0', 5000).Trim(); if (ReadTimedOut) { RMLog.Error("Timeout exceeded while waiting for complete handshake"); return(false); } RMLog.Trace("Handshake Line: " + InLine); // Check for blank line (indicates we have most of the header, and only the last 8 bytes remain if (string.IsNullOrEmpty(InLine)) { switch (_Header["Version"]) { case "0": if (_Header.ContainsKey("Sec-WebSocket-Key1")) { _ProtocolVersion = ProtocolVersion.Hixie76; return(ShakeHandsHixie76()); } else { // Only used by Chrome 4 and iOS 5.0.0 so probably not worth bothering _ProtocolVersion = ProtocolVersion.Hixie75; return(false); } case "7": case "8": case "13": _ProtocolVersion = ProtocolVersion.RFC6455; return(ShakeHandsRFC6455()); default: // TODO If this version does not // match a version understood by the server, the server MUST // abort the websocket handshake described in this section and // instead send an appropriate HTTP error code (such as 426 // Upgrade Required), and a |Sec-WebSocket-Version| header // indicating the version(s) the server is capable of // understanding. return(false); } break; } else if (InLine.StartsWith("Connection:")) { // Example: "Connection: Upgrade" // NB: New in protocol 8+ _Header["Connection"] = InLine.Replace("Connection:", "").Trim(); } else if (InLine.StartsWith("GET")) { // Example: "GET /demo HTTP/1.1" string[] GET = InLine.Split(' '); _Header["Path"] = GET[1]; } else if (InLine.StartsWith("Host:")) { // Example: "Host: example.com" _Header["Host"] = InLine.Replace("Host:", "").Trim(); } else if (InLine.StartsWith("Origin:")) { // Example: "Origin: http://example.com" // NB: Not used in protocol 8+ _Header["Origin"] = InLine.Replace("Origin:", "").Trim(); } else if (InLine.StartsWith("Sec-WebSocket-Key:")) { // Example: "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==" // NB: New in protocol 8+ _Header["Key"] = InLine.Replace("Sec-WebSocket-Key:", "").Trim(); } else if (InLine.StartsWith("Sec-WebSocket-Key1:")) { // Example: "Sec-WebSocket-Key1: 4 @1 46546xW%0l 1 5" // NB: Not used in protocol 8+ _Header["Key1"] = InLine.Replace("Sec-WebSocket-Key1:", "").Trim(); } else if (InLine.StartsWith("Sec-WebSocket-Key2:")) { // Example: "Sec-WebSocket-Key2: 12998 5 Y3 1 .P00" // NB: Not used in protocol 8+ _Header["Key2"] = InLine.Replace("Sec-WebSocket-Key2:", "").Trim(); } else if (InLine.StartsWith("Sec-WebSocket-Origin:")) { // Example: "Sec-WebSocket-Origin: http://example.com" // NB: New in protocol 8+ _Header["Origin"] = InLine.Replace("Sec-WebSocket-Origin:", "").Trim(); } else if (InLine.StartsWith("Sec-WebSocket-Protocol:")) { // Example: "Sec-WebSocket-Protocol: sample" _Header["SubProtocol"] = InLine.Replace("Sec-WebSocket-Protocol:", "").Trim(); } else if (InLine.StartsWith("Sec-WebSocket-Draft")) { // Example: "Sec-WebSocket-Draft: 2" _Header["Version"] = InLine.Replace("Sec-WebSocket-Draft:", "").Trim(); } else if (InLine.StartsWith("Sec-WebSocket-Version")) { // Example: "Sec-WebSocket-Version: 8" _Header["Version"] = InLine.Replace("Sec-WebSocket-Version:", "").Trim(); } else if (InLine.StartsWith("Upgrade:")) { // Example: "Upgrade: websocket" // NB: New in protocol 8+ _Header["Upgrade"] = InLine.Replace("Upgrade:", "").Trim(); } else if (InLine.StartsWith("<policy-file-request")) { string PolicyResponse = "<?xml version=\"1.0\"?>\n" + "<cross-domain-policy>\n" + " <allow-access-from domain=\"*\" to-ports=\"*\"/>\n" + " <site-control permitted-cross-domain-policies=\"all\"/>\n" + "</cross-domain-policy>\n" + "\0"; WriteRaw(Encoding.UTF8.GetBytes(PolicyResponse)); FlashPolicyFileRequest = true; return(false); } } } catch (Exception ex) { RMLog.Exception(ex, "Exception in WebSocketConnection::ShakeHands()"); } return(false); }