protected virtual async Task CreateSSLConnectionAsync() { var client = new TcpClient(_parameters.Uri, _parameters.Port) { ReceiveTimeout = 60000 }; var clientCertificates = new X509Certificate2Collection(); if (_parameters.ClientCertificates != null) { for (int i = 0; i < _parameters.ClientCertificates.Length; i++) { clientCertificates.Add(_parameters.ClientCertificates[i]); } } var sslStream = new SslStream(client.GetStream()); await sslStream.AuthenticateAsClientAsync(_parameters.Uri, clientCertificates, _parameters.EnabledSslProtocols, false);; if (sslStream.IsAuthenticated && sslStream.IsEncrypted) { _connection = new ConnectionWS { Client = client, Stream = sslStream }; } else { throw new Exception("Could not create connection - SSL cert has validation problem."); } }
protected virtual Task CreateConnectionAsync() { var client = new TcpClient(_parameters.Uri, _parameters.Port) { ReceiveTimeout = 60000 }; var stream = client.GetStream(); _connection = new ConnectionWS { Client = client, Stream = stream }; return(Task.CompletedTask); }
public virtual async Task <bool> DisconnectAsync() { try { if (_connection != null) { if (_connection.Websocket != null && _connection.Websocket.State == WebSocketState.Open) { await _connection.Websocket.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None); } if (_connection.Client.Connected) { _connection.Client.Close(); _connection.Client.Dispose(); } await FireEventAsync(this, new WSConnectionClientEventArgs { ConnectionEventType = ConnectionEventType.Disconnect, Connection = _connection }); _connection = null; return(true); } } catch (Exception ex) { await FireEventAsync(this, new WSErrorClientEventArgs { Exception = ex, Message = "Error in StopAsync()", Connection = _connection }); } return(false); }
protected async Task <(string, string[])> ParseAndValidateConnectResponseAsync(IConnectionWS connection, string expectedSecWebSocketAccept) { while (connection.Client.Connected) { if (connection.Client.Available > 0) { break; } } if (!connection.Client.Connected) { await DisconnectAsync(); return(null, null); } var readBuffer = new byte[connection.Client.Available]; await connection.Stream.ReadAsync(readBuffer, 0, readBuffer.Length); var message = Encoding.UTF8.GetString(readBuffer); var messagesSplit = message.Split("\r\n"); if (messagesSplit.Length <= 0) { throw new WebSocketException("Not valid handshake"); } // Depending on the underlying sockets implementation and timing, connecting to a server that then // immediately closes the connection may either result in an exception getting thrown from the connect // earlier, or it may result in getting to here but reading 0 bytes. If we read 0 bytes and thus have // an empty status line, treat it as a connect failure. if (string.IsNullOrEmpty(messagesSplit[0])) { throw new WebSocketException("Connection failure."); } const string ExpectedStatusStart = "HTTP/1.1 "; const string ExpectedStatusStatWithCode = "HTTP/1.1 101"; // 101 == SwitchingProtocols // If the status line doesn't begin with "HTTP/1.1" or isn't long enough to contain a status code, fail. if (!messagesSplit[0].StartsWith(ExpectedStatusStart, StringComparison.Ordinal) || messagesSplit[0].Length < ExpectedStatusStatWithCode.Length) { throw new WebSocketException(WebSocketError.HeaderError, $"Connection failure (status line = '{messagesSplit[0]}')."); } // If the status line doesn't contain a status code 101, or if it's long enough to have a status description // but doesn't contain whitespace after the 101, fail. if (!messagesSplit[0].StartsWith(ExpectedStatusStatWithCode, StringComparison.Ordinal) || (messagesSplit[0].Length > ExpectedStatusStatWithCode.Length && !char.IsWhiteSpace(messagesSplit[0][ExpectedStatusStatWithCode.Length]))) { throw new WebSocketException(WebSocketError.HeaderError, $"Connection failure (status line = '{messagesSplit[0]}')."); } // Read each response header. Be liberal in parsing the response header, treating // everything to the left of the colon as the key and everything to the right as the value, trimming both. // For each header, validate that we got the expected value. bool foundUpgrade = false, foundConnection = false, foundSecWebSocketAccept = false; string subprotocol = null; var remainingMessages = new List <string>();; for (int i = 1; i < messagesSplit.Length; i++) { if (string.IsNullOrEmpty(messagesSplit[i]) && messagesSplit.Length >= i + 1) { for (int j = i + 1; j < messagesSplit.Length; j++) { if (!string.IsNullOrWhiteSpace(RemoveBytesFromString(messagesSplit[j]))) { remainingMessages.Add(RemoveBytesFromString(messagesSplit[j].Trim())); } } break; } var colonIndex = messagesSplit[i].IndexOf(':'); if (colonIndex == -1) { throw new WebSocketException(WebSocketError.HeaderError); } var headerName = SubstringTrim(messagesSplit[i], 0, colonIndex); var headerValue = SubstringTrim(messagesSplit[i], colonIndex + 1); // The Connection, Upgrade, and SecWebSocketAccept headers are required and with specific values. ValidateAndTrackHeader(HttpKnownHeaderNames.Connection, "Upgrade", headerName, headerValue, ref foundConnection); ValidateAndTrackHeader(HttpKnownHeaderNames.Upgrade, "websocket", headerName, headerValue, ref foundUpgrade); ValidateAndTrackHeader(HttpKnownHeaderNames.SecWebSocketAccept, expectedSecWebSocketAccept, headerName, headerValue, ref foundSecWebSocketAccept); // The SecWebSocketProtocol header is optional. We should only get it with a non-empty value if we requested subprotocols, // and then it must only be one of the ones we requested. If we got a subprotocol other than one we requested (or if we // already got one in a previous header), fail. Otherwise, track which one we got. if (string.Equals(HttpKnownHeaderNames.SecWebSocketProtocol, headerName, StringComparison.OrdinalIgnoreCase) && !string.IsNullOrWhiteSpace(headerValue)) { if (_parameters.RequestedSubProtocols == null) { throw new WebSocketException("Requested sub protocols cannot be empty if server returns sub protocol"); } var newSubprotocol = _parameters.RequestedSubProtocols.ToList().Find(requested => string.Equals(requested, headerValue, StringComparison.OrdinalIgnoreCase)); if (newSubprotocol == null || subprotocol != null) { throw new WebSocketException( string.Format("Unsupported sub-protocol '{0}' (expected one of [{1}]).", subprotocol, string.Join(", ", _parameters.RequestedSubProtocols) ) ); } subprotocol = newSubprotocol; } } if (!foundUpgrade || !foundConnection || !foundSecWebSocketAccept) { throw new WebSocketException("Connection failure."); } return(subprotocol, remainingMessages.ToArray()); }