I’m working on a prototype of wasi-http for Go, starting with TinyGo, that adapts the Go stdlib net/http
semantics to wasi-http APIs.
Currently, the wasip2
target in TinyGo targets the wasi:cli/command
world, and various parts of the runtime, os
, and syscall
packages depend on WASI packages imported by wasi:cli/command
. One challenge implementing wasi:http/proxy
is the lack of certain packages that the stdlib depends on, such as wasi:cli/environment
.
The Go net/http
package that defines the standard HTTP interfaces imports the os
package (among others) that when initialized, make calls that are implemented by WASI packages that are not included in the wasi:http/proxy
world.
Fixing this is tricky, and requires potentially invasive surgery to the Go stdlib, or would require Go programmers to not reach for packages they’re familiar with in order to use WASI. Given the tradeoffs, is there a role for wasm-tools
or the Component Model standard to address this?
Some options:
net/http
or any other stdlib package that imports APIs unimplemented in wasi:http/proxy
. I consider this a nonstarter, as Go programmers can and should expect to import stdlib packages with familiar interfaces, and reimplementing them in a third-party package would result in costly duplication of effort, and feature/fix/security gaps.wasi-http
as another GOOS
target, e.g. GOOS=wasip2-http
. This would effectively "freeze" wasi-http support into the stdlib and toolchain, but doesn’t scale—adding a new GOOS has initial and ongoing maintenance costs in Go, and wouldn’t be flexible to support other WASI worlds.wasi:http/proxy
, along the lines of the wasi_snapshot_preview_1
adapter.wasm-tools
to stub missing imports as no-ops when creating a component, or allow a component to specify that it can survive with missing or unimplemented imports.wasi:http/proxy
world (in say, version 0.2.2) to import a "core" set of WASI packages that are, by convention, present in all official WASI worlds, and recommended to be present in vendor-specific worlds. Likely wasi:cli/{environment,stdin,stdout,stderr}
, wasi:clocks
, wasi:random
, wasi:io
, and potentially a stubbed/empty wasi:filesystem
.Approach 5 is more or less what composing with wasi-virt is for
Have you looked into that?
I’ll take a look at wasi-virt, thanks!
Why was wasi:cli/environment
excluded from wasi:http/proxy
?
Asking because the Go standard library uses a number of environment variables like GODEBUG
for additional telemetry or runtime configuration like GC settings. Removing these would be a challenge (if possible at all), and would further distance WASI from other Go targets.
cc @Luke Wagner you might be able to answer wasi:cli/environment
not being in wasi:http/proxy
In general I think we'd prefer that toolchains not target specific worlds but rather just import what a particular program needs. That's a bit hairy for wasi:cli/environment
in particular since many language runtimes parse environ unconditionally at init time.
Though looking at Go I think that only applies to unix
, so maybe syscall.Getenv
could still work with that approach
I guess that still leaves a large number of libraries including the Go stdlib that are always checking env anyway.
The arguments and CWD parts of wasi:cli/environment
aren't really meaningful in an HTTP proxy. The env vars could potentially make sense for exposing configuration, but wasi:config/runtime
is the better match there, so the idea is to add that once wasi:config/runtime
has sufficiently advanced phase, and then let env vars be virtualized in terms of wasi:config/runtime
.
Also, at some point in the future, I think the ergonomics of wasi:cli/command
would be much-improved to have the currently-imported arguments and CWD be made parameters of the main/run function export so that command
components can be imported (e.g., from CPython or Node) and their main
called like a function passing arguments as arguments without shenanigans.
For common stuff like time/random/config, though, one idea we've talked about is in the short term is defining a wasi:io/core
world that contains the intersection between all the other WASI worlds that toolchains can depend on without knowing the actual target world. Longer-term, I think each of these is a strong candidate for building into the C-M directly, with wasi:io
ultimately disappearing so that there are no ubiquitous WASI imports, so wasi:io/core
would be considered transitional.
In terms of future features I would expect defaulted value import to be a good fit for something like GODEBUG
Something like wasi:io/core
seems reasonable. For Go (and other languages) with libraries or packages that expose knobs via environment variables, having environment variables accessible via wasi:http/proxy
and other worlds makes sense.
I was surprised that CWD and args were part of environment
too!
fwiw, I have a working implementation of http serving here: https://github.com/dev-wasm/dev-wasm-go/blob/main/webserver/wasi-http/main.go it uses net/http
and it doesn't seem to encounter the issues you are running into.
run: main_0_2_0.component.wasm ; WASMTIME_BACKTRACE_DETAILS=1 wasmtime serve -Scommon -Dlogging=y main_0_2_0.component.wasm
the -Scommon
(or -Scli
) is the magic
Last updated: Dec 23 2024 at 12:05 UTC