Skip to content

Commit

Permalink
Add option to serve WebAssembly app with multithreading (#54062)
Browse files Browse the repository at this point in the history
  • Loading branch information
SteveSandersonMS authored Feb 19, 2024
1 parent f03012c commit 8e435d1
Show file tree
Hide file tree
Showing 24 changed files with 562 additions and 11 deletions.
23 changes: 21 additions & 2 deletions AspNetCore.sln
Original file line number Diff line number Diff line change
Expand Up @@ -1322,6 +1322,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StandaloneApp", "src\Compon
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThreadingApp", "src\Components\WebAssembly\testassets\ThreadingApp\ThreadingApp.csproj", "{A40350FE-4334-4007-B1C3-6BEB1B070308}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThreadingApp.Server", "src\Components\WebAssembly\testassets\ThreadingApp.Server\ThreadingApp.Server.csproj", "{F1792637-28B9-4F2A-B318-FA923C365049}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "HealthChecks", "HealthChecks", "{C1E7F837-6988-43E2-9E1C-7302DB484F99}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{2A91479A-4ABE-4BB7-9A5E-CA3B9CCFC69E}"
Expand Down Expand Up @@ -1778,7 +1780,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Output
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.OutputCaching.StackExchangeRedis", "src\Middleware\Microsoft.AspNetCore.OutputCaching.StackExchangeRedis\src\Microsoft.AspNetCore.OutputCaching.StackExchangeRedis.csproj", "{F232B503-D412-45EE-8B31-EFD46B9FA302}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.InternalTesting", "src\Testing\src\Microsoft.AspNetCore.InternalTesting.csproj", "{B0A8E5D4-BC5A-448E-B222-431B6B2EB58E}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.InternalTesting", "src\Testing\src\Microsoft.AspNetCore.InternalTesting.csproj", "{B0A8E5D4-BC5A-448E-B222-431B6B2EB58E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.InternalTesting.Tests", "src\Testing\test\Microsoft.AspNetCore.InternalTesting.Tests.csproj", "{15D08EA7-8C63-45FB-8B4D-C5F8E43B433E}"
EndProject
Expand Down Expand Up @@ -8250,6 +8252,22 @@ Global
{A40350FE-4334-4007-B1C3-6BEB1B070308}.Release|x64.Build.0 = Release|Any CPU
{A40350FE-4334-4007-B1C3-6BEB1B070308}.Release|x86.ActiveCfg = Release|Any CPU
{A40350FE-4334-4007-B1C3-6BEB1B070308}.Release|x86.Build.0 = Release|Any CPU
{F1792637-28B9-4F2A-B318-FA923C365049}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F1792637-28B9-4F2A-B318-FA923C365049}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F1792637-28B9-4F2A-B318-FA923C365049}.Debug|arm64.ActiveCfg = Debug|Any CPU
{F1792637-28B9-4F2A-B318-FA923C365049}.Debug|arm64.Build.0 = Debug|Any CPU
{F1792637-28B9-4F2A-B318-FA923C365049}.Debug|x64.ActiveCfg = Debug|Any CPU
{F1792637-28B9-4F2A-B318-FA923C365049}.Debug|x64.Build.0 = Debug|Any CPU
{F1792637-28B9-4F2A-B318-FA923C365049}.Debug|x86.ActiveCfg = Debug|Any CPU
{F1792637-28B9-4F2A-B318-FA923C365049}.Debug|x86.Build.0 = Debug|Any CPU
{F1792637-28B9-4F2A-B318-FA923C365049}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F1792637-28B9-4F2A-B318-FA923C365049}.Release|Any CPU.Build.0 = Release|Any CPU
{F1792637-28B9-4F2A-B318-FA923C365049}.Release|arm64.ActiveCfg = Release|Any CPU
{F1792637-28B9-4F2A-B318-FA923C365049}.Release|arm64.Build.0 = Release|Any CPU
{F1792637-28B9-4F2A-B318-FA923C365049}.Release|x64.ActiveCfg = Release|Any CPU
{F1792637-28B9-4F2A-B318-FA923C365049}.Release|x64.Build.0 = Release|Any CPU
{F1792637-28B9-4F2A-B318-FA923C365049}.Release|x86.ActiveCfg = Release|Any CPU
{F1792637-28B9-4F2A-B318-FA923C365049}.Release|x86.Build.0 = Release|Any CPU
{B06040BC-DA28-4923-8CAC-20EB517D471B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B06040BC-DA28-4923-8CAC-20EB517D471B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B06040BC-DA28-4923-8CAC-20EB517D471B}.Debug|arm64.ActiveCfg = Debug|Any CPU
Expand Down Expand Up @@ -11405,7 +11423,8 @@ Global
{9788C76F-658B-4441-88F8-22C6B86FAD27} = {7D2B0799-A634-42AC-AE77-5D167BA51389}
{1970D5CD-D9A4-4673-A297-179BB04199F4} = {7D2B0799-A634-42AC-AE77-5D167BA51389}
{A40350FE-4334-4007-B1C3-6BEB1B070309} = {7D2B0799-A634-42AC-AE77-5D167BA51389}
{A40350FE-4334-4007-B1C3-6BEB1B070308} = {6126DCE4-9692-4EE2-B240-C65743572995}
{A40350FE-4334-4007-B1C3-6BEB1B070308} = {7D2B0799-A634-42AC-AE77-5D167BA51389}
{F1792637-28B9-4F2A-B318-FA923C365049} = {7D2B0799-A634-42AC-AE77-5D167BA51389}
{C1E7F837-6988-43E2-9E1C-7302DB484F99} = {017429CC-C5FB-48B4-9C46-034E29EE2F06}
{2A91479A-4ABE-4BB7-9A5E-CA3B9CCFC69E} = {C1E7F837-6988-43E2-9E1C-7302DB484F99}
{7CB09412-C9B0-47E8-A8C3-311AA4CFDE04} = {C1E7F837-6988-43E2-9E1C-7302DB484F99}
Expand Down
1 change: 1 addition & 0 deletions src/Components/Components.slnf
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"src\\Components\\WebAssembly\\testassets\\HostedInAspNet.Server\\HostedInAspNet.Server.csproj",
"src\\Components\\WebAssembly\\testassets\\StandaloneApp\\StandaloneApp.csproj",
"src\\Components\\WebAssembly\\testassets\\ThreadingApp\\ThreadingApp.csproj",
"src\\Components\\WebAssembly\\testassets\\ThreadingApp.Server\\ThreadingApp.Server.csproj",
"src\\Components\\WebAssembly\\testassets\\Wasm.Prerendered.Client\\Wasm.Prerendered.Client.csproj",
"src\\Components\\WebAssembly\\testassets\\Wasm.Prerendered.Server\\Wasm.Prerendered.Server.csproj",
"src\\Components\\WebAssembly\\testassets\\WasmLinkerTest\\WasmLinkerTest.csproj",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
<Reference Include="Microsoft.AspNetCore.Components.Endpoints" />
<Reference Include="Microsoft.AspNetCore.Components.Server" />
<Reference Include="Microsoft.AspNetCore.HttpsPolicy" />
<Reference Include="Microsoft.Extensions.Hosting" />
<Reference Include="Microsoft.AspNetCore.StaticFiles" />
<Reference Include="Microsoft.AspNetCore.Mvc" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,15 @@ public sealed class WebAssemblyComponentsEndpointOptions
/// This path must correspond to a referenced Blazor WebAssembly application project.
/// </summary>
public PathString PathPrefix { get; set; }

