Skip to content

Commit ea4b52c

Browse files
committed
Merge remote-tracking branch 'origin/release/3.1'
2 parents 3a9368b + b085deb commit ea4b52c

File tree

9 files changed

+123
-30
lines changed

9 files changed

+123
-30
lines changed

src/Components/Web.JS/src/Platform/WebAssemblyResourceLoader.ts

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,14 @@ export class WebAssemblyResourceLoader {
8585
const cacheKey = toAbsoluteUri(`${url}.${contentHash}`);
8686
this.usedCacheKeys[cacheKey] = true;
8787

88-
const cachedResponse = await cache.match(cacheKey);
88+
let cachedResponse: Response | undefined;
89+
try {
90+
cachedResponse = await cache.match(cacheKey);
91+
} catch {
92+
// Be tolerant to errors reading from the cache. This is a guard for https://bugs.chromium.org/p/chromium/issues/detail?id=968444 where
93+
// chromium browsers may sometimes throw when working with the cache.
94+
}
95+
8996
if (cachedResponse) {
9097
// It's in the cache.
9198
const responseBytes = parseInt(cachedResponse.headers.get('content-length') || '0');
@@ -136,12 +143,19 @@ export class WebAssemblyResourceLoader {
136143

137144
// Add to cache as a custom response object so we can track extra data such as responseBytes
138145
// We can't rely on the server sending content-length (ASP.NET Core doesn't by default)
139-
await cache.put(cacheKey, new Response(responseData, {
146+
const responseToCache = new Response(responseData, {
140147
headers: {
141148
'content-type': response.headers.get('content-type') || '',
142149
'content-length': (responseBytes || response.headers.get('content-length') || '').toString()
143150
}
144-
}));
151+
});
152+
153+
try {
154+
await cache.put(cacheKey, responseToCache);
155+
} catch {
156+
// Be tolerant to errors writing to the cache. This is a guard for https://bugs.chromium.org/p/chromium/issues/detail?id=968444 where
157+
// chromium browsers may sometimes throw when performing cache operations.
158+
}
145159
}
146160
}
147161

src/Components/WebAssembly/Build/src/targets/ServiceWorkerAssetsManifest.targets

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@
163163

164164
<WriteLinesToFile
165165
File="$(_CombinedHashIntermediatePath)"
166-
Lines="@(_ServiceWorkerAssetsManifestItemWithHash->'%(FileHash)')"
166+
Lines="@(_ServiceWorkerAssetsManifestItemWithHash->'%(Integrity)')"
167167
WriteOnlyWhenDifferent="true"
168168
Overwrite="true" />
169169

src/Components/WebAssembly/Build/test/BuildIntegrationTests/PwaManifestTests.cs

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System.IO;
22
using System.Linq;
33
using System.Text.Json;
4+
using System.Text.RegularExpressions;
45
using System.Threading.Tasks;
56
using Xunit;
67

@@ -41,5 +42,77 @@ public async Task Build_ServiceWorkerAssetsManifest_Works()
4142
var entries = assets.EnumerateArray().Select(e => e.GetProperty("url").GetString()).OrderBy(e => e).ToArray();
4243
Assert.All(entries, e => expectedExtensions.Contains(Path.GetExtension(e)));
4344
}
45+
46+
[Fact]
47+
public async Task Publish_UpdatesServiceWorkerVersionHash_WhenSourcesChange()
48+
{
49+
// Arrange
50+
using var project = ProjectDirectory.Create("standalone", additionalProjects: new[] { "razorclasslibrary" });
51+
var result = await MSBuildProcessManager.DotnetMSBuild(project, "Publish", args: "/bl:initial.binlog /p:ServiceWorkerAssetsManifest=service-worker-assets.js");
52+
53+
Assert.BuildPassed(result);
54+
55+
var publishOutputDirectory = project.PublishOutputDirectory;
56+
57+
var serviceWorkerFile = Assert.FileExists(result, publishOutputDirectory, "wwwroot", "serviceworkers", "my-service-worker.js");
58+
var version = File.ReadAllLines(serviceWorkerFile).Last();
59+
var match = Regex.Match(version, "\\/\\* Manifest version: (.{8}) \\*\\/");
60+
Assert.True(match.Success);
61+
Assert.Equal(2, match.Groups.Count);
62+
Assert.NotNull(match.Groups[1].Value);
63+
var capture = match.Groups[1].Value;
64+
65+
// Act
66+
var cssFile = Path.Combine(project.DirectoryPath, "LinkToWebRoot", "css", "app.css");
67+
File.WriteAllText(cssFile, ".updated { }");
68+
69+
// Assert
70+
var updatedResult = await MSBuildProcessManager.DotnetMSBuild(project, "Publish", args: "/bl:updated.binlog /p:ServiceWorkerAssetsManifest=service-worker-assets.js");
71+
72+
Assert.BuildPassed(result);
73+
74+
var updatedVersion = File.ReadAllLines(serviceWorkerFile).Last();
75+
var updatedMatch = Regex.Match(updatedVersion, "\\/\\* Manifest version: (.{8}) \\*\\/");
76+
Assert.True(updatedMatch.Success);
77+
Assert.Equal(2, updatedMatch.Groups.Count);
78+
Assert.NotNull(updatedMatch.Groups[1].Value);
79+
var updatedCapture = updatedMatch.Groups[1].Value;
80+
81+
Assert.NotEqual(capture, updatedCapture);
82+
}
83+
84+
[Fact]
85+
public async Task Publish_DeterministicAcrossBuilds_WhenNoSourcesChange()
86+
{
87+
// Arrange
88+
using var project = ProjectDirectory.Create("standalone", additionalProjects: new[] { "razorclasslibrary" });
89+
var result = await MSBuildProcessManager.DotnetMSBuild(project, "Publish", args: "/bl:initial.binlog /p:ServiceWorkerAssetsManifest=service-worker-assets.js");
90+
91+
Assert.BuildPassed(result);
92+
93+
var publishOutputDirectory = project.PublishOutputDirectory;
94+
95+
var serviceWorkerFile = Assert.FileExists(result, publishOutputDirectory, "wwwroot", "serviceworkers", "my-service-worker.js");
96+
var version = File.ReadAllLines(serviceWorkerFile).Last();
97+
var match = Regex.Match(version, "\\/\\* Manifest version: (.{8}) \\*\\/");
98+
Assert.True(match.Success);
99+
Assert.Equal(2, match.Groups.Count);
100+
Assert.NotNull(match.Groups[1].Value);
101+
var capture = match.Groups[1].Value;
102+
103+
// Act && Assert
104+
var updatedResult = await MSBuildProcessManager.DotnetMSBuild(project, "Publish", args: "/bl:updated.binlog /p:ServiceWorkerAssetsManifest=service-worker-assets.js");
105+
106+
Assert.BuildPassed(result);
107+
108+
var updatedVersion = File.ReadAllLines(serviceWorkerFile).Last();
109+
var updatedMatch = Regex.Match(updatedVersion, "\\/\\* Manifest version: (.{8}) \\*\\/");
110+
Assert.True(updatedMatch.Success);
111+
Assert.Equal(2, updatedMatch.Groups.Count);
112+
Assert.NotNull(updatedMatch.Groups[1].Value);
113+
var updatedCapture = updatedMatch.Groups[1].Value;
114+
115+
Assert.Equal(capture, updatedCapture);
116+
}
44117
}
45118
}

