public void SetupViewModel(FrontendContext frontendContext, IContent node, Frontends.Engines.Jit.ViewModels.NodeViewModel viewModel) { if (node.ContentItem.ContentType == "AssociativyTagNode") { viewModel.name = node.As<IAssociativyNodeLabelAspect>().Label; } else viewModel.name = node.As<ITitleAspect>().Title; }
public void RaiseRawObjectEvents(object obj) { int? priority = int.MinValue; bool handled = false; foreach (var frontend in Frontends.OrderByDescending(k => k.MiddlewareConfig?.Priority)) { int?p = frontend.MiddlewareConfig?.Priority; if (p < priority && handled) { break; } priority = frontend.MiddlewareConfig?.Priority; handled = frontend.RawObject_Received(obj); } }
/// <summary> /// <b>INTERNAL USE ONLY:</b> Returns the frontend corresponding to a cache warming target URI. /// </summary> /// <param name="target">The warming target.</param> /// <returns>The corresponding <see cref="TrafficHttpFrontend"/>"/> or <c>null</c> if there's no match.</returns> public TrafficHttpFrontend GetFrontendForWarmTarget(TrafficWarmTarget target) { // We'll match frontends on scheme, hostname, port, and longest path prefix. // // Select the candidate frontends that match on scheme, hostname, and port: var uri = new Uri(target.Uri); var tls = uri.Scheme.Equals("https", StringComparison.InvariantCultureIgnoreCase); var candidates = Frontends.Where(fe => fe.Tls == tls && fe.Host.Equals(uri.Host, StringComparison.InvariantCultureIgnoreCase) && fe.ProxyPort == uri.Port).ToList(); // We're done if there's no or only one candidate. if (candidates.Count == 0) { return(null); } else if (candidates.Count == 1) { return(candidates.Single()); } // There's more than one candidate, so we'll try to match the frontend based on the // the path prefix, longest prefixes first. foreach (var frontend in candidates.OrderByDescending(fe => (fe.PathPrefix ?? string.Empty).Length)) { var frontendPrefix = frontend.PathPrefix ?? string.Empty; if (uri.AbsolutePath.StartsWith(frontendPrefix)) { return(frontend); } } // None of the prefixes matched. return(null); }
/// <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."); } } } }