public static bool VerifyHandshake(HandshakeContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }
            if (context.ResponseBuffer == null)
            {
                throw new ArgumentNullException("context.ResponseBuffer");
            }
            if (string.IsNullOrEmpty(context.SecWebSocketKey))
            {
                throw new ArgumentNullException("context.SecWebSocketKey");
            }

            var response = Encoding.UTF8.GetString(context.ResponseBuffer, context.ResponseBufferOffset, context.ResponseBufferCount);

            // HTTP/1.1 101 Switching Protocols
            // Upgrade: websocket
            // Connection: Upgrade
            // Sec-WebSocket-Accept: 1tGBmA9p0DQDgmFll6P0/UcVS/E=
            // Sec-WebSocket-Protocol: chat
            Dictionary <string, string> headers;
            List <string> extensions;
            List <string> protocols;

            ParseOpenningHandshakeResponseHeaders(response, out headers, out extensions, out protocols);

            if (!headers.ContainsKey("HttpStatusCode"))
            {
                return(false);
            }

            // Any status code other than 101 indicates that the WebSocket handshake
            // has not completed and that the semantics of HTTP still apply.
            if (headers["HttpStatusCode"] != "101")
            {
                return(false);
            }

            if (!headers.ContainsKey("Sec-WebSocket-Accept"))
            {
                return(false);
            }

            string challenge =
                Convert.ToBase64String(
                    SHA1.Create().ComputeHash(
                        Encoding.ASCII.GetBytes(
                            context.SecWebSocketKey + Consts.SecWebSocketKeyGuid)));

            return(headers["Sec-WebSocket-Accept"].Equals(challenge, StringComparison.OrdinalIgnoreCase));
        }
        public static HandshakeContext BuildHandeshakeContext(
            string host,
            string path,
            string key        = null,
            string protocol   = null,
            string version    = null,
            string extensions = null,
            string origin     = null,
            IEnumerable <KeyValuePair <string, string> > cookies = null)
        {
            if (string.IsNullOrEmpty(host))
            {
                throw new ArgumentNullException("host");
            }
            if (string.IsNullOrEmpty(path))
            {
                throw new ArgumentNullException("path");
            }

            var sb = new StringBuilder();

            if (string.IsNullOrEmpty(key))
            {
                key = Convert.ToBase64String(Encoding.ASCII.GetBytes(Guid.NewGuid().ToString().Substring(0, 16)));
            }

            sb.AppendFormatWithCrCf("GET {0} HTTP/1.1", path);
            sb.AppendFormatWithCrCf("Host: {0}", host);

            sb.AppendWithCrCf("Upgrade: websocket");
            sb.AppendWithCrCf("Connection: Upgrade");

            // In addition to Upgrade headers, the client sends a Sec-WebSocket-Key header
            // containing base64-encoded random bytes, and the server replies with a hash of the key
            // in the Sec-WebSocket-Accept header. This is intended to prevent a caching proxy
            // from re-sending a previous WebSocket conversation, and does not provide any authentication,
            // privacy or integrity. The hashing function appends the
            // fixed string 258EAFA5-E914-47DA-95CA-C5AB0DC85B11 (a GUID) to the value
            // from Sec-WebSocket-Key header (which is not decoded from base64),
            // applies the SHA-1 hashing function, and encodes the result using base64.
            sb.AppendFormatWithCrCf("Sec-WebSocket-Key: {0}", key);

            // The |Sec-WebSocket-Version| header field in the client's
            // handshake includes the version of the WebSocket Protocol with
            // which the client is attempting to communicate.  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
            // field indicating the version(s)the server is capable of understanding.
            if (!string.IsNullOrEmpty(version))
            {
                sb.AppendFormatWithCrCf("Sec-WebSocket-Version: {0}", version);
            }
            else
            {
                sb.AppendFormatWithCrCf("Sec-WebSocket-Version: {0}", 13);
            }

            // Optionally
            // The |Sec-WebSocket-Protocol| request-header field can be
            // used to indicate what subprotocols(application - level protocols
            // layered over the WebSocket Protocol) are acceptable to the client.
            if (!string.IsNullOrEmpty(protocol))
            {
                sb.AppendFormatWithCrCf("Sec-WebSocket-Protocol: {0}", protocol);
            }

            // Optionally
            // A (possibly empty) list representing the protocol-level
            // extensions the server is ready to use.
            if (!string.IsNullOrEmpty(extensions))
            {
                sb.AppendFormatWithCrCf("Sec-WebSocket-Extensions: {0}", extensions);
            }

            // Optionally
            // The |Origin| header field is used to protect against
            // unauthorized cross-origin use of a WebSocket server by scripts using
            // the WebSocket API in a web browser.
            // This header field is sent by browser clients; for non-browser clients,
            // this header field may be sent if it makes sense in the context of those clients.
            if (!string.IsNullOrEmpty(origin))
            {
                sb.AppendFormatWithCrCf("Origin: {0}", origin);
            }

            if (cookies != null && cookies.Any())
            {
                string[] pairs = new string[cookies.Count()];

                for (int i = 0; i < cookies.Count(); i++)
                {
                    var item = cookies.ElementAt(i);
                    pairs[i] = item.Key + "=" + Uri.EscapeUriString(item.Value);
                }

                sb.AppendFormatWithCrCf("Cookie: {0}", string.Join(";", pairs));
            }

            sb.AppendWithCrCf();

            // GET /chat HTTP/1.1
            // Host: server.example.com
            // Upgrade: websocket
            // Connection: Upgrade
            // Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
            // Sec-WebSocket-Protocol: chat, superchat
            // Sec-WebSocket-Version: 13
            // Origin: http://example.com
            var message = sb.ToString();

            var requestBuffer = Encoding.UTF8.GetBytes(message);
            var context       = new HandshakeContext()
            {
                RequestBuffer       = requestBuffer,
                RequestBufferOffset = 0,
                RequestBufferCount  = requestBuffer.Length,
                SecWebSocketKey     = key,
            };

            return(context);
        }
        public static HandshakeContext BuildHandeshakeContext(
            string host,
            string path,
            string key = null,
            string protocol = null,
            string version = null,
            string extensions = null,
            string origin = null,
            IEnumerable<KeyValuePair<string, string>> cookies = null)
        {
            if (string.IsNullOrEmpty(host))
                throw new ArgumentNullException("host");
            if (string.IsNullOrEmpty(path))
                throw new ArgumentNullException("path");

            var sb = new StringBuilder();

            if (string.IsNullOrEmpty(key))
                key = Convert.ToBase64String(Encoding.ASCII.GetBytes(Guid.NewGuid().ToString().Substring(0, 16)));

            sb.AppendFormatWithCrCf("GET {0} HTTP/1.1", path);
            sb.AppendFormatWithCrCf("Host: {0}", host);

            sb.AppendWithCrCf("Upgrade: websocket");
            sb.AppendWithCrCf("Connection: Upgrade");

            // In addition to Upgrade headers, the client sends a Sec-WebSocket-Key header 
            // containing base64-encoded random bytes, and the server replies with a hash of the key 
            // in the Sec-WebSocket-Accept header. This is intended to prevent a caching proxy 
            // from re-sending a previous WebSocket conversation, and does not provide any authentication, 
            // privacy or integrity. The hashing function appends the 
            // fixed string 258EAFA5-E914-47DA-95CA-C5AB0DC85B11 (a GUID) to the value 
            // from Sec-WebSocket-Key header (which is not decoded from base64), 
            // applies the SHA-1 hashing function, and encodes the result using base64.
            sb.AppendFormatWithCrCf("Sec-WebSocket-Key: {0}", key);

            // The |Sec-WebSocket-Version| header field in the client's
            // handshake includes the version of the WebSocket Protocol with
            // which the client is attempting to communicate.  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
            // field indicating the version(s)the server is capable of understanding.
            if (!string.IsNullOrEmpty(version))
                sb.AppendFormatWithCrCf("Sec-WebSocket-Version: {0}", version);
            else
                sb.AppendFormatWithCrCf("Sec-WebSocket-Version: {0}", 13);

            // Optionally
            // The |Sec-WebSocket-Protocol| request-header field can be
            // used to indicate what subprotocols(application - level protocols
            // layered over the WebSocket Protocol) are acceptable to the client.
            if (!string.IsNullOrEmpty(protocol))
                sb.AppendFormatWithCrCf("Sec-WebSocket-Protocol: {0}", protocol);

            // Optionally
            // A (possibly empty) list representing the protocol-level
            // extensions the server is ready to use.
            if (!string.IsNullOrEmpty(extensions))
                sb.AppendFormatWithCrCf("Sec-WebSocket-Extensions: {0}", extensions);

            // Optionally
            // The |Origin| header field is used to protect against
            // unauthorized cross-origin use of a WebSocket server by scripts using         
            // the WebSocket API in a web browser.
            // This header field is sent by browser clients; for non-browser clients, 
            // this header field may be sent if it makes sense in the context of those clients.
            if (!string.IsNullOrEmpty(origin))
                sb.AppendFormatWithCrCf("Origin: {0}", origin);

            if (cookies != null && cookies.Any())
            {
                string[] pairs = new string[cookies.Count()];

                for (int i = 0; i < cookies.Count(); i++)
                {
                    var item = cookies.ElementAt(i);
                    pairs[i] = item.Key + "=" + Uri.EscapeUriString(item.Value);
                }

                sb.AppendFormatWithCrCf("Cookie: {0}", string.Join(";", pairs));
            }

            sb.AppendWithCrCf();

            // GET /chat HTTP/1.1
            // Host: server.example.com
            // Upgrade: websocket
            // Connection: Upgrade
            // Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
            // Sec-WebSocket-Protocol: chat, superchat
            // Sec-WebSocket-Version: 13
            // Origin: http://example.com
            var message = sb.ToString();

            var requestBuffer = Encoding.UTF8.GetBytes(message);
            var context = new HandshakeContext()
            {
                RequestBuffer = requestBuffer,
                RequestBufferOffset = 0,
                RequestBufferCount = requestBuffer.Length,
                SecWebSocketKey = key,
            };
            return context;
        }
        public static bool VerifyHandshake(HandshakeContext context)
        {
            if (context == null)
                throw new ArgumentNullException("context");
            if (context.ResponseBuffer == null)
                throw new ArgumentNullException("context.ResponseBuffer");
            if (string.IsNullOrEmpty(context.SecWebSocketKey))
                throw new ArgumentNullException("context.SecWebSocketKey");

            var response = Encoding.UTF8.GetString(context.ResponseBuffer, context.ResponseBufferOffset, context.ResponseBufferCount);

            // HTTP/1.1 101 Switching Protocols
            // Upgrade: websocket
            // Connection: Upgrade
            // Sec-WebSocket-Accept: 1tGBmA9p0DQDgmFll6P0/UcVS/E=
            // Sec-WebSocket-Protocol: chat
            Dictionary<string, string> headers;
            List<string> extensions;
            List<string> protocols;
            ParseOpenningHandshakeResponseHeaders(response, out headers, out extensions, out protocols);

            if (!headers.ContainsKey("HttpStatusCode"))
                return false;

            // Any status code other than 101 indicates that the WebSocket handshake
            // has not completed and that the semantics of HTTP still apply.
            if (headers["HttpStatusCode"] != "101")
                return false;

            if (!headers.ContainsKey("Sec-WebSocket-Accept"))
                return false;

            string challenge =
                Convert.ToBase64String(
                    SHA1.Create().ComputeHash(
                        Encoding.ASCII.GetBytes(
                            context.SecWebSocketKey + Consts.SecWebSocketKeyGuid)));

            return headers["Sec-WebSocket-Accept"].Equals(challenge, StringComparison.OrdinalIgnoreCase);
        }