public async Task AllValues_Added(string startValue, ForwardedTransformActions action, string expected)
        {
            var randomFactory = new TestRandomFactory();

            randomFactory.Instance = new TestRandom()
            {
                Sequence = new[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17 }
            };
            var httpContext = new DefaultHttpContext();

            httpContext.Request.Scheme             = "https";
            httpContext.Request.Host               = new HostString("myHost", 80);
            httpContext.Connection.RemoteIpAddress = IPAddress.IPv6Loopback;
            httpContext.Connection.RemotePort      = 10;
            var proxyRequest = new HttpRequestMessage();

            proxyRequest.Headers.Add("Forwarded", startValue.Split("|", StringSplitOptions.RemoveEmptyEntries));
            var transform = new RequestHeaderForwardedTransform(randomFactory,
                                                                forFormat: NodeFormat.IpAndPort,
                                                                byFormat: NodeFormat.Random,
                                                                host: true, proto: true, action);
            await transform.ApplyAsync(new RequestTransformContext()
            {
                HttpContext   = httpContext,
                ProxyRequest  = proxyRequest,
                HeadersCopied = true,
            });

            Assert.Equal(expected.Split("|", StringSplitOptions.RemoveEmptyEntries), proxyRequest.Headers.GetValues("Forwarded"));
        }
        public async Task Proto_Added(string startValue, string scheme, ForwardedTransformActions action, string expected)
        {
            var randomFactory = new TestRandomFactory();
            var httpContext   = new DefaultHttpContext();

            httpContext.Request.Scheme = scheme;
            var proxyRequest = new HttpRequestMessage();

            proxyRequest.Headers.Add("Forwarded", startValue.Split("|", StringSplitOptions.RemoveEmptyEntries));
            var transform = new RequestHeaderForwardedTransform(randomFactory, forFormat: NodeFormat.None,
                                                                byFormat: NodeFormat.None, host: false, proto: true, action);
            await transform.ApplyAsync(new RequestTransformContext()
            {
                HttpContext   = httpContext,
                ProxyRequest  = proxyRequest,
                HeadersCopied = true,
            });

            if (string.IsNullOrEmpty(expected))
            {
                Assert.False(proxyRequest.Headers.TryGetValues("Forwarded", out _));
            }
            else
            {
                Assert.Equal(expected.Split("|", StringSplitOptions.RemoveEmptyEntries), proxyRequest.Headers.GetValues("Forwarded"));
            }
        }
        public async Task By_Added(string startValue, string ip, int port, NodeFormat format, ForwardedTransformActions action, string expected)
        {
            var randomFactory = new TestRandomFactory();

            randomFactory.Instance = new TestRandom()
            {
                Sequence = new[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17 }
            };
            var httpContext = new DefaultHttpContext();

            httpContext.Connection.LocalIpAddress = string.IsNullOrEmpty(ip) ? null : IPAddress.Parse(ip);
            httpContext.Connection.LocalPort      = port;
            var proxyRequest = new HttpRequestMessage();

            proxyRequest.Headers.Add("Forwarded", startValue.Split("|", StringSplitOptions.RemoveEmptyEntries));
            var transform = new RequestHeaderForwardedTransform(randomFactory, forFormat: NodeFormat.None,
                                                                byFormat: format, host: false, proto: false, action);
            await transform.ApplyAsync(new RequestTransformContext()
            {
                HttpContext   = httpContext,
                ProxyRequest  = proxyRequest,
                HeadersCopied = true,
            });

            Assert.Equal(expected.Split("|", StringSplitOptions.RemoveEmptyEntries), proxyRequest.Headers.GetValues("Forwarded"));
        }