src/Mvc/Mvc.Formatters.Xml/src/XmlDataContractSerializerInputFormatter.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,8 @@ public override async Task<InputFormatterResult> ReadRequestBodyAsync(InputForma
143143
}
144144

145145
readStream = new FileBufferingReadStream(request.Body, memoryThreshold);
146+
// Ensure the file buffer stream is always disposed at the end of a request.
147+
request.HttpContext.Response.RegisterForDispose(readStream);
146148

147149
await readStream.DrainAsync(CancellationToken.None);
148150
readStream.Seek(0L, SeekOrigin.Begin);

src/Mvc/Mvc.Formatters.Xml/src/XmlSerializerInputFormatter.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,8 @@ public override async Task<InputFormatterResult> ReadRequestBodyAsync(
124124
}
125125

126126
readStream = new FileBufferingReadStream(request.Body, memoryThreshold);
127+
// Ensure the file buffer stream is always disposed at the end of a request.
128+
request.HttpContext.Response.RegisterForDispose(readStream);
127129

128130
await readStream.DrainAsync(CancellationToken.None);
129131
readStream.Seek(0L, SeekOrigin.Begin);

src/Mvc/Mvc.NewtonsoftJson/src/NewtonsoftJsonInputFormatter.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,8 @@ public override async Task<InputFormatterResult> ReadRequestBodyAsync(
153153
}
154154

155155
readStream = new FileBufferingReadStream(request.Body, memoryThreshold);
156+
// Ensure the file buffer stream is always disposed at the end of a request.
157+
request.HttpContext.Response.RegisterForDispose(readStream);
156158

