protected override IFullHttpResponse NewHandshakeResponse(IFullHttpRequest req, HttpHeaders headers)
        {
            // Serve the WebSocket handshake request.
            if (!req.Headers.ContainsValue(HttpHeaderNames.Connection, HttpHeaderValues.Upgrade, true) ||
                !req.Headers.TryGet(HttpHeaderNames.Upgrade, out ICharSequence value) ||
                !HttpHeaderValues.Websocket.ContentEqualsIgnoreCase(value))
            {
                throw new WebSocketHandshakeException("not a WebSocket handshake request: missing upgrade");
            }

            // Hixie 75 does not contain these headers while Hixie 76 does
            bool isHixie76 = req.Headers.Contains(HttpHeaderNames.SecWebsocketKey1) &&
                             req.Headers.Contains(HttpHeaderNames.SecWebsocketKey2);

            // Create the WebSocket handshake response.
            var res = new DefaultFullHttpResponse(HttpVersion.Http11,
                                                  new HttpResponseStatus(101, new AsciiString(isHixie76 ? "WebSocket Protocol Handshake" : "Web Socket Protocol Handshake")));

            if (headers != null)
            {
                res.Headers.Add(headers);
            }

            res.Headers.Add(HttpHeaderNames.Upgrade, HttpHeaderValues.Websocket);
            res.Headers.Add(HttpHeaderNames.Connection, HttpHeaderValues.Upgrade);

            // Fill in the headers and contents depending on handshake getMethod.
            if (isHixie76)
            {
                // New handshake getMethod with a challenge:
                value = req.Headers.Get(HttpHeaderNames.Origin, null);
                Debug.Assert(value != null);
                res.Headers.Add(HttpHeaderNames.SecWebsocketOrigin, value);
                res.Headers.Add(HttpHeaderNames.SecWebsocketLocation, this.Uri);

                if (req.Headers.TryGet(HttpHeaderNames.SecWebsocketProtocol, out ICharSequence subprotocols))
                {
                    string selectedSubprotocol = this.SelectSubprotocol(subprotocols.ToString());
                    if (selectedSubprotocol == null)
                    {
                        if (Logger.DebugEnabled)
                        {
                            Logger.Debug("Requested subprotocol(s) not supported: {}", subprotocols);
                        }
                    }
                    else
                    {
                        res.Headers.Add(HttpHeaderNames.SecWebsocketProtocol, selectedSubprotocol);
                    }
                }

                // Calculate the answer of the challenge.
                value = req.Headers.Get(HttpHeaderNames.SecWebsocketKey1, null);
                Debug.Assert(value != null, $"{HttpHeaderNames.SecWebsocketKey1} must exist");
                string key1 = value.ToString();
                value = req.Headers.Get(HttpHeaderNames.SecWebsocketKey2, null);
                Debug.Assert(value != null, $"{HttpHeaderNames.SecWebsocketKey2} must exist");
                string key2 = value.ToString();
                int    a    = (int)(long.Parse(BeginningDigit.Replace(key1, "")) /
                                    BeginningSpace.Replace(key1, "").Length);
                int b = (int)(long.Parse(BeginningDigit.Replace(key2, "")) /
                              BeginningSpace.Replace(key2, "").Length);
                long        c     = req.Content.ReadLong();
                IByteBuffer input = Unpooled.Buffer(16);
                input.WriteInt(a);
                input.WriteInt(b);
                input.WriteLong(c);
                res.Content.WriteBytes(WebSocketUtil.Md5(input.Array));
            }
            else
            {
                // Old Hixie 75 handshake getMethod with no challenge:
                value = req.Headers.Get(HttpHeaderNames.Origin, null);
                Debug.Assert(value != null);
                res.Headers.Add(HttpHeaderNames.WebsocketOrigin, value);
                res.Headers.Add(HttpHeaderNames.WebsocketLocation, this.Uri);

                if (req.Headers.TryGet(HttpHeaderNames.WebsocketProtocol, out ICharSequence protocol))
                {
                    res.Headers.Add(HttpHeaderNames.WebsocketProtocol, this.SelectSubprotocol(protocol.ToString()));
                }
            }

            return(res);
        }
        protected internal override unsafe IFullHttpRequest NewHandshakeRequest()
        {
            // Make keys
            int spaces1 = WebSocketUtil.RandomNumber(1, 12);
            int spaces2 = WebSocketUtil.RandomNumber(1, 12);

            int max1 = int.MaxValue / spaces1;
            int max2 = int.MaxValue / spaces2;

            int number1 = WebSocketUtil.RandomNumber(0, max1);
            int number2 = WebSocketUtil.RandomNumber(0, max2);

            int product1 = number1 * spaces1;
            int product2 = number2 * spaces2;

            string key1 = Convert.ToString(product1);
            string key2 = Convert.ToString(product2);

            key1 = InsertRandomCharacters(key1);
            key2 = InsertRandomCharacters(key2);

            key1 = InsertSpaces(key1, spaces1);
            key2 = InsertSpaces(key2, spaces2);

            byte[] key3      = WebSocketUtil.RandomBytes(8);
            var    challenge = new byte[16];

            fixed(byte *bytes = challenge)
            {
                Unsafe.WriteUnaligned(bytes, number1);
                Unsafe.WriteUnaligned(bytes + 4, number2);
                PlatformDependent.CopyMemory(key3, 0, bytes + 8, 8);
            }

            this.expectedChallengeResponseBytes = Unpooled.WrappedBuffer(WebSocketUtil.Md5(challenge));

            // Get path
            Uri    wsUrl = this.Uri;
            string path  = RawPath(wsUrl);

            // Format request
            var         request = new DefaultFullHttpRequest(HttpVersion.Http11, HttpMethod.Get, path);
            HttpHeaders headers = request.Headers;

            headers.Add(HttpHeaderNames.Upgrade, Websocket)
            .Add(HttpHeaderNames.Connection, HttpHeaderValues.Upgrade)
            .Add(HttpHeaderNames.Host, WebsocketHostValue(wsUrl))
            .Add(HttpHeaderNames.Origin, WebsocketOriginValue(wsUrl))
            .Add(HttpHeaderNames.SecWebsocketKey1, key1)
            .Add(HttpHeaderNames.SecWebsocketKey2, key2);

            string expectedSubprotocol = this.ExpectedSubprotocol;

            if (!string.IsNullOrEmpty(expectedSubprotocol))
            {
                headers.Add(HttpHeaderNames.SecWebsocketProtocol, expectedSubprotocol);
            }

            if (this.CustomHeaders != null)
            {
                headers.Add(this.CustomHeaders);
            }

            // Set Content-Length to workaround some known defect.
            // See also: http://www.ietf.org/mail-archive/web/hybi/current/msg02149.html
            headers.Set(HttpHeaderNames.ContentLength, key3.Length);
            request.Content.WriteBytes(key3);
            return(request);
        }
        /// <summary>
        /// <para>
        /// Handle the web socket handshake for the web socket specification <a href=
        /// "http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-00">HyBi version 0</a> and lower. This standard
        /// is really a rehash of <a href="http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76" >hixie-76</a> and
        /// <a href="http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-75" >hixie-75</a>.
        /// </para>
        ///
        /// <para>
        /// Browser request to the server:
        /// </para>
        ///
        /// <![CDATA[
        /// GET /demo HTTP/1.1
        /// Upgrade: WebSocket
        /// Connection: Upgrade
        /// Host: example.com
        /// Origin: http://example.com
        /// Sec-WebSocket-Protocol: chat, sample
        /// Sec-WebSocket-Key1: 4 @1  46546xW%0l 1 5
        /// Sec-WebSocket-Key2: 12998 5 Y3 1  .P00
        ///
        /// ^n:ds[4U
        /// ]]>
        ///
        /// <para>
        /// Server response:
        /// </para>
        ///
        /// <![CDATA[
        /// HTTP/1.1 101 WebSocket Protocol Handshake
        /// Upgrade: WebSocket
        /// Connection: Upgrade
        /// Sec-WebSocket-Origin: http://example.com
        /// Sec-WebSocket-Location: ws://example.com/demo
        /// Sec-WebSocket-Protocol: sample
        ///
        /// 8jKS'y:G*Co,Wxa-
        /// ]]>
        /// </summary>
        /// <param name="req"></param>
        /// <param name="headers"></param>
        /// <returns></returns>
        protected override IFullHttpResponse NewHandshakeResponse(IFullHttpRequest req, HttpHeaders headers)
        {
            // Serve the WebSocket handshake request.
            if (!req.Headers.ContainsValue(HttpHeaderNames.Connection, HttpHeaderValues.Upgrade, true) ||
                !req.Headers.TryGet(HttpHeaderNames.Upgrade, out ICharSequence value) ||
                !HttpHeaderValues.Websocket.ContentEqualsIgnoreCase(value))
            {
                ThrowHelper.ThrowWebSocketHandshakeException_MissingUpgrade();
            }

            // Hixie 75 does not contain these headers while Hixie 76 does
            bool isHixie76 = req.Headers.Contains(HttpHeaderNames.SecWebsocketKey1) &&
                             req.Headers.Contains(HttpHeaderNames.SecWebsocketKey2);

            var origin = req.Headers.Get(HttpHeaderNames.Origin, null);

            //throw before allocating FullHttpResponse
            if (origin is null && !isHixie76)
            {
                ThrowHelper.ThrowWebSocketHandshakeException_Missing_origin_header(req);
            }

            // Create the WebSocket handshake response.
            var res = new DefaultFullHttpResponse(HttpVersion.Http11, new HttpResponseStatus(101,
                                                                                             new AsciiString(isHixie76 ? "WebSocket Protocol Handshake" : "Web Socket Protocol Handshake")),
                                                  req.Content.Allocator.Buffer(0));

            if (headers is object)
            {
                _ = res.Headers.Add(headers);
            }

            _ = res.Headers.Add(HttpHeaderNames.Upgrade, HttpHeaderValues.Websocket);
            _ = res.Headers.Add(HttpHeaderNames.Connection, HttpHeaderValues.Upgrade);

            // Fill in the headers and contents depending on handshake getMethod.
            if (isHixie76)
            {
                // New handshake getMethod with a challenge:
                _ = res.Headers.Add(HttpHeaderNames.SecWebsocketOrigin, origin);
                _ = res.Headers.Add(HttpHeaderNames.SecWebsocketLocation, this.Uri);

                if (req.Headers.TryGet(HttpHeaderNames.SecWebsocketProtocol, out ICharSequence subprotocols))
                {
                    string selectedSubprotocol = this.SelectSubprotocol(subprotocols.ToString());
                    if (selectedSubprotocol is null)
                    {
                        if (Logger.DebugEnabled)
                        {
                            Logger.RequestedSubprotocolNotSupported(subprotocols);
                        }
                    }
                    else
                    {
                        _ = res.Headers.Add(HttpHeaderNames.SecWebsocketProtocol, selectedSubprotocol);
                    }
                }

                // Calculate the answer of the challenge.
                value = req.Headers.Get(HttpHeaderNames.SecWebsocketKey1, null);
                Debug.Assert(value is object, $"{HttpHeaderNames.SecWebsocketKey1} must exist");
                string key1 = value.ToString();
                value = req.Headers.Get(HttpHeaderNames.SecWebsocketKey2, null);
                Debug.Assert(value is object, $"{HttpHeaderNames.SecWebsocketKey2} must exist");
                string key2 = value.ToString();
                int    a    = (int)(long.Parse(BeginningDigit.Replace(key1, "")) /
                                    BeginningSpace.Replace(key1, "").Length);
                int b = (int)(long.Parse(BeginningDigit.Replace(key2, "")) /
                              BeginningSpace.Replace(key2, "").Length);
                long        c     = req.Content.ReadLong();
                IByteBuffer input = Unpooled.WrappedBuffer(new byte[16]).SetIndex(0, 0);
                _ = input.WriteInt(a);
                _ = input.WriteInt(b);
                _ = input.WriteLong(c);
                _ = res.Content.WriteBytes(WebSocketUtil.Md5(input.Array));
            }
            else
            {
                // Old Hixie 75 handshake getMethod with no challenge:
                _ = res.Headers.Add(HttpHeaderNames.WebsocketOrigin, origin);
                _ = res.Headers.Add(HttpHeaderNames.WebsocketLocation, this.Uri);

                if (req.Headers.TryGet(HttpHeaderNames.WebsocketProtocol, out ICharSequence protocol))
                {
                    _ = res.Headers.Add(HttpHeaderNames.WebsocketProtocol, this.SelectSubprotocol(protocol.ToString()));
                }
            }

            return(res);
        }