Skip to content

Commit 749b2d2

Browse files
pavelsavararadical
andauthored
[browser] app start benchmark fix, loadBootResource fix (#89857)
Co-authored-by: Ankit Jain <radical@gmail.com>
1 parent 5730c20 commit 749b2d2

19 files changed

+331
-195
lines changed

src/mono/sample/wasm/browser-advanced/Wasm.Advanced.Sample.csproj

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
</PropertyGroup>
2222
<ItemGroup>
2323
<WasmExtraFilesToDeploy Include="main.js" />
24+
<WasmExtraFilesToDeploy Include="advanced-sample.lib.module.js" />
2425
<!-- add export GL object from Module -->
2526
<EmccExportedRuntimeMethod Include="GL" />
2627
<NativeFileReference Include="fibonacci.c" />
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
export function onRuntimeConfigLoaded(config) {
5+
console.log("advanced-sample onRuntimeConfigLoaded")
6+
}
7+
8+
export async function onRuntimeReady({ getAssemblyExports, getConfig }) {
9+
console.log("advanced-sample onRuntimeReady")
10+
}

src/mono/sample/wasm/browser-advanced/index.html

+3-3
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,14 @@
99
<meta name="viewport" content="width=device-width, initial-scale=1.0">
1010
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'wasm-unsafe-eval';" />
1111
<script type='module' src="./main.js"></script>
12-
<script type='module' src="./_framework/dotnet.js"></script>
13-
<link rel="preload" href="./_framework/blazor.boot.json" as="fetch" crossorigin="anonymous">
12+
<script type='module' src="./dotnet.js"></script>
13+
<link rel="preload" href="./blazor.boot.json" as="fetch" crossorigin="use-credentials">
1414
<link rel="prefetch" href="./dotnet.native.js" as="fetch" crossorigin="anonymous">
1515
<link rel="prefetch" href="./dotnet.runtime.js" as="fetch" crossorigin="anonymous">
1616
<link rel="prefetch" href="./dotnet.native.wasm" as="fetch" crossorigin="anonymous">
1717
<!-- users should consider if they optimize for the first load or subsequent load from memory snapshot -->
1818
<link rel="prefetch" href="./icudt.dat" as="fetch" crossorigin="anonymous">
19-
<link rel="prefetch" href="./managed/System.Private.CoreLib.wasm" as="fetch" crossorigin="anonymous">
19+
<link rel="prefetch" href="./System.Private.CoreLib.wasm" as="fetch" crossorigin="anonymous">
2020
</head>
2121

2222
<body>

src/mono/sample/wasm/browser-advanced/main.js

+3
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ try {
3838
// config is loaded and could be tweaked before the rest of the runtime startup sequence
3939
config.environmentVariables["MONO_LOG_LEVEL"] = "debug";
4040
config.browserProfilerOptions = {};
41+
config.resources.modulesAfterConfigLoaded = {
42+
"advanced-sample.lib.module.js": ""
43+
}
4144
},
4245
preInit: () => { console.log('user code Module.preInit'); },
4346
preRun: () => { console.log('user code Module.preRun'); },

src/mono/sample/wasm/browser-bench/appstart-frame.html

+5-5
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,12 @@
99
<meta name="viewport" content="width=device-width, initial-scale=1.0">
1010
<script type="module" src="./frame-main.js"></script>
1111
<script type='module' src="./_framework/dotnet.js"></script>
12-
<link rel="preload" href="./_framework/blazor.boot.json" as="fetch" crossorigin="anonymous">
13-
<link rel="prefetch" href="./dotnet.native.js" as="fetch" crossorigin="anonymous">
14-
<link rel="prefetch" href="./dotnet.runtime.js" as="fetch" crossorigin="anonymous">
15-
<link rel="prefetch" href="./dotnet.native.wasm" as="fetch" crossorigin="anonymous">
12+
<link rel="preload" href="./_framework/blazor.boot.json" as="fetch" crossorigin="use-credentials">
13+
<link rel="prefetch" href="./_framework/dotnet.native.js" as="fetch" crossorigin="anonymous">
14+
<link rel="prefetch" href="./_framework/dotnet.runtime.js" as="fetch" crossorigin="anonymous">
15+
<link rel="prefetch" href="./_framework/dotnet.native.wasm" as="fetch" crossorigin="anonymous">
1616
<!-- users should consider if they optimize for the first load or subsequent load from memory snapshot -->
17-
<link rel="prefetch" href="./managed/System.Private.CoreLib.dll" as="fetch" crossorigin="anonymous">
17+
<link rel="prefetch" href="./_framework/System.Private.CoreLib.dll" as="fetch" crossorigin="anonymous">
1818
</head>
1919

2020
<body>

src/mono/sample/wasm/simple-server/Program.cs

+42-16
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using System.Collections.Concurrent;
77
using System.Runtime.InteropServices;
88
using System;
9+
using System.Security.Cryptography;
910

1011
namespace HttpServer
1112
{
@@ -16,11 +17,13 @@ public sealed class Session
1617
public int Finished { get; set; } = 0;
1718
}
1819

20+
public sealed record FileContent(byte[] buffer, string hash);
21+
1922
public sealed class Program
2023
{
2124
private bool Verbose = false;
2225
private ConcurrentDictionary<string, Session> Sessions = new ConcurrentDictionary<string, Session>();
23-
private Dictionary<string, byte[]> cache = new Dictionary<string, byte[]>(StringComparer.OrdinalIgnoreCase);
26+
private Dictionary<string, FileContent> cache = new(StringComparer.OrdinalIgnoreCase);
2427

2528
public static int Main()
2629
{
@@ -104,7 +107,7 @@ private void HandleRequest(HttpListener listener)
104107
ReceivePostAsync(context);
105108
}
106109

107-
private async Task<byte[]?> GetFileContent(string path)
110+
private async Task<FileContent?> GetFileContent(string path)
108111
{
109112
if (Verbose)
110113
await Console.Out.WriteLineAsync($"get content for: {path}");
@@ -124,9 +127,13 @@ private void HandleRequest(HttpListener listener)
124127
if (Verbose)
125128
await Console.Out.WriteLineAsync($"adding content to cache for: {path}");
126129

127-
cache[path] = content;
130+
using HashAlgorithm hashAlgorithm = SHA256.Create();
131+
byte[] hash = hashAlgorithm.ComputeHash(content);
132+
var fc = new FileContent(content, "sha256-" + Convert.ToBase64String(hash));
133+
134+
cache[path] = fc;
128135

129-
return content;
136+
return fc;
130137
}
131138

132139
private async void ReceivePostAsync(HttpListenerContext context)
@@ -189,14 +196,14 @@ private async void ServeAsync(HttpListenerContext context)
189196
else if (path.StartsWith("/"))
190197
path = path.Substring(1);
191198

192-
byte[]? buffer;
199+
FileContent? fc;
193200
try
194201
{
195-
buffer = await GetFileContent(path);
202+
fc = await GetFileContent(path);
196203

197-
if (buffer != null && throttleMbps > 0)
204+
if (fc != null && throttleMbps > 0)
198205
{
199-
double delaySeconds = (buffer.Length * 8) / (throttleMbps * 1024 * 1024);
206+
double delaySeconds = (fc.buffer.Length * 8) / (throttleMbps * 1024 * 1024);
200207
int delayMs = (int)(delaySeconds * 1000);
201208
if (session != null)
202209
{
@@ -246,12 +253,20 @@ private async void ServeAsync(HttpListenerContext context)
246253
}
247254
}
248255
}
249-
catch (Exception)
256+
catch (System.IO.DirectoryNotFoundException)
257+
{
258+
if (Verbose)
259+
Console.WriteLine($"Not found: {path}");
260+
fc = null;
261+
}
262+
catch (Exception ex)
250263
{
251-
buffer = null;
264+
if (Verbose)
265+
Console.WriteLine($"Exception: {ex}");
266+
fc = null;
252267
}
253268

254-
if (buffer != null)
269+
if (fc != null)
255270
{
256271
string? contentType = null;
257272
if (path.EndsWith(".wasm"))
@@ -270,7 +285,7 @@ private async void ServeAsync(HttpListenerContext context)
270285
{
271286
Console.WriteLine("Faking 500 " + url);
272287
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
273-
await stream.WriteAsync(buffer, 0, 0).ConfigureAwait(false);
288+
await stream.WriteAsync(fc.buffer, 0, 0).ConfigureAwait(false);
274289
await stream.FlushAsync();
275290
context.Response.Close();
276291
return;
@@ -279,25 +294,36 @@ private async void ServeAsync(HttpListenerContext context)
279294
if (contentType != null)
280295
context.Response.ContentType = contentType;
281296

282-
context.Response.ContentLength64 = buffer.Length;
283-
context.Response.AppendHeader("cache-control", "public, max-age=31536000");
297+
// context.Response.AppendHeader("cache-control", "public, max-age=31536000");
284298
context.Response.AppendHeader("Cross-Origin-Embedder-Policy", "require-corp");
285299
context.Response.AppendHeader("Cross-Origin-Opener-Policy", "same-origin");
300+
context.Response.AppendHeader("ETag", fc.hash);
286301

287302
// test download re-try
288303
if (url.Query.Contains("testAbort"))
289304
{
290305
Console.WriteLine("Faking abort " + url);
291-
await stream.WriteAsync(buffer, 0, 10).ConfigureAwait(false);
306+
context.Response.ContentLength64 = fc.buffer.Length;
307+
await stream.WriteAsync(fc.buffer, 0, 10).ConfigureAwait(false);
292308
await stream.FlushAsync();
293309
await Task.Delay(100);
294310
context.Response.Abort();
295311
return;
296312
}
313+
var ifNoneMatch = context.Request.Headers.Get("If-None-Match");
314+
if (ifNoneMatch == fc.hash)
315+
{
316+
context.Response.StatusCode = 304;
317+
await stream.FlushAsync();
318+
stream.Close();
319+
context.Response.Close();
320+
return;
321+
}
297322

298323
try
299324
{
300-
await stream.WriteAsync(buffer).ConfigureAwait(false);
325+
context.Response.ContentLength64 = fc.buffer.Length;
326+
await stream.WriteAsync(fc.buffer).ConfigureAwait(false);
301327
}
302328
catch (Exception e)
303329
{

src/mono/wasm/features.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ See also [fetch integrity on MDN](https://developer.mozilla.org/en-US/docs/Web/A
191191
In order to start downloading application resources as soon as possible you can add HTML elements to `<head>` of your page similar to:
192192

193193
```html
194-
<link rel="preload" href="./_framework/blazor.boot.json" as="fetch" crossorigin="anonymous">
194+
<link rel="preload" href="./_framework/blazor.boot.json" as="fetch" crossorigin="use-credentials">
195195
<link rel="prefetch" href="./_framework/dotnet.native.js" as="fetch" crossorigin="anonymous">
196196
<link rel="prefetch" href="./_framework/dotnet.runtime.js" as="fetch" crossorigin="anonymous">
197197
<link rel="prefetch" href="./_framework/dotnet.native.wasm" as="fetch" crossorigin="anonymous">

src/mono/wasm/runtime/dotnet.d.ts

+26-8
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,14 @@ type MonoConfig = {
129129
* Gets a value that determines whether to enable caching of the 'resources' inside a CacheStorage instance within the browser.
130130
*/
131131
cacheBootResources?: boolean;
132+
/**
133+
* Configures use of the `integrity` directive for fetching assets
134+
*/
135+
disableIntegrityCheck?: boolean;
136+
/**
137+
* Configures use of the `no-cache` directive for fetching assets
138+
*/
139+
disableNoCacheFetch?: boolean;
132140
/**
133141
* Enables diagnostic log messages during startup
134142
*/
@@ -212,18 +220,28 @@ type ResourceList = {
212220
* When returned string is not qualified with `./` or absolute URL, it will be resolved against the application base URI.
213221
*/
214222
type LoadBootResourceCallback = (type: WebAssemblyBootResourceType, name: string, defaultUri: string, integrity: string, behavior: AssetBehaviors) => string | Promise<Response> | null | undefined;
215-
interface ResourceRequest {
216-
name: string;
217-
behavior: AssetBehaviors;
218-
resolvedUrl?: string;
219-
hash?: string | null | "";
220-
}
221223
interface LoadingResource {
222224
name: string;
223225
url: string;
224226
response: Promise<Response>;
225227
}
226-
interface AssetEntry extends ResourceRequest {
228+
interface AssetEntry {
229+
/**
230+
* the name of the asset, including extension.
231+
*/
232+
name: string;
233+
/**
234+
* determines how the asset will be handled once loaded
235+
*/
236+
behavior: AssetBehaviors;
237+
/**
238+
* this should be absolute url to the asset
239+
*/
240+
resolvedUrl?: string;
241+
/**
242+
* the integrity hash of the asset (if any)
243+
*/
244+
hash?: string | null | "";
227245
/**
228246
* If specified, overrides the path of the asset in the virtual filesystem and similar data structures once downloaded.
229247
*/
@@ -442,4 +460,4 @@ declare global {
442460
}
443461
declare const createDotnetRuntime: CreateDotnetRuntimeType;
444462

445-
export { AssetBehaviors, AssetEntry, CreateDotnetRuntimeType, DotnetHostBuilder, DotnetModuleConfig, EmscriptenModule, GlobalizationMode, IMemoryView, ModuleAPI, MonoConfig, ResourceRequest, RuntimeAPI, createDotnetRuntime as default, dotnet, exit };
463+
export { AssetBehaviors, AssetEntry, CreateDotnetRuntimeType, DotnetHostBuilder, DotnetModuleConfig, EmscriptenModule, GlobalizationMode, IMemoryView, ModuleAPI, MonoConfig, RuntimeAPI, createDotnetRuntime as default, dotnet, exit };

0 commit comments

Comments
 (0)