157159
await readStream.DrainAsync(CancellationToken.None);
158160
readStream.Seek(0L, SeekOrigin.Begin);
@@ -278,7 +280,7 @@ void ErrorHandler(object sender, Newtonsoft.Json.Serialization.ErrorEventArgs ev
278280

279281
/// <summary>
280282
/// Called during deserialization to get the <see cref="JsonSerializer"/>. The formatter context
281-
/// that is passed gives an ability to create serializer specific to the context.
283+
/// that is passed gives an ability to create serializer specific to the context.
282284
/// </summary>
283285
/// <returns>The <see cref="JsonSerializer"/> used during deserialization.</returns>
284286
/// <remarks>
@@ -297,7 +299,7 @@ protected virtual JsonSerializer CreateJsonSerializer()
297299

298300
/// <summary>
299301
/// Called during deserialization to get the <see cref="JsonSerializer"/>. The formatter context
300-
/// that is passed gives an ability to create serializer specific to the context.
302+
/// that is passed gives an ability to create serializer specific to the context.
301303
/// </summary>
302304
/// <param name="context">A context object used by an input formatter for deserializing the request body into an object.</param>
303305
/// <returns>The <see cref="JsonSerializer"/> used during deserialization.</returns>
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
1-
@page "/error"
2-
3-
4-
<h1 class="text-danger">Error.</h1>
5-
<h2 class="text-danger">An error occurred while processing your request.</h2>
6-
7-
<h3>Development Mode</h3>
8-
<p>
9-
Swapping to <strong>Development</strong> environment will display more detailed information about the error that occurred.
10-
</p>
11-
<p>
12-
<strong>The Development environment shouldn't be enabled for deployed applications.</strong>
13-
It can result in displaying sensitive information from exceptions to end users.
14-
For local debugging, enable the <strong>Development</strong> environment by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong>
15-
and restarting the app.
16-
</p>
1+
@page
2+
3+
4+
<h1 class="text-danger">Error.</h1>
5+
<h2 class="text-danger">An error occurred while processing your request.</h2>
6+
7+
<h3>Development Mode</h3>
8+
<p>
9+
Swapping to <strong>Development</strong> environment will display more detailed information about the error that occurred.
10+
</p>
11+
<p>
12+
<strong>The Development environment shouldn't be enabled for deployed applications.</strong>
13+
It can result in displaying sensitive information from exceptions to end users.
14+
For local debugging, enable the <strong>Development</strong> environment by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong>
15+
and restarting the app.
16+
</p>

src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Client/Program.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,13 @@ public static async Task Main(string[] args)
2525
builder.RootComponents.Add<App>("app");
2626

2727
#if (!Hosted || NoAuth)
28-
builder.Services.AddTransient(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
28+
builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
2929
#else
3030
builder.Services.AddHttpClient("ComponentsWebAssembly_CSharp.ServerAPI", client => client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress))
3131
.AddHttpMessageHandler<BaseAddressAuthorizationMessageHandler>();
3232

3333
// Supply HttpClient instances that include access tokens when making requests to the server project
34-
builder.Services.AddTransient(sp => sp.GetRequiredService<IHttpClientFactory>().CreateClient("ComponentsWebAssembly_CSharp.ServerAPI"));
34+
builder.Services.AddScoped(sp => sp.GetRequiredService<IHttpClientFactory>().CreateClient("ComponentsWebAssembly_CSharp.ServerAPI"));
3535
#endif
3636
#if(!NoAuth)
3737

src/ProjectTemplates/test/template-baselines.json

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -914,7 +914,7 @@
914914
"Data/Migrations/00000000000000_CreateIdentitySchema.Designer.cs",
915915
"Data/Migrations/ApplicationDbContextModelSnapshot.cs",
916916
"Pages/Counter.razor",
917-
"Pages/Error.razor",
917+
"Pages/Error.cshtml",
918918
"Pages/FetchData.razor",
919919
"Pages/Index.razor",
920920
"Pages/_Host.cshtml",
@@ -951,7 +951,7 @@
951951
"Data/WeatherForecast.cs",
952952
"Data/WeatherForecastService.cs",
953953
"Pages/Counter.razor",
954-
"Pages/Error.razor",
954+
"Pages/Error.cshtml",
955955
"Pages/FetchData.razor",
956956
"Pages/Index.razor",
957957
"Pages/_Host.cshtml",
@@ -988,7 +988,7 @@
988988
"Data/WeatherForecast.cs",
989989
"Data/WeatherForecastService.cs",
990990
"Pages/Counter.razor",
991-
"Pages/Error.razor",
991+
"Pages/Error.cshtml",
992992
"Pages/FetchData.razor",
993993
"Pages/Index.razor",
994994
"Pages/_Host.cshtml",
@@ -1024,7 +1024,7 @@
10241024
"_Imports.razor",
10251025
"Data/WeatherForecast.cs",
10261026
"Data/WeatherForecastService.cs",
1027-
"Pages/Error.razor",
1027+
"Pages/Error.cshtml",
10281028
"Pages/Counter.razor",
10291029
"Pages/FetchData.razor",
10301030
"Pages/Index.razor",
@@ -1061,7 +1061,7 @@
10611061
"Data/WeatherForecast.cs",
10621062
"Data/WeatherForecastService.cs",
10631063
"Pages/Counter.razor",
1064-
"Pages/Error.razor",
1064+
"Pages/Error.cshtml",
10651065
"Pages/FetchData.razor",
10661066
"Pages/Index.razor",
10671067
"Pages/_Host.cshtml",
@@ -1098,7 +1098,7 @@
10981098
"Data/WeatherForecast.cs",
10991099
"Data/WeatherForecastService.cs",
11001100
"Pages/Counter.razor",
1101-
"Pages/Error.razor",
1101+
"Pages/Error.cshtml",
11021102
"Pages/FetchData.razor",
11031103
"Pages/Index.razor",
11041104
"Pages/_Host.cshtml",

0 commit comments

Comments
 (0)