Skip to content

Commit 3ca6b33

Browse files
authored
Merge pull request #43955 from dotnet-maestro-bot/merge/release/7.0-to-main
[automated] Merge branch 'release/7.0' => 'main'
2 parents 4aaf22f + 56dce73 commit 3ca6b33

40 files changed

+1175
-174
lines changed

eng/Publishing.props

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,10 @@
5757
<ItemGroup>
5858
<!-- Do not push .nupkg files from Linux and macOS builds. They'll be packed up separately and signed on Windows.
5959
Do not remove if post build sign is true, as we avoid the xplat codesign jobs, and need to have
60-
the nupkgs pushed. -->
61-
<ItemsToPushToBlobFeed Remove="@(ItemsToPushToBlobFeed)" Condition="'$(OS)' != 'Windows_NT' and '$(PostBuildSign)' != 'true'" />
60+
the nupkgs pushed. Do not do this if building from source, since we want the source build intermediate package
61+
to be published. Use ArcadeBuildFromSource as DotNetBuildFromSource is only set in the internal source build,
62+
and Build.proj is invoked from the wrapper build. -->
63+
<ItemsToPushToBlobFeed Remove="@(ItemsToPushToBlobFeed)" Condition="'$(OS)' != 'Windows_NT' and '$(PostBuildSign)' != 'true' and '$(ArcadeBuildFromSource)' != 'true'" />
6264

6365
<ItemsToPushToBlobFeed Include="@(_ChecksumsToPublish)">
6466
<ManifestArtifactData>NonShipping=true</ManifestArtifactData>

src/Middleware/Diagnostics/src/DeveloperExceptionPage/DeveloperExceptionPageMiddlewareImpl.cs

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -179,18 +179,11 @@ private async Task DisplayExceptionContent(ErrorContext errorContext)
179179
Status = httpContext.Response.StatusCode
180180
};
181181

182-
var exceptionDetails = _exceptionDetailsProvider.GetDetails(errorContext.Exception);
183182
problemDetails.Extensions["exception"] = new
184183
{
185-
Details = exceptionDetails.Select(d => new
186-
{
187-
Message = d.ErrorMessage ?? d.Error?.Message,
188-
Type = TypeNameHelper.GetTypeDisplayName(d.Error),
189-
StackFrames = d.StackFrames,
190-
}),
184+
Details = errorContext.Exception.ToString(),
191185
Headers = httpContext.Request.Headers,
192-
Error = errorContext.Exception.ToString(),
193-
Path = httpContext.Request.Path,
186+
Path = httpContext.Request.Path.ToString(),
194187
Endpoint = httpContext.GetEndpoint()?.ToString(),
195188
RouteValues = httpContext.Features.Get<IRouteValuesFeature>()?.RouteValues,
196189
};

src/Middleware/OutputCaching/samples/OutputCachingSample/Startup.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
});
4242

4343
// Cached entries will vary by culture, but any other additional query is ignored and returns the same cached content
44-
app.MapGet("/query", Gravatar.WriteGravatar).CacheOutput(p => p.VaryByQuery("culture"));
44+
app.MapGet("/query", Gravatar.WriteGravatar).CacheOutput(p => p.SetVaryByQuery("culture"));
4545

4646
app.MapGet("/vary", Gravatar.WriteGravatar).CacheOutput(c => c.VaryByValue((context) => new KeyValuePair<string, string>("time", (DateTime.Now.Second % 2).ToString(CultureInfo.InvariantCulture))));
4747

@@ -52,7 +52,7 @@
5252
{
5353
await Task.Delay(1000);
5454
await context.Response.WriteAsync($"<pre>{requests++}</pre>");
55-
}).CacheOutput(p => p.AllowLocking(false).Expire(TimeSpan.FromMilliseconds(1)));
55+
}).CacheOutput(p => p.SetLocking(false).Expire(TimeSpan.FromMilliseconds(1)));
5656

5757
// Etag
5858
app.MapGet("/etag", async (context) =>

src/Middleware/OutputCaching/src/CacheVaryByRules.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,10 @@ public sealed class CacheVaryByRules
3939
/// Gets or sets a prefix to vary by.
4040
/// </summary>
4141
public string? CacheKeyPrefix { get; set; }
42+
43+
/// <summary>
44+
/// Gets or sets whether to vary by the HOST header value or not.
45+
/// </summary>
46+
/// <remarks>Default is <c>true</c></remarks>
47+
public bool VaryByHost { get; set; } = true;
4248
}

src/Middleware/OutputCaching/src/Memory/MemoryOutputCacheStore.cs

Lines changed: 79 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,27 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4+
using System.Diagnostics;
45
using Microsoft.Extensions.Caching.Memory;
56

67
namespace Microsoft.AspNetCore.OutputCaching.Memory;
78

