Closed ydnar closed 1 year ago
Great question! Go in v1.21 will support vanilla WASI preview 1, which has only limited networking capabilities. Specifically, you can only interact with a pre-opened socket file descriptors via net.FileListener
and net.FileConn
(see https://github.com/golang/go/commit/a17de43ef12250cd9a0ffdd8ff2d05fb18fcf322 and https://gist.github.com/chriso/6c71e968ef1002981a6ff46ceaa39ee3). You cannot create sockets with WASI preview 1.
This library provides a WasmEdge sockets extension, and may support other socket extensions in future (e.g. WASIX via https://github.com/stealthrocket/wasi-go/issues/32). It sets up the WebAssembly runtime on the host side with the necessary system call implementations, but the guest (in this case, Go with GOOS=wasip1
) is missing them.
There are two options:
GOROOT
to the Go version found in https://github.com/stealthrocket/go/pull/2, where we've implemented the extra system calls on the guest side. We are working on getting this upstreamed in some form so that Go has native support for creating sockets. With this approach, no changes are required to your Go application.net
package, one that has alternative implementations of Listen
and Dial
that make the necessary system calls and then setup a net.Listener
and net.Conn
backed by either net.FileListener
or net.FileConn
. We net.Dial
function or net.Listener
implementation into higher-level components in your application, for example when making HTTP requests or connecting to databases.This is still a rapidly evolving area so we'd appreciate any feedback you have :smile:
I should add that these issues affect Go only. You should (in theory) be able to compile applications from other languages (e.g. C/C++/Rust/Zig) and get full networking support.
Ah, makes sense. We’re using gotip
for 1.21 features, including the pre-opened fd listeners. Thanks for the CLs!
One tricky bit is that net.Dialer
isn’t an interface, and a few packages we use require a concrete net.Dialer
, as opposed to something that implements DialContext
.
What do you think the odds are that stealthrocket/go#2 lands in 1.21? Is it possible to extract that into an alternative net
package?
This is new: https://github.com/tetratelabs/wazero/releases/tag/v1.2.0
Looks like built-in support for pre-opened sockets: https://github.com/tetratelabs/wazero/pull/1493
Hello @ydnar!
We open-sourced https://github.com/stealthrocket/net which has a net.Dial
and net.Listen
function working with socket extensions compatible with WasmEdge and https://github.com/stealthrocket/wasi-go
Let us know if this is useful and if you have any feedback!
Nice! I'll check it out.
What are your thoughts on WASIX?
On WASIX, we love that important industry actors are trying to innovate and create experiments to enable more and more applications to run on WebAssembly.
We've started a spike on adding ABI support for WASIX sockets in wasi-go https://github.com/stealthrocket/wasi-go/pull/44
At first, it seems like the WASIX ABI footprint is all-or-nothing, programs compiled to WASIX easily take dependencies on threads and other extensions, which makes it a challenge to adopt incrementally. We're gonna keep investigating and help where we can, interoperability is an amazing feature of WASM so the more we can make systems compatible with one another the better!
On the subject of threads, what's the current state of wasi-go with respect to non-blocking IO and goroutines?
wasi-go has full non-blocking support, a Go program compiled with GOOS=wasip1 will be able to do cooperative scheduling and dispatch I/O to goroutines just like any other target gang Go compiled to.
We still have a CL that needs to be merged in Go to enable non-blocking on stdio https://go-review.googlesource.com/c/go/+/498196, if you're interested in seeing this happen in 1.21 you could chime in there and voice your support for the change!
Will do.
Would love to nudge this along more: https://go-review.googlesource.com/c/go/+/500576
Ah yeah! This first attempt isn't going to make it but we have an alternative, stay tuned!
I got this working with a proxy package that substitutes net/wasip1
primitives for net
, but it’s unable to open sockets to localhost with this error:
dial tcp 0.0.0.0:6379: context deadline exceeded
(Note: this is to 127.0.0.1:6379, without having to resolve localhost
) Is there a command line flag to wasirun
that enables this?
The commands I’m using to build and run:
wasm: dist-dir go.sum tools $(go_source) $(resource_files)
GOOS=wasip1 GOARCH=wasm CGO_ENABLED=0 gotip build -ldflags "$(GO_LDFLAGS)" -o $(wasm_binary)
wasm-run: $(wasm_binary)
wasirun --non-blocking-stdio --sockets auto --listen 127.0.0.1:8080 $(wasm_binary)
Would you happen to be able to share the code snippet for your application so we can reproduce the issue?
We’re using github.com/gomodule/redigo/redis to create a connection pool. It has a config option to override DialContext
, which uses our wnet.DialContext
, which is aliased to wasip1.DialContext
.
pool := &redis.Pool{
MaxIdle: 64,
MaxActive: 128,
IdleTimeout: 5 * time.Minute,
DialContext: func(ctx context.Context) (redis.Conn, error) {
options := []redis.DialOption{
redis.DialContextFunc(wnet.DialContext),
redis.DialConnectTimeout(redisConnectTimeout),
redis.DialReadTimeout(redisReadTimeout),
redis.DialWriteTimeout(redisWriteTimeout),
redis.DialPassword(password),
}
return redis.DialContext(ctx, platform.TCP, addr, options...)
},
TestOnBorrow: func(conn redis.Conn, t time.Time) error {
if time.Since(t) < time.Minute {
return nil
}
_, err := conn.Do("PING")
return err
},
}
wnet_wasip1.go:
//go:build wasip1
package wnet
import "github.com/stealthrocket/net/wasip1"
// Dialer implements Dial and DialContext.
type Dialer = wasip1.Dialer
// DialContext opens a new net.Conn.
var DialContext = wasip1.DialContext
// Listen opens a net.Listener.
var Listen = wasip1.Listen
wnet_unix.go:
//go:build unix
package wnet
import "net"
// Dialer implements Dial and DialContext.
type Dialer = net.Dialer
// DialContext opens a new net.Conn.
var DialContext = (&net.Dialer{}).DialContext
// Listen opens a net.Listener.
var Listen = net.Listen
I disabled SIGTERM
handling, which fixed the server request latency, allowing me to debug the networking code.
if runtime.GOOS != "wasip1" {
signal.Notify(done, os.Interrupt, syscall.SIGTERM)
}
@ydnar try running wasirun with wasirun --trace
to see what system calls are made.
This is working great. Closing, thanks for your help!
Hi there, pardon if this isn’t the right place to ask a question…
Thanks for building this! Yesterday I was able to build and run a reasonable chunk of our stack with WASI after patching a few pieces (filesystem access, a few upstream dependencies without wasm/wasip1 compatibility), and immediately ran into the fake
net
package that’s currently shipping ingotip
.Would love to continue grinding on this so our stack can be—at least partially—deployed into a WASM/Wasi environment.
net
package semantics?Thanks!