diff --git a/src/mono/wasm/runtime/http.ts b/src/mono/wasm/runtime/http.ts index 9034494a4615c..ff9dbbbcfec1b 100644 --- a/src/mono/wasm/runtime/http.ts +++ b/src/mono/wasm/runtime/http.ts @@ -2,15 +2,26 @@ // The .NET Foundation licenses this file to you under the MIT license. import { wrap_as_cancelable_promise } from "./cancelable-promise"; -import { Module, loaderHelpers } from "./globals"; +import { ENVIRONMENT_IS_NODE, Module, loaderHelpers } from "./globals"; import { MemoryViewType, Span } from "./marshal"; import type { VoidPtr } from "./types/emscripten"; + +function verifyEnvironment() { + if (typeof globalThis.fetch !== "function" || typeof globalThis.AbortController !== "function") { + const message = ENVIRONMENT_IS_NODE + ? "Please install `node-fetch` and `node-abort-controller` npm packages to enable HTTP client support." + : "This browser doesn't support fetch API. Please use a modern browser."; + throw new Error(message); + } +} + export function http_wasm_supports_streaming_response(): boolean { return typeof Response !== "undefined" && "body" in Response.prototype && typeof ReadableStream === "function"; } export function http_wasm_create_abort_controler(): AbortController { + verifyEnvironment(); return new AbortController(); } @@ -38,6 +49,7 @@ export function http_wasm_fetch_bytes(url: string, header_names: string[], heade } export function http_wasm_fetch(url: string, header_names: string[], header_values: string[], option_names: string[], option_values: any[], abort_controller: AbortController, body: string | Uint8Array | null): Promise { + verifyEnvironment(); mono_assert(url && typeof url === "string", "expected url string"); mono_assert(header_names && header_values && Array.isArray(header_names) && Array.isArray(header_values) && header_names.length === header_values.length, "expected headerNames and headerValues arrays"); mono_assert(option_names && option_values && Array.isArray(option_names) && Array.isArray(option_values) && option_names.length === option_values.length, "expected headerNames and headerValues arrays"); diff --git a/src/mono/wasm/runtime/loader/index.ts b/src/mono/wasm/runtime/loader/index.ts index 9c2f0d00f181c..6818186c36a02 100644 --- a/src/mono/wasm/runtime/loader/index.ts +++ b/src/mono/wasm/runtime/loader/index.ts @@ -3,6 +3,7 @@ import type { DotnetHostBuilder } from "../types"; import { mono_exit } from "./exit"; +import { verifyEnvironment } from "./polyfills"; import { HostBuilder, createEmscripten } from "./run"; // export external API @@ -10,5 +11,7 @@ const dotnet: DotnetHostBuilder = new HostBuilder(); const exit = mono_exit; const legacyEntrypoint = createEmscripten; +verifyEnvironment(); + export { dotnet, exit }; export default legacyEntrypoint; diff --git a/src/mono/wasm/runtime/loader/polyfills.ts b/src/mono/wasm/runtime/loader/polyfills.ts index eed7a45fff718..cf4b5abdb5d10 100644 --- a/src/mono/wasm/runtime/loader/polyfills.ts +++ b/src/mono/wasm/runtime/loader/polyfills.ts @@ -1,3 +1,7 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +import MonoWasmThreads from "consts:monoWasmThreads"; import type { DotnetModuleInternal } from "../types/internal"; import { INTERNAL, ENVIRONMENT_IS_NODE, ENVIRONMENT_IS_SHELL, loaderHelpers, ENVIRONMENT_IS_WEB } from "./globals"; @@ -14,6 +18,19 @@ const URLPolyfill = class URL { } }; +export function verifyEnvironment() { + mono_assert(ENVIRONMENT_IS_SHELL || typeof globalThis.URL === "function", "This browser/engine doesn't support URL API. Please use a modern version."); + mono_assert(typeof globalThis.BigInt64Array === "function", "This browser/engine doesn't support BigInt64Array API. Please use a modern version."); + if (MonoWasmThreads) { + mono_assert(!ENVIRONMENT_IS_SHELL && !ENVIRONMENT_IS_NODE, "This build of dotnet is multi-threaded, it doesn't support shell environments like V8 or NodeJS."); + mono_assert(globalThis.SharedArrayBuffer !== undefined, "SharedArrayBuffer is not enabled on this page. Please use a modern browser and set Cross-Origin-Opener-Policy and Cross-Origin-Embedder-Policy http headers. See also https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer#security_requirements"); + mono_assert(typeof globalThis.EventTarget === "function", "This browser/engine doesn't support EventTarget API. Please use a modern version."); + } + + // TODO detect other (WASM) features that are required for the runtime + // See https://github.com/dotnet/runtime/issues/84574 +} + export async function detect_features_and_polyfill(module: DotnetModuleInternal): Promise { const scriptUrlQuery =/* webpackIgnore: true */import.meta.url; diff --git a/src/mono/wasm/runtime/loader/worker.ts b/src/mono/wasm/runtime/loader/worker.ts index 2f2024140d8f4..a22c65fcb7881 100644 --- a/src/mono/wasm/runtime/loader/worker.ts +++ b/src/mono/wasm/runtime/loader/worker.ts @@ -1,3 +1,6 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + import { MonoConfig } from "../types"; import { MonoConfigInternal } from "../types/internal"; import { deep_merge_config, normalizeConfig } from "./config"; diff --git a/src/mono/wasm/runtime/web-socket.ts b/src/mono/wasm/runtime/web-socket.ts index 00ca6c8e70ebb..72ee49fd4d550 100644 --- a/src/mono/wasm/runtime/web-socket.ts +++ b/src/mono/wasm/runtime/web-socket.ts @@ -5,7 +5,7 @@ import MonoWasmThreads from "consts:monoWasmThreads"; import { prevent_timer_throttling } from "./scheduling"; import { Queue } from "./queue"; -import { createPromiseController } from "./globals"; +import { ENVIRONMENT_IS_NODE, ENVIRONMENT_IS_SHELL, createPromiseController } from "./globals"; import { setI32, localHeapViewU8 } from "./memory"; import { VoidPtr } from "./types/emscripten"; import { PromiseController } from "./types/internal"; @@ -26,7 +26,20 @@ let mono_wasm_web_socket_close_warning = false; const ws_send_buffer_blocking_threshold = 65536; const emptyBuffer = new Uint8Array(); +function verifyEnvironment() { + if (ENVIRONMENT_IS_SHELL) { + throw new Error("WebSockets are not supported in shell JS engine."); + } + if (typeof globalThis.WebSocket !== "function") { + const message = ENVIRONMENT_IS_NODE + ? "Please install `ws` npm package to enable networking support." + : "This browser doesn't support WebSocket API. Please use a modern browser."; + throw new Error(message); + } +} + export function ws_wasm_create(uri: string, sub_protocols: string[] | null, receive_status_ptr: VoidPtr, onClosed: (code: number, reason: string) => void): WebSocketExtension { + verifyEnvironment(); mono_assert(uri && typeof uri === "string", () => `ERR12: Invalid uri ${typeof uri}`); const ws = new globalThis.WebSocket(uri, sub_protocols || undefined) as WebSocketExtension;