Ejemplo n.º 1
0
        public async Task AllowedHeaders_Copied(string names, int expected)
        {
            var httpContext   = new DefaultHttpContext();
            var proxyResponse = new HttpResponseMessage();

            proxyResponse.Headers.TryAddWithoutValidation("header1", "value1");
            proxyResponse.Headers.TryAddWithoutValidation("header2", "value2");
            proxyResponse.Headers.TryAddWithoutValidation("header3", "value3");
            proxyResponse.Headers.TryAddWithoutValidation("header4", "value4");
            proxyResponse.Headers.TryAddWithoutValidation("header5", "value5");

            var allowed          = names.Split(';');
            var transform        = new ResponseHeadersAllowedTransform(allowed);
            var transformContext = new ResponseTransformContext()
            {
                HttpContext   = httpContext,
                ProxyResponse = proxyResponse,
                HeadersCopied = false,
            };
            await transform.ApplyAsync(transformContext);

            Assert.True(transformContext.HeadersCopied);

            Assert.Equal(expected, httpContext.Response.Headers.Count());
            foreach (var header in httpContext.Response.Headers)
            {
                Assert.Contains(header.Key, allowed, StringComparer.OrdinalIgnoreCase);
            }
        }
Ejemplo n.º 2
0
    /// <summary>
    /// Removes and returns the current header value by first checking the HttpResponse
    /// and falling back to the value from HttpResponseMessage or HttpContent only if
    /// <see cref="ResponseTransformContext.HeadersCopied"/> is not set.
    /// This ordering allows multiple transforms to mutate the same header.
    /// </summary>
    /// <param name="context">The transform context.</param>
    /// <param name="headerName">The name of the header to take.</param>
    /// <returns>The response header value, or StringValues.Empty if none.</returns>
    public static StringValues TakeHeader(ResponseTransformContext context, string headerName)
    {
        if (context is null)
        {
            throw new ArgumentNullException(nameof(context));
        }

        if (string.IsNullOrEmpty(headerName))
        {
            throw new ArgumentException($"'{nameof(headerName)}' cannot be null or empty.", nameof(headerName));
        }

        if (context.HttpContext.Response.Headers.TryGetValue(headerName, out var existingValues))
        {
            context.HttpContext.Response.Headers.Remove(headerName);
        }
        else if (context.ProxyResponse is { } proxyResponse&& !context.HeadersCopied)
        {
            if (!RequestUtilities.TryGetValues(proxyResponse.Headers, headerName, out existingValues))
            {
                RequestUtilities.TryGetValues(proxyResponse.Content.Headers, headerName, out existingValues);
            }
        }

        return(existingValues);
    }
Ejemplo n.º 3
0
        public override async ValueTask <bool> TransformResponseAsync(HttpContext httpContext, HttpResponseMessage?proxyResponse)
        {
            if (ShouldCopyResponseHeaders.GetValueOrDefault(true))
            {
                await base.TransformResponseAsync(httpContext, proxyResponse);
            }

            if (ResponseTransforms.Count == 0)
            {
                return(true);
            }

            var transformContext = new ResponseTransformContext()
            {
                HttpContext   = httpContext,
                ProxyResponse = proxyResponse,
                HeadersCopied = ShouldCopyResponseHeaders.GetValueOrDefault(true),
            };

            foreach (var responseTransform in ResponseTransforms)
            {
                await responseTransform.ApplyAsync(transformContext);
            }

            return(!transformContext.SuppressResponseBody);
        }
Ejemplo n.º 4
0
    /// <summary>
    /// Removes and returns the current header value by first checking the HttpResponse
    /// and falling back to the value from HttpResponseMessage or HttpContent only if
    /// <see cref="ResponseTransformContext.HeadersCopied"/> is not set.
    /// This ordering allows multiple transforms to mutate the same header.
    /// </summary>
    /// <param name="context">The transform context.</param>
    /// <param name="headerName">The name of the header to take.</param>
    /// <returns>The response header value, or StringValues.Empty if none.</returns>
    public static StringValues TakeHeader(ResponseTransformContext context, string headerName)
    {
        if (context is null)
        {
            throw new System.ArgumentNullException(nameof(context));
        }

        if (string.IsNullOrEmpty(headerName))
        {
            throw new System.ArgumentException($"'{nameof(headerName)}' cannot be null or empty.", nameof(headerName));
        }

        var existingValues = StringValues.Empty;

        if (context.HttpContext.Response.Headers.TryGetValue(headerName, out var responseValues))
        {
            context.HttpContext.Response.Headers.Remove(headerName);
            existingValues = responseValues;
        }
        else if (context.ProxyResponse != null && !context.HeadersCopied &&
                 (context.ProxyResponse.Headers.TryGetValues(headerName, out var values) ||
                  context.ProxyResponse.Content.Headers.TryGetValues(headerName, out values)))
        {
            existingValues = (string[])values;
        }

        return(existingValues);
    }
        public async Task ApplyAsync_InvokeAffinitizeRequest()
        {
            var cluster     = GetCluster();
            var destination = cluster.DestinationManager.Items[0];
            var provider    = new Mock <ISessionAffinityProvider>(MockBehavior.Strict);

            provider.Setup(p => p.AffinitizeRequest(It.IsAny <HttpContext>(), It.IsNotNull <SessionAffinityOptions>(), It.IsAny <DestinationInfo>()));

            var transform = new AffinitizeTransform(provider.Object);

            var context = new DefaultHttpContext();

            context.Features.Set <IReverseProxyFeature>(new ReverseProxyFeature()
            {
                ClusterSnapshot    = cluster.Config,
                ProxiedDestination = destination,
            });

            var transformContext = new ResponseTransformContext()
            {
                HttpContext = context,
            };
            await transform.ApplyAsync(transformContext);

            provider.Verify();
        }
