public BinaryFrame(byte[] data, int offset, int count, bool isMasked = true) { BufferValidator.ValidateBuffer(data, offset, count, "data"); this.Data = data; this.Offset = offset; this.Count = count; this.IsMasked = isMasked; }
public BinaryFragmentationFrame(OpCode opCode, byte[] data, int offset, int count, bool isFin = false, bool isMasked = true) { BufferValidator.ValidateBuffer(data, offset, count, "data"); _opCode = opCode; this.Data = data; this.Offset = offset; this.Count = count; this.IsFin = isFin; this.IsMasked = isMasked; }
internal static bool VerifyOpenningHandshakeResponse(AsyncWebSocketClient client, byte[] buffer, int offset, int count, string secWebSocketKey) { BufferValidator.ValidateBuffer(buffer, offset, count, "buffer"); if (string.IsNullOrEmpty(secWebSocketKey)) { throw new ArgumentNullException("secWebSocketKey"); } var response = Encoding.UTF8.GetString(buffer, offset, count); #if DEBUG _log.DebugFormat("[{0}]{1}{2}", client.RemoteEndPoint, Environment.NewLine, response); #endif try { // 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 == null) { throw new WebSocketHandshakeException(string.Format( "Handshake with remote [{0}] failed due to invalid headers.", client.RemoteEndPoint)); } // If the status code received from the server is not 101, the // client handles the response per HTTP [RFC2616] procedures. In // particular, the client might perform authentication if it // receives a 401 status code; the server might redirect the client // using a 3xx status code (but clients are not required to follow them), etc. if (!headers.ContainsKey(Consts.HttpStatusCodeName)) { throw new WebSocketHandshakeException(string.Format( "Handshake with remote [{0}] failed due to lack of status code.", client.RemoteEndPoint)); } if (!headers.ContainsKey(Consts.HttpStatusCodeDescription)) { throw new WebSocketHandshakeException(string.Format( "Handshake with remote [{0}] failed due to lack of status description.", client.RemoteEndPoint)); } if (headers[Consts.HttpStatusCodeName] == ((int)HttpStatusCode.BadRequest).ToString()) { throw new WebSocketHandshakeException(string.Format( "Handshake with remote [{0}] failed due to bad request [{1}].", client.RemoteEndPoint, headers[Consts.HttpStatusCodeName])); } if (headers[Consts.HttpStatusCodeName] != ((int)HttpStatusCode.SwitchingProtocols).ToString()) { throw new WebSocketHandshakeException(string.Format( "Handshake with remote [{0}] failed due to expected 101 Switching Protocols but received [{1}].", client.RemoteEndPoint, headers[Consts.HttpStatusCodeName])); } // If the response lacks an |Upgrade| header field or the |Upgrade| // header field contains a value that is not an ASCII case- // insensitive match for the value "websocket", the client MUST // _Fail the WebSocket Connection_. if (!headers.ContainsKey(HttpKnownHeaderNames.Connection)) { throw new WebSocketHandshakeException(string.Format( "Handshake with remote [{0}] failed due to lack of connection header item.", client.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}].", client.RemoteEndPoint, headers[HttpKnownHeaderNames.Connection])); } // If the response lacks a |Connection| header field or the // |Connection| header field doesn't contain a token that is an // ASCII case-insensitive match for the value "Upgrade", the client // MUST _Fail the WebSocket Connection_. if (!headers.ContainsKey(HttpKnownHeaderNames.Upgrade)) { throw new WebSocketHandshakeException(string.Format( "Handshake with remote [{0}] failed due to lack of upgrade header item.", client.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}].", client.RemoteEndPoint, headers[HttpKnownHeaderNames.Upgrade])); } // If the response lacks a |Sec-WebSocket-Accept| header field or // the |Sec-WebSocket-Accept| contains a value other than the // base64-encoded SHA-1 of the concatenation of the |Sec-WebSocket- // Key| (as a string, not base64-decoded) with the string "258EAFA5- // E914-47DA-95CA-C5AB0DC85B11" but ignoring any leading and // trailing whitespace, the client MUST _Fail the WebSocket Connection_. if (!headers.ContainsKey(HttpKnownHeaderNames.SecWebSocketAccept)) { throw new WebSocketHandshakeException(string.Format( "Handshake with remote [{0}] failed due to lack of Sec-WebSocket-Accept header item.", client.RemoteEndPoint)); } string challenge = GetSecWebSocketAcceptString(secWebSocketKey); if (!headers[HttpKnownHeaderNames.SecWebSocketAccept].Equals(challenge, StringComparison.OrdinalIgnoreCase)) { throw new WebSocketHandshakeException(string.Format( "Handshake with remote [{0}] failed due to invalid Sec-WebSocket-Accept header item value [{1}].", client.RemoteEndPoint, headers[HttpKnownHeaderNames.SecWebSocketAccept])); } // If the response includes a |Sec-WebSocket-Extensions| header // field and this header field indicates the use of an extension // that was not present in the client's handshake (the server has // indicated an extension not requested by the client), the client // MUST _Fail the WebSocket Connection_. if (extensions != null) { 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.", client.RemoteEndPoint)); } } client.AgreeExtensions(extensions); } // If the response includes a |Sec-WebSocket-Protocol| header field // and this header field indicates the use of a subprotocol that was // not present in the client's handshake (the server has indicated a // subprotocol not requested by the client), the client MUST _Fail // the WebSocket Connection_. if (protocols != null) { if (!protocols.Any()) { throw new WebSocketHandshakeException(string.Format( "Handshake with remote [{0}] failed due to empty sub-protocol.", client.RemoteEndPoint)); } if (protocols.Count > 1) { throw new WebSocketHandshakeException(string.Format( "Handshake with remote [{0}] failed due to suggest to use multiple sub-protocols.", client.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.", client.RemoteEndPoint)); } } var suggestedProtocols = protocols.First().Split(',') .Select(p => p.TrimStart().TrimEnd()).Where(p => !string.IsNullOrWhiteSpace(p)); if (!suggestedProtocols.Any()) { throw new WebSocketHandshakeException(string.Format( "Handshake with remote [{0}] failed due to invalid sub-protocol.", client.RemoteEndPoint)); } if (suggestedProtocols.Count() > 1) { throw new WebSocketHandshakeException(string.Format( "Handshake with remote [{0}] failed due to suggest to use multiple sub-protocols.", client.RemoteEndPoint)); } // The value chosen MUST be derived // from the client's handshake, specifically by selecting one of // the values from the |Sec-WebSocket-Protocol| field that the // server is willing to use for this connection (if any). client.UseSubProtocol(suggestedProtocols.First()); } } catch (Exception ex) { _log.ErrorFormat("{0}{1}{2}", client, Environment.NewLine, response); _log.Error(ex.Message, ex); throw; } return(true); }
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 bool VerifyOpenningHandshakeResponse(WebSocketClient client, byte[] buffer, int offset, int count, string secWebSocketKey) { BufferValidator.ValidateBuffer(buffer, offset, count, "buffer"); if (string.IsNullOrEmpty(secWebSocketKey)) { throw new ArgumentNullException("secWebSocketKey"); } var response = Encoding.UTF8.GetString(buffer, offset, count); try { // 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 == null) { throw new WebSocketHandshakeException(string.Format( "Handshake with remote [{0}] failed due to invalid headers.", client.RemoteEndPoint)); } // If the status code received from the server is not 101, the // client handles the response per HTTP [RFC2616] procedures. In // particular, the client might perform authentication if it // receives a 401 status code; the server might redirect the client // using a 3xx status code (but clients are not required to follow them), etc. if (!headers.ContainsKey(Consts.HttpStatusCodeName)) { throw new WebSocketHandshakeException(string.Format( "Handshake with remote [{0}] failed due to lack of status code.", client.RemoteEndPoint)); } if (!headers.ContainsKey(Consts.HttpStatusCodeDescription)) { throw new WebSocketHandshakeException(string.Format( "Handshake with remote [{0}] failed due to lack of status description.", client.RemoteEndPoint)); } if (headers[Consts.HttpStatusCodeName] == ((int)HttpStatusCode.BadRequest).ToString()) { throw new WebSocketHandshakeException(string.Format( "Handshake with remote [{0}] failed due to bad request [{1}].", client.RemoteEndPoint, headers[Consts.HttpStatusCodeName])); } if (headers[Consts.HttpStatusCodeName] != ((int)HttpStatusCode.SwitchingProtocols).ToString()) { throw new WebSocketHandshakeException(string.Format( "Handshake with remote [{0}] failed due to expected 101 Switching Protocols but received [{1}].", client.RemoteEndPoint, headers[Consts.HttpStatusCodeName])); } // If the response lacks an |Upgrade| header field or the |Upgrade| // header field contains a value that is not an ASCII case- // insensitive match for the value "websocket", the client MUST // _Fail the WebSocket Connection_. if (!headers.ContainsKey(HttpKnownHeaderNames.Connection)) { throw new WebSocketHandshakeException(string.Format( "Handshake with remote [{0}] failed due to lack of connection header item.", client.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}].", client.RemoteEndPoint, headers[HttpKnownHeaderNames.Connection])); } // If the response lacks a |Connection| header field or the // |Connection| header field doesn't contain a token that is an // ASCII case-insensitive match for the value "Upgrade", the client // MUST _Fail the WebSocket Connection_. if (!headers.ContainsKey(HttpKnownHeaderNames.Upgrade)) { throw new WebSocketHandshakeException(string.Format( "Handshake with remote [{0}] failed due to lack of upgrade header item.", client.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}].", client.RemoteEndPoint, headers[HttpKnownHeaderNames.Upgrade])); } // If the response lacks a |Sec-WebSocket-Accept| header field or // the |Sec-WebSocket-Accept| contains a value other than the // base64-encoded SHA-1 of the concatenation of the |Sec-WebSocket- // Key| (as a string, not base64-decoded) with the string "258EAFA5- // E914-47DA-95CA-C5AB0DC85B11" but ignoring any leading and // trailing whitespace, the client MUST _Fail the WebSocket Connection_. if (!headers.ContainsKey(HttpKnownHeaderNames.SecWebSocketAccept)) { throw new WebSocketHandshakeException(string.Format( "Handshake with remote [{0}] failed due to lack of Sec-WebSocket-Accept header item.", client.RemoteEndPoint)); } string challenge = GetSecWebSocketAcceptString(secWebSocketKey); if (!headers[HttpKnownHeaderNames.SecWebSocketAccept].Equals(challenge, StringComparison.OrdinalIgnoreCase)) { throw new WebSocketHandshakeException(string.Format( "Handshake with remote [{0}] failed due to invalid Sec-WebSocket-Accept header item value [{1}].", client.RemoteEndPoint, headers[HttpKnownHeaderNames.SecWebSocketAccept])); } // If the response includes a |Sec-WebSocket-Extensions| header // field and this header field indicates the use of an extension // that was not present in the client's handshake (the server has // indicated an extension not requested by the client), the client // MUST _Fail the WebSocket Connection_. // If the response includes a |Sec-WebSocket-Protocol| header field // and this header field indicates the use of a subprotocol that was // not present in the client's handshake (the server has indicated a // subprotocol not requested by the client), the client MUST _Fail // the WebSocket Connection_. } catch (Exception) { throw; } return(true); }