/// <summary> /// Starts the <see cref="Connection"/>. /// </summary> /// <param name="transport">The transport to use.</param> /// <returns>A task that represents when the connection has started.</returns> public Task Start(IClientTransport transport) { lock (_startLock) { _connectTask = TaskAsyncHelper.Empty; _disconnectCts = new CancellationTokenSource(); if (!ChangeState(ConnectionState.Disconnected, ConnectionState.Connecting)) { return(_connectTask); } _monitor = new HeartbeatMonitor(this, _stateLock); _transport = transport; _connectTask = Negotiate(transport); } return(_connectTask); }
private Task Negotiate(IClientTransport transport) { _connectionData = OnSending(); return(transport.Negotiate(this, _connectionData) .Then(negotiationResponse => { VerifyProtocolVersion(negotiationResponse.ProtocolVersion); ConnectionId = negotiationResponse.ConnectionId; ConnectionToken = negotiationResponse.ConnectionToken; _disconnectTimeout = TimeSpan.FromSeconds(negotiationResponse.DisconnectTimeout); _totalTransportConnectTimeout = TransportConnectTimeout + TimeSpan.FromSeconds(negotiationResponse.TransportConnectTimeout); // Default the beat interval to be 5 seconds in case keep alive is disabled. var beatInterval = TimeSpan.FromSeconds(5); // If we have a keep alive if (negotiationResponse.KeepAliveTimeout != null) { _keepAliveData = new KeepAliveData(TimeSpan.FromSeconds(negotiationResponse.KeepAliveTimeout.Value)); _reconnectWindow = _disconnectTimeout + _keepAliveData.Timeout; beatInterval = _keepAliveData.CheckInterval; } else { _reconnectWindow = _disconnectTimeout; } _monitor = new HeartbeatMonitor(this, _stateLock, beatInterval); return StartTransport(); }) .ContinueWithNotComplete(() => Disconnect())); }
private Task Negotiate(IClientTransport transport) { // Captured and used by StartNegotiation to track attempts var negotiationAttempts = 0; Task CompleteNegotiation(NegotiationResponse negotiationResponse) { ConnectionId = negotiationResponse.ConnectionId; ConnectionToken = negotiationResponse.ConnectionToken; _disconnectTimeout = TimeSpan.FromSeconds(negotiationResponse.DisconnectTimeout); _totalTransportConnectTimeout = TransportConnectTimeout + TimeSpan.FromSeconds(negotiationResponse.TransportConnectTimeout); // Default the beat interval to be 5 seconds in case keep alive is disabled. var beatInterval = TimeSpan.FromSeconds(5); // If we have a keep alive if (negotiationResponse.KeepAliveTimeout != null) { _keepAliveData = new KeepAliveData(TimeSpan.FromSeconds(negotiationResponse.KeepAliveTimeout.Value)); _reconnectWindow = _disconnectTimeout + _keepAliveData.Timeout; beatInterval = _keepAliveData.CheckInterval; } else { _reconnectWindow = _disconnectTimeout; } Monitor = new HeartbeatMonitor(this, _stateLock, beatInterval); return(StartTransport()); } Task StartNegotiation() { return(transport.Negotiate(this, _connectionData) .Then(negotiationResponse => { VerifyProtocolVersion(negotiationResponse.ProtocolVersion); if (negotiationResponse.ProtocolVersion.Equals("2.0")) { if (!string.IsNullOrEmpty(negotiationResponse.Error)) { throw new StartException(string.Format(Resources.Error_ErrorFromServer, negotiationResponse.Error)); } if (!string.IsNullOrEmpty(negotiationResponse.RedirectUrl)) { if (!negotiationResponse.RedirectUrl.EndsWith("/")) { negotiationResponse.RedirectUrl += "/"; } // Update the URL based on the redirect response and restart the negotiation Url = negotiationResponse.RedirectUrl; if (!string.IsNullOrEmpty(negotiationResponse.AccessToken)) { // This will stomp on the current Authorization header, but that's by design. // If the server specified a token, that is expected to overrule the token the client is currently using. Headers["Authorization"] = $"Bearer {negotiationResponse.AccessToken}"; } negotiationAttempts += 1; if (negotiationAttempts >= MaxRedirects) { throw new InvalidOperationException(Resources.Error_NegotiationLimitExceeded); } return StartNegotiation(); } } return CompleteNegotiation(negotiationResponse); }) .ContinueWithNotComplete(() => Disconnect())); } _connectionData = OnSending(); return(StartNegotiation()); }