/// <summary>
/// Gets or sets a flag to determine whether to enable WebAssembly multithreading. If true,
/// the server will add headers similar to <code>Cross-Origin-Embedder-Policy: require-corp</code> and
/// <code>Cross-Origin-Opener-Policy: same-origin</code> on the response for the host page, because
/// this is required to enable the SharedArrayBuffer feature in the browser.
///
/// Note that enabling this feature can restrict your ability to use other JavaScript APIs. For more
/// information, see <see href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer#security_requirements" />.
/// </summary>
public bool ServeMultithreadingHeaders { get; set; }
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Microsoft.AspNetCore.Components.Endpoints;
using Microsoft.AspNetCore.Components.Endpoints.Infrastructure;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Components.WebAssembly.Server;
using System.Linq;

namespace Microsoft.AspNetCore.Builder;

Expand All @@ -24,6 +26,24 @@ public static RazorComponentsEndpointConventionBuilder AddInteractiveWebAssembly

callback?.Invoke(options);

if (options.ServeMultithreadingHeaders)
{
builder.Add(endpointBuilder =>
{
var needsCoopHeaders = endpointBuilder.Metadata.OfType<ComponentTypeMetadata>().Any() // e.g., /somecomponent
|| endpointBuilder.Metadata.OfType<WebAssemblyRenderModeWithOptions>().Any(); // e.g., /_framework/*
if (needsCoopHeaders && endpointBuilder.RequestDelegate is { } originalDelegate)
{
endpointBuilder.RequestDelegate = httpContext =>
{
httpContext.Response.Headers["Cross-Origin-Embedder-Policy"] = "require-corp";
httpContext.Response.Headers["Cross-Origin-Opener-Policy"] = "same-origin";
return originalDelegate(httpContext);
};
}
});
}