Ejemplo n.º 6
0
    public async Task ApplyAsync_InvokeAffinitizeRequest()
    {
        var cluster     = GetCluster();
        var destination = cluster.Destinations.Values.First();
        var provider    = new Mock <ISessionAffinityPolicy>(MockBehavior.Strict);

        provider.Setup(p => p.AffinitizeResponse(It.IsAny <HttpContext>(), It.IsAny <ClusterState>(), It.IsNotNull <SessionAffinityConfig>(), It.IsAny <DestinationState>()));

        var transform = new AffinitizeTransform(provider.Object);

        var context = new DefaultHttpContext();

        context.Features.Set <IReverseProxyFeature>(new ReverseProxyFeature()
        {
            Cluster            = cluster.Model,
            Route              = new RouteModel(new RouteConfig(), cluster, HttpTransformer.Default),
            ProxiedDestination = destination,
        });

        var transformContext = new ResponseTransformContext()
        {
            HttpContext = context,
        };
        await transform.ApplyAsync(transformContext);

        provider.Verify();
    }
Ejemplo n.º 7
0
    public override ValueTask ApplyAsync(ResponseTransformContext context)
    {
        var proxyFeature = context.HttpContext.GetReverseProxyFeature();
        var options      = proxyFeature.Cluster.Config.SessionAffinity;

        // The transform should only be added to routes that have affinity enabled.
        // However, the cluster can be re-assigned dynamically.
        if (options is null || !options.Enabled.GetValueOrDefault())
        {
            return(default);
    /// <inheritdoc/>
    public override ValueTask ApplyAsync(ResponseTransformContext context)
    {
        if (context is null)
        {
            throw new ArgumentNullException(nameof(context));
        }

        if (context.ProxyResponse == null)
        {
            return(default);
Ejemplo n.º 9
0
        public override ValueTask ApplyAsync(ResponseTransformContext context)
        {
            var proxyFeature = context.HttpContext.GetRequiredProxyFeature();
            var options      = proxyFeature.ClusterSnapshot.Options.SessionAffinity;

            // The transform should only be added to routes that have affinity enabled.
            Debug.Assert(options?.Enabled ?? true, "Session affinity is not enabled");
            var selectedDestination = proxyFeature.ProxiedDestination;

            _sessionAffinityProvider.AffinitizeRequest(context.HttpContext, options, selectedDestination);
            return(default);
Ejemplo n.º 10
0
    public override ValueTask ApplyAsync(ResponseTransformContext context)
    {
        var proxyFeature = context.HttpContext.GetReverseProxyFeature();
        var options      = proxyFeature.Cluster.Config.SessionAffinity;

        // The transform should only be added to routes that have affinity enabled.
        Debug.Assert(options?.Enabled ?? true, "Session affinity is not enabled");
        var selectedDestination = proxyFeature.ProxiedDestination !;

        _sessionAffinityPolicy.AffinitizeResponse(context.HttpContext, proxyFeature.Route.Cluster !, options !, selectedDestination);
        return(default);
Ejemplo n.º 11
0
    // Assumes the response status code has been set on the HttpContext already.
    /// <inheritdoc/>
    public override ValueTask ApplyAsync(ResponseTransformContext context)
    {
        if (context is null)
        {
            throw new ArgumentNullException(nameof(context));
        }

        if (Condition == ResponseCondition.Always ||
            Success(context) == (Condition == ResponseCondition.Success))
        {
            context.HttpContext.Response.Headers.Remove(HeaderName);
        }

        return(default);
Ejemplo n.º 12
0
    public async Task ProxyResponseNull_DoNothing()
    {
        var httpContext = new DefaultHttpContext();

        httpContext.Response.StatusCode = 502;

        var transform        = new ResponseHeadersAllowedTransform(new[] { "header1" });
        var transformContext = new ResponseTransformContext()
        {
            HttpContext   = httpContext,
            ProxyResponse = null,
            HeadersCopied = false,
        };
        await transform.ApplyAsync(transformContext);
    }
Ejemplo n.º 13
0
        public async Task AddResponseHeader_Success(string startValue, int status, string value, bool append, ResponseCondition condition, string expected, bool responseNull)
        {
            var httpContext = new DefaultHttpContext();

            httpContext.Response.Headers["name"] = startValue.Split(";", System.StringSplitOptions.RemoveEmptyEntries);
            httpContext.Response.StatusCode      = status;
            var transformContext = new ResponseTransformContext()
            {
                HttpContext   = httpContext,
                ProxyResponse = responseNull ? null :  new HttpResponseMessage(),
                HeadersCopied = true,
            };
            var transform = new ResponseHeaderValueTransform("name", value, append, condition);
            await transform.ApplyAsync(transformContext);

            Assert.Equal(expected.Split(";", System.StringSplitOptions.RemoveEmptyEntries), httpContext.Response.Headers["name"]);
        }
Ejemplo n.º 14
0
    // Assumes the response status code has been set on the HttpContext already.
    /// <inheritdoc/>
    public override ValueTask ApplyAsync(ResponseTransformContext context)
    {
        if (context is null)
        {
            throw new ArgumentNullException(nameof(context));
        }

        if (Condition == ResponseCondition.Always ||
            Success(context) == (Condition == ResponseCondition.Success))
        {
            var existingHeader = TakeHeader(context, HeaderName);
            if (Append)
            {
                var value = StringValues.Concat(existingHeader, Value);
                SetHeader(context, HeaderName, value);
            }
            else
            {
                SetHeader(context, HeaderName, Value);
            }
        }

        return(default);
Ejemplo n.º 15
0
        public async Task ContentHeaders_CopiedIfAllowed(string names, int expected)
        {
            var httpContext   = new DefaultHttpContext();
            var proxyResponse = new HttpResponseMessage();

            proxyResponse.Content = new StringContent("");
            proxyResponse.Content.Headers.TryAddWithoutValidation("header0", "value0");
            proxyResponse.Content.Headers.TryAddWithoutValidation(HeaderNames.Allow, "value1");
            proxyResponse.Content.Headers.TryAddWithoutValidation(HeaderNames.ContentDisposition, "value2");
            proxyResponse.Content.Headers.TryAddWithoutValidation(HeaderNames.ContentEncoding, "value3");
            proxyResponse.Content.Headers.TryAddWithoutValidation(HeaderNames.ContentLanguage, "value4");
            proxyResponse.Content.Headers.TryAddWithoutValidation(HeaderNames.ContentLocation, "value5");
            proxyResponse.Content.Headers.TryAddWithoutValidation(HeaderNames.ContentMD5, "value6");
            proxyResponse.Content.Headers.TryAddWithoutValidation(HeaderNames.ContentRange, "value7");
            proxyResponse.Content.Headers.TryAddWithoutValidation(HeaderNames.ContentType, "value8");
            proxyResponse.Content.Headers.TryAddWithoutValidation(HeaderNames.Expires, "value9");
            proxyResponse.Content.Headers.TryAddWithoutValidation(HeaderNames.LastModified, "value10");
            proxyResponse.Content.Headers.TryAddWithoutValidation(HeaderNames.ContentLength, "0");

            var allowed          = names.Split(';');
            var transform        = new ResponseHeadersAllowedTransform(allowed);
            var transformContext = new ResponseTransformContext()
            {
                HttpContext   = httpContext,
                ProxyResponse = proxyResponse,
                HeadersCopied = false,
            };
            await transform.ApplyAsync(transformContext);

            Assert.True(transformContext.HeadersCopied);

            Assert.Equal(expected, httpContext.Response.Headers.Count());
            foreach (var header in httpContext.Response.Headers)
            {
                Assert.Contains(header.Key, allowed, StringComparer.OrdinalIgnoreCase);
            }
        }
Ejemplo n.º 16
0
 internal static bool Success(ResponseTransformContext context)
 {
     // TODO: How complex should this get? Compare with http://nginx.org/en/docs/http/ngx_http_headers_module.html#add_header
     return(context.HttpContext.Response.StatusCode < 400);
 }
Ejemplo n.º 17
0
 /// <summary>
 /// Sets the given header on the HttpResponse.
 /// </summary>
 public static void SetHeader(ResponseTransformContext context, string headerName, StringValues values)
 {
     context.HttpContext.Response.Headers[headerName] = values;
 }
Ejemplo n.º 18
0
 /// <inheritdoc/>
 public override ValueTask ApplyAsync(ResponseTransformContext context)
 {
     return(_func(context));
 }
Ejemplo n.º 19
0
 /// <summary>
 /// Transforms the given response. The status and headers will have (optionally) already been
 /// copied to the <see cref="HttpResponse"/> and any changes should be made there.
 /// </summary>
 public abstract ValueTask ApplyAsync(ResponseTransformContext context);