diff --git a/docs/docfx/articles/direct-forwarding.md b/docs/docfx/articles/direct-forwarding.md index 26c85572f..8def2a07a 100644 --- a/docs/docfx/articles/direct-forwarding.md +++ b/docs/docfx/articles/direct-forwarding.md @@ -53,13 +53,13 @@ public void Configure(IApplicationBuilder app, IHttpForwarder forwarder) UseProxy = false, AllowAutoRedirect = false, AutomaticDecompression = DecompressionMethods.None, - UseCookies = false + UseCookies = false, + ActivityHeadersPropagator = new ReverseProxyPropagator(DistributedContextPropagator.Current) }); var transformer = new CustomTransformer(); // or HttpTransformer.Default; var requestConfig = new ForwarderRequestConfig { ActivityTimeout = TimeSpan.FromSeconds(100) }; app.UseRouting(); - app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.Map("/{**catch-all}", async httpContext => @@ -83,9 +83,18 @@ private class CustomTransformer : HttpTransformer public override async ValueTask TransformRequestAsync(HttpContext httpContext, HttpRequestMessage proxyRequest, string destinationPrefix) { - // Copy headers normally and then remove the original host. - // Use the destination host from proxyRequest.RequestUri instead. + // Copy all request headers await base.TransformRequestAsync(httpContext, proxyRequest, destinationPrefix); + + // Customize the query string: + var queryContext = new QueryTransformContext(httpContext.Request); + queryContext.Collection.Remove("param1"); + queryContext.Collection["area"] = "xx2"; + + // Assign the custom uri. Be careful about extra slashes when concatenating here. RequestUtilities.MakeDestinationAddress is a safe default. + proxyRequest.RequestUri = RequestUtilities.MakeDestinationAddress("https://example.com", httpContext.Request.Path, queryContext.QueryString); + + // Suppress the original request header, use the one from the destination Uri. proxyRequest.Headers.Host = null; } } diff --git a/docs/docfx/articles/header-guidelines.md b/docs/docfx/articles/header-guidelines.md index 7d5072730..8fa66e30a 100644 --- a/docs/docfx/articles/header-guidelines.md +++ b/docs/docfx/articles/header-guidelines.md @@ -38,6 +38,11 @@ This response header is used with HTTP/3 upgrades and only applies to the immedi ### TraceParent, Request-Id, TraceState, Baggage, Correlation-Context These headers relate to distributed tracing. They are automatically removed on .NET 6 or later so that the forwarding HttpClient can replace them with updated values. +You can opt out of modifying these headers by setting `SocketsHttpHandler.ActivityHeadersPropagator ` to `null`: +```C# +services.AddReverseProxy() + .ConfigureHttpClient((_, handler) => handler.ActivityHeadersPropagator = null); +``` ## Other Header Guidelines diff --git a/samples/ReverseProxy.Direct.Sample/Startup.cs b/samples/ReverseProxy.Direct.Sample/Startup.cs index f72760131..bdb9c360d 100644 --- a/samples/ReverseProxy.Direct.Sample/Startup.cs +++ b/samples/ReverseProxy.Direct.Sample/Startup.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System; +using System.Diagnostics; using System.Net; using System.Net.Http; using System.Threading.Tasks; @@ -38,7 +39,10 @@ public void Configure(IApplicationBuilder app, IHttpForwarder forwarder) UseProxy = false, AllowAutoRedirect = false, AutomaticDecompression = DecompressionMethods.None, - UseCookies = false + UseCookies = false, +#if NET6_0_OR_GREATER + ActivityHeadersPropagator = new ReverseProxyPropagator(DistributedContextPropagator.Current) +#endif }); // Setup our own request transform class diff --git a/src/ReverseProxy/Forwarder/ForwarderHttpClientFactory.cs b/src/ReverseProxy/Forwarder/ForwarderHttpClientFactory.cs index 81dad1a85..2c4d83446 100644 --- a/src/ReverseProxy/Forwarder/ForwarderHttpClientFactory.cs +++ b/src/ReverseProxy/Forwarder/ForwarderHttpClientFactory.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System; +using System.Diagnostics; using System.Net; using System.Net.Http; using System.Text; @@ -45,7 +46,10 @@ public HttpMessageInvoker CreateClient(ForwarderHttpClientContext context) UseProxy = false, AllowAutoRedirect = false, AutomaticDecompression = DecompressionMethods.None, - UseCookies = false + UseCookies = false, +#if NET6_0_OR_GREATER + ActivityHeadersPropagator = new ReverseProxyPropagator(DistributedContextPropagator.Current) +#endif // NOTE: MaxResponseHeadersLength = 64, which means up to 64 KB of headers are allowed by default as of .NET Core 3.1. }; diff --git a/src/ReverseProxy/Forwarder/RequestUtilities.cs b/src/ReverseProxy/Forwarder/RequestUtilities.cs index 1a99e7e85..82ca843a8 100644 --- a/src/ReverseProxy/Forwarder/RequestUtilities.cs +++ b/src/ReverseProxy/Forwarder/RequestUtilities.cs @@ -83,15 +83,6 @@ internal static bool ShouldSkipResponseHeader(string headerName) #else "Alt-Svc", #endif - -#if NET6_0_OR_GREATER - // Distributed context headers - HeaderNames.TraceParent, - HeaderNames.RequestId, - HeaderNames.TraceState, - HeaderNames.Baggage, - HeaderNames.CorrelationContext, -#endif }; // Headers marked as HttpHeaderType.Content in diff --git a/src/ReverseProxy/Forwarder/ReverseProxyPropagator.cs b/src/ReverseProxy/Forwarder/ReverseProxyPropagator.cs new file mode 100644 index 000000000..2f75fbebb --- /dev/null +++ b/src/ReverseProxy/Forwarder/ReverseProxyPropagator.cs @@ -0,0 +1,53 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#if NET6_0_OR_GREATER +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Net.Http; + +namespace Yarp.ReverseProxy.Forwarder; + +/// +/// Removes existing headers and then delegates to the inner propagator. +/// +public sealed class ReverseProxyPropagator : DistributedContextPropagator +{ + private readonly DistributedContextPropagator _innerPropagator; + private readonly string[] _headersToRemove; + + /// + /// ReverseProxyPropagator removes headers pointed out in innerPropagator. + /// + public ReverseProxyPropagator(DistributedContextPropagator innerPropagator) + { + _innerPropagator = innerPropagator ?? throw new ArgumentNullException(nameof(innerPropagator)); + _headersToRemove = _innerPropagator.Fields.ToArray(); + } + + public override void Inject(Activity? activity, object? carrier, PropagatorSetterCallback? setter) + { + if (carrier is HttpRequestMessage message) + { + var headers = message.Headers; + + foreach (var header in _headersToRemove) + { + headers.Remove(header); + } + } + + _innerPropagator.Inject(activity, carrier, setter); + } + + public override void ExtractTraceIdAndState(object? carrier, PropagatorGetterCallback? getter, out string? traceId, out string? traceState) => + _innerPropagator.ExtractTraceIdAndState(carrier, getter, out traceId, out traceState); + + public override IEnumerable>? ExtractBaggage(object? carrier, PropagatorGetterCallback? getter) => + _innerPropagator.ExtractBaggage(carrier, getter); + + public override IReadOnlyCollection Fields => _innerPropagator.Fields; +} +#endif diff --git a/test/ReverseProxy.Tests/Forwarder/HttpForwarderTests.cs b/test/ReverseProxy.Tests/Forwarder/HttpForwarderTests.cs index d6367e461..65713d252 100644 --- a/test/ReverseProxy.Tests/Forwarder/HttpForwarderTests.cs +++ b/test/ReverseProxy.Tests/Forwarder/HttpForwarderTests.cs @@ -2432,13 +2432,6 @@ public static IEnumerable GetProhibitedHeaders() "HTTP2-Settings: value", "Upgrade-Insecure-Requests: value", "Alt-Svc: value", -#if NET6_0_OR_GREATER - "traceparent: value", - "Request-Id: value", - "tracestate: value", - "baggage: value", - "Correlation-Context: value", -#endif }; foreach (var header in headers) diff --git a/test/ReverseProxy.Tests/Forwarder/HttpTransformerTests.cs b/test/ReverseProxy.Tests/Forwarder/HttpTransformerTests.cs index 4cbf88172..17d4af5bb 100644 --- a/test/ReverseProxy.Tests/Forwarder/HttpTransformerTests.cs +++ b/test/ReverseProxy.Tests/Forwarder/HttpTransformerTests.cs @@ -35,13 +35,6 @@ public class HttpTransformerTests HeaderNames.AltSvc, #else "Alt-Svc", -#endif -#if NET6_0_OR_GREATER - HeaderNames.TraceParent, - HeaderNames.RequestId, - HeaderNames.TraceState, - HeaderNames.Baggage, - HeaderNames.CorrelationContext, #endif };