diff --git a/docs/docfx/articles/header-guidelines.md b/docs/docfx/articles/header-guidelines.md
index 36c5edead..c47376cdf 100644
--- a/docs/docfx/articles/header-guidelines.md
+++ b/docs/docfx/articles/header-guidelines.md
@@ -9,7 +9,7 @@ Headers are a very important part of processing HTTP requests and each have thei
## YARP header filtering
-YARP automatically removes request and response headers that could impact its ability to forward a request correctly, or that may be used maliciously to bypass features of the proxy. A complete list can be found [here](https://github.com/microsoft/reverse-proxy/blob/main/src/ReverseProxy/Forwarder/RequestUtilities.cs#L70), with some highlights described below.
+YARP automatically removes request and response headers that could impact its ability to forward a request correctly, or that may be used maliciously to bypass features of the proxy. A complete list can be found [here](https://github.com/microsoft/reverse-proxy/blob/main/src/ReverseProxy/Forwarder/RequestUtilities.cs#L71), with some highlights described below.
### Connection, KeepAlive, Close
diff --git a/eng/Versions.props b/eng/Versions.props
index e90859d3d..d6d29b142 100644
--- a/eng/Versions.props
+++ b/eng/Versions.props
@@ -12,6 +12,7 @@
+ 8.0.0-rc.1.23419.4
8.0.0-rc.1.23419.4
8.0.0-beta.23463.1
diff --git a/src/ReverseProxy/Configuration/ConfigValidator.cs b/src/ReverseProxy/Configuration/ConfigValidator.cs
index 486cf8937..51d9c4da0 100644
--- a/src/ReverseProxy/Configuration/ConfigValidator.cs
+++ b/src/ReverseProxy/Configuration/ConfigValidator.cs
@@ -2,6 +2,7 @@
// Licensed under the MIT License.
using System;
+using System.Collections.Frozen;
using System.Collections.Generic;
using System.Linq;
using System.Net;
@@ -31,11 +32,11 @@ internal sealed class ConfigValidator : IConfigValidator
private readonly IAuthorizationPolicyProvider _authorizationPolicyProvider;
private readonly IYarpRateLimiterPolicyProvider _rateLimiterPolicyProvider;
private readonly ICorsPolicyProvider _corsPolicyProvider;
- private readonly IDictionary _loadBalancingPolicies;
- private readonly IDictionary _affinityFailurePolicies;
- private readonly IDictionary _availableDestinationsPolicies;
- private readonly IDictionary _activeHealthCheckPolicies;
- private readonly IDictionary _passiveHealthCheckPolicies;
+ private readonly FrozenDictionary _loadBalancingPolicies;
+ private readonly FrozenDictionary _affinityFailurePolicies;
+ private readonly FrozenDictionary _availableDestinationsPolicies;
+ private readonly FrozenDictionary _activeHealthCheckPolicies;
+ private readonly FrozenDictionary _passiveHealthCheckPolicies;
private readonly ILogger _logger;
diff --git a/src/ReverseProxy/Forwarder/RequestUtilities.cs b/src/ReverseProxy/Forwarder/RequestUtilities.cs
index da70a1688..e2914d4de 100644
--- a/src/ReverseProxy/Forwarder/RequestUtilities.cs
+++ b/src/ReverseProxy/Forwarder/RequestUtilities.cs
@@ -3,6 +3,7 @@
using System;
using System.Buffers;
+using System.Collections.Frozen;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
@@ -67,7 +68,7 @@ internal static bool ShouldSkipResponseHeader(string headerName)
return _headersToExclude.Contains(headerName);
}
- private static readonly HashSet _headersToExclude = new(18, StringComparer.OrdinalIgnoreCase)
+ private static readonly FrozenSet _headersToExclude = new HashSet(18, StringComparer.OrdinalIgnoreCase)
{
HeaderNames.Connection,
HeaderNames.TransferEncoding,
@@ -87,11 +88,11 @@ internal static bool ShouldSkipResponseHeader(string headerName)
HeaderNames.TE,
HeaderNames.AltSvc,
HeaderNames.StrictTransportSecurity,
- };
+ }.ToFrozenSet(StringComparer.OrdinalIgnoreCase);
// Headers marked as HttpHeaderType.Content in
// https://github.com/dotnet/runtime/blob/main/src/libraries/System.Net.Http/src/System/Net/Http/Headers/KnownHeaders.cs
- private static readonly HashSet _contentHeaders = new(11, StringComparer.OrdinalIgnoreCase)
+ private static readonly FrozenSet _contentHeaders = new HashSet(11, StringComparer.OrdinalIgnoreCase)
{
HeaderNames.Allow,
HeaderNames.ContentDisposition,
@@ -104,7 +105,7 @@ internal static bool ShouldSkipResponseHeader(string headerName)
HeaderNames.ContentType,
HeaderNames.Expires,
HeaderNames.LastModified
- };
+ }.ToFrozenSet(StringComparer.OrdinalIgnoreCase);
///
/// Appends the given path and query to the destination prefix while avoiding duplicate '/'.
diff --git a/src/ReverseProxy/Health/ActiveHealthCheckMonitor.cs b/src/ReverseProxy/Health/ActiveHealthCheckMonitor.cs
index 383ff619d..cd4f2e23c 100644
--- a/src/ReverseProxy/Health/ActiveHealthCheckMonitor.cs
+++ b/src/ReverseProxy/Health/ActiveHealthCheckMonitor.cs
@@ -2,6 +2,7 @@
// Licensed under the MIT License.
using System;
+using System.Collections.Frozen;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net.Http;
@@ -17,7 +18,7 @@ namespace Yarp.ReverseProxy.Health;
internal partial class ActiveHealthCheckMonitor : IActiveHealthCheckMonitor, IClusterChangeListener, IDisposable
{
private readonly ActiveHealthCheckMonitorOptions _monitorOptions;
- private readonly IDictionary _policies;
+ private readonly FrozenDictionary _policies;
private readonly IProbingRequestFactory _probingRequestFactory;
private readonly ILogger _logger;
diff --git a/src/ReverseProxy/Health/ClusterDestinationsUpdater.cs b/src/ReverseProxy/Health/ClusterDestinationsUpdater.cs
index 7dd698a51..1b00c8923 100644
--- a/src/ReverseProxy/Health/ClusterDestinationsUpdater.cs
+++ b/src/ReverseProxy/Health/ClusterDestinationsUpdater.cs
@@ -2,6 +2,7 @@
// Licensed under the MIT License.
using System;
+using System.Collections.Frozen;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Threading;
@@ -13,7 +14,7 @@ namespace Yarp.ReverseProxy.Health;
internal sealed class ClusterDestinationsUpdater : IClusterDestinationsUpdater
{
private readonly ConditionalWeakTable _clusterLocks = new ConditionalWeakTable();
- private readonly IDictionary _destinationPolicies;
+ private readonly FrozenDictionary _destinationPolicies;
public ClusterDestinationsUpdater(IEnumerable destinationPolicies)
{
diff --git a/src/ReverseProxy/Health/PassiveHealthCheckMiddleware.cs b/src/ReverseProxy/Health/PassiveHealthCheckMiddleware.cs
index c8ff54a80..801c4c2dc 100644
--- a/src/ReverseProxy/Health/PassiveHealthCheckMiddleware.cs
+++ b/src/ReverseProxy/Health/PassiveHealthCheckMiddleware.cs
@@ -2,6 +2,7 @@
// Licensed under the MIT License.
using System;
+using System.Collections.Frozen;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
@@ -12,7 +13,7 @@ namespace Yarp.ReverseProxy.Health;
public class PassiveHealthCheckMiddleware
{
private readonly RequestDelegate _next;
- private readonly IDictionary _policies;
+ private readonly FrozenDictionary _policies;
public PassiveHealthCheckMiddleware(RequestDelegate next, IEnumerable policies)
{
diff --git a/src/ReverseProxy/LoadBalancing/LoadBalancingMiddleware.cs b/src/ReverseProxy/LoadBalancing/LoadBalancingMiddleware.cs
index ec61b2c00..64830a2e2 100644
--- a/src/ReverseProxy/LoadBalancing/LoadBalancingMiddleware.cs
+++ b/src/ReverseProxy/LoadBalancing/LoadBalancingMiddleware.cs
@@ -2,6 +2,7 @@
// Licensed under the MIT License.
using System;
+using System.Collections.Frozen;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
@@ -17,7 +18,7 @@ namespace Yarp.ReverseProxy.LoadBalancing;
internal sealed class LoadBalancingMiddleware
{
private readonly ILogger _logger;
- private readonly IDictionary _loadBalancingPolicies;
+ private readonly FrozenDictionary _loadBalancingPolicies;
private readonly RequestDelegate _next;
public LoadBalancingMiddleware(
diff --git a/src/ReverseProxy/SessionAffinity/AffinitizeTransformProvider.cs b/src/ReverseProxy/SessionAffinity/AffinitizeTransformProvider.cs
index e33f6d22f..2934d8be0 100644
--- a/src/ReverseProxy/SessionAffinity/AffinitizeTransformProvider.cs
+++ b/src/ReverseProxy/SessionAffinity/AffinitizeTransformProvider.cs
@@ -2,6 +2,7 @@
// Licensed under the MIT License.
using System;
+using System.Collections.Frozen;
using System.Collections.Generic;
using Yarp.ReverseProxy.Transforms.Builder;
using Yarp.ReverseProxy.Utilities;
@@ -10,7 +11,7 @@ namespace Yarp.ReverseProxy.SessionAffinity;
internal sealed class AffinitizeTransformProvider : ITransformProvider
{
- private readonly IDictionary _sessionAffinityPolicies;
+ private readonly FrozenDictionary _sessionAffinityPolicies;
public AffinitizeTransformProvider(IEnumerable sessionAffinityPolicies)
{
diff --git a/src/ReverseProxy/SessionAffinity/SessionAffinityMiddleware.cs b/src/ReverseProxy/SessionAffinity/SessionAffinityMiddleware.cs
index 043bf2c18..c72b81f78 100644
--- a/src/ReverseProxy/SessionAffinity/SessionAffinityMiddleware.cs
+++ b/src/ReverseProxy/SessionAffinity/SessionAffinityMiddleware.cs
@@ -2,6 +2,7 @@
// Licensed under the MIT License.
using System;
+using System.Collections.Frozen;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
@@ -18,8 +19,8 @@ namespace Yarp.ReverseProxy.SessionAffinity;
internal sealed class SessionAffinityMiddleware
{
private readonly RequestDelegate _next;
- private readonly IDictionary _sessionAffinityPolicies;
- private readonly IDictionary _affinityFailurePolicies;
+ private readonly FrozenDictionary _sessionAffinityPolicies;
+ private readonly FrozenDictionary _affinityFailurePolicies;
private readonly ILogger _logger;
public SessionAffinityMiddleware(
diff --git a/src/ReverseProxy/Transforms/RequestHeadersAllowedTransform.cs b/src/ReverseProxy/Transforms/RequestHeadersAllowedTransform.cs
index 6385d5088..f02630e96 100644
--- a/src/ReverseProxy/Transforms/RequestHeadersAllowedTransform.cs
+++ b/src/ReverseProxy/Transforms/RequestHeadersAllowedTransform.cs
@@ -2,6 +2,7 @@
// Licensed under the MIT License.
using System;
+using System.Collections.Frozen;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading.Tasks;
@@ -22,12 +23,12 @@ public RequestHeadersAllowedTransform(string[] allowedHeaders)
}
AllowedHeaders = allowedHeaders;
- AllowedHeadersSet = new HashSet(allowedHeaders, StringComparer.OrdinalIgnoreCase);
+ AllowedHeadersSet = new HashSet(allowedHeaders, StringComparer.OrdinalIgnoreCase).ToFrozenSet(StringComparer.OrdinalIgnoreCase);
}
internal string[] AllowedHeaders { get; }
- private HashSet AllowedHeadersSet { get; }
+ private FrozenSet AllowedHeadersSet { get; }
///
public override ValueTask ApplyAsync(RequestTransformContext context)
diff --git a/src/ReverseProxy/Transforms/ResponseHeadersAllowedTransform.cs b/src/ReverseProxy/Transforms/ResponseHeadersAllowedTransform.cs
index d6334030d..f7ab2b7f9 100644
--- a/src/ReverseProxy/Transforms/ResponseHeadersAllowedTransform.cs
+++ b/src/ReverseProxy/Transforms/ResponseHeadersAllowedTransform.cs
@@ -2,6 +2,7 @@
// Licensed under the MIT License.
using System;
+using System.Collections.Frozen;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net.Http.Headers;
@@ -24,12 +25,12 @@ public ResponseHeadersAllowedTransform(string[] allowedHeaders)
}
AllowedHeaders = allowedHeaders;
- AllowedHeadersSet = new HashSet(allowedHeaders, StringComparer.OrdinalIgnoreCase);
+ AllowedHeadersSet = new HashSet(allowedHeaders, StringComparer.OrdinalIgnoreCase).ToFrozenSet(StringComparer.OrdinalIgnoreCase);
}
internal string[] AllowedHeaders { get; }
- private HashSet AllowedHeadersSet { get; }
+ private FrozenSet AllowedHeadersSet { get; }
///
public override ValueTask ApplyAsync(ResponseTransformContext context)
diff --git a/src/ReverseProxy/Transforms/ResponseTrailersAllowedTransform.cs b/src/ReverseProxy/Transforms/ResponseTrailersAllowedTransform.cs
index 0ad794798..0c072227d 100644
--- a/src/ReverseProxy/Transforms/ResponseTrailersAllowedTransform.cs
+++ b/src/ReverseProxy/Transforms/ResponseTrailersAllowedTransform.cs
@@ -2,6 +2,7 @@
// Licensed under the MIT License.
using System;
+using System.Collections.Frozen;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net.Http.Headers;
@@ -25,12 +26,12 @@ public ResponseTrailersAllowedTransform(string[] allowedHeaders)
}
AllowedHeaders = allowedHeaders;
- AllowedHeadersSet = new HashSet(allowedHeaders, StringComparer.OrdinalIgnoreCase);
+ AllowedHeadersSet = new HashSet(allowedHeaders, StringComparer.OrdinalIgnoreCase).ToFrozenSet(StringComparer.OrdinalIgnoreCase);
}
internal string[] AllowedHeaders { get; }
- private HashSet AllowedHeadersSet { get; }
+ private FrozenSet AllowedHeadersSet { get; }
///
public override ValueTask ApplyAsync(ResponseTrailersTransformContext context)
diff --git a/src/ReverseProxy/Utilities/ServiceLookupHelper.cs b/src/ReverseProxy/Utilities/ServiceLookupHelper.cs
index 264c0a881..95de05354 100644
--- a/src/ReverseProxy/Utilities/ServiceLookupHelper.cs
+++ b/src/ReverseProxy/Utilities/ServiceLookupHelper.cs
@@ -2,13 +2,14 @@
// Licensed under the MIT License.
using System;
+using System.Collections.Frozen;
using System.Collections.Generic;
namespace Yarp.ReverseProxy.Utilities;
internal static class ServiceLookupHelper
{
- public static IDictionary ToDictionaryByUniqueId(this IEnumerable services, Func idSelector)
+ public static FrozenDictionary ToDictionaryByUniqueId(this IEnumerable services, Func idSelector)
{
if (services is null)
{
@@ -25,10 +26,10 @@ public static IDictionary ToDictionaryByUniqueId(this IEnumerable<
}
}
- return result;
+ return result.ToFrozenDictionary(StringComparer.OrdinalIgnoreCase);
}
- public static T GetRequiredServiceById(this IDictionary services, string? id, string defaultId)
+ public static T GetRequiredServiceById(this FrozenDictionary services, string? id, string defaultId)
{
var lookup = id;
if (string.IsNullOrEmpty(lookup))
diff --git a/src/ReverseProxy/Yarp.ReverseProxy.csproj b/src/ReverseProxy/Yarp.ReverseProxy.csproj
index b6f447e10..e45cf4b00 100644
--- a/src/ReverseProxy/Yarp.ReverseProxy.csproj
+++ b/src/ReverseProxy/Yarp.ReverseProxy.csproj
@@ -13,6 +13,7 @@
+