internal static byte[] CreateOpenningHandshakeResponse(AsyncWebSocketSession session, string secWebSocketKey)
        {
            var sb = new StringBuilder();

            // A Status-Line with a 101 response code as per RFC 2616
            // [RFC2616].  Such a response could look like "HTTP/1.1 101 Switching Protocols".
            sb.AppendFormatWithCrCf("HTTP/{0} {1} {2}",
                Consts.HttpVersion,
                (int)HttpStatusCode.SwitchingProtocols,
                @"Switching Protocols");

            // An |Upgrade| header field with value "websocket" as per RFC2616 [RFC2616].
            sb.AppendFormatWithCrCf(Consts.HeaderLineFormat, HttpKnownHeaderNames.Upgrade, Consts.WebSocketUpgradeToken);

            // A |Connection| header field with value "Upgrade".
            sb.AppendFormatWithCrCf(Consts.HeaderLineFormat, HttpKnownHeaderNames.Connection, Consts.WebSocketConnectionToken);

            // A |Sec-WebSocket-Accept| header field.  The value of this
            // header field is constructed by concatenating /key/, defined
            // above in step 4 in Section 4.2.2, with the string "258EAFA5-
            // E914-47DA-95CA-C5AB0DC85B11", taking the SHA-1 hash of this
            // concatenated value to obtain a 20-byte value and base64-
            // encoding (see Section 4 of [RFC4648]) this 20-byte hash.
            var secWebSocketAccept = GetSecWebSocketAcceptString(secWebSocketKey);
            sb.AppendFormatWithCrCf(Consts.HeaderLineFormat, HttpKnownHeaderNames.SecWebSocketAccept, secWebSocketAccept);

            // Optionally, a |Sec-WebSocket-Extensions| header field, with a
            // value /extensions/ as defined in step 4 in Section 4.2.2.  If
            // multiple extensions are to be used, they can all be listed in
            // a single |Sec-WebSocket-Extensions| header field or split
            // between multiple instances of the |Sec-WebSocket-Extensions| header field.
            // A server accepts one or more extensions by including a
            // |Sec-WebSocket-Extensions| header field containing one or more
            // extensions that were requested by the client.  The interpretation of
            // any extension parameters, and what constitutes a valid response by a
            // server to a requested set of parameters by a client, will be defined
            // by each such extension.
            if (session.NegotiatedExtensions != null && session.NegotiatedExtensions.Any())
            {
                foreach (var extension in session.NegotiatedExtensions.Values)
                {
                    var offer = extension.GetAgreedOffer();
                    sb.AppendFormatWithCrCf(Consts.HeaderLineFormat, HttpKnownHeaderNames.SecWebSocketExtensions, offer);
                }
            }

            /**
                // Optionally, a |Sec-WebSocket-Protocol| header field, with a
                // value /subprotocol/ as defined in step 4 in Section 4.2.2.
                // 
                // The client can request that the server use a specific subprotocol by
                // including the |Sec-WebSocket-Protocol| field in its handshake.  If it
                // is specified, the server needs to include the same field and one of
                // the selected subprotocol values in its response for the connection to
                // be established.
                // 
                // These subprotocol names should be registered as per Section 11.5.  To
                // avoid potential collisions, it is recommended to use names that
                // contain the ASCII version of the domain name of the subprotocol's
                // originator.  For example, if Example Corporation were to create a
                // Chat subprotocol to be implemented by many servers around the Web,
                // they could name it "chat.example.com".  If the Example Organization
                // called their competing subprotocol "chat.example.org", then the two
                // subprotocols could be implemented by servers simultaneously, with the
                // server dynamically selecting which subprotocol to use based on the
                // value sent by the client.
                // 
                // Subprotocols can be versioned in backward-incompatible ways by
                // changing the subprotocol name, e.g., going from
                // "bookings.example.net" to "v2.bookings.example.net".  These
                // subprotocols would be considered completely separate by WebSocket
                // clients.  Backward-compatible versioning can be implemented by
                // reusing the same subprotocol string but carefully designing the
                // actual subprotocol to support this kind of extensibility.
                */

            sb.AppendWithCrCf();

            // HTTP/1.1 101 Switching Protocols
            // Upgrade: websocket
            // Connection: Upgrade
            // Sec-WebSocket-Accept: 1tGBmA9p0DQDgmFll6P0/UcVS/E=
            // Sec-WebSocket-Protocol: chat
            var response = sb.ToString();
#if DEBUG
            _log.DebugFormat("[{0}]{1}{2}", session.RemoteEndPoint, Environment.NewLine, response);
#endif
            return Encoding.UTF8.GetBytes(response);
        }
        internal static byte[] CreateOpenningHandshakeBadRequestResponse(AsyncWebSocketSession session)
        {
            var sb = new StringBuilder();

            // HTTP/1.1 400 Bad Request
            sb.AppendFormatWithCrCf("HTTP/{0} {1} {2}",
                Consts.HttpVersion,
                (int)HttpStatusCode.BadRequest,
                @"Bad Request");

            // Upgrade: websocket
            sb.AppendFormatWithCrCf(Consts.HeaderLineFormat, HttpKnownHeaderNames.Upgrade, Consts.WebSocketUpgradeToken);

            // Connection: Upgrade
            sb.AppendFormatWithCrCf(Consts.HeaderLineFormat, HttpKnownHeaderNames.Connection, Consts.WebSocketConnectionToken);

            // Sec-WebSocket-Version: 13
            sb.AppendFormatWithCrCf(Consts.HeaderLineFormat, HttpKnownHeaderNames.SecWebSocketVersion, Consts.WebSocketVersion);

            sb.AppendWithCrCf();

            var response = sb.ToString();
#if DEBUG
            _log.DebugFormat("[{0}]{1}{2}", session.RemoteEndPoint, Environment.NewLine, response);
#endif
            return Encoding.UTF8.GetBytes(response);
        }
 public virtual async Task OnSessionFragmentationStreamClosed(AsyncWebSocketSession session, byte[] data, int offset, int count)
 {
     await Task.CompletedTask;
 }
        internal static bool HandleOpenningHandshakeRequest(AsyncWebSocketSession session, byte[] buffer, int offset, int count,
            out string secWebSocketKey,
            out string path,
            out string query)
        {
            BufferValidator.ValidateBuffer(buffer, offset, count, "buffer");

            var request = Encoding.UTF8.GetString(buffer, offset, count);
#if DEBUG
            _log.DebugFormat("[{0}]{1}{2}", session.RemoteEndPoint, Environment.NewLine, request);
#endif
            try
            {
                // GET /chat HTTP/1.1
                // Host: server.example.com
                // Upgrade: websocket
                // Connection: Upgrade
                // Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
                // Origin: http://example.com
                // Sec-WebSocket-Protocol: chat, superchat
                // Sec-WebSocket-Version: 13
                Dictionary<string, string> headers;
                List<string> extensions;
                List<string> protocols;
                ParseOpenningHandshakeRequestHeaders(request, out headers, out extensions, out protocols);
                if (headers == null)
                    throw new WebSocketHandshakeException(string.Format(
                        "Handshake with remote [{0}] failed due to invalid headers.", session.RemoteEndPoint));

                // An HTTP/1.1 or higher GET request, including a "Request-URI"
                // [RFC2616] that should be interpreted as a /resource name/
                // defined in Section 3 (or an absolute HTTP/HTTPS URI containing the /resource name/).
                // A |Host| header field containing the server's authority.
                if (!headers.ContainsKey(Consts.HttpGetMethodName))
                    throw new WebSocketHandshakeException(string.Format(
                        "Handshake with remote [{0}] failed due to lack of get method.", session.RemoteEndPoint));
                if (!headers.ContainsKey(HttpKnownHeaderNames.Host))
                    throw new WebSocketHandshakeException(string.Format(
                        "Handshake with remote [{0}] failed due to lack of host authority.", session.RemoteEndPoint));
                string uriString = string.Format("ws://{0}{1}", headers[HttpKnownHeaderNames.Host], headers[Consts.HttpGetMethodName]);
                Uri requestUri = null;
                if (!Uri.TryCreate(uriString, UriKind.RelativeOrAbsolute, out requestUri))
                {
                    throw new WebSocketHandshakeException(string.Format(
                        "Handshake with remote [{0}] failed due to invalid requested resource name.", session.RemoteEndPoint));
                }
                path = requestUri.AbsolutePath;
                query = requestUri.Query;

                // A |Connection| header field that includes the token "Upgrade",
                // treated as an ASCII case-insensitive value.
                if (!headers.ContainsKey(HttpKnownHeaderNames.Connection))
                    throw new WebSocketHandshakeException(string.Format(
                        "Handshake with remote [{0}] failed due to lack of connection header item.", session.RemoteEndPoint));
                if (headers[HttpKnownHeaderNames.Connection].ToLowerInvariant() != Consts.WebSocketConnectionToken.ToLowerInvariant())
                    throw new WebSocketHandshakeException(string.Format(
                        "Handshake with remote [{0}] failed due to invalid connection header item value [{1}].",
                        session.RemoteEndPoint, headers[HttpKnownHeaderNames.Connection]));

                // An |Upgrade| header field containing the value "websocket",
                // treated as an ASCII case-insensitive value.
                if (!headers.ContainsKey(HttpKnownHeaderNames.Upgrade))
                    throw new WebSocketHandshakeException(string.Format(
                        "Handshake with remote [{0}] failed due to lack of upgrade header item.", session.RemoteEndPoint));
                if (headers[HttpKnownHeaderNames.Upgrade].ToLowerInvariant() != Consts.WebSocketUpgradeToken.ToLowerInvariant())
                    throw new WebSocketHandshakeException(string.Format(
                        "Handshake with remote [{0}] failed due to invalid upgrade header item value [{1}].",
                        session.RemoteEndPoint, headers[HttpKnownHeaderNames.Upgrade]));

                // A |Sec-WebSocket-Key| header field with a base64-encoded (see
                // Section 4 of [RFC4648]) value that, when decoded, is 16 bytes in length.
                if (!headers.ContainsKey(HttpKnownHeaderNames.SecWebSocketKey))
                    throw new WebSocketHandshakeException(string.Format(
                        "Handshake with remote [{0}] failed due to lack of Sec-WebSocket-Key header item.", session.RemoteEndPoint));
                if (string.IsNullOrWhiteSpace(headers[HttpKnownHeaderNames.SecWebSocketKey]))
                    throw new WebSocketHandshakeException(string.Format(
                        "Handshake with remote [{0}] failed due to invalid Sec-WebSocket-Key header item value [{1}].",
                        session.RemoteEndPoint, headers[HttpKnownHeaderNames.SecWebSocketKey]));
                secWebSocketKey = headers[HttpKnownHeaderNames.SecWebSocketKey];

                // A |Sec-WebSocket-Version| header field, with a value of 13.
                if (!headers.ContainsKey(HttpKnownHeaderNames.SecWebSocketVersion))
                    throw new WebSocketHandshakeException(string.Format(
                        "Handshake with remote [{0}] failed due to lack of Sec-WebSocket-Version header item.", session.RemoteEndPoint));
                if (headers[HttpKnownHeaderNames.SecWebSocketVersion].ToLowerInvariant() != Consts.WebSocketVersion.ToLowerInvariant())
                    throw new WebSocketHandshakeException(string.Format(
                        "Handshake with remote [{0}] failed due to invalid Sec-WebSocket-Version header item value [{1}].",
                        session.RemoteEndPoint, headers[HttpKnownHeaderNames.SecWebSocketVersion]));

                // Optionally, a |Sec-WebSocket-Extensions| header field, with a
                // list of values indicating which extensions the client would like
                // to speak.  The interpretation of this header field is discussed in Section 9.1.
                if (extensions != null)
                {
                    if (!extensions.Any())
                        throw new WebSocketHandshakeException(string.Format(
                            "Handshake with remote [{0}] failed due to empty extension.", session.RemoteEndPoint));
                    foreach (var extension in extensions)
                    {
                        // The empty string is not the same as the null value for these 
                        // purposes and is not a legal value for this field.
                        if (string.IsNullOrWhiteSpace(extension))
                            throw new WebSocketHandshakeException(string.Format(
                                "Handshake with remote [{0}] failed due to empty extension.", session.RemoteEndPoint));
                    }

                    session.AgreeExtensions(extensions);
                }

                // Optionally, a |Sec-WebSocket-Protocol| header field, with a list
                // of values indicating which protocols the client would like to
                // speak, ordered by preference.
                if (protocols != null)
                {
                    if (!protocols.Any())
                        throw new WebSocketHandshakeException(string.Format(
                            "Handshake with remote [{0}] failed due to empty sub-protocol.", session.RemoteEndPoint));
                    foreach (var protocol in protocols)
                    {
                        // The empty string is not the same as the null value for these 
                        // purposes and is not a legal value for this field.
                        if (string.IsNullOrWhiteSpace(protocol))
                            throw new WebSocketHandshakeException(string.Format(
                                "Handshake with remote [{0}] failed due to empty sub-protocol.", session.RemoteEndPoint));
                    }

                    session.AgreeSubProtocols(string.Join(",", protocols));
                }

                // Optionally, an |Origin| header field.  This header field is sent
                // by all browser clients.  A connection attempt lacking this
                // header field SHOULD NOT be interpreted as coming from a browser client.
                //
                // Servers that are not intended to process input from any web page but
                // only for certain sites SHOULD verify the |Origin| field is an origin
                // they expect.  If the origin indicated is unacceptable to the server,
                // then it SHOULD respond to the WebSocket handshake with a reply
                // containing HTTP 403 Forbidden status code.
                // 
                // The |Origin| header field protects from the attack cases when the
                // untrusted party is typically the author of a JavaScript application
                // that is executing in the context of the trusted client.  The client
                // itself can contact the server and, via the mechanism of the |Origin|
                // header field, determine whether to extend those communication
                // privileges to the JavaScript application.  The intent is not to
                // prevent non-browsers from establishing connections but rather to
                // ensure that trusted browsers under the control of potentially
                // malicious JavaScript cannot fake a WebSocket handshake.

                // Optionally, other header fields, such as those used to send
                // cookies or request authentication to a server.  Unknown header
                // fields are ignored, as per [RFC2616].
            }
            catch (Exception ex)
            {
                _log.ErrorFormat("{0}{1}{2}", session, Environment.NewLine, request);
                _log.Error(ex.Message, ex);
                throw;
            }

            return true;
        }
        private async Task Process(TcpClient acceptedTcpClient)
        {
            var session = new AsyncWebSocketSession(acceptedTcpClient, _configuration, _bufferManager, _routeResolver, this);

            if (_sessions.TryAdd(session.SessionKey, session))
            {
                _log.DebugFormat("New session [{0}].", session);
                try
                {
                    await session.Start();
                }
                catch (Exception ex)
                when (ex is TimeoutException || ex is WebSocketException)
                {
                    _log.Error(ex.Message, ex);
                }
                finally
                {
                    AsyncWebSocketSession throwAway;
                    if (_sessions.TryRemove(session.SessionKey, out throwAway))
                    {
                        _log.DebugFormat("Close session [{0}].", throwAway);
                    }
                }
            }
        }
 public virtual async Task OnSessionClosed(AsyncWebSocketSession session)
 {
     AsyncWebSocketSession throwAway;
     _sessions.TryRemove(session.SessionKey, out throwAway);
     await Task.CompletedTask;
 }
 public virtual async Task OnSessionStarted(AsyncWebSocketSession session)
 {
     _sessions.TryAdd(session.SessionKey, session);
     await Task.CompletedTask;
 }
 public virtual async Task OnSessionBinaryReceived(AsyncWebSocketSession session, byte[] data, int offset, int count)
 {
     await Task.CompletedTask;
 }
        internal static bool HandleOpenningHandshakeRequest(AsyncWebSocketSession session, byte[] buffer, int offset, int count,
                                                            out string secWebSocketKey,
                                                            out string path,
                                                            out string query)
        {
            BufferValidator.ValidateBuffer(buffer, offset, count, "buffer");

            var request = Encoding.UTF8.GetString(buffer, offset, count);

#if DEBUG
            _log.DebugFormat("[{0}]{1}{2}", session.RemoteEndPoint, Environment.NewLine, request);
#endif
            try
            {
                // GET /chat HTTP/1.1
                // Host: server.example.com
                // Upgrade: websocket
                // Connection: Upgrade
                // Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
                // Origin: http://example.com
                // Sec-WebSocket-Protocol: chat, superchat
                // Sec-WebSocket-Version: 13
                Dictionary <string, string> headers;
                List <string> extensions;
                List <string> protocols;
                ParseOpenningHandshakeRequestHeaders(request, out headers, out extensions, out protocols);
                if (headers == null)
                {
                    throw new WebSocketHandshakeException(string.Format(
                                                              "Handshake with remote [{0}] failed due to invalid headers.", session.RemoteEndPoint));
                }

                // An HTTP/1.1 or higher GET request, including a "Request-URI"
                // [RFC2616] that should be interpreted as a /resource name/
                // defined in Section 3 (or an absolute HTTP/HTTPS URI containing the /resource name/).
                // A |Host| header field containing the server's authority.
                if (!headers.ContainsKey(Consts.HttpGetMethodName))
                {
                    throw new WebSocketHandshakeException(string.Format(
                                                              "Handshake with remote [{0}] failed due to lack of get method.", session.RemoteEndPoint));
                }
                if (!headers.ContainsKey(HttpKnownHeaderNames.Host))
                {
                    throw new WebSocketHandshakeException(string.Format(
                                                              "Handshake with remote [{0}] failed due to lack of host authority.", session.RemoteEndPoint));
                }
                string uriString  = string.Format("ws://{0}{1}", headers[HttpKnownHeaderNames.Host], headers[Consts.HttpGetMethodName]);
                Uri    requestUri = null;
                if (!Uri.TryCreate(uriString, UriKind.RelativeOrAbsolute, out requestUri))
                {
                    throw new WebSocketHandshakeException(string.Format(
                                                              "Handshake with remote [{0}] failed due to invalid requested resource name.", session.RemoteEndPoint));
                }
                path  = requestUri.AbsolutePath;
                query = requestUri.Query;

                // A |Connection| header field that includes the token "Upgrade",
                // treated as an ASCII case-insensitive value.
                if (!headers.ContainsKey(HttpKnownHeaderNames.Connection))
                {
                    throw new WebSocketHandshakeException(string.Format(
                                                              "Handshake with remote [{0}] failed due to lack of connection header item.", session.RemoteEndPoint));
                }
                if (headers[HttpKnownHeaderNames.Connection].ToLowerInvariant() != Consts.WebSocketConnectionToken.ToLowerInvariant())
                {
                    throw new WebSocketHandshakeException(string.Format(
                                                              "Handshake with remote [{0}] failed due to invalid connection header item value [{1}].",
                                                              session.RemoteEndPoint, headers[HttpKnownHeaderNames.Connection]));
                }

                // An |Upgrade| header field containing the value "websocket",
                // treated as an ASCII case-insensitive value.
                if (!headers.ContainsKey(HttpKnownHeaderNames.Upgrade))
                {
                    throw new WebSocketHandshakeException(string.Format(
                                                              "Handshake with remote [{0}] failed due to lack of upgrade header item.", session.RemoteEndPoint));
                }
                if (headers[HttpKnownHeaderNames.Upgrade].ToLowerInvariant() != Consts.WebSocketUpgradeToken.ToLowerInvariant())
                {
                    throw new WebSocketHandshakeException(string.Format(
                                                              "Handshake with remote [{0}] failed due to invalid upgrade header item value [{1}].",
                                                              session.RemoteEndPoint, headers[HttpKnownHeaderNames.Upgrade]));
                }

                // A |Sec-WebSocket-Key| header field with a base64-encoded (see
                // Section 4 of [RFC4648]) value that, when decoded, is 16 bytes in length.
                if (!headers.ContainsKey(HttpKnownHeaderNames.SecWebSocketKey))
                {
                    throw new WebSocketHandshakeException(string.Format(
                                                              "Handshake with remote [{0}] failed due to lack of Sec-WebSocket-Key header item.", session.RemoteEndPoint));
                }
                if (string.IsNullOrWhiteSpace(headers[HttpKnownHeaderNames.SecWebSocketKey]))
                {
                    throw new WebSocketHandshakeException(string.Format(
                                                              "Handshake with remote [{0}] failed due to invalid Sec-WebSocket-Key header item value [{1}].",
                                                              session.RemoteEndPoint, headers[HttpKnownHeaderNames.SecWebSocketKey]));
                }
                secWebSocketKey = headers[HttpKnownHeaderNames.SecWebSocketKey];

                // A |Sec-WebSocket-Version| header field, with a value of 13.
                if (!headers.ContainsKey(HttpKnownHeaderNames.SecWebSocketVersion))
                {
                    throw new WebSocketHandshakeException(string.Format(
                                                              "Handshake with remote [{0}] failed due to lack of Sec-WebSocket-Version header item.", session.RemoteEndPoint));
                }
                if (headers[HttpKnownHeaderNames.SecWebSocketVersion].ToLowerInvariant() != Consts.WebSocketVersion.ToLowerInvariant())
                {
                    throw new WebSocketHandshakeException(string.Format(
                                                              "Handshake with remote [{0}] failed due to invalid Sec-WebSocket-Version header item value [{1}].",
                                                              session.RemoteEndPoint, headers[HttpKnownHeaderNames.SecWebSocketVersion]));
                }

                // Optionally, a |Sec-WebSocket-Extensions| header field, with a
                // list of values indicating which extensions the client would like
                // to speak.  The interpretation of this header field is discussed in Section 9.1.
                if (extensions != null)
                {
                    if (!extensions.Any())
                    {
                        throw new WebSocketHandshakeException(string.Format(
                                                                  "Handshake with remote [{0}] failed due to empty extension.", session.RemoteEndPoint));
                    }
                    foreach (var extension in extensions)
                    {
                        // The empty string is not the same as the null value for these
                        // purposes and is not a legal value for this field.
                        if (string.IsNullOrWhiteSpace(extension))
                        {
                            throw new WebSocketHandshakeException(string.Format(
                                                                      "Handshake with remote [{0}] failed due to empty extension.", session.RemoteEndPoint));
                        }
                    }

                    session.AgreeExtensions(extensions);
                }

                // Optionally, a |Sec-WebSocket-Protocol| header field, with a list
                // of values indicating which protocols the client would like to
                // speak, ordered by preference.
                if (protocols != null)
                {
                    if (!protocols.Any())
                    {
                        throw new WebSocketHandshakeException(string.Format(
                                                                  "Handshake with remote [{0}] failed due to empty sub-protocol.", session.RemoteEndPoint));
                    }
                    foreach (var protocol in protocols)
                    {
                        // The empty string is not the same as the null value for these
                        // purposes and is not a legal value for this field.
                        if (string.IsNullOrWhiteSpace(protocol))
                        {
                            throw new WebSocketHandshakeException(string.Format(
                                                                      "Handshake with remote [{0}] failed due to empty sub-protocol.", session.RemoteEndPoint));
                        }
                    }

                    session.AgreeSubProtocols(string.Join(",", protocols));
                }

                // Optionally, an |Origin| header field.  This header field is sent
                // by all browser clients.  A connection attempt lacking this
                // header field SHOULD NOT be interpreted as coming from a browser client.
                //
                // Servers that are not intended to process input from any web page but
                // only for certain sites SHOULD verify the |Origin| field is an origin
                // they expect.  If the origin indicated is unacceptable to the server,
                // then it SHOULD respond to the WebSocket handshake with a reply
                // containing HTTP 403 Forbidden status code.
                //
                // The |Origin| header field protects from the attack cases when the
                // untrusted party is typically the author of a JavaScript application
                // that is executing in the context of the trusted client.  The client
                // itself can contact the server and, via the mechanism of the |Origin|
                // header field, determine whether to extend those communication
                // privileges to the JavaScript application.  The intent is not to
                // prevent non-browsers from establishing connections but rather to
                // ensure that trusted browsers under the control of potentially
                // malicious JavaScript cannot fake a WebSocket handshake.

                // Optionally, other header fields, such as those used to send
                // cookies or request authentication to a server.  Unknown header
                // fields are ignored, as per [RFC2616].
            }
            catch (Exception ex)
            {
                _log.ErrorFormat("{0}{1}{2}", session, Environment.NewLine, request);
                _log.Error(ex.Message, ex);
                throw;
            }

            return(true);
        }
        internal static byte[] CreateOpenningHandshakeResponse(AsyncWebSocketSession session, string secWebSocketKey)
        {
            var sb = new StringBuilder();

            // A Status-Line with a 101 response code as per RFC 2616
            // [RFC2616].  Such a response could look like "HTTP/1.1 101 Switching Protocols".
            sb.AppendFormatWithCrCf("HTTP/{0} {1} {2}",
                                    Consts.HttpVersion,
                                    (int)HttpStatusCode.SwitchingProtocols,
                                    @"Switching Protocols");

            // An |Upgrade| header field with value "websocket" as per RFC2616 [RFC2616].
            sb.AppendFormatWithCrCf(Consts.HttpHeaderLineFormat, HttpKnownHeaderNames.Upgrade, Consts.WebSocketUpgradeToken);

            // A |Connection| header field with value "Upgrade".
            sb.AppendFormatWithCrCf(Consts.HttpHeaderLineFormat, HttpKnownHeaderNames.Connection, Consts.WebSocketConnectionToken);

            // A |Sec-WebSocket-Accept| header field.  The value of this
            // header field is constructed by concatenating /key/, defined
            // above in step 4 in Section 4.2.2, with the string "258EAFA5-
            // E914-47DA-95CA-C5AB0DC85B11", taking the SHA-1 hash of this
            // concatenated value to obtain a 20-byte value and base64-
            // encoding (see Section 4 of [RFC4648]) this 20-byte hash.
            var secWebSocketAccept = GetSecWebSocketAcceptString(secWebSocketKey);

            sb.AppendFormatWithCrCf(Consts.HttpHeaderLineFormat, HttpKnownHeaderNames.SecWebSocketAccept, secWebSocketAccept);

            // Optionally, a |Sec-WebSocket-Extensions| header field, with a
            // value /extensions/ as defined in step 4 in Section 4.2.2.  If
            // multiple extensions are to be used, they can all be listed in
            // a single |Sec-WebSocket-Extensions| header field or split
            // between multiple instances of the |Sec-WebSocket-Extensions| header field.
            // A server accepts one or more extensions by including a
            // |Sec-WebSocket-Extensions| header field containing one or more
            // extensions that were requested by the client.  The interpretation of
            // any extension parameters, and what constitutes a valid response by a
            // server to a requested set of parameters by a client, will be defined
            // by each such extension.
            if (session.NegotiatedExtensions != null && session.NegotiatedExtensions.Any())
            {
                foreach (var extension in session.NegotiatedExtensions.Values)
                {
                    var offer = extension.GetAgreedOffer();
                    sb.AppendFormatWithCrCf(Consts.HttpHeaderLineFormat, HttpKnownHeaderNames.SecWebSocketExtensions, offer);
                }
            }

            /**
             *  // Optionally, a |Sec-WebSocket-Protocol| header field, with a
             *  // value /subprotocol/ as defined in step 4 in Section 4.2.2.
             *  //
             *  // The client can request that the server use a specific subprotocol by
             *  // including the |Sec-WebSocket-Protocol| field in its handshake.  If it
             *  // is specified, the server needs to include the same field and one of
             *  // the selected subprotocol values in its response for the connection to
             *  // be established.
             *  //
             *  // These subprotocol names should be registered as per Section 11.5.  To
             *  // avoid potential collisions, it is recommended to use names that
             *  // contain the ASCII version of the domain name of the subprotocol's
             *  // originator.  For example, if Example Corporation were to create a
             *  // Chat subprotocol to be implemented by many servers around the Web,
             *  // they could name it "chat.example.com".  If the Example Organization
             *  // called their competing subprotocol "chat.example.org", then the two
             *  // subprotocols could be implemented by servers simultaneously, with the
             *  // server dynamically selecting which subprotocol to use based on the
             *  // value sent by the client.
             *  //
             *  // Subprotocols can be versioned in backward-incompatible ways by
             *  // changing the subprotocol name, e.g., going from
             *  // "bookings.example.net" to "v2.bookings.example.net".  These
             *  // subprotocols would be considered completely separate by WebSocket
             *  // clients.  Backward-compatible versioning can be implemented by
             *  // reusing the same subprotocol string but carefully designing the
             *  // actual subprotocol to support this kind of extensibility.
             */

            sb.AppendWithCrCf();

            // HTTP/1.1 101 Switching Protocols
            // Upgrade: websocket
            // Connection: Upgrade
            // Sec-WebSocket-Accept: 1tGBmA9p0DQDgmFll6P0/UcVS/E=
            // Sec-WebSocket-Protocol: chat
            var response = sb.ToString();

#if DEBUG
            _log.DebugFormat("[{0}]{1}{2}", session.RemoteEndPoint, Environment.NewLine, response);
#endif
            return(Encoding.UTF8.GetBytes(response));
        }
