Skip to content

Option to serve WebAssembly app with multithreading #54071

@SteveSandersonMS

Description

@SteveSandersonMS

Background and Motivation

Work continues in the runtime repo on WebAssembly multithreading. It's now pretty easy to enable and use - people just need to:

A. Add <WasmEnableThreads>true</WasmEnableThreads> in the wasm project csproj

B. Ensure the site is served with the relevant Cross-Origin-... headers as per browser requirements

  • For Blazor WebAssembly Standalone, during development the dev server will automatically serve these headers based on WasmEnableThreads (because there's no easy way to configure the dev server, so we have to do it automatically). And in production, the developer has to configure the server's response headers themselves because Blazor/ASP.NETCore is not involved. This PR does not affect it.
  • For traditional Blazor WebAssembly hosted-on-ASP.NET-Core (i.e., using MapBlazorFrameworkFiles and MapFallbackToFile, not the new .NET 8 Blazor Web model), developers can easily write a few lines of ASP.NET Core middleware to serve the necessary headers, which we can document. This PR does not affect it.
  • For Blazor Web using the InteractiveWebAssembly render mode, this PR adds a simple API for serving the headers on the correct endpoints matching what gets registered by MapRazorComponents. That is, it serves them for any Razor Components endpoint (since it may be or contain an InteractiveWebAssembly component), plus for _framework/* (since that's required to load the Blazor WebAssembly runtime with SharedArrayBuffer support in the worker thread).

Proposed API

namespace Microsoft.AspNetCore.Components.WebAssembly.Server;

public sealed class WebAssemblyComponentsEndpointOptions
{
+    /// <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; }
}

Usage Examples

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

Alternative Designs

Overall, since this feature is nascent and remains experimental, I'm erring on the side of making it more explicit and literal so people who turn it on know what they are getting into. In the future if we get to the point that wasm multithreading is so good that most people should enable it, we retain the option to streamline the usage.

Alternative: Infer it automatically

We could try to infer this based on <WasmEnableThreads> in the wasm csproj, but:

  1. There could be multiple wasm apps. What if they want different configurations?
  2. Since this is an early and quite experimental feature, it's really good to force developers to opt in to serving the COOP headers explicitly. Doing so can restrict the availability of other JS APIs (e.g., can interfere with popups that might be used for auth flows) as described at https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer#security_requirements. It might turn out to be fine, but in case this causes issues for people, it's really good that the developer has a chance to discover what they are enabling and the possible side-effects of that.

Alternative: Extension method

An alternative API would be making it an extension method on RazorComponentsEndpointConventionBuilder, e.g.:

app.MapRazorComponents<App>().AddInteractiveWebAssemblyRenderMode().ServeWasmMultithreadingHeaders();

... but that would be very strange and ambiguous since you could also write:

app.MapRazorComponents<App>().AddInteractiveServerRenderMode().ServeWasmMultithreadingHeaders();

I prefer it being an option on AddInteractiveWebAssemblyRenderMode since it is more specific and accurate, as it only applies within that case.

Alternative: Naming

It would be more obvious to call it EnableMultithreading instead of ServeMultithreadingHeaders, but that would not so well serve the purpose of helping developer understand what exactly they are enabling and how it might have other side effects.

Risks

Nothing obvious.

Metadata

Metadata

Labels

api-approvedAPI was approved in API review, it can be implementedarea-blazorIncludes: Blazor, Razor Components

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions