// internally, this is very similar to RawResult, except it is designed to be usable // outside of the IO-processing pipeline: the buffers are standalone, etc internal static RedisResult TryCreate(PhysicalConnection connection, RawResult result) { try { switch (result.Type) { case ResultType.Integer: case ResultType.SimpleString: case ResultType.BulkString: return new SingleRedisResult(result.AsRedisValue()); case ResultType.MultiBulk: var items = result.GetItems(); var arr = new RedisResult[items.Length]; for (int i = 0; i < arr.Length; i++) { var next = TryCreate(connection, items[i]); if (next == null) return null; // means we didn't understand arr[i] = next; } return new ArrayRedisResult(arr); case ResultType.Error: return new ErrorRedisResult(result.GetString()); default: return null; } } catch(Exception ex) { connection?.OnInternalError(ex); return null; // will be logged as a protocol fail by the processor } }
internal async Task OnConnectedAsync(PhysicalConnection connection, TextWriter log) { Trace("OnConnected"); if (physical == connection && !isDisposed && ChangeState(State.Connecting, State.ConnectedEstablishing)) { await ServerEndPoint.OnEstablishingAsync(connection, log).ForAwait(); } else { try { connection.Dispose(); } catch { } } }
private void SelectDatabase(PhysicalConnection connection, Message message) { int db = message.Db; if (db >= 0) { var sel = connection.GetSelectDatabaseCommand(db, message); if (sel != null) { connection.Enqueue(sel); sel.WriteImpl(connection); sel.SetRequestSent(); IncrementOpCount(); } } }
internal void WriteTo(PhysicalConnection physical) { try { WriteImpl(physical); } catch (RedisCommandException) { // these have specific meaning; don't wrap throw; } catch (Exception ex) { physical?.OnInternalError(ex); Fail(ConnectionFailureType.InternalFailure, ex); } }
// note that top-level error messages still get handled by SetResult, but nested errors // (is that a thing?) will be wrapped in the RedisResult protected override bool SetResultCore(PhysicalConnection connection, Message message, RawResult result) { switch (result.Type) { case ResultType.BulkString: var hash = result.GetBlob(); var sl = message as RedisDatabase.ScriptLoadMessage; if (sl != null) { connection.Bridge.ServerEndPoint.AddScript(sl.Script, hash); } SetResult(message, hash); return(true); } return(false); }
internal void WriteDirectOrQueueFireAndForget <T>(PhysicalConnection connection, Message message, ResultProcessor <T> processor) { if (message != null) { message.SetSource(processor, null); if (connection == null) { multiplexer.Trace("Enqueue: " + message); GetBridge(message.Command).TryEnqueue(message, isSlave); } else { multiplexer.Trace("Writing direct: " + message); connection.Bridge.WriteMessageDirect(connection, message); } } }
internal void OnConnected(PhysicalConnection connection, TextWriter log) { Trace("OnConnected"); if (physical == connection && !isDisposed && ChangeState(State.Connecting, State.ConnectedEstablishing)) { ServerEndPoint.OnEstablishing(connection, log); } else { try { connection.Dispose(); } catch { } } }
protected override bool SetResultCore(PhysicalConnection connection, Message message, RawResult result) { switch (result.Type) { case ResultType.Integer: case ResultType.SimpleString: case ResultType.BulkString: long i64; if (result.TryGetInt64(out i64)) { SetResult(message, i64); return(true); } break; } return(false); }
private void WritePendingBacklogSync(PhysicalConnection connection) { if (connection != null) { Message next; do { next = DequeueNextPendingBacklog(); #pragma warning disable CS0618 if (next != null) { WriteMessageTakingWriteLockSync(connection, next); } #pragma warning restore CS0618 } while (next != null); } }
protected override bool SetResultCore(PhysicalConnection connection, Message message, RawResult result) { switch (result.Type) { case ResultType.Integer: case ResultType.SimpleString: case ResultType.BulkString: string nodes = result.GetString(); try { ClusterNodesProcessor.Parse(connection, nodes); } catch { /* tralalalala */ } SetResult(message, nodes); return(true); } return(false); }
// internally, this is very similar to RawResult, except it is designed to be usable // outside of the IO-processing pipeline: the buffers are standalone, etc internal static RedisResult TryCreate(PhysicalConnection connection, RawResult result) { try { switch (result.Type) { case ResultType.Integer: case ResultType.SimpleString: case ResultType.BulkString: return(new SingleRedisResult(result.AsRedisValue(), result.Type)); case ResultType.MultiBulk: if (result.IsNull) { return(NullArray); } var items = result.GetItems(); if (items.Length == 0) { return(EmptyArray); } var arr = new RedisResult[items.Length]; for (int i = 0; i < arr.Length; i++) { var next = TryCreate(connection, items[i]); if (next == null) { return(null); // means we didn't understand } arr[i] = next; } return(new ArrayRedisResult(arr)); case ResultType.Error: return(new ErrorRedisResult(result.GetString())); default: return(null); } } catch (Exception ex) { connection?.OnInternalError(ex); return(null); // will be logged as a protocol fail by the processor } }
protected override bool SetResultCore(PhysicalConnection connection, Message message, RawResult result) { switch (result.Type) { case ResultType.MultiBulk: var arr = result.GetItems(); long i64; if (arr.Length == 2 && arr[1].Type == ResultType.MultiBulk && arr[0].TryGetInt64(out i64)) { var keysResult = new ScanResult(i64, arr[1].GetItemsAsKeys()); SetResult(message, keysResult); return(true); } break; } return(false); }
private static Action <Task <int> > EndReadFactory(PhysicalConnection physical) { return(result => { // can't capture AsyncState on SocketRead, so we'll do it once per physical instead try { physical.Multiplexer.Trace("Completed asynchronously: processing in callback", physical.physicalName); if (physical.EndReading(result)) { physical.BeginReading(); } } catch (Exception ex) { physical.RecordConnectionFailed(ConnectionFailureType.InternalFailure, ex); } }); }
public override bool SetResult(PhysicalConnection connection, Message message, RawResult result) { if (result.IsError) { var tran = message as TransactionMessage; if (tran != null) { string error = result.GetString(); var bridge = connection.Bridge; foreach (var op in tran.InnerOperations) { ServerFail(op.Wrapped, error); bridge.CompleteSyncOrAsync(op.Wrapped); } } } return(base.SetResult(connection, message, result)); }
private WriteResult WriteMessageInsideLock(PhysicalConnection physical, Message message) { WriteResult result; var existingMessage = Interlocked.CompareExchange(ref _activeMesssage, message, null); if (existingMessage != null) { Multiplexer?.OnInfoMessage($"reentrant call to WriteMessageTakingWriteLock for {message.CommandAndKey}, {existingMessage.CommandAndKey} is still active"); return(WriteResult.NoConnectionAvailable); } physical.SetWriting(); var messageIsSent = false; if (message is IMultiMessage) { SelectDatabaseInsideWriteLock(physical, message); // need to switch database *before* the transaction foreach (var subCommand in ((IMultiMessage)message).GetMessages(physical)) { result = WriteMessageToServerInsideWriteLock(physical, subCommand); if (result != WriteResult.Success) { // we screwed up; abort; note that WriteMessageToServer already // killed the underlying connection Trace("Unable to write to server"); message.Fail(ConnectionFailureType.ProtocolFailure, null, "failure before write: " + result.ToString()); this.CompleteSyncOrAsync(message); return(result); } //The parent message (next) may be returned from GetMessages //and should not be marked as sent again below messageIsSent = messageIsSent || subCommand == message; } if (!messageIsSent) { message.SetRequestSent(); // well, it was attempted, at least... } return(WriteResult.Success); } else { return(WriteMessageToServerInsideWriteLock(physical, message)); } }
internal WriteResult WriteMessageTakingWriteLockSync(PhysicalConnection physical, Message message) { Trace("Writing: " + message); message.SetEnqueued(physical); // this also records the read/write stats at this point LockToken token = default; try { token = _singleWriterMutex.TryWait(WaitOptions.NoDelay); if (!token.Success) { // we can't get it *instantaneously*; is there // perhaps a backlog and active backlog processor? if (PushToBacklog(message, onlyIfExists: true)) { return(WriteResult.Success); // queued counts as success } // no backlog... try to wait with the timeout; // if we *still* can't get it: that counts as // an actual timeout token = _singleWriterMutex.TryWait(); if (!token.Success) { return(TimedOutBeforeWrite(message)); } } var result = WriteMessageInsideLock(physical, message); if (result == WriteResult.Success) { #pragma warning disable CS0618 result = physical.FlushSync(false, TimeoutMilliseconds); #pragma warning restore CS0618 } UnmarkActiveMessage(message); physical.SetIdle(); return(result); } catch (Exception ex) { return(HandleWriteException(message, ex)); } finally { token.Dispose(); } }
internal void OnFullyEstablished(PhysicalConnection connection) { Trace("OnFullyEstablished"); if (physical == connection && !isDisposed && ChangeState(State.ConnectedEstablishing, State.ConnectedEstablished)) { reportNextFailure = reconfigureNextFailure = true; Interlocked.Exchange(ref failConnectCount, 0); serverEndPoint.OnFullyEstablished(connection); multiplexer.RequestWrite(this, true); if (connectionType == ConnectionType.Interactive) { serverEndPoint.CheckInfoReplication(); } } else { try { connection.Dispose(); } catch { } } }
internal void OnFullyEstablished(PhysicalConnection connection) { try { if (connection == null) { return; } var bridge = connection.Bridge; if (bridge == subscription) { multiplexer.ResendSubscriptions(this); } multiplexer.OnConnectionRestored(endpoint, bridge.ConnectionType); } catch (Exception ex) { connection.RecordConnectionFailed(ConnectionFailureType.InternalFailure, ex); } }
protected override bool SetResultCore(PhysicalConnection connection, Message message, RawResult result) { long unixTime, micros; switch (result.Type) { case ResultType.Integer: if (result.TryGetInt64(out unixTime)) { var time = RedisBase.UnixEpoch.AddSeconds(unixTime); SetResult(message, time); return(true); } break; case ResultType.MultiBulk: var arr = result.GetItems(); switch (arr.Length) { case 1: if (arr[0].TryGetInt64(out unixTime)) { var time = RedisBase.UnixEpoch.AddSeconds(unixTime); SetResult(message, time); return(true); } break; case 2: if (arr[0].TryGetInt64(out unixTime) && arr[1].TryGetInt64(out micros)) { var time = RedisBase.UnixEpoch.AddSeconds(unixTime).AddTicks(micros * 10); // datetime ticks are 100ns SetResult(message, time); return(true); } break; } break; } return(false); }
protected override bool SetResultCore(PhysicalConnection connection, Message message, RawResult result) { bool happy; switch (message.Command) { case RedisCommand.ECHO: happy = result.Type == ResultType.BulkString && (!establishConnection || result.IsEqual(connection.Multiplexer.UniqueId)); break; case RedisCommand.PING: happy = result.Type == ResultType.SimpleString && result.IsEqual(RedisLiterals.BytesPONG); break; case RedisCommand.TIME: happy = result.Type == ResultType.MultiBulk && result.GetItems().Length == 2; break; case RedisCommand.EXISTS: happy = result.Type == ResultType.Integer; break; default: happy = true; break; } if (happy) { if (establishConnection) { connection.Bridge.OnFullyEstablished(connection); } SetResult(message, happy); return(true); } else { connection.RecordConnectionFailed(ConnectionFailureType.ProtocolFailure); return(false); } }
protected override bool SetResultCore(PhysicalConnection connection, Message message, RawResult result) { switch (result.Type) { case ResultType.SimpleString: case ResultType.BulkString: string s = result.GetString(); RedisType value; if (string.Equals(s, "zset", StringComparison.OrdinalIgnoreCase)) { value = Redis.RedisType.SortedSet; } else if (!Enum.TryParse <RedisType>(s, true, out value)) { value = global::StackExchange.Redis.RedisType.Unknown; } SetResult(message, value); return(true); } return(false); }
protected override bool SetResultCore(PhysicalConnection connection, Message message, RawResult result) { switch (result.Type) { case ResultType.MultiBulk: var parts = result.GetItems(); CommandTrace[] arr = new CommandTrace[parts.Length]; for (int i = 0; i < parts.Length; i++) { var subParts = parts[i].GetItems(); if (!subParts[0].TryGetInt64(out long uniqueid) || !subParts[1].TryGetInt64(out long time) || !subParts[2].TryGetInt64(out long duration)) { return(false); } arr[i] = new CommandTrace(uniqueid, time, duration, subParts[3].GetItemsAsValues()); } SetResult(message, arr); return(true); } return(false); }
public override bool SetResult(PhysicalConnection connection, Message message, RawResult result) { var final = base.SetResult(connection, message, result); if (result.IsError) { if (result.IsEqual(authFail)) { connection.RecordConnectionFailed(ConnectionFailureType.AuthenticationFailure); } else if (result.AssertStarts(loading)) { connection.RecordConnectionFailed(ConnectionFailureType.Loading); } else { connection.RecordConnectionFailed(ConnectionFailureType.ProtocolFailure); } } return(final); }
internal Task OnEstablishingAsync(PhysicalConnection connection, TextWriter log) { try { if (connection == null) { return(Task.CompletedTask); } var handshake = HandshakeAsync(connection, log); if (handshake.Status != TaskStatus.RanToCompletion) { return(OnEstablishingAsyncAwaited(connection, handshake)); } } catch (Exception ex) { connection.RecordConnectionFailed(ConnectionFailureType.InternalFailure, ex); } return(Task.CompletedTask); }
/// <summary> /// This writes a message to the output stream /// </summary> /// <param name="physical">The phsyical connection to write to.</param> /// <param name="message">The message to be written.</param> internal ValueTask <WriteResult> WriteMessageTakingWriteLockAsync(PhysicalConnection physical, Message message) { Trace("Writing: " + message); message.SetEnqueued(physical); // this also records the read/write stats at this point bool haveLock = false; try { // try to acquire it synchronously haveLock = _SingleWriterLock.Wait(0); if (!haveLock) { return(new ValueTask <WriteResult>(WriteMessageTakingDelayedWriteLockAsync(physical, message))); } var result = WriteMessageInsideLock(physical, message); if (result == WriteResult.Success) { var flush = physical.FlushAsync(false); if (!flush.IsCompletedSuccessfully) { haveLock = false; // so we don't release prematurely return(new ValueTask <WriteResult>(CompleteWriteAndReleaseLockAsync(flush, message))); } result = flush.Result; // we know it was completed, this is fine } physical.SetIdle(); return(new ValueTask <WriteResult>(result)); } catch (Exception ex) { return(new ValueTask <WriteResult>(HandleWriteException(message, ex))); } finally { if (haveLock) { ReleaseSingleWriterLock(message); } } }
protected override bool SetResultCore(PhysicalConnection connection, Message message, RawResult result) { switch (result.Type) { case ResultType.Integer: case ResultType.SimpleString: case ResultType.BulkString: if (result.IsNull) { SetResult(message, null); return(true); } double val; if (result.TryGetDouble(out val)) { SetResult(message, val); return(true); } break; } return(false); }
internal void OnFullyEstablished(PhysicalConnection connection) { Trace("OnFullyEstablished"); connection?.SetIdle(); if (physical == connection && !isDisposed && ChangeState(State.ConnectedEstablishing, State.ConnectedEstablished)) { reportNextFailure = reconfigureNextFailure = true; LastException = null; Interlocked.Exchange(ref failConnectCount, 0); ServerEndPoint.OnFullyEstablished(connection); WritePendingBacklog(connection); if (ConnectionType == ConnectionType.Interactive) { ServerEndPoint.CheckInfoReplication(); } } else { try { connection.Dispose(); } catch { } } }
internal void OnFullyEstablished(PhysicalConnection connection) { try { if (connection == null) { return; } var bridge = connection.BridgeCouldBeNull; if (bridge != null) { if (bridge == subscription) { Multiplexer.ResendSubscriptions(this); } Multiplexer.OnConnectionRestored(EndPoint, bridge.ConnectionType, connection?.ToString()); } } catch (Exception ex) { connection.RecordConnectionFailed(ConnectionFailureType.InternalFailure, ex); } }
internal WriteResult WriteMessageTakingWriteLockSync(PhysicalConnection physical, Message message) { Trace("Writing: " + message); message.SetEnqueued(physical); // this also records the read/write stats at this point bool haveLock = false; try { haveLock = _SingleWriterLock.Wait(TimeoutMilliseconds); if (!haveLock) { message.Cancel(); Multiplexer?.OnMessageFaulted(message, null); this.CompleteSyncOrAsync(message); return(WriteResult.TimeoutBeforeWrite); } var result = WriteMessageInsideLock(physical, message); if (result == WriteResult.Success) { #pragma warning disable CS0618 result = physical.FlushSync(false, TimeoutMilliseconds); #pragma warning restore CS0618 } physical.SetIdle(); return(result); } catch (Exception ex) { return(HandleWriteException(message, ex)); } finally { if (haveLock) { ReleaseSingleWriterLock(message); } } }
internal void OnDisconnected(ConnectionFailureType failureType, PhysicalConnection connection, out bool isCurrent, out State oldState) { Trace("OnDisconnected"); // if the next thing in the pipe is a PING, we can tell it that we failed (this really helps spot doomed connects) // note that for simplicity we haven't removed it from the queue; that's OK int count; var ping = queue.PeekPing(out count); if (ping != null) { Trace("Marking PING as failed (queue length: " + count + ")"); ping.Fail(failureType, null); CompleteSyncOrAsync(ping); } oldState = default(State); // only defined when isCurrent = true if (isCurrent = (physical == connection)) { Trace("Bridge noting disconnect from active connection" + (isDisposed ? " (disposed)" : "")); oldState = ChangeState(State.Disconnected); physical = null; if (!isDisposed && Interlocked.Increment(ref failConnectCount) == 1) { GetConnection(null); // try to connect immediately } } else if (physical == null) { Trace("Bridge noting disconnect (already terminated)"); } else { Trace("Bridge noting disconnect, but from different connection"); } }
protected override bool SetResultCore(PhysicalConnection connection, Message message, RawResult result) { if (result.Type == ResultType.BulkString) { string category = Normalize(null), line; var list = new List <Tuple <string, KeyValuePair <string, string> > >(); using (var reader = new StringReader(result.GetString())) { while ((line = reader.ReadLine()) != null) { if (string.IsNullOrWhiteSpace(line)) { continue; } if (line.StartsWith("# ")) { category = Normalize(line.Substring(2)); continue; } int idx = line.IndexOf(':'); if (idx < 0) { continue; } var pair = new KeyValuePair <string, string>( line.Substring(0, idx).Trim(), line.Substring(idx + 1).Trim()); list.Add(Tuple.Create(category, pair)); } } var final = list.GroupBy(x => x.Item1, x => x.Item2).ToArray(); SetResult(message, final); return(true); } return(false); }
private bool WriteMessageToServer(PhysicalConnection connection, Message message) { if (message == null) return true; try { var cmd = message.Command; bool isMasterOnly = message.IsMasterOnly(); if (isMasterOnly && serverEndPoint.IsSlave && (serverEndPoint.SlaveReadOnly || !serverEndPoint.AllowSlaveWrites)) { throw ExceptionFactory.MasterOnly(multiplexer.IncludeDetailInExceptions, message.Command, message, ServerEndPoint); } SelectDatabase(connection, message); if (!connection.TransactionActive) { var readmode = connection.GetReadModeCommand(isMasterOnly); if (readmode != null) { connection.Enqueue(readmode); readmode.WriteTo(connection); readmode.SetRequestSent(); IncrementOpCount(); } if (message.IsAsking) { var asking = ReusableAskingCommand; connection.Enqueue(asking); asking.WriteImpl(connection); asking.SetRequestSent(); IncrementOpCount(); } } switch (cmd) { case RedisCommand.WATCH: case RedisCommand.MULTI: connection.TransactionActive = true; break; case RedisCommand.UNWATCH: case RedisCommand.EXEC: case RedisCommand.DISCARD: connection.TransactionActive = false; break; } connection.Enqueue(message); message.WriteImpl(connection); message.SetRequestSent(); IncrementOpCount(); // some commands smash our ability to trust the database; some commands // demand an immediate flush switch (cmd) { case RedisCommand.EVAL: case RedisCommand.EVALSHA: if(!serverEndPoint.GetFeatures().ScriptingDatabaseSafe) { connection.SetUnknownDatabase(); } break; case RedisCommand.DISCARD: case RedisCommand.EXEC: connection.SetUnknownDatabase(); break; } return true; } catch (RedisCommandException ex) { Trace("Write failed: " + ex.Message); message.Fail(ConnectionFailureType.ProtocolFailure, ex); CompleteSyncOrAsync(message); // this failed without actually writing; we're OK with that... unless there's a transaction if (connection != null && connection.TransactionActive) { // we left it in a broken state; need to kill the connection connection.RecordConnectionFailed(ConnectionFailureType.ProtocolFailure, ex); return false; } return true; } catch (Exception ex) { Trace("Write failed: " + ex.Message); message.Fail(ConnectionFailureType.InternalFailure, ex); CompleteSyncOrAsync(message); // we're not sure *what* happened here; probably an IOException; kill the connection if(connection != null) connection.RecordConnectionFailed(ConnectionFailureType.InternalFailure, ex); return false; } }
private PhysicalConnection GetConnection(TextWriter log) { if (state == (int)State.Disconnected) { try { if (!multiplexer.IsDisposed) { Multiplexer.LogLocked(log, "Connecting {0}...", Name); Multiplexer.Trace("Connecting...", Name); if (ChangeState(State.Disconnected, State.Connecting)) { Interlocked.Increment(ref socketCount); Interlocked.Exchange(ref connectStartTicks, Environment.TickCount); // separate creation and connection for case when connection completes synchronously // in that case PhysicalConnection will call back to PhysicalBridge, and most of PhysicalBridge methods assumes that physical is not null; physical = new PhysicalConnection(this); physical.BeginConnect(log); } } return null; } catch (Exception ex) { Multiplexer.LogLocked(log, "Connect {0} failed: {1}", Name, ex.Message); Multiplexer.Trace("Connect failed: " + ex.Message, Name); ChangeState(State.Disconnected); OnInternalError(ex); throw; } } return physical; }
/// <summary> /// This writes a message **directly** to the output stream; note /// that this ignores the queue, so should only be used *either* /// from the regular dequeue loop, *or* from the "I've just /// connected" handshake (when there is no dequeue loop) - otherwise, /// you can pretty much assume you're going to destroy the stream /// </summary> internal bool WriteMessageDirect(PhysicalConnection tmp, Message next) { Trace("Writing: " + next); if (next is IMultiMessage) { SelectDatabase(tmp, next); // need to switch database *before* the transaction foreach (var subCommand in ((IMultiMessage)next).GetMessages(tmp)) { if (!WriteMessageToServer(tmp, subCommand)) { // we screwed up; abort; note that WriteMessageToServer already // killed the underlying connection Trace("Unable to write to server"); next.Fail(ConnectionFailureType.ProtocolFailure, null); CompleteSyncOrAsync(next); return false; } } next.SetRequestSent(); return true; } else { return WriteMessageToServer(tmp, next); } }
internal void RemovePhysical(PhysicalConnection connection) { #pragma warning disable 0420 Interlocked.CompareExchange(ref physical, null, connection); #pragma warning restore 0420 }
internal void OnFullyEstablished(PhysicalConnection connection) { Trace("OnFullyEstablished"); if (physical == connection && !isDisposed && ChangeState(State.ConnectedEstablishing, State.ConnectedEstablished)) { reportNextFailure = reconfigureNextFailure = true; Interlocked.Exchange(ref failConnectCount, 0); serverEndPoint.OnFullyEstablished(connection); multiplexer.RequestWrite(this, true); if(connectionType == ConnectionType.Interactive) serverEndPoint.CheckInfoReplication(); } else { try { connection.Dispose(); } catch { } } }
internal void OnConnectionFailed(PhysicalConnection connection, ConnectionFailureType failureType, Exception innerException) { if (reportNextFailure) { reportNextFailure = false; // until it is restored var endpoint = serverEndPoint.EndPoint; multiplexer.OnConnectionFailed(endpoint, connectionType, failureType, innerException, reconfigureNextFailure); } }
internal void OnConnected(PhysicalConnection connection, TextWriter log) { Trace("OnConnected"); if (physical == connection && !isDisposed && ChangeState(State.Connecting, State.ConnectedEstablishing)) { serverEndPoint.OnEstablishing(connection, log); } else { try { connection.Dispose(); } catch { } } }
public void Dispose() { isDisposed = true; using (var tmp = physical) { physical = null; } }
private PhysicalConnection GetConnection() { if (state == (int)State.Disconnected) { try { if (!multiplexer.IsDisposed) { Multiplexer.Trace("Connecting...", Name); if (ChangeState(State.Disconnected, State.Connecting)) { Interlocked.Increment(ref socketCount); physical = new PhysicalConnection(this); } } return null; } catch (Exception ex) { Multiplexer.Trace("Connect failed: " + ex.Message, Name); ChangeState(State.Disconnected); OnInternalError(ex); throw; } } return physical; }