ComponentEndpointConventionBuilderHelper.AddRenderMode(builder, new WebAssemblyRenderModeWithOptions(options));
return builder;
}
Expand Down
2 changes: 2 additions & 0 deletions src/Components/WebAssembly/Server/src/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
#nullable enable
Microsoft.AspNetCore.Components.WebAssembly.Server.WebAssemblyComponentsEndpointOptions.ServeMultithreadingHeaders.get -> bool
Microsoft.AspNetCore.Components.WebAssembly.Server.WebAssemblyComponentsEndpointOptions.ServeMultithreadingHeaders.set -> void
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
<Reference Include="Microsoft.AspNetCore" />
<Reference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" />
<Reference Include="Microsoft.AspNetCore.Diagnostics" />
<Reference Include="Microsoft.Extensions.Hosting" />
<Reference Include="Microsoft.AspNetCore.Mvc" />
<!-- Avoid MSB3277 warnings due to dependencies brought in through Microsoft.AspNetCore.Blazor targeting netstandard2.0. -->
<Reference Include="System.Text.Json" />
</ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<base href="/" />
<link href="bootstrap.min.css" rel="stylesheet" />
<link href="app.css" rel="stylesheet" />
<HeadOutlet @rendermode="RenderMode.InteractiveWebAssembly" />
</head>

<body>
<app>Loading...</app>

<div id="blazor-error-ui">
An unhandled exception has occurred. See browser dev tools for details.
<a href="" class="reload">Reload</a>
<a class="dismiss">🗙</a>
</div>

<script src="_framework/blazor.web.js"></script>
</body>

</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
@using System.Net.Http
@using System.Net.Http.Json
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web
@using static Microsoft.AspNetCore.Components.Web.RenderMode
@using Microsoft.AspNetCore.Components.Web.Virtualization
@using Microsoft.JSInterop
@using ThreadingApp.Server
@using ThreadingApp.Server.Components
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace ThreadingApp.Server;

public class Program
{
private static void Main(string[] args)
=> BuildWebHost(args).Run();

public static IHost BuildWebHost(string[] args)
{
var builder = WebApplication.CreateBuilder(args);

// We require this line because we run in Production environment
// and static web assets are only on by default during development.
builder.Environment.ApplicationName = typeof(Program).Assembly.GetName().Name!;
builder.WebHost.UseStaticWebAssets();

// Add services to the container.
builder.Services.AddRazorComponents()
.AddInteractiveWebAssemblyComponents();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error", createScopeForErrors: true);
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}

app.UseHttpsRedirection();

app.UseStaticFiles();
app.UseAntiforgery();

app.MapRazorComponents<Server.Components.App>()
.AddInteractiveWebAssemblyRenderMode(options => { options.ServeMultithreadingHeaders = true; })
.AddAdditionalAssemblies(typeof(ThreadingApp.Pages.Index).Assembly);

return app;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"$schema": "https://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:14795",
"sslPort": 44365
}
},
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "http://localhost:5252",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "https://localhost:7146;http://localhost:5252",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\ThreadingApp\ThreadingApp.csproj" />
</ItemGroup>

<ItemGroup>
<Reference Include="Microsoft.AspNetCore" />
<Reference Include="Microsoft.AspNetCore.Components.Endpoints" />
<Reference Include="Microsoft.AspNetCore.Components.Server" />
<Reference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" />
<Reference Include="Microsoft.AspNetCore.HttpsPolicy" />
<Reference Include="Microsoft.AspNetCore.StaticFiles" />
<Reference Include="Microsoft.AspNetCore.Mvc" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,22 @@
currentCount++;
}

async Task WaitUntilBackgroundThreadsReady()
{
for (var i = 0; i < 10; i++)
{
var backgroundThreadId = await Task.Run(() => Thread.CurrentThread.ManagedThreadId);
if (backgroundThreadId != 1)
{
return;
}

await Task.Delay(1000);
}

throw new InvalidOperationException("Timed out after 10 seconds waiting for background threads to become available");
}

async Task TestThreads()
{
if (!OperatingSystem.IsBrowser())
Expand All @@ -32,6 +48,8 @@
throw new Exception("We should be on main thread!");
}

await WaitUntilBackgroundThreadsReady();

Exception exc = null;

// run in the thread pool
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,7 @@

<h1>Hello, world!</h1>

Welcome to your new app.
<p>
This app can be run either standalone on the dev server (by launching <code>ThreadingApp</code>) or hosted in a
Blazor Web server app (by launching <code>ThreadingApp.Server</code>).
</p>
Loading

0 comments on commit 8e435d1

Please sign in to comment.