/// <summary> /// Implemented by inheritors to perform the request on the provided web client. /// </summary> /// <param name="connection"></param> protected override async Task OnProcessRequest(IWebChannelConnection connection) { byte[] requestedHubConfigurationRawData = await connection.DownloadData("/Hub/Configuration.xml").ConfigureAwait(false); //and now do it without using XMLSerializer since that doesn't work in the agent. Configuration = DataConverter.ByteArrayToHubConfigurationXml(requestedHubConfigurationRawData); }
/// <summary> /// Get a server configuration from a redirect request /// </summary> /// <param name="configuration"></param> /// <returns></returns> public static ServerConfiguration ToServerConfiguration(this HubConfigurationXml configuration) { if (configuration == null) { throw new ArgumentNullException(nameof(configuration)); } if (configuration.redirectRequested == false) { throw new InvalidOperationException("No connection redirection information provided"); } var newConfiguration = new ServerConfiguration(); if (configuration.redirectUseGibraltarSdsSpecified) { newConfiguration.UseGibraltarService = configuration.redirectUseGibraltarSds; } newConfiguration.CustomerName = configuration.redirectCustomerName; newConfiguration.Server = configuration.redirectHostName; if (configuration.redirectPortSpecified) { newConfiguration.Port = configuration.redirectPort; } if (configuration.redirectUseSslSpecified) { newConfiguration.UseSsl = configuration.redirectUseSsl; } newConfiguration.ApplicationBaseDirectory = configuration.redirectApplicationBaseDirectory; newConfiguration.Repository = configuration.redirectCustomerName; //it doesn't specify repository. return(newConfiguration); }
/// <summary> /// Attempts to connect to the server and returns information about the connection status. /// </summary> /// <remarks>This method will keep the connection if it is made, improving efficiency if you are then going to use the connection.</remarks> /// <returns>True if the configuration is valid and the server is available, false otherwise.</returns> public async Task <HubConnectionStatus> CanConnect() { WebChannel currentChannel; lock (m_ChannelLock) { currentChannel = m_CurrentChannel; System.Threading.Monitor.PulseAll(m_ChannelLock); } //if we have a current connection we'll need to see if we can keep using it if (currentChannel != null) { var status = HubStatus.Maintenance; try { var configurationGetRequest = new HubConfigurationGetRequest(); await currentChannel.ExecuteRequest(configurationGetRequest, 1).ConfigureAwait(false); //we'd like it to succeed, so we'll give it one retry //now, if we got back a redirect we need to go THERE to get the status. HubConfigurationXml configurationXml = configurationGetRequest.Configuration; if ((configurationXml.redirectRequested == false) && (configurationXml.status == HubStatusXml.available)) { //we can just keep using this connection, so lets do that. return(new HubConnectionStatus(null, true, HubStatus.Available, null)); } status = (HubStatus)configurationXml.status; //we define these to be equal. } catch (Exception ex) { if (!Logger.SilentMode) { Logger.Write(LogMessageSeverity.Information, ex, false, LogCategory, "Unable to get server configuration, connection will be assumed unavailable.", "Due to an exception we were unable to retrieve the server configuration. We'll assume the server is in maintenance until we can succeed. Exception: {0}\r\n", ex.Message); } } //drop the connection - we might do better, unless we're already at the root. if (IsRootHub(currentChannel.HostName, currentChannel.Port, currentChannel.UseSsl, currentChannel.ApplicationBaseDirectory)) { //we are the root - what we are is the best we are. string message = null; switch (status) { case HubStatus.Expired: message = "your subscription is expired"; break; case HubStatus.Maintenance: message = "the repository is in maintenance"; break; } return(new HubConnectionStatus(null, false, status, message)); } } //if we don't have a connection (either we didn't before or we just invalidated the current connection) get a new one. var connectionStatus = await Connect().ConfigureAwait(false); SetCurrentChannel(connectionStatus.Channel); //before we return, lets set our status to track what we just calculated. lock (m_StatusLock) { //make a copy of the connection status so the caller does NOT get our connection object. m_HubStatus = new HubConnectionStatus(connectionStatus.Configuration, null, connectionStatus.Repository, connectionStatus.IsValid, connectionStatus.Status, connectionStatus.Message); m_HubRepository = connectionStatus.Repository; } return(connectionStatus); }
/// <summary> /// Convert a byte array to a Server Configuration XML object without relying on XML Serializer /// </summary> /// <param name="rawData"></param> /// <returns></returns> public static HubConfigurationXml ByteArrayToHubConfigurationXml(byte[] rawData) { HubConfigurationXml configurationXml = new HubConfigurationXml(); using (MemoryStream documentStream = new MemoryStream(rawData)) { XmlDocument xml = new XmlDocument(); xml.Load(documentStream); // xml.LoadXml(Encoding.UTF8.GetString(rawData)); XmlNode hubConfigurationNode = xml.DocumentElement; if (hubConfigurationNode == null) { throw new GibraltarException("There is no server configuration data in the provided raw data"); } //read up our attributes XmlAttribute attribute = hubConfigurationNode.Attributes["id"]; if (attribute != null) { configurationXml.id = attribute.InnerText; } attribute = hubConfigurationNode.Attributes["redirectRequested"]; if (attribute != null) { if (string.IsNullOrEmpty(attribute.InnerText) == false) { configurationXml.redirectRequested = bool.Parse(attribute.InnerText); } } attribute = hubConfigurationNode.Attributes["status"]; if (attribute != null) { if (string.IsNullOrEmpty(attribute.InnerText) == false) { configurationXml.status = (HubStatusXml)Enum.Parse(typeof(HubStatusXml), attribute.InnerText, true); } } attribute = hubConfigurationNode.Attributes["timeToLive"]; if (attribute != null) { if (string.IsNullOrEmpty(attribute.InnerText) == false) { configurationXml.timeToLive = int.Parse(attribute.InnerText); } } attribute = hubConfigurationNode.Attributes["protocolVersion"]; if (attribute != null) { configurationXml.protocolVersion = attribute.InnerText; } //we only read redirect information if we actually got a redirect request. if (configurationXml.redirectRequested) { attribute = hubConfigurationNode.Attributes["redirectApplicationBaseDirectory"]; if (attribute != null) { configurationXml.redirectApplicationBaseDirectory = attribute.InnerText; } attribute = hubConfigurationNode.Attributes["redirectCustomerName"]; if (attribute != null) { configurationXml.redirectCustomerName = attribute.InnerText; } attribute = hubConfigurationNode.Attributes["redirectHostName"]; if (attribute != null) { configurationXml.redirectHostName = attribute.InnerText; } attribute = hubConfigurationNode.Attributes["redirectPort"]; if ((attribute != null) && (string.IsNullOrEmpty(attribute.InnerText) == false)) { configurationXml.redirectPort = int.Parse(attribute.InnerText); configurationXml.redirectPortSpecified = true; } attribute = hubConfigurationNode.Attributes["redirectUseGibraltarSds"]; if ((attribute != null) && (string.IsNullOrEmpty(attribute.InnerText) == false)) { configurationXml.redirectUseGibraltarSds = bool.Parse(attribute.InnerText); configurationXml.redirectUseGibraltarSdsSpecified = true; } attribute = hubConfigurationNode.Attributes["redirectUseSsl"]; if ((attribute != null) && (string.IsNullOrEmpty(attribute.InnerText) == false)) { configurationXml.redirectUseSsl = bool.Parse(attribute.InnerText); configurationXml.redirectUseSslSpecified = true; } } //now move on to the child elements.. I'm avoiding XPath to avoid failure due to XML schema variations if (hubConfigurationNode.HasChildNodes) { XmlNode expirationDtNode = null; XmlNode publicKeyNode = null; XmlNode liveStreamNode = null; foreach (XmlNode curChildNode in hubConfigurationNode.ChildNodes) { if (curChildNode.NodeType == XmlNodeType.Element) { switch (curChildNode.Name) { case "expirationDt": expirationDtNode = curChildNode; break; case "publicKey": publicKeyNode = curChildNode; break; case "liveStream": liveStreamNode = curChildNode; break; default: break; } if ((expirationDtNode != null) && (publicKeyNode != null) && (liveStreamNode != null)) { break; } } } if (expirationDtNode != null) { attribute = expirationDtNode.Attributes["DateTime"]; string dateTimeRaw = attribute.InnerText; attribute = expirationDtNode.Attributes["Offset"]; string timeZoneOffset = attribute.InnerText; if ((string.IsNullOrEmpty(dateTimeRaw) == false) && (string.IsNullOrEmpty(timeZoneOffset) == false)) { configurationXml.expirationDt = new DateTimeOffsetXml(); configurationXml.expirationDt.DateTime = DateTime.Parse(dateTimeRaw); configurationXml.expirationDt.Offset = int.Parse(timeZoneOffset); } } if (publicKeyNode != null) { configurationXml.publicKey = publicKeyNode.InnerText; } if (liveStreamNode != null) { attribute = liveStreamNode.Attributes["agentPort"]; string agentPortRaw = attribute.InnerText; attribute = liveStreamNode.Attributes["clientPort"]; string clientPortRaw = attribute.InnerText; attribute = liveStreamNode.Attributes["useSsl"]; string useSslRaw = attribute.InnerText; if ((string.IsNullOrEmpty(agentPortRaw) == false) && (string.IsNullOrEmpty(clientPortRaw) == false) && (string.IsNullOrEmpty(useSslRaw) == false)) { configurationXml.liveStream = new LiveStreamServerXml(); configurationXml.liveStream.agentPort = int.Parse(agentPortRaw); configurationXml.liveStream.clientPort = int.Parse(clientPortRaw); configurationXml.liveStream.useSsl = bool.Parse(useSslRaw); } } } } return(configurationXml); }
/// <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 //now, if we got back a redirect we need to go THERE to get the status. HubConfigurationXml configurationXml = configurationGetRequest.Configuration; if (configurationXml.redirectRequested) { //recursively try again. channel.Dispose(); connectionStatus = await Connect(configuration).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 = "The server does not support this service or the specified directory is not valid"; } connectionStatus = new HubConnectionStatus(configuration, false, status, statusMessage); } catch (WebException ex) { canConnect = false; HttpWebResponse response = (HttpWebResponse)ex.Response; statusMessage = response.StatusDescription; //by default we'll use the detailed description we got back from the web server. //we want to be somewhat more intelligent in our responses to decode what these might MEAN. if (configuration.UseGibraltarService) { switch (response.StatusCode) { case HttpStatusCode.NotFound: case HttpStatusCode.BadRequest: status = HubStatus.Expired; statusMessage = "The specified customer name is not valid"; break; } } else { switch (response.StatusCode) { case HttpStatusCode.NotFound: statusMessage = "No service could be found with the provided information"; break; case HttpStatusCode.BadRequest: statusMessage = "The server does not support this service or the specified directory is not valid"; break; } } 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); }