89
internal sealed class MemoryOutputCacheStore : IOutputCacheStore
910
{
10-
private readonly IMemoryCache _cache;
11+
private readonly MemoryCache _cache;
1112
private readonly Dictionary<string, HashSet<string>> _taggedEntries = new();
1213
private readonly object _tagsLock = new();
1314

14-
internal MemoryOutputCacheStore(IMemoryCache cache)
15+
internal MemoryOutputCacheStore(MemoryCache cache)
1516
{
1617
ArgumentNullException.ThrowIfNull(cache);
1718

1819
_cache = cache;
1920
}
2021

22+
// For testing
23+
internal Dictionary<string, HashSet<string>> TaggedEntries => _taggedEntries;
24+
2125
public ValueTask EvictByTagAsync(string tag, CancellationToken cancellationToken)
2226
{
2327
ArgumentNullException.ThrowIfNull(tag);
@@ -26,12 +30,29 @@ public ValueTask EvictByTagAsync(string tag, CancellationToken cancellationToken
2630
{
2731
if (_taggedEntries.TryGetValue(tag, out var keys))
2832
{
29-
foreach (var key in keys)
33+
if (keys != null && keys.Count > 0)
3034
{
31-
_cache.Remove(key);
32-
}
35+
// If MemoryCache changed to run eviction callbacks inline in Remove, iterating over keys could throw
36+
// To prevent allocating a copy of the keys we check if the eviction callback ran,
37+
// and if it did we restart the loop.
3338

34-
_taggedEntries.Remove(tag);
39+
var i = keys.Count;
40+
while (i > 0)
41+
{
42+
var oldCount = keys.Count;
43+
foreach (var key in keys)
44+
{
45+
_cache.Remove(key);
46+
i--;
47+
if (oldCount != keys.Count)
48+
{
49+
// eviction callback ran inline, we need to restart the loop to avoid
50+
// "collection modified while iterating" errors
51+
break;
52+
}
53+
}
54+
}
55+
}
3556
}
3657
}
3758

@@ -62,35 +83,75 @@ public ValueTask SetAsync(string key, byte[] value, string[]? tags, TimeSpan val
6283
{
6384
foreach (var tag in tags)
6485
{
86+
if (tag is null)
87+
{
88+
throw new ArgumentException(Resources.TagCannotBeNull);
89+
}
90+
6591
if (!_taggedEntries.TryGetValue(tag, out var keys))
6692
{
6793
keys = new HashSet<string>();
6894
_taggedEntries[tag] = keys;
6995
}
7096

97+
Debug.Assert(keys != null);
98+
7199
keys.Add(key);
72100
}
73101

74-
SetEntry();
102+
SetEntry(key, value, tags, validFor);
75103
}
76104
}
77105
else
78106
{
79-
SetEntry();
107+
SetEntry(key, value, tags, validFor);
80108
}
81109

82-
void SetEntry()
110+
return ValueTask.CompletedTask;
111+
}
112+
113+
void SetEntry(string key, byte[] value, string[]? tags, TimeSpan validFor)
114+
{
115+
Debug.Assert(key != null);
116+
117+
var options = new MemoryCacheEntryOptions
83118
{
84-
_cache.Set(
85-
key,
86-
value,
87-
new MemoryCacheEntryOptions
88-
{
89-
AbsoluteExpirationRelativeToNow = validFor,
90-
Size = value.Length
91-
});
119+
AbsoluteExpirationRelativeToNow = validFor,
120+
Size = value.Length
121+
};
122+
123+
if (tags != null && tags.Length > 0)
124+
{
125+
// Remove cache keys from tag lists when the entry is evicted
126+
options.RegisterPostEvictionCallback(RemoveFromTags, tags);
92127
}
93128

94-
return ValueTask.CompletedTask;
129+
_cache.Set(key, value, options);
130+
}
131+
132+
void RemoveFromTags(object key, object? value, EvictionReason reason, object? state)
133+
{
134+
var tags = state as string[];
135+
136+
Debug.Assert(tags != null);
137+
Debug.Assert(tags.Length > 0);
138+
Debug.Assert(key is string);
139+
140+
lock (_tagsLock)
141+
{
142+
foreach (var tag in tags)
143+
{
144+
if (_taggedEntries.TryGetValue(tag, out var tagged))
145+
{
146+
tagged.Remove((string)key);
147+
148+
// Remove the collection if there is no more keys in it
149+
if (tagged.Count == 0)
150+
{
151+
_taggedEntries.Remove(tag);
152+
}
153+
}
154+
}
155+
}
95156
}
96157
}

src/Middleware/OutputCaching/src/OutputCacheAttribute.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,17 +80,17 @@ internal IOutputCachePolicy BuildPolicy()
8080

