private static StructuredTransformer CreateTransformer(TransformBuilderContext context) { // RequestHeaderOriginalHostKey defaults to false, and CopyRequestHeaders defaults to true. // If RequestHeaderOriginalHostKey was not specified then we need to make sure the transform gets // added anyways to remove the original host. If CopyRequestHeaders is false then we can omit the // transform. if (context.CopyRequestHeaders.GetValueOrDefault(true) && !context.RequestTransforms.Any(item => item is RequestHeaderOriginalHostTransform)) { context.AddOriginalHost(false); } // Add default forwarders only if they haven't already been added or disabled. if (context.UseDefaultForwarders.GetValueOrDefault(true)) { context.AddXForwarded(); } return(new StructuredTransformer( context.CopyRequestHeaders, context.CopyResponseHeaders, context.CopyResponseTrailers, context.RequestTransforms, context.ResponseTransforms, context.ResponseTrailersTransforms)); }
/// <summary> /// Adds the transform that will append or set the query parameter from a route value. /// </summary> public static TransformBuilderContext AddQueryRouteValue(this TransformBuilderContext context, string queryKey, string routeValueKey, bool append = true) { context.RequestTransforms.Add(new QueryParameterRouteTransform( append ? QueryStringTransformMode.Append : QueryStringTransformMode.Set, queryKey, routeValueKey)); return(context); }
private static void ValidatePathRouteValues(TransformBuilderContext builderContext) { var requestTransform = Assert.Single(builderContext.RequestTransforms); var pathRouteValuesTransform = Assert.IsType <PathRouteValuesTransform>(requestTransform); Assert.Equal("/path#", pathRouteValuesTransform.Pattern.RawText); }
public bool Build(TransformBuilderContext context, IReadOnlyDictionary <string, string> transformValues) { if (transformValues.TryGetValue(PathSetKey, out var pathSet)) { TransformHelpers.CheckTooManyParameters(transformValues, expected: 1); var path = MakePathString(pathSet); context.AddPathSet(path); } else if (transformValues.TryGetValue(PathPrefixKey, out var pathPrefix)) { TransformHelpers.CheckTooManyParameters(transformValues, expected: 1); var path = MakePathString(pathPrefix); context.AddPathPrefix(path); } else if (transformValues.TryGetValue(PathRemovePrefixKey, out var pathRemovePrefix)) { TransformHelpers.CheckTooManyParameters(transformValues, expected: 1); var path = MakePathString(pathRemovePrefix); context.AddPathRemovePrefix(path); } else if (transformValues.TryGetValue(PathPatternKey, out var pathPattern)) { TransformHelpers.CheckTooManyParameters(transformValues, expected: 1); var path = MakePathString(pathPattern); // We don't use the extension here because we want to avoid doing a DI lookup for the binder every time. context.RequestTransforms.Add(new PathRouteValuesTransform(path.Value, _binderFactory)); } else { return(false); } return(true); }
internal static void RemoveAllXForwardedHeaders(TransformBuilderContext context, string prefix) { context.AddXForwardedFor(prefix + ForwardedTransformFactory.ForKey, ForwardedTransformActions.Remove); context.AddXForwardedPrefix(prefix + ForwardedTransformFactory.PrefixKey, ForwardedTransformActions.Remove); context.AddXForwardedHost(prefix + ForwardedTransformFactory.HostKey, ForwardedTransformActions.Remove); context.AddXForwardedProto(prefix + ForwardedTransformFactory.ProtoKey, ForwardedTransformActions.Remove); }
public bool Build(TransformBuilderContext context, IReadOnlyDictionary <string, string> transformValues) { if (transformValues.TryGetValue(RequestHeadersCopyKey, out var copyHeaders)) { TransformHelpers.CheckTooManyParameters(transformValues, expected: 1); context.CopyRequestHeaders = bool.Parse(copyHeaders); } else if (transformValues.TryGetValue(RequestHeaderOriginalHostKey, out var originalHost)) { TransformHelpers.CheckTooManyParameters(transformValues, expected: 1); context.UseOriginalHost = bool.Parse(originalHost); } else if (transformValues.TryGetValue(RequestHeaderKey, out var headerName)) { TransformHelpers.CheckTooManyParameters(transformValues, expected: 2); if (transformValues.TryGetValue(SetKey, out var setValue)) { context.AddRequestHeader(headerName, setValue, append: false); } else if (transformValues.TryGetValue(AppendKey, out var appendValue)) { context.AddRequestHeader(headerName, appendValue, append: true); } else { throw new ArgumentException($"Unexpected parameters for RequestHeader: {string.Join(';', transformValues.Keys)}. Expected 'Set' or 'Append'"); } } else { return(false); } return(true); }
public bool Build(TransformBuilderContext context, IReadOnlyDictionary <string, string> transformValues) { Assert.NotNull(context.Services); Assert.NotNull(context.Route); BuildCalls++; if (transformValues.TryGetValue(_v, out var _)) { context.AddResponseTrailersTransform(context => default);
private static void ValidatePathRemovePrefix(TransformBuilderContext builderContext) { var requestTransform = Assert.Single(builderContext.RequestTransforms); var pathStringTransform = Assert.IsType <PathStringTransform>(requestTransform); Assert.Equal(PathStringTransform.PathTransformMode.RemovePrefix, pathStringTransform.Mode); Assert.Equal("/path#", pathStringTransform.Value.Value); }
/// <summary> /// Adds the transform which will add X-Forwarded-* request headers. /// </summary> public static TransformBuilderContext AddXForwarded(this TransformBuilderContext context, ForwardedTransformActions action = ForwardedTransformActions.Set) { context.AddXForwardedFor(action: action); context.AddXForwardedPrefix(action: action); context.AddXForwardedHost(action: action); context.AddXForwardedProto(action: action); return(context); }
public void Apply(TransformBuilderContext context) { var options = context.Cluster?.SessionAffinity; if (options != null && options.Enabled.GetValueOrDefault()) { var policy = _sessionAffinityPolicies.GetRequiredServiceById(options.Policy, SessionAffinityConstants.Policies.Cookie); context.ResponseTransforms.Add(new AffinitizeTransform(policy)); } }
public void Apply(TransformBuilderContext context) { var options = context.Cluster?.SessionAffinity; if ((options?.Enabled).GetValueOrDefault()) { var provider = _sessionAffinityProviders.GetRequiredServiceById(options.Mode, SessionAffinityConstants.Modes.Cookie); context.ResponseTransforms.Add(new AffinitizeTransform(provider)); } }
/// <summary> /// Adds the transform which will add X-Forwarded-Proto request header. /// </summary> public static TransformBuilderContext AddXForwardedProto(this TransformBuilderContext context, string headerName = "X-Forwarded-Proto", ForwardedTransformActions action = ForwardedTransformActions.Set) { context.UseDefaultForwarders = false; if (action == ForwardedTransformActions.Off) { return(context); } context.RequestTransforms.Add(new RequestHeaderXForwardedProtoTransform(headerName, action)); return(context); }
internal StructuredTransformer CreateInternal(Action <TransformBuilderContext> action) { var context = new TransformBuilderContext { Services = _services, }; action(context); return(CreateTransformer(context)); }
/// <summary> /// Adds the transform which will add X-Forwarded-* request headers. /// </summary> public static TransformBuilderContext AddXForwarded(this TransformBuilderContext context, ForwardedTransformActions action = ForwardedTransformActions.Set) { context.AddXForwardedFor(action: action); context.AddXForwardedPrefix(action: action); context.AddXForwardedHost(action: action); context.AddXForwardedProto(action: action); // Remove the Forwarded header when an X-Forwarded transform is enabled TransformHelpers.RemoveForwardedHeader(context); return(context); }
/// <summary> /// Clones the route and adds the transform which will set the request path with the given value. /// </summary> public static TransformBuilderContext AddPathRouteValues(this TransformBuilderContext context, PathString pattern) { if (pattern.Value is null) { throw new ArgumentNullException(nameof(pattern)); } var binder = context.Services.GetRequiredService <TemplateBinderFactory>(); context.RequestTransforms.Add(new PathRouteValuesTransform(pattern.Value, binder)); return(context); }
/// <summary> /// Adds the transform which will copy or remove the original host header. /// </summary> public static TransformBuilderContext AddOriginalHost(this TransformBuilderContext context, bool useOriginal = true) { if (useOriginal) { context.RequestTransforms.Add(RequestHeaderOriginalHostTransform.OriginalHost); } else { context.RequestTransforms.Add(RequestHeaderOriginalHostTransform.SuppressHost); } return(context); }
public bool Build(TransformBuilderContext context, IReadOnlyDictionary <string, string> transformValues) { if (transformValues.TryGetValue("CustomTransform", out var value)) { if (string.IsNullOrEmpty(value)) { throw new ArgumentException("A non-empty CustomTransform value is required"); } context.AddRequestTransform(transformContext => { transformContext.ProxyRequest.Options.Set(new HttpRequestOptionsKey <string>("CustomTransform"), value); return(default);
/// <summary> /// Adds the transform which will add the Forwarded header as defined by [RFC 7239](https://tools.ietf.org/html/rfc7239). /// </summary> public static TransformBuilderContext AddForwarded(this TransformBuilderContext context, bool useHost = true, bool useProto = true, NodeFormat forFormat = NodeFormat.Random, NodeFormat byFormat = NodeFormat.Random, bool append = true) { context.UseDefaultForwarders = false; if (byFormat != NodeFormat.None || forFormat != NodeFormat.None || useHost || useProto) { var random = context.Services.GetRequiredService <IRandomFactory>(); context.RequestTransforms.Add(new RequestHeaderForwardedTransform(random, forFormat, byFormat, useHost, useProto, append)); } return(context); }
/// <inheritdoc /> public void Apply(TransformBuilderContext transformBuildContext) { var routeValue = transformBuildContext.Route.Metadata?.GetValueOrDefault(Constants.Yarp.TokenTypeMetadata); var clusterValue = transformBuildContext.Cluster?.Metadata?.GetValueOrDefault(Constants.Yarp.TokenTypeMetadata); // no metadata if (string.IsNullOrEmpty(routeValue) && string.IsNullOrEmpty(clusterValue)) { return; } var values = new HashSet <string>(); if (!string.IsNullOrEmpty(routeValue)) { values.Add(routeValue); } if (!string.IsNullOrEmpty(clusterValue)) { values.Add(clusterValue); } if (values.Count > 1) { throw new ArgumentException( "Mismatching Duende.Bff.Yarp.TokenType route or cluster metadata values found"); } if (!TokenType.TryParse(values.First(), true, out TokenType tokenType)) { throw new ArgumentException("Invalid value for Duende.Bff.Yarp.TokenType metadata"); } transformBuildContext.AddRequestTransform(async transformContext => { transformContext.HttpContext.CheckForBffMiddleware(_options); var token = await transformContext.HttpContext.GetManagedAccessToken(tokenType); if (!string.IsNullOrEmpty(token)) { transformContext.ProxyRequest.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token); } else { _logger.AccessTokenMissing(transformBuildContext?.Route?.RouteId ?? "unknown route", tokenType); } }); }
public bool Build(TransformBuilderContext context, IReadOnlyDictionary <string, string> transformValues) { if (transformValues.TryGetValue(RequestHeadersCopyKey, out var copyHeaders)) { TransformHelpers.CheckTooManyParameters(transformValues, expected: 1); context.CopyRequestHeaders = bool.Parse(copyHeaders); } else if (transformValues.TryGetValue(RequestHeaderOriginalHostKey, out var originalHost)) { TransformHelpers.CheckTooManyParameters(transformValues, expected: 1); context.AddOriginalHost(bool.Parse(originalHost)); } else if (transformValues.TryGetValue(RequestHeaderKey, out var headerName)) { TransformHelpers.CheckTooManyParameters(transformValues, expected: 2); if (transformValues.TryGetValue(SetKey, out var setValue)) { context.AddRequestHeader(headerName, setValue, append: false); } else if (transformValues.TryGetValue(AppendKey, out var appendValue)) { context.AddRequestHeader(headerName, appendValue, append: true); } else { throw new ArgumentException($"Unexpected parameters for RequestHeader: {string.Join(';', transformValues.Keys)}. Expected 'Set' or 'Append'"); } } else if (transformValues.TryGetValue(RequestHeaderRemoveKey, out var removeHeaderName)) { TransformHelpers.CheckTooManyParameters(transformValues, expected: 1); context.AddRequestHeaderRemove(removeHeaderName); } else if (transformValues.TryGetValue(RequestHeadersAllowedKey, out var allowedHeaders)) { TransformHelpers.CheckTooManyParameters(transformValues, expected: 1); var headersList = allowedHeaders.Split(';', options: StringSplitOptions.RemoveEmptyEntries #if NET | StringSplitOptions.TrimEntries #endif ); context.AddRequestHeadersAllowed(headersList); } else { return(false); } return(true); }
/// <summary> /// Adds a transform Func that runs on each response for the given route. /// </summary> public static TransformBuilderContext AddResponseTrailersTransform(this TransformBuilderContext context, Func <ResponseTrailersTransformContext, ValueTask> func) { if (context is null) { throw new ArgumentNullException(nameof(context)); } if (func is null) { throw new ArgumentNullException(nameof(func)); } context.ResponseTrailersTransforms.Add(new ResponseTrailersFuncTransform(func)); return(context); }
public void Apply(TransformBuilderContext transformBuildContext) { // Check all routes for a custom property and add the associated transform. if ((transformBuildContext.Route.Metadata?.TryGetValue("CustomMetadata", out var value) ?? false) || (transformBuildContext.Cluster?.Metadata?.TryGetValue("CustomMetadata", out value) ?? false)) { if (string.IsNullOrEmpty(value)) { throw new ArgumentException("A non-empty CustomMetadata value is required"); } transformBuildContext.AddRequestTransform(transformContext => { transformContext.ProxyRequest.Options.Set(new HttpRequestOptionsKey <string>("CustomMetadata"), value); return(default);
public bool Build(TransformBuilderContext context, IReadOnlyDictionary <string, string> transformValues) { if (transformValues.TryGetValue(QueryValueParameterKey, out var queryValueParameter)) { TransformHelpers.CheckTooManyParameters(transformValues, expected: 2); if (transformValues.TryGetValue(AppendKey, out var appendValue)) { context.AddQueryValue(queryValueParameter, appendValue, append: true); } else if (transformValues.TryGetValue(SetKey, out var setValue)) { context.AddQueryValue(queryValueParameter, setValue, append: false); } else { throw new NotSupportedException(string.Join(";", transformValues.Keys)); } } else if (transformValues.TryGetValue(QueryRouteParameterKey, out var queryRouteParameter)) { TransformHelpers.CheckTooManyParameters(transformValues, expected: 2); if (transformValues.TryGetValue(AppendKey, out var routeValueKeyAppend)) { context.AddQueryRouteValue(queryRouteParameter, routeValueKeyAppend, append: true); } else if (transformValues.TryGetValue(SetKey, out var routeValueKeySet)) { context.AddQueryRouteValue(queryRouteParameter, routeValueKeySet, append: false); } else { throw new NotSupportedException(string.Join(";", transformValues.Keys)); } } else if (transformValues.TryGetValue(QueryRemoveParameterKey, out var removeQueryParameter)) { TransformHelpers.CheckTooManyParameters(transformValues, expected: 1); context.AddQueryRemoveKey(removeQueryParameter); } else { return(false); } return(true); }
public bool Build(TransformBuilderContext context, IReadOnlyDictionary <string, string> transformValues) { if (transformValues.TryGetValue(HttpMethodChangeKey, out var fromHttpMethod)) { TransformHelpers.CheckTooManyParameters(transformValues, expected: 2); if (transformValues.TryGetValue(SetKey, out var toHttpMethod)) { context.AddHttpMethodChange(fromHttpMethod, toHttpMethod); } } else { return(false); } return(true); }
public void Apply(TransformBuilderContext context) { string daprAct = null; if (context.Route.Metadata?.TryGetValue(DaprYarpConst.MetaKeys.Dapr, out daprAct) ?? false) { switch (daprAct) { case DaprYarpConst.DaprAct.Method: context.AddRequestTransform(transformContext => { var index = transformContext.Path.Value !.IndexOf('/', 5); // format: /api/xxxx var appId = transformContext.Path.Value.Substring(5, index - 5); var newPath = transformContext.Path.Value.Substring(index); transformContext.ProxyRequest.RequestUri = new Uri($"{transformContext.DestinationPrefix}/v1.0/invoke/{appId}/method{newPath}"); return(ValueTask.CompletedTask); }); break; } } }
// This is separate from Build for testing purposes. internal StructuredTransformer BuildInternal(ProxyRoute route, Cluster cluster) { var rawTransforms = route.Transforms; var context = new TransformBuilderContext { Services = _services, Route = route, Cluster = cluster, }; if (rawTransforms?.Count > 0) { foreach (var rawTransform in rawTransforms) { var handled = false; foreach (var factory in _factories) { if (factory.Build(context, rawTransform)) { handled = true; break; } } if (!handled) { throw new ArgumentException($"Unknown transform: {string.Join(';', rawTransform.Keys)}"); } } } // Let the app add any more transforms it wants. foreach (var transformProvider in _providers) { transformProvider.Apply(context); } return(CreateTransformer(context)); }
/// <summary> /// Adds the transform which will add X-Forwarded-* request headers. /// </summary> public static TransformBuilderContext AddXForwarded(this TransformBuilderContext context, string headerPrefix = "X-Forwarded-", bool useFor = true, bool useHost = true, bool useProto = true, bool usePrefix = true, bool append = true) { context.UseDefaultForwarders = false; if (useFor) { context.RequestTransforms.Add(new RequestHeaderXForwardedForTransform(headerPrefix + ForwardedTransformFactory.ForKey, append)); } if (useHost) { context.RequestTransforms.Add(new RequestHeaderXForwardedHostTransform(headerPrefix + ForwardedTransformFactory.HostKey, append)); } if (useProto) { context.RequestTransforms.Add(new RequestHeaderXForwardedProtoTransform(headerPrefix + ForwardedTransformFactory.ProtoKey, append)); } if (usePrefix) { context.RequestTransforms.Add(new RequestHeaderXForwardedPrefixTransform(headerPrefix + ForwardedTransformFactory.PrefixKey, append)); } return(context); }
/// <summary> /// Adds the transform which will add the Forwarded header as defined by [RFC 7239](https://tools.ietf.org/html/rfc7239). /// </summary> public static TransformBuilderContext AddForwarded(this TransformBuilderContext context, bool useHost = true, bool useProto = true, NodeFormat forFormat = NodeFormat.Random, NodeFormat byFormat = NodeFormat.Random, ForwardedTransformActions action = ForwardedTransformActions.Set) { context.UseDefaultForwarders = false; if (action == ForwardedTransformActions.Off) { return(context); } if (byFormat != NodeFormat.None || forFormat != NodeFormat.None || useHost || useProto) { var random = context.Services.GetRequiredService <IRandomFactory>(); context.RequestTransforms.Add(new RequestHeaderForwardedTransform(random, forFormat, byFormat, useHost, useProto, action)); // Remove the X-Forwarded headers when an Forwarded transform is enabled TransformHelpers.RemoveAllXForwardedHeaders(context, ForwardedTransformFactory.DefaultXForwardedPrefix); } return(context); }
public void EnableSession_InvalidMode_Fails() { var affinityPolicy = new Mock <ISessionAffinityPolicy>(MockBehavior.Strict); affinityPolicy.SetupGet(p => p.Name).Returns("Policy"); var transformProvider = new AffinitizeTransformProvider(new[] { affinityPolicy.Object }); var cluster = new ClusterConfig { ClusterId = "cluster1", SessionAffinity = new SessionAffinityConfig { Enabled = true, Policy = "Invalid", AffinityKeyName = "Key1" } }; var validationContext = new TransformClusterValidationContext() { Cluster = cluster, }; transformProvider.ValidateCluster(validationContext); var ex = Assert.Single(validationContext.Errors); Assert.Equal("No matching ISessionAffinityPolicy found for the session affinity policy 'Invalid' set on the cluster 'cluster1'.", ex.Message); var builderContext = new TransformBuilderContext() { Cluster = cluster, }; ex = Assert.Throws <ArgumentException>(() => transformProvider.Apply(builderContext)); Assert.Equal($"No {typeof(ISessionAffinityPolicy).FullName} was found for the id 'Invalid'. (Parameter 'id')", ex.Message); }
public void EnableSessionAffinity_AddsTransform() { var affinityPolicy = new Mock <ISessionAffinityPolicy>(MockBehavior.Strict); affinityPolicy.SetupGet(p => p.Name).Returns("Policy"); var transformProvider = new AffinitizeTransformProvider(new[] { affinityPolicy.Object }); var cluster = new ClusterConfig { ClusterId = "cluster1", SessionAffinity = new SessionAffinityConfig { Enabled = true, Policy = "Policy", AffinityKeyName = "Key1" } }; var validationContext = new TransformClusterValidationContext() { Cluster = cluster, }; transformProvider.ValidateCluster(validationContext); Assert.Empty(validationContext.Errors); var builderContext = new TransformBuilderContext() { Cluster = cluster, }; transformProvider.Apply(builderContext); Assert.IsType <AffinitizeTransform>(builderContext.ResponseTransforms.Single()); }