Skip to content

[wasi] Build-switchable wasi:http capability for the CoreCLR-WASI test host (corerun) #130742

Closed

Description

@lewing

Context

The CoreCLR-WASI library-test leg (#130129) relinks corerun per-app via WasiApp.CoreCLR.targets. Test assemblies that reference System.Net.Http/System.Net.Quic pull in [WasmImportLinkage] bindings that emit direct component-model imports for the wasi:http world (wasi:http/types@0.2.x, wasi:http/outgoing-handler@0.2.x). corerun's baked CLI/command world does not declare wasi:http (http lives in the proxy/serve world), so wasm-component-ld fails to encode a bundle that references those imports unless the world is extended.

The baked corerun already declares wasi:io/* and wasi:clocks/monotonic-clock (from WasiPollWorld, used by framework timers/polling), and those encode + run fine — only wasi:http/* is new to the app bundle.

Current state (smoke leg)

The smoke leg ships an http-capable corerun: the relink adds -Wl,--component-type,WasiHttpWorld_component_type.wit (mirroring Mono's WasiApp.targets) so corerun's world declares wasi:http, and it runs under wasmtime -S http (already passed by eng/testing/tests.wasi.targets). This is validated end-to-end (System.Runtime.Tests: 9930 cases discovered + executed).

WASI-preview2 constraint: a component that declares a wasi:http import must have the host provide it at instantiation, or wasmtime refuses to instantiate. So an http-capable corerun requires -S http on every run. That is fine for the current leg (it always passes -S http), but it is a capability the non-networking smoke does not actually use.

Proposal

Add a build-time switch on the relink (e.g. WasmHostWasiHttp) selecting between two corerun variants:

  • no-http (default candidate for non-networking legs): do not declare wasi:http; instead add the two modules to _WasmIgnoredPInvokeModules so the generated p/invoke table omits them (mirrors the existing IgnoredPInvokeModules wiring in BrowserWasmApp.CoreCLR.targets). No -S http needed. Safe because those imports bind lazily and are never referenced by non-networking tests.
    <_WasmIgnoredPInvokeModules Include="wasi:http/outgoing-handler@0.2.8" />
    <_WasmIgnoredPInvokeModules Include="wasi:http/types@0.2.8" />
  • http-capable (for System.Net.*.Tests): declare wasi:http via --component-type and run with wasmtime --wasi http (the current behavior).

Caveats to resolve in the switch:

  • The ignore-list strings are ordinal, version-pinned (@0.2.8); a prefix-ignore for wasi:http/ in the generator would be more robust as versions bump.
  • Enumerate the current wasi:http/* module strings from the generated callhelpers-pinvoke.cpp / WASM0066 warnings to be sure all are covered.

Why now vs. later

Browser never hits this (HttpClientHandler uses BrowserHttpHandler/JS-fetch under #if !TARGET_WASI; WasiHttpHandler + its wasi:http interop is TARGET_WASI-only). So there is no browser prior art for the capability split — it is genuinely new wasi work. The smoke leg is unblocked today by the http-capable variant; this issue tracks making the capability build-switchable so non-networking legs can ship a minimal no-http host and networking legs get a properly-declared http world.

Related: #130129 (CoreCLR-WASI library tests), #130634 (R2R call_indirect path), #130739 (reverse-thunk key robustness).

Note

This issue was drafted with GitHub Copilot.

Activity

  1. dotnet-policy-service commented on Jul 14, 2026

    @dotnet-policy-service
    Contributor

    Tagging subscribers to 'arch-wasm': @lewing, @pavelsavara
    See info in area-owners.md if you want to be subscribed.

  2. lewing commented on Jul 15, 2026

    @lewing
    MemberAuthor

    Additional framing (thanks @lewing): the over-declaration is fundamentally an untrimmed-test-bundle artifact, not a general corerun-world problem.

    ManagedToNativeGenerator/PInvokeCollector is a pure metadata scan — type.GetMethods(...) over every method looking for DllImportAttribute/WasmImportLinkageAttribute (src/tasks/WasmAppBuilder/coreclr/PInvokeCollector.cs:77). It is not reachability-based: it emits an import for every [WasmImportLinkage] method present in the scanned assemblies, whether or not it is ever called.

    Consequence: if the bundle were trimmed, the trimmer would remove the unreferenced WasiHttpHandler (a non-networking bundle like System.Runtime.Tests never reaches it), the generator would never see those methods, and no wasi:http/* import would be emitted — so neither --component-type nor the ignore-list would be needed, and the host would not need -S http.

    • Self-contained apps are published trimmed, so they already get this for free: only the wasi:http they actually reach is declared, and --wasi http is required only when http is genuinely used.
    • Library test bundles are deliberately untrimmed (xunit discovery + tests are reflection-heavy; trimming breaks discovery). So trimming is not a lever for the test leg, which is exactly why the test host needs an explicit decision (http-capable via --component-type, or no-http via the ignore-list).

    So the WasmHostWasiHttp build-switch proposed above is really "how the untrimmed test host handles imports the app metadata declares but never calls." A generator-side wasi:http/ prefix-ignore (or a reachability-aware emit) would also address it without a per-bundle world change.

    Note

    This comment was drafted with GitHub Copilot.

  3. lewing commented on Jul 15, 2026

    @lewing
    MemberAuthor

    Path forward (per @lewing): rather than declaring the world (--component-type) or dropping the imports (ignore-list/trimming), statify the imports that are there — provide static stub definitions in the host that satisfy the app-declared wasi:http/* (and any other unused world) imports, trapping if actually invoked. That makes the relinked corerun self-contained: wasm-component-ld resolves the imports locally, so no host-provided world is required at instantiation and no -S http is needed, regardless of whether the (untrimmed) bundle over-declares. The --component-type approach in the current smoke leg is a rough patch; the stub-satisfy path is the intended direction.

    Note

    This comment was drafted with GitHub Copilot.

  4. lewing commented on Jul 15, 2026

    @lewing
    MemberAuthor

    Implementation note for the statify path (from the dispatch analysis in the Wasi r2r prototype session): callhelpers-pinvoke.cpp currently emits the wasi:http/* externs with the import_module/import_name attributes, and dispatch flows through callhelpers_pinvoke_override (ResolvePInvokeTarget → PInvokeOverride::GetMethodImpl → the attributed extern). Two ways to statify:

    • (i) generator emits a local trap-stub definition instead of the import_module extern for a configured stub-list — keeps it in the generated table (one source of truth), and a call resolves to a local abort/trap thunk instead of nullptr → LoadLibrary-fail.
    • (ii) host provides a strong local definition overriding the import.

    (i) is cleaner. Either makes corerun self-contained (no --component-type world declaration, no -S http) and safe (an actually-invoked stubbed import traps rather than mis-dispatches). Post-smoke; the --component-type variant ships now.

    Note

    This comment was drafted with GitHub Copilot.

  5. locked and limited conversation to collaborators on Aug 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    • Status
      No status

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions