/// <summary> /// Execute the provided request. /// </summary> /// <param name="newRequest"></param> /// <param name="maxRetries">The maximum number of times to retry the connection. Use -1 to retry indefinitely.</param> public async Task ExecuteRequest(IWebRequest newRequest, int maxRetries) { //make sure we have a channel WebChannel channel = await GetCurrentChannel().ConfigureAwait(false); //this throws exceptions when it can't connect and is thread safe. //if we have a channel and NOW get an exception, here is where we would recheck the status of our connection. bool retryAuthentication = false; bool resetAndRetry = false; try { await channel.ExecuteRequest(newRequest, maxRetries).ConfigureAwait(false); } catch (WebChannelAuthorizationException ex) { //request better credentials.. Logger.Write(LogMessageSeverity.Warning, ex, true, LogCategory, "Requesting updated credentials for the server connection due to " + ex.GetType(), "We're going to assume the user can provide current credentials.\r\nDetails: {0}", ex.Message); if (CachedCredentialsManager.UpdateCredentials(channel, m_ClientRepositoryId, false)) { //they entered new creds.. lets give them a go. retryAuthentication = true; } else { //they canceled, lets call it. throw; } } catch (WebChannelConnectFailureException) { //clear our current channel and try again if we're on a child server. if (IsRootHub(channel.HostName, channel.Port, channel.UseSsl, channel.ApplicationBaseDirectory) == false) { resetAndRetry = true; } } if (retryAuthentication) { await ExecuteRequest(newRequest, maxRetries).ConfigureAwait(false); } else if (resetAndRetry) { await ResetChannel().ConfigureAwait(false); //safely clears the current channel and gets a fresh one if possible } }
/// <summary> /// Connects to the specified hub (or another hub if this hub is redirecting) /// </summary> /// <returns>The last web channel it was able to connect to after processing redirections.</returns> private static async Task <HubConnectionStatus> Connect(ServerConfiguration configuration) { WebChannel channel = null; bool canConnect = true; HubStatus status = HubStatus.Maintenance; //a reasonable default. string statusMessage = null; Guid? serverRepositoryId = null; DateTimeOffset? expirationDt = null; Version protocolVersion = new Version(0, 0); NetworkConnectionOptions agentLiveStream = null; NetworkConnectionOptions clientLiveStream = null; //first, is it a valid config? No point in trying to connect if it's a bum config. HubConnectionStatus connectionStatus; try { configuration.Validate(); } catch (Exception ex) { canConnect = false; statusMessage = "Invalid configuration: " + ex.Message; connectionStatus = new HubConnectionStatus(configuration, false, status, statusMessage); return(connectionStatus); } //and now try to connect to the server try { channel = CreateChannel(configuration); var configurationGetRequest = new HubConfigurationGetRequest(); await channel.ExecuteRequest(configurationGetRequest, 1) .ConfigureAwait(false); //we'd like it to succeed, so we'll give it one retry var configurationXml = configurationGetRequest.Configuration; //now, if we got back a redirect we need to go THERE to get the status. if (configurationXml.redirectRequested) { //recursively try again. channel.Dispose(); connectionStatus = await Connect(configurationXml.ToServerConfiguration()).ConfigureAwait(false); } else { //set the right status message status = (HubStatus)configurationXml.status; switch (status) { case HubStatus.Available: break; case HubStatus.Expired: statusMessage = "The Server's license has expired. " + (configuration.UseGibraltarService ? "You can reactivate your license in seconds at www.GibraltarSoftware.com." : "To renew your license, run the Administration tool on the Loupe Server." ); break; case HubStatus.Maintenance: statusMessage = "The Server is currently undergoing maintenance and can't process requests."; break; default: throw new ArgumentOutOfRangeException("status"); } if (configurationXml.id != null) { serverRepositoryId = new Guid(configurationXml.id); } if (configurationXml.expirationDt != null) { expirationDt = DataConverter.FromDateTimeOffsetXml(configurationXml.expirationDt); } string publicKey = configurationXml.publicKey; if (string.IsNullOrEmpty(configurationXml.protocolVersion) == false) { protocolVersion = new Version(configurationXml.protocolVersion); } LiveStreamServerXml liveStreamConfig = configurationXml.liveStream; if (liveStreamConfig != null) { agentLiveStream = new NetworkConnectionOptions { HostName = channel.HostName, Port = liveStreamConfig.agentPort, UseSsl = liveStreamConfig.useSsl }; clientLiveStream = new NetworkConnectionOptions { HostName = channel.HostName, Port = liveStreamConfig.clientPort, UseSsl = liveStreamConfig.useSsl }; } //We've connected for sure, time to set up our connection status to return to our caller with the full connection info connectionStatus = new HubConnectionStatus(configuration, channel, new HubRepository(expirationDt, serverRepositoryId, protocolVersion, publicKey, agentLiveStream, clientLiveStream), true, status, statusMessage); } } catch (WebChannelFileNotFoundException) { canConnect = false; if (configuration.UseGibraltarService) { //we'll treat file not found (e.g. customer never existed) as expired to get the right UI behavior. status = HubStatus.Expired; statusMessage = "The specified customer name is not valid"; } else { statusMessage = "No service could be found with the provided information"; } connectionStatus = new HubConnectionStatus(configuration, false, status, statusMessage); } catch (WebChannelBadRequestException) { canConnect = false; //we want to be somewhat more intelligent in our responses to decode what these might MEAN. if (configuration.UseGibraltarService) { status = HubStatus.Expired; statusMessage = "The specified customer name is not valid"; } else { statusMessage = "The server does not support this service or the specified directory is not valid"; } connectionStatus = new HubConnectionStatus(configuration, false, status, statusMessage); } catch (Exception ex) { canConnect = false; statusMessage = ex.Message; connectionStatus = new HubConnectionStatus(configuration, false, status, statusMessage); } //before we return make sure we clean up an errant channel if we don't need it. if ((canConnect == false) && (channel != null)) { channel.Dispose(); channel = null; } return(connectionStatus); }