8181
if (VaryByQueryKeys != null)
8282
{
83-
builder.VaryByQuery(VaryByQueryKeys);
83+
builder.SetVaryByQuery(VaryByQueryKeys);
8484
}
8585

8686
if (VaryByHeaderNames != null)
8787
{
88-
builder.VaryByHeader(VaryByHeaderNames);
88+
builder.SetVaryByHeader(VaryByHeaderNames);
8989
}
9090

9191
if (VaryByRouteValueNames != null)
9292
{
93-
builder.VaryByRouteValue(VaryByRouteValueNames);
93+
builder.SetVaryByRouteValue(VaryByRouteValueNames);
9494
}
9595

9696
if (_duration != null)

src/Middleware/OutputCaching/src/OutputCacheKeyProvider.cs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,8 +107,17 @@ public bool TryAppendBaseKey(OutputCacheContext context, StringBuilder builder)
107107
.AppendUpperInvariant(request.Method)
108108
.Append(KeyDelimiter)
109109
.AppendUpperInvariant(request.Scheme)
110-
.Append(KeyDelimiter)
111-
.AppendUpperInvariant(request.Host.Value);
110+
.Append(KeyDelimiter);
111+
112+
if (context.CacheVaryByRules.VaryByHost)
113+
{
114+
builder.AppendUpperInvariant(request.Host.Value);
115+
}
116+
else
117+
{
118+
// Use a fake HOST header to prevent substitutions
119+
builder.AppendUpperInvariant("*:*");
120+
}
112121

113122
if (_options.UseCaseSensitivePaths)
114123
{

src/Middleware/OutputCaching/src/OutputCacheOptions.cs

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -63,10 +63,19 @@ public void AddPolicy(string name, IOutputCachePolicy policy)
6363
/// Defines a <see cref="IOutputCachePolicy"/> which can be referenced by name.
6464
/// </summary>
6565
/// <param name="name">The name of the policy.</param>
66-
/// <param name="build">an action on <see cref="OutputCachePolicyBuilder"/>.</param>
67-
public void AddPolicy(string name, Action<OutputCachePolicyBuilder> build)
66+
/// <param name="build">An action on <see cref="OutputCachePolicyBuilder"/>.</param>
67+
/// <remarks>The built policy will be based on the default policy.</remarks>
68+
public void AddPolicy(string name, Action<OutputCachePolicyBuilder> build) => AddPolicy(name, build, false);
69+
70+
/// <summary>
71+
/// Defines a <see cref="IOutputCachePolicy"/> which can be referenced by name.
72+
/// </summary>
73+
/// <param name="name">The name of the policy.</param>
74+
/// <param name="build">An action on <see cref="OutputCachePolicyBuilder"/>.</param>
75+
/// <param name="excludeDefaultPolicy">Whether to exclude the default policy or not.</param>
76+
public void AddPolicy(string name, Action<OutputCachePolicyBuilder> build, bool excludeDefaultPolicy)
6877
{
69-
var builder = new OutputCachePolicyBuilder();
78+
var builder = new OutputCachePolicyBuilder(excludeDefaultPolicy);
7079
build(builder);
7180
NamedPolicies ??= new Dictionary<string, IOutputCachePolicy>(StringComparer.OrdinalIgnoreCase);
7281
NamedPolicies[name] = builder.Build();
@@ -85,10 +94,18 @@ public void AddBasePolicy(IOutputCachePolicy policy)
8594
/// <summary>
8695
/// Builds and adds an <see cref="IOutputCachePolicy"/> instance to base policies.
8796
/// </summary>
88-
/// <param name="build">an action on <see cref="OutputCachePolicyBuilder"/>.</param>
89-
public void AddBasePolicy(Action<OutputCachePolicyBuilder> build)
97+
/// <param name="build">An action on <see cref="OutputCachePolicyBuilder"/>.</param>
98+
/// <remarks>The built policy will be based on the default policy.</remarks>
99+
public void AddBasePolicy(Action<OutputCachePolicyBuilder> build) => AddBasePolicy(build, false);
100+
101+
/// <summary>
102+
/// Builds and adds an <see cref="IOutputCachePolicy"/> instance to base policies.
103+
/// </summary>
104+
/// <param name="build">An action on <see cref="OutputCachePolicyBuilder"/>.</param>
105+
/// <param name="excludeDefaultPolicy">Whether to exclude the default policy or not.</param>
106+
public void AddBasePolicy(Action<OutputCachePolicyBuilder> build, bool excludeDefaultPolicy)
90107
{
91-
var builder = new OutputCachePolicyBuilder();
108+
var builder = new OutputCachePolicyBuilder(excludeDefaultPolicy);
92109
build(builder);
93110
BasePolicies ??= new();
94111
BasePolicies.Add(builder.Build());

0 commit comments

Comments
 (0)