Ejemplo n.º 1
0
        /// <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 internal 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.Set(HttpHeaderNames.Upgrade, HttpHeaderValues.Websocket)
                .Set(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 DEBUG
                        if (Logger.DebugEnabled)
                        {
                            Logger.RequestedSubprotocolNotSupported(subprotocols);
                        }
#endif
                    }
                    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);
        }