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