/// <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);
        }