/// <summary> /// Validates the instance. /// </summary> /// <param name="context">The validation context.</param> public void Validate(TrafficValidationContext context) { if (ConnectSeconds < 0.0) { context.Error($"Load balancer timeout [{nameof(ConnectSeconds)}={ConnectSeconds}] is not valid."); } if (ClientSeconds < 0.0) { context.Error($"Load balancer timeout [{nameof(ClientSeconds)}={ClientSeconds}] is not valid."); } if (HttpKeepAliveSeconds < 0.0) { context.Error($"Load balancer timeout [{nameof(HttpKeepAliveSeconds)}={HttpKeepAliveSeconds}] is not valid."); } if (ServerSeconds < 0.0) { context.Error($"Load balancer timeout [{nameof(ServerSeconds)}={ServerSeconds}] is not valid."); } if (CheckSeconds <= 0.0) { context.Error($"Load balancer timeout [{nameof(CheckSeconds)}={CheckSeconds}] is not positive."); } }
/// <summary> /// Validates the instance. /// </summary> /// <param name="context">The validation context.</param> public void Validate(TrafficValidationContext context) { if (string.IsNullOrWhiteSpace(Name)) { context.Error($"Load balancer resolver [{nameof(Name)}] cannot be null or empty."); } if (NameServers == null || NameServers.Count == 0) { context.Error($"Load balancer resolver [{nameof(NameServers)}] at least one name server must be specified."); } if (ResolveRetries < 0) { context.Error($"Load balancer resolver [{nameof(ResolveRetries)}={ResolveRetries}] is not valid."); } if (RetrySeconds <= 0.0) { context.Error($"Load balancer resolver [{nameof(RetrySeconds)}={RetrySeconds}] is not valid."); } if (HoldSeconds < 0.0) { context.Error($"Load balancer resolver [{nameof(HoldSeconds)}={HoldSeconds}] is not valid."); } }
/// <summary> /// Validates the instance. /// </summary> /// <param name="context">The validation context.</param> public void Validate(TrafficValidationContext context) { if (string.IsNullOrWhiteSpace(Name)) { context.Error("Load balancer nameserver name cannot be null or empty."); } var isValid = false; if (!string.IsNullOrWhiteSpace(Endpoint)) { var colonPos = Endpoint.LastIndexOf(':'); if (colonPos >= 0) { var addressPart = Endpoint.Substring(0, colonPos); var portPart = Endpoint.Substring(colonPos + 1); ushort port; isValid = IPAddress.TryParse(addressPart, out var address) && ushort.TryParse(portPart, out port); } if (!isValid) { context.Error($"[{nameof(TrafficNameserver)}.{nameof(Name)}={Endpoint}] is not valid."); } } }
/// <summary> /// Validates the header. /// </summary> /// <param name="context">The validation context.</param> /// <param name="rule">The parent rule.</param> public void Validate(TrafficValidationContext context, TrafficRule rule) { if (string.IsNullOrWhiteSpace(Name)) { context.Error($"Rule [{rule.Name}] specifies a NULL or empty [{nameof(TrafficCheckHeader)}.{nameof(TrafficCheckHeader.Name)}]."); } foreach (var ch in Name) { if (char.IsLetterOrDigit(ch) || ch == '-' || ch == '_') { continue; } context.Error($"Rule [{rule.Name}] specifies a [{nameof(TrafficCheckHeader)}.{nameof(TrafficCheckHeader.Name)}] with the invalid character [{ch}]."); break; } if (string.IsNullOrWhiteSpace(Value)) { context.Error($"Rule [{rule.Name}] specifies a NULL [{nameof(TrafficCheckHeader)}.{nameof(TrafficCheckHeader.Value)}]."); } // $todo(jeff.lill): // // We could try to validate the [Value] property too (e.g. to ensure that it doesn't include "\r\n") but // that could be overly restrictive if I'm not careful. I'm going to leave this be for now. }
/// <summary> /// Validates the instance. /// </summary> /// <param name="context">The validation context.</param> public void Validate(TrafficValidationContext context) { Timeouts = Timeouts ?? new TrafficTimeouts(); Resolvers = Resolvers ?? new List <TrafficResolver>(); BridgeTargetAddresses = BridgeTargetAddresses ?? new List <IPAddress>(); if (!Resolvers.Exists(r => r.Name == "docker")) { Resolvers.Add( new TrafficResolver() { Name = "docker", NameServers = new List <TrafficNameserver>() { new TrafficNameserver() { Name = "docker0", Endpoint = HiveConst.DockerDnsEndpoint } } }); } if (!NetHelper.IsValidPort(ProxyPorts.PortRange.FirstPort) || !NetHelper.IsValidPort(ProxyPorts.PortRange.LastPort) || ProxyPorts.PortRange.LastPort <= ProxyPorts.PortRange.FirstPort + 1) { context.Error($"Load balancer port block [{ProxyPorts.PortRange.FirstPort}-{ProxyPorts.PortRange.LastPort}] range is not valid."); } if (MaxConnections <= 0) { context.Error($"Load balancer settings [{nameof(MaxConnections)}={MaxConnections}] is not positive."); } Timeouts.Validate(context); if (!Resolvers.Exists(r => r.Name == "docker")) { context.Error($"Load balancer settings [{nameof(Resolvers)}] must include a [docker] definition."); } foreach (var resolver in Resolvers) { resolver.Validate(context); } if (BridgeTargetCount < 0) { context.Error($"Load balancer settings [{nameof(BridgeTargetCount)}={BridgeTargetCount}] cannot be negative."); } if (BridgeTargetCount == 0 && BridgeTargetAddresses.Count == 0) { context.Error($"Load balancer settings no bridge targets are specified."); } }
/// <summary> /// Validates the frontend. /// </summary> /// <param name="context">The validation context.</param> /// <param name="rule">The parent rule.</param> public void Validate(TrafficValidationContext context, TrafficTcpRule rule) { base.Validate(context, rule); if (PublicPort > 0 && !NetHelper.IsValidPort(PublicPort)) { context.Error($"Load balancer [{nameof(PublicPort)}={PublicPort}] is not a valid network port."); } if (!context.Settings.ProxyPorts.IsValidTcpPort(ProxyPort)) { context.Error($"Rule [{rule.Name}] assigns [{nameof(ProxyPort)}={ProxyPort}] which is outside the range of valid frontend TCP ports for this traffic manager [{context.Settings.ProxyPorts}]."); } }
/// <summary> /// Validates the rule. /// </summary> /// <param name="context">The validation context.</param> public override void Validate(TrafficValidationContext context) { base.Validate(context); Frontends = Frontends ?? new List <TrafficTcpFrontend>(); Backends = Backends ?? new List <TrafficTcpBackend>(); if (Frontends.Count == 0) { context.Error($"Rule [{Name}] has does not define a frontend."); } if (Backends.Count == 0) { context.Error($"Rule [{Name}] has does not define a backend."); } foreach (var frontend in Frontends) { frontend.Validate(context, this); } foreach (var backend in Backends) { backend.Validate(context, this); } // Verify that the ports are unique for each frontend and that none of these TCP // target one of the reserved HTTP/HTTPS proxy ports. var frontendMap = new HashSet <int>(); foreach (var frontend in Frontends) { var key = frontend.ProxyPort; if (frontendMap.Contains(key)) { context.Error($"TCP rule [{Name}] includes two or more frontends that map to port [{key}]."); } if (frontend.ProxyPort == HiveHostPorts.ProxyPublicHttp || frontend.ProxyPort == HiveHostPorts.ProxyPublicHttps || frontend.ProxyPort == HiveHostPorts.ProxyPublicHttp || frontend.ProxyPort == HiveHostPorts.ProxyPublicHttps) { context.Error($"Rule [{Name}] has a TCP frontend with [{nameof(frontend.ProxyPort)}={frontend.ProxyPort}] that is incorrectly mapped to a reserved HTTP/HTTPS port."); } frontendMap.Add(key); } }
/// <summary> /// Validates the instance. /// </summary> /// <param name="context">The validation context.</param> public virtual void Validate(TrafficValidationContext context) { Timeouts = Timeouts ?? new TrafficTimeouts(); if (string.IsNullOrEmpty(Name)) { context.Error($"Load balancer rule name is required."); } if (!HiveDefinition.NameRegex.IsMatch(Name)) { context.Error($"Load balancer rule name [{nameof(Name)}={Name}] is not valid."); } if (context.ValidateResolvers) { if (!string.IsNullOrWhiteSpace(Resolver) && context.Settings != null && context.Settings.Resolvers.Count(r => string.Equals(Resolver, r.Name, StringComparison.OrdinalIgnoreCase)) == 0) { context.Error($"Load balancer resolver [{nameof(Resolver)}={Resolver}] does not exist."); } } Resolver = Resolver ?? defaultResolverName; CheckHeaders = CheckHeaders ?? new List <TrafficCheckHeader>(); foreach (var checkHeader in CheckHeaders) { checkHeader.Validate(context, this); } if (UseHttpCheckMode) { if (string.IsNullOrEmpty(CheckUri) || !Uri.TryCreate(CheckUri, UriKind.RelativeOrAbsolute, out var uri)) { context.Error($"Rule [{nameof(Name)}] has invalid [{nameof(CheckUri)}={CheckUri}]."); } } if (CheckSeconds < 0.0) { CheckSeconds = 5.0; } Timeouts.Validate(context); }
/// <summary> /// Validates the item. /// </summary> /// <param name="context">The validation context.</param> public void Validate(TrafficValidationContext context) { if (string.IsNullOrEmpty(Uri)) { context.Error($"[{nameof(TrafficWarmTarget)}.{nameof(Uri)}] cannot be NULL or empty."); } if (!System.Uri.TryCreate(Uri, UriKind.Absolute, out var uri)) { context.Error($"[{nameof(TrafficWarmTarget)}.{nameof(Uri)}={Uri}] is not a valid fully qualified URI."); } if (UpdateSeconds <= 0) { UpdateSeconds = defaultUpdateSeconds; } }
/// <summary> /// Validates the frontend. /// </summary> /// <param name="context">The validation context.</param> /// <param name="rule">The parent rule.</param> public void Validate(TrafficValidationContext context, TrafficRule rule) { // Verify [MaxConnections] if (MaxConnections < 0) { context.Error($"Rule [{rule.Name}] specifies invalid [{nameof(MaxConnections)}={MaxConnections}]."); } }
/// <summary> /// Validates the settings. /// </summary> /// <param name="context">The validation context.</param> /// <param name="rule">The parent rule.</param> public void Validate(TrafficValidationContext context, TrafficHttpRule rule) { if (DnsTTL < 1) { context.Error($"[{nameof(TrafficHttpCache)}.{nameof(DnsTTL)}={DnsTTL}] cannot be less than 1 second."); } WarmTargets = WarmTargets ?? new List <TrafficWarmTarget>(); // Verify that each warm target has valid properties and that they // all match at one of the rule frontends. foreach (var target in WarmTargets) { target.Validate(context); if (rule.GetFrontendForWarmTarget(target) == null) { context.Error($"Rule [{rule.Name}] includes the [{target.Uri}] cache warming target which cannot be mapped to a rule frontend."); } } }
/// <summary> /// Validates the backend. /// </summary> /// <param name="context">The validation context.</param> /// <param name="ruleName">The parent rule name.</param> public virtual void Validate(TrafficValidationContext context, string ruleName) { if (!string.IsNullOrEmpty(Name) && !HiveDefinition.IsValidName(Name)) { context.Error($"Rule [{ruleName}] has backend server with invalid [{nameof(Name)}={Name}]."); } if (!string.IsNullOrEmpty(Group)) { if (!HiveDefinition.IsValidName(Group)) { context.Error($"Rule [{ruleName}] has backend with [{nameof(Group)}={Group}] which is not a valid group name."); } if (GroupLimit < 0) { context.Error($"Rule [{ruleName}] has backend with [{nameof(GroupLimit)}={GroupLimit}] which may not be less than zero."); } } else { if (string.IsNullOrEmpty(Server) || (!IPAddress.TryParse(Server, out var address) && !HiveDefinition.DnsHostRegex.IsMatch(Server))) { context.Error($"Rule [{ruleName}] has backend server [{Server}] which is not valid. A DNS name or IP address was expected."); } } if (!NetHelper.IsValidPort(Port)) { context.Error($"Rule [{ruleName}] has backend server with invalid [{nameof(Port)}={Port}] which is outside the range of valid TCP ports."); } if (MaxConnections < 0) { context.Error($"Rule [{ruleName}] has backend server with invalid [{nameof(MaxConnections)}={MaxConnections}]."); } }
/// <summary> /// Validates the frontend. /// </summary> /// <param name="context">The validation context.</param> /// <param name="rule">The parent rule.</param> public void Validate(TrafficValidationContext context, TrafficHttpRule rule) { base.Validate(context, rule); if (rule.Frontends.Count > 1 || !string.IsNullOrEmpty(CertName) || ProxyPort == 0 || ProxyPort == HiveHostPorts.ProxyPublicHttp || ProxyPort == HiveHostPorts.ProxyPublicHttps || ProxyPort == HiveHostPorts.ProxyPrivateHttp || ProxyPort == HiveHostPorts.ProxyPrivateHttps) { // The hostname is required so verify it. if (string.IsNullOrEmpty(Host)) { context.Error($"Rule [{rule.Name}] has a frontend without a [{nameof(Host)}] specified. HTTP rules targeting the default traffic manager HTTP/S ports, with more than one frontend, or secured by TLS requires frontend hostnames."); } else if (!HiveDefinition.DnsHostRegex.IsMatch(Host)) { context.Error($"Rule [{rule.Name}] defines the invalid hostname [{Host}]."); } } else { // The hostname is not required but verify it if one is specified. if (!string.IsNullOrEmpty(Host) && !HiveDefinition.DnsHostRegex.IsMatch(Host)) { context.Error($"Rule [{rule.Name}] defines the invalid hostname [{Host}]."); } } if (!string.IsNullOrEmpty(PathPrefix)) { if (!PathPrefix.StartsWith("/")) { context.Error($"Rule [{rule.Name}] references has [{nameof(PathPrefix)}={PathPrefix}] that does not begin with a forward slash."); } else { if (!PathPrefix.EndsWith("/")) { PathPrefix += "/"; } if (!Uri.TryCreate(PathPrefix, UriKind.Relative, out Uri uri)) { context.Error($"Rule [{rule.Name}] references has [{nameof(PathPrefix)}={PathPrefix}] that is not a valid relative URI."); } } } if (CertName != null && context.ValidateCertificates) { TlsCertificate certificate; if (!context.Certificates.TryGetValue(CertName, out certificate)) { context.Error($"Rule [{rule.Name}] references certificate [{CertName}] that does not exist or could not be loaded."); } else { if (!certificate.IsValidHost(Host)) { context.Error($"Rule [{rule.Name}] references certificate [{CertName}] which does not cover host [{Host}]."); } if (!certificate.IsValidDate(DateTime.UtcNow)) { context.Error($"Rule [{rule.Name}] references certificate [{CertName}] which expired on [{certificate.ValidUntil}]."); } } } if (ProxyPort != 0) { if (!context.Settings.ProxyPorts.IsValidPort(ProxyPort)) { context.Error($"Rule [{rule.Name}] assigns [{nameof(ProxyPort)}={ProxyPort}] which is outside the range of valid frontend ports for this traffic manager [{context.Settings.ProxyPorts}]."); } } else { if (CertName == null) { ProxyPort = context.Settings.DefaultHttpPort; } else { ProxyPort = context.Settings.DefaultHttpsPort; } } if (PublicPort == -1) { if (CertName == null) { PublicPort = NetworkPorts.HTTP; } else { PublicPort = NetworkPorts.HTTPS; } } if (PublicPort > 0 && !NetHelper.IsValidPort(PublicPort)) { context.Error($"Load balancer [{nameof(PublicPort)}={PublicPort}] is not a valid network port."); } if (RedirectTo != null) { // Strip off the path/query part of the URI, if necessary. if (RedirectTo.PathAndQuery != "/") { RedirectTo = new Uri($"{RedirectTo.Scheme}://{RedirectTo.Host}:{RedirectTo.Port}/"); } } }
/// <summary> /// Validates the traffic manager definition. /// </summary> /// <param name="certificates">The dictionary of hive certificates keyed by name.</param> /// <returns>The <see cref="TrafficValidationContext"/>.</returns> public TrafficValidationContext Validate(Dictionary <string, TlsCertificate> certificates) { Covenant.Requires <ArgumentNullException>(certificates != null); var context = new TrafficValidationContext(Name, Settings, certificates); // Validate the existing settings and rules. Settings.Validate(context); foreach (var rule in Rules.Values) { rule.Validate(context); } // Verify that there are no existing frontend port/host conflicts: // // * HTTP rules can share ports but hostnames must be unique. // * HTTP rules on the same port cannot mix TLS and non-TLS. // * Only one TCP port per rule is allowed. var httpMap = new Dictionary <string, TrafficHttpRule>(StringComparer.OrdinalIgnoreCase); var httpPortToTls = new Dictionary <int, bool>(); var tcpMap = new Dictionary <int, TrafficTcpRule>(); // Scan HTTP rules. foreach (var rule in Rules.Values.Where(r => r.Mode == TrafficMode.Http).OrderBy(r => r.Name.ToLowerInvariant())) { var httpRule = (TrafficHttpRule)rule; foreach (var frontend in httpRule.Frontends) { var key = $"{frontend.Host}:{frontend.ProxyPort}"; if (!string.IsNullOrEmpty(frontend.PathPrefix)) { key += frontend.PathPrefix; } if (httpMap.ContainsKey(key)) { context.Error($"HTTP rule [{httpRule.Name}] has a frontend on [{key}] that conflicts with rule [{httpMap[key].Name}]."); continue; } httpMap.Add(key, httpRule); if (!httpPortToTls.TryGetValue(frontend.ProxyPort, out var isTls)) { isTls = frontend.Tls; httpPortToTls.Add(frontend.ProxyPort, isTls); } if (isTls != frontend.Tls) { if (frontend.Tls) { context.Error($"HTTP rule [{httpRule.Name}] has a TLS frontend on port [{frontend.ProxyPort}] that conflicts with non-TLS frontends on this port."); } else { context.Error($"HTTP rule [{httpRule.Name}] has a non-TLS frontend on port [{frontend.ProxyPort}] that conflicts with TLS frontends on this port."); } } } } // Scan the TCP rules. foreach (var rule in Rules.Values.Where(r => r.Mode == TrafficMode.Tcp).OrderBy(r => r.Name.ToLowerInvariant())) { var tcpRule = (TrafficTcpRule)rule; foreach (var frontend in tcpRule.Frontends) { var port = frontend.ProxyPort; if (port == Settings.DefaultHttpPort || port == Settings.DefaultHttpsPort) { context.Error($"TCP rule [{tcpRule.Name}] has a frontend on [{port}] that conflicts the default traffic manager HTTP or HTTPS port."); } if (httpPortToTls.ContainsKey(port)) { context.Error($"TCP rule [{tcpRule.Name}] has a frontend on [{port}] that conflicts with one or more HTTP traffic manager frontends on the same port."); } if (tcpMap.ContainsKey(port)) { context.Error($"TCP rule [{tcpRule.Name}] has a frontend on [{port}] that conflicts with rule [{tcpMap[port].Name}]."); } tcpMap.Add(port, tcpRule); } } return(context); }
/// <summary> /// Validates the rule. /// </summary> /// <param name="context">The validation context.</param> public override void Validate(TrafficValidationContext context) { base.Validate(context); Frontends = Frontends ?? new List <TrafficHttpFrontend>(); Backends = Backends ?? new List <TrafficHttpBackend>(); if (Frontends.Count == 0) { context.Error($"Rule [{Name}] has does not define a frontend."); } if (!string.IsNullOrEmpty(CheckUri)) { if (!Uri.TryCreate(CheckUri, UriKind.Relative, out var uri)) { context.Error($"Rule [{Name}] has invalid [{nameof(CheckUri)}={CheckUri}]."); } } if (string.IsNullOrEmpty(CheckMethod) || CheckMethod.IndexOfAny(new char[] { ' ', '\r', '\n', '\t' }) != -1) { context.Error($"Rule [{Name}] has invalid [{nameof(CheckMethod)}={CheckMethod}]."); } if (string.IsNullOrEmpty(CheckVersion)) { CheckVersion = "1.0"; } var regex = new Regex(@"^\d+\.\d+$"); if (!regex.Match(CheckVersion).Success) { context.Error($"Rule [{Name}] has invalid [{nameof(CheckVersion)}={CheckVersion}]."); } if (!string.IsNullOrEmpty(CheckHost) && !HiveDefinition.DnsHostRegex.Match(CheckHost).Success) { context.Error($"Rule [{Name}] has invalid [{nameof(CheckHost)}={CheckHost}]."); } if (!string.IsNullOrEmpty(CheckExpect)) { var error = $"Rule [{Name}] has invalid [{nameof(CheckExpect)}={CheckExpect}]."; var value = CheckExpect.Trim(); if (value.StartsWith("! ")) { value = value.Substring(2).Trim(); } var pos = value.IndexOf(' '); if (pos == -1) { context.Error(error + " Expected: <match> <pattern>"); } else { var match = value.Substring(0, pos); var pattern = value.Substring(pos).Trim(); if (pattern.Replace("\\ ", string.Empty).IndexOf(' ') != -1) { context.Error(error + $" Pattern [{pattern}] includes unescaped spaces."); } switch (match) { case "status": case "string": break; case "rstatus": case "rstring": try { new Regex(pattern); } catch (Exception e) { context.Error(error + $" Pattern regex [{pattern}] parsing error: {e.Message}."); } break; default: context.Error(error + " Invalid [match], expected one of: status, rstatus, string, rstring"); break; } } } foreach (var frontend in Frontends) { frontend.Validate(context, this); } foreach (var backend in Backends) { backend.Validate(context, this); } // Verify that the port/host combinations are unique for each frontend. var frontendMap = new HashSet <string>(StringComparer.OrdinalIgnoreCase); foreach (var frontend in Frontends) { if (string.IsNullOrEmpty(frontend.PathPrefix)) { var key = $"{frontend.Host}:{frontend.ProxyPort}"; if (frontendMap.Contains(key)) { context.Error($"HTTP rule [{Name}] includes two or more frontends that map to [{key}]."); } frontendMap.Add(key); } } foreach (var frontend in Frontends) { if (!string.IsNullOrEmpty(frontend.PathPrefix)) { var key = $"{frontend.Host}:{frontend.ProxyPort}{frontend.PathPrefix}"; if (frontendMap.Contains($"{frontend.Host}:{frontend.ProxyPort}") || // Ensure there's no *all* path frontend frontendMap.Contains(key)) { context.Error($"HTTP rule [{Name}] includes two or more frontends that map to [{key}]."); } frontendMap.Add(key); } } if (Cache != null && Cache.Enabled) { Cache.Validate(context, this); // The Varnish open source release doesn't support TLS backends. This requires // Varnish Plus (of course) which is very expensive. foreach (TrafficHttpBackend backend in Backends) { if (backend.Tls) { context.Error($"HTTP rule [{Name}] cannot support caching because one or more backends required TLS."); break; } } // Varnish doesn't support comparing health probe status codes with a regex // like HAProxy does. We're going to enforce having CheckExpect set to // something like "status 200". var statusFields = CheckExpect.Split(' '); if (statusFields.Length != 2 || statusFields[0] != "status" || !int.TryParse(statusFields[1], out var statusCode) || statusCode < 100 || 600 <= statusCode) { context.Error($"HTTP rule [{Name}] cannot support caching because [{nameof(CheckExpect)}={CheckExpect}] doesn't specify a fixed status code like [status 200]. Varnish-Cache does not support verifying health probe status codes as regular expressions like HAProxy can."); } // $todo(jeff.lill): // // We need to enforce some restrictions due to Varnish limitations // described here: // // https://github.com/jefflill/NeonForge/issues/379 // // It would be nice to revisit this in the future. // Ensure that: // // * If one backend has a hostname then it must be the only backend. // * IP address and hostname backends cannot be mixed. if (Backends.Count > 1) { var hasHostname = false; var hasIPAddress = false; foreach (var backend in Backends) { if (IPAddress.TryParse(backend.Server, out var address)) { hasIPAddress = true; } else { hasHostname = true; } } if (hasIPAddress) { context.Error($"HTTP rule [{Name}] has multiple backends reachable via hostname which is not supported. You may define only a single backend that requires a DNS lookup."); } else if (hasIPAddress && hasHostname) { context.Error($"HTTP rule [{Name}] has backends reachable via IP address and hostname which is not supported. You cannot mix backends with IP address and hostnames in the same rule."); } } // Ensure that all cache warming targets have schemes, hostnames, ports that // match a rule frontend, and that HTTP rules don't map to reserved HTTPS ports // and HTTPS rules don't map to reserved HTTP ports. foreach (var frontend in Frontends) { if (frontend.Tls) { if (frontend.ProxyPort == HiveHostPorts.ProxyPublicHttp || frontend.ProxyPort == HiveHostPorts.ProxyPrivateHttp) { context.Error($"Rule [{Name}] has an HTTPS frontend with [{nameof(frontend.ProxyPort)}={frontend.ProxyPort}] that is incorrectly mapped to a reserved HTTP port."); } } else { if (frontend.ProxyPort == HiveHostPorts.ProxyPublicHttps || frontend.ProxyPort == HiveHostPorts.ProxyPrivateHttps) { context.Error($"Rule [{Name}] has an HTTP frontend with [{nameof(frontend.ProxyPort)}={frontend.ProxyPort}] that is incorrectly mapped to a reserved HTTPS port."); } } } // Ensure that all cache warming targets have schemes, hostnames, and ports that // match a rule frontend. foreach (var warmTarget in Cache.WarmTargets) { var uri = new Uri(warmTarget.Uri); var tls = uri.Scheme.Equals("https", StringComparison.InvariantCultureIgnoreCase); if (Frontends.IsEmpty(fe => fe.Tls == tls && fe.Host.Equals(uri.Host, StringComparison.InvariantCultureIgnoreCase) && fe.ProxyPort == uri.Port)) { context.Error($"Cache warm target [{uri}] does not match one of the [{Name}] traffic manager frontends."); } } } }