Beispiel #11
0
 public async Task SendBinaryToAsync(AsyncWebSocketSession session, byte[] data, int offset, int count)
 {
     AsyncWebSocketSession sessionFound;
     if (_sessions.TryGetValue(session.SessionKey, out sessionFound))
     {
         await sessionFound.SendBinaryAsync(data, offset, count);
     }
     else
     {
         _log.WarnFormat("Cannot find session [{0}].", session);
     }
 }
Beispiel #12
0
 public async Task SendBinaryToAsync(AsyncWebSocketSession session, byte[] data)
 {
     await SendBinaryToAsync(session, data, 0, data.Length);
 }
Beispiel #13
0
 public async Task SendTextToAsync(AsyncWebSocketSession session, string text)
 {
     AsyncWebSocketSession sessionFound;
     if (_sessions.TryGetValue(session.SessionKey, out sessionFound))
     {
         await sessionFound.SendTextAsync(text);
     }
     else
     {
         _log.WarnFormat("Cannot find session [{0}].", session);
     }
 }
 public virtual async Task OnSessionStarted(AsyncWebSocketSession session)
 {
     _sessions.TryAdd(session.SessionKey, session);
     await Task.CompletedTask;
 }
 public virtual async Task OnSessionTextReceived(AsyncWebSocketSession session, string text)
 {
     await Task.CompletedTask;
 }
 public virtual async Task OnSessionTextReceived(AsyncWebSocketSession session, string text)
 {
     await Task.CompletedTask;
 }
 public virtual async Task OnSessionBinaryReceived(AsyncWebSocketSession session, byte[] data, int offset, int count)
 {
     await Task.CompletedTask;
 }
 public virtual async Task OnSessionFragmentationStreamClosed(AsyncWebSocketSession session, byte[] data, int offset, int count)
 {
     await Task.CompletedTask;
 }
Beispiel #19
0
 public async Task SendBinaryToAsync(AsyncWebSocketSession session, byte[] data)
 {
     await SendBinaryToAsync(session, data, 0, data.Length);
 }