diff --git a/src/mono/sample/wasm/browser/Wasm.Browser.Sample.csproj b/src/mono/sample/wasm/browser/Wasm.Browser.Sample.csproj index 817971712ae5fe..c1eca5280663e1 100644 --- a/src/mono/sample/wasm/browser/Wasm.Browser.Sample.csproj +++ b/src/mono/sample/wasm/browser/Wasm.Browser.Sample.csproj @@ -14,5 +14,5 @@ <_SampleProject>Wasm.Browser.Sample.csproj - + diff --git a/src/mono/sample/wasm/browser/main.js b/src/mono/sample/wasm/browser/main.js index afe23356a73951..cf71a8e416b30b 100644 --- a/src/mono/sample/wasm/browser/main.js +++ b/src/mono/sample/wasm/browser/main.js @@ -8,12 +8,28 @@ function sub(a, b) { return a - b; } +let testError = true; +let testAbort = true; try { const { runtimeBuildInfo, setModuleImports, getAssemblyExports, runMain, getConfig } = await dotnet .withConsoleForwarding() .withElementOnExit() .withModuleConfig({ configSrc: "./mono-config.json", + imports: { + fetch: (url, fetchArgs) => { + // we are testing that we can retry loading of the assembly + if (testAbort && url.indexOf('System.Private.Uri.dll') != -1) { + testAbort = false; + return fetch(url + "?testAbort=true", fetchArgs); + } + if (testError && url.indexOf('System.Console.dll') != -1) { + testError = false; + return fetch(url + "?testError=true", fetchArgs); + } + return fetch(url, fetchArgs); + } + }, onConfigLoaded: (config) => { // This is called during emscripten `dotnet.wasm` instantiation, after we fetched config. console.log('user code Module.onConfigLoaded'); diff --git a/src/mono/sample/wasm/simple-server/Program.cs b/src/mono/sample/wasm/simple-server/Program.cs index 70ef1e573093f1..7a26f4406fb0dd 100644 --- a/src/mono/sample/wasm/simple-server/Program.cs +++ b/src/mono/sample/wasm/simple-server/Program.cs @@ -159,12 +159,36 @@ private async void ServeAsync(HttpListenerContext context) if (path.EndsWith(".js") || path.EndsWith(".mjs") || path.EndsWith(".cjs")) contentType = "text/javascript"; + var stream = context.Response.OutputStream; + + // test download re-try + if (url.Query.Contains("testError")) + { + Console.WriteLine("Faking 500 " + url); + context.Response.StatusCode = (int)HttpStatusCode.InternalServerError; + await stream.WriteAsync(buffer, 0, 0).ConfigureAwait(false); + await stream.FlushAsync(); + context.Response.Close(); + return; + } + if (contentType != null) context.Response.ContentType = contentType; context.Response.ContentLength64 = buffer.Length; context.Response.AppendHeader("cache-control", "public, max-age=31536000"); - var stream = context.Response.OutputStream; + + // test download re-try + if (url.Query.Contains("testAbort")) + { + Console.WriteLine("Faking abort " + url); + await stream.WriteAsync(buffer, 0, 10).ConfigureAwait(false); + await stream.FlushAsync(); + await Task.Delay(100); + context.Response.Abort(); + return; + } + try { await stream.WriteAsync(buffer).ConfigureAwait(false); diff --git a/src/mono/wasm/runtime/assets.ts b/src/mono/wasm/runtime/assets.ts index 80d63429102938..8e3630188ddbfb 100644 --- a/src/mono/wasm/runtime/assets.ts +++ b/src/mono/wasm/runtime/assets.ts @@ -76,10 +76,10 @@ export async function mono_download_assets(): Promise { asset.pendingDownloadInternal = asset.pendingDownload; const waitForExternalData: () => Promise = async () => { const response = await asset.pendingDownloadInternal!.response; - ++actual_downloaded_assets_count; if (!headersOnly) { asset.buffer = await response.arrayBuffer(); } + ++actual_downloaded_assets_count; return { asset, buffer: asset.buffer }; }; promises_of_assets_with_buffer.push(waitForExternalData()); @@ -122,6 +122,10 @@ export async function mono_download_assets(): Promise { if (!skipInstantiateByAssetTypes[asset.behavior]) { expected_instantiated_assets_count--; } + } else { + if (skipBufferByAssetTypes[asset.behavior]) { + ++actual_downloaded_assets_count; + } } } })()); @@ -197,7 +201,9 @@ async function start_asset_download_with_throttle(asset: AssetEntry, downloadDat if (!downloadData || !response) { return undefined; } - return await response.arrayBuffer(); + const buffer = await response.arrayBuffer(); + ++actual_downloaded_assets_count; + return buffer; } finally { --parallel_count; @@ -226,7 +232,6 @@ async function start_asset_download_sources(asset: AssetEntryInternal): Promise< } }) as any }; - ++actual_downloaded_assets_count; return asset.pendingDownloadInternal.response; } if (asset.pendingDownloadInternal && asset.pendingDownloadInternal.response) { @@ -262,7 +267,6 @@ async function start_asset_download_sources(asset: AssetEntryInternal): Promise< if (!response.ok) { continue;// next source } - ++actual_downloaded_assets_count; return response; } catch (err) { @@ -293,7 +297,7 @@ function resolve_path(asset: AssetEntry, sourcePrefix: string): string { : asset.name; } else if (asset.behavior === "resource") { - const path = asset.culture !== "" ? `${asset.culture}/${asset.name}` : asset.name; + const path = asset.culture && asset.culture !== "" ? `${asset.culture}/${asset.name}` : asset.name; attemptUrl = assemblyRootFolder ? (assemblyRootFolder + "/" + path) : path; @@ -420,7 +424,7 @@ function _instantiate_asset(asset: AssetEntry, url: string, bytes: Uint8Array) { Module.printErr(`MONO_WASM: Error loading ICU asset ${asset.name}`); } else if (asset.behavior === "resource") { - cwraps.mono_wasm_add_satellite_assembly(virtualName, asset.culture!, offset!, bytes.length); + cwraps.mono_wasm_add_satellite_assembly(virtualName, asset.culture || "", offset!, bytes.length); } ++actual_instantiated_assets_count; } @@ -429,10 +433,10 @@ export async function instantiate_wasm_asset( pendingAsset: AssetEntryInternal, wasmModuleImports: WebAssembly.Imports, successCallback: InstantiateWasmSuccessCallback, -) { - mono_assert(pendingAsset && pendingAsset.pendingDownloadInternal, "Can't load dotnet.wasm"); +): Promise { + mono_assert(pendingAsset && pendingAsset.pendingDownloadInternal && pendingAsset.pendingDownloadInternal.response, "Can't load dotnet.wasm"); const response = await pendingAsset.pendingDownloadInternal.response; - const contentType = response.headers ? response.headers.get("Content-Type") : undefined; + const contentType = response.headers && response.headers.get ? response.headers.get("Content-Type") : undefined; let compiledInstance: WebAssembly.Instance; let compiledModule: WebAssembly.Module; if (typeof WebAssembly.instantiateStreaming === "function" && contentType === "application/wasm") {