Пример #4
0
        /// <inheritdoc/>
        public Transforms Build(IList <IDictionary <string, string> > rawTransforms)
        {
            bool?copyRequestHeaders        = null;
            bool?useOriginalHost           = null;
            bool?forwardersSet             = null;
            var  requestTransforms         = new List <RequestParametersTransform>();
            var  requestHeaderTransforms   = new Dictionary <string, RequestHeaderTransform>(StringComparer.OrdinalIgnoreCase);
            var  responseHeaderTransforms  = new Dictionary <string, ResponseHeaderTransform>(StringComparer.OrdinalIgnoreCase);
            var  responseTrailerTransforms = new Dictionary <string, ResponseHeaderTransform>(StringComparer.OrdinalIgnoreCase);

            if (rawTransforms?.Count > 0)
            {
                foreach (var rawTransform in rawTransforms)
                {
                    if (rawTransform.TryGetValue("PathSet", out var pathSet))
                    {
                        CheckTooManyParameters(rawTransform, expected: 1);
                        var path = MakePathString(pathSet);
                        requestTransforms.Add(new PathStringTransform(PathStringTransform.PathTransformMode.Set, path));
                    }
                    else if (rawTransform.TryGetValue("PathPrefix", out var pathPrefix))
                    {
                        CheckTooManyParameters(rawTransform, expected: 1);
                        var path = MakePathString(pathPrefix);
                        requestTransforms.Add(new PathStringTransform(PathStringTransform.PathTransformMode.Prefix, path));
                    }
                    else if (rawTransform.TryGetValue("PathRemovePrefix", out var pathRemovePrefix))
                    {
                        CheckTooManyParameters(rawTransform, expected: 1);
                        var path = MakePathString(pathRemovePrefix);
                        requestTransforms.Add(new PathStringTransform(PathStringTransform.PathTransformMode.RemovePrefix, path));
                    }
                    else if (rawTransform.TryGetValue("PathPattern", out var pathPattern))
                    {
                        CheckTooManyParameters(rawTransform, expected: 1);
                        var path = MakePathString(pathPattern);
                        requestTransforms.Add(new PathRouteValuesTransform(path.Value, _binderFactory));
                    }
                    else if (rawTransform.TryGetValue("RequestHeadersCopy", out var copyHeaders))
                    {
                        CheckTooManyParameters(rawTransform, expected: 1);
                        copyRequestHeaders = string.Equals("True", copyHeaders, StringComparison.OrdinalIgnoreCase);
                    }
                    else if (rawTransform.TryGetValue("RequestHeaderOriginalHost", out var originalHost))
                    {
                        CheckTooManyParameters(rawTransform, expected: 1);
                        useOriginalHost = string.Equals("True", originalHost, StringComparison.OrdinalIgnoreCase);
                    }
                    else if (rawTransform.TryGetValue("RequestHeader", out var headerName))
                    {
                        CheckTooManyParameters(rawTransform, expected: 2);
                        if (rawTransform.TryGetValue("Set", out var setValue))
                        {
                            // TODO: What about multiple transforms per header? Last wins? We don't have any examples for needing multiple.
                            requestHeaderTransforms[headerName] = new RequestHeaderValueTransform(setValue, append: false);
                        }
                        else if (rawTransform.TryGetValue("Append", out var appendValue))
                        {
                            requestHeaderTransforms[headerName] = new RequestHeaderValueTransform(appendValue, append: true);
                        }
                        else
                        {
                            throw new NotSupportedException(string.Join(';', rawTransform.Keys));
                        }
                    }
                    else if (rawTransform.TryGetValue("ResponseHeader", out var responseHeaderName))
                    {
                        var always = false;
                        if (rawTransform.TryGetValue("When", out var whenValue))
                        {
                            CheckTooManyParameters(rawTransform, expected: 3);
                            always = string.Equals("always", whenValue, StringComparison.OrdinalIgnoreCase);
                        }
                        else
                        {
                            CheckTooManyParameters(rawTransform, expected: 2);
                        }

                        if (rawTransform.TryGetValue("Set", out var setValue))
                        {
                            responseHeaderTransforms[responseHeaderName] = new ResponseHeaderValueTransform(setValue, append: false, always);
                        }
                        else if (rawTransform.TryGetValue("Append", out var appendValue))
                        {
                            responseHeaderTransforms[responseHeaderName] = new ResponseHeaderValueTransform(appendValue, append: true, always);
                        }
                        else
                        {
                            throw new NotSupportedException(string.Join(';', rawTransform.Keys));
                        }
                    }
                    else if (rawTransform.TryGetValue("ResponseTrailer", out var responseTrailerName))
                    {
                        var always = false;
                        if (rawTransform.TryGetValue("When", out var whenValue))
                        {
                            CheckTooManyParameters(rawTransform, expected: 3);
                            always = string.Equals("always", whenValue, StringComparison.OrdinalIgnoreCase);
                        }
                        else
                        {
                            CheckTooManyParameters(rawTransform, expected: 2);
                        }

                        if (rawTransform.TryGetValue("Set", out var setValue))
                        {
                            responseTrailerTransforms[responseTrailerName] = new ResponseHeaderValueTransform(setValue, append: false, always);
                        }
                        else if (rawTransform.TryGetValue("Append", out var appendValue))
                        {
                            responseTrailerTransforms[responseTrailerName] = new ResponseHeaderValueTransform(appendValue, append: true, always);
                        }
                        else
                        {
                            throw new NotSupportedException(string.Join(';', rawTransform.Keys));
                        }
                    }
                    else if (rawTransform.TryGetValue("X-Forwarded", out var xforwardedHeaders))
                    {
                        forwardersSet = true;
                        var expected = 1;

                        var append = true;
                        if (rawTransform.TryGetValue("Append", out var appendValue))
                        {
                            expected++;
                            append = string.Equals("true", appendValue, StringComparison.OrdinalIgnoreCase);
                        }

                        var prefix = "X-Forwarded-";
                        if (rawTransform.TryGetValue("Prefix", out var prefixValue))
                        {
                            expected++;
                            prefix = prefixValue;
                        }

                        CheckTooManyParameters(rawTransform, expected);

                        // for, host, proto, PathBase
                        var tokens = xforwardedHeaders.Split(new[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries);

                        foreach (var token in tokens)
                        {
                            if (string.Equals(token, "For", StringComparison.OrdinalIgnoreCase))
                            {
                                requestHeaderTransforms[prefix + "For"] = new RequestHeaderXForwardedForTransform(append);
                            }
                            else if (string.Equals(token, "Host", StringComparison.OrdinalIgnoreCase))
                            {
                                requestHeaderTransforms[prefix + "Host"] = new RequestHeaderXForwardedHostTransform(append);
                            }
                            else if (string.Equals(token, "Proto", StringComparison.OrdinalIgnoreCase))
                            {
                                requestHeaderTransforms[prefix + "Proto"] = new RequestHeaderXForwardedProtoTransform(append);
                            }
                            else if (string.Equals(token, "PathBase", StringComparison.OrdinalIgnoreCase))
                            {
                                requestHeaderTransforms[prefix + "PathBase"] = new RequestHeaderXForwardedPathBaseTransform(append);
                            }
                            else
                            {
                                throw new NotSupportedException(token);
                            }
                        }
                    }
                    else if (rawTransform.TryGetValue("Forwarded", out var forwardedHeader))
                    {
                        forwardersSet = true;

                        var useHost   = false;
                        var useProto  = false;
                        var useFor    = false;
                        var useBy     = false;
                        var forFormat = RequestHeaderForwardedTransform.NodeFormat.None;
                        var byFormat  = RequestHeaderForwardedTransform.NodeFormat.None;

                        // for, host, proto, PathBase
                        var tokens = forwardedHeader.Split(new[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries);

                        foreach (var token in tokens)
                        {
                            if (string.Equals(token, "For", StringComparison.OrdinalIgnoreCase))
                            {
                                useFor    = true;
                                forFormat = RequestHeaderForwardedTransform.NodeFormat.Random; // RFC Default
                            }
                            else if (string.Equals(token, "By", StringComparison.OrdinalIgnoreCase))
                            {
                                useBy    = true;
                                byFormat = RequestHeaderForwardedTransform.NodeFormat.Random; // RFC Default
                            }
                            else if (string.Equals(token, "Host", StringComparison.OrdinalIgnoreCase))
                            {
                                useHost = true;
                            }
                            else if (string.Equals(token, "Proto", StringComparison.OrdinalIgnoreCase))
                            {
                                useProto = true;
                            }
                            else
                            {
                                throw new NotSupportedException(token);
                            }
                        }

                        var expected = 1;

                        var append = true;
                        if (rawTransform.TryGetValue("Append", out var appendValue))
                        {
                            expected++;
                            append = string.Equals("true", appendValue, StringComparison.OrdinalIgnoreCase);
                        }

                        if (useFor && rawTransform.TryGetValue("ForFormat", out var forFormatString))
                        {
                            expected++;
                            forFormat = Enum.Parse <RequestHeaderForwardedTransform.NodeFormat>(forFormatString, ignoreCase: true);
                        }

                        if (useBy && rawTransform.TryGetValue("ByFormat", out var byFormatString))
                        {
                            expected++;
                            byFormat = Enum.Parse <RequestHeaderForwardedTransform.NodeFormat>(byFormatString, ignoreCase: true);
                        }

                        CheckTooManyParameters(rawTransform, expected);

                        if (useBy || useFor || useHost || useProto)
                        {
                            requestHeaderTransforms["Forwarded"] = new RequestHeaderForwardedTransform(_randomFactory, forFormat, byFormat, useHost, useProto, append);
                        }
                    }
                    else if (rawTransform.TryGetValue("ClientCert", out var clientCertHeader))
                    {
                        CheckTooManyParameters(rawTransform, expected: 1);
                        requestHeaderTransforms[clientCertHeader] = new RequestHeaderClientCertTransform();
                    }
                    else
                    {
                        throw new NotSupportedException(string.Join(';', rawTransform.Keys));
                    }
                }
            }

            // If there's no transform defined for Host, suppress the host by default
            if (!requestHeaderTransforms.ContainsKey(HeaderNames.Host) && !(useOriginalHost ?? false))
            {
                requestHeaderTransforms[HeaderNames.Host] = new RequestHeaderValueTransform(string.Empty, append: false);
            }

            // Add default forwarders
            if (!forwardersSet.GetValueOrDefault())
            {
                if (!requestHeaderTransforms.ContainsKey(ForwardedHeadersDefaults.XForwardedProtoHeaderName))
                {
                    requestHeaderTransforms[ForwardedHeadersDefaults.XForwardedProtoHeaderName] = new RequestHeaderXForwardedProtoTransform(append: true);
                }
                if (!requestHeaderTransforms.ContainsKey(ForwardedHeadersDefaults.XForwardedHostHeaderName))
                {
                    requestHeaderTransforms[ForwardedHeadersDefaults.XForwardedHostHeaderName] = new RequestHeaderXForwardedHostTransform(append: true);
                }
                if (!requestHeaderTransforms.ContainsKey(ForwardedHeadersDefaults.XForwardedForHeaderName))
                {
                    requestHeaderTransforms[ForwardedHeadersDefaults.XForwardedForHeaderName] = new RequestHeaderXForwardedForTransform(append: true);
                }
                if (!requestHeaderTransforms.ContainsKey("X-Forwarded-PathBase"))
                {
                    requestHeaderTransforms["X-Forwarded-PathBase"] = new RequestHeaderXForwardedPathBaseTransform(append: true);
                }
            }

            return(new Transforms(copyRequestHeaders, requestTransforms, requestHeaderTransforms, responseHeaderTransforms, responseTrailerTransforms));
        }