Quick Start
This guide walks you through the process of building a simple WebAssembly transport module in Go with TinyGo, with the helper libraries provided by Project WATER.
Prerequisites
- TinyGo v0.31.0 or later
- TinyGo v0.31.0 is used in this guide
- Go compatible with your TinyGo version
- Go 1.22 is used in this guide
- Internet connection for Go to download the required packages
Build a String Reversing WATM
First, create a new directory for your WATM project and navigate to it. Initialize your Go module with go mod init
and required parameters.
package main
import (
"io"
v0 "github.com/refraction-networking/watm/tinygo/v0" // import the v0 transport module spec
v0net "github.com/refraction-networking/watm/tinygo/v0/net" // substitute the standard net package with WebAssembly-specific implementation
)
// type guard: ReverseWrappingTransport must implement [v0.WrappingTransport].
var _ v0.WrappingTransport = (*ReverseWrappingTransport)(nil)
// Inside the init function, we register the ReverseWrappingTransport with
// the helper libraries to enable all three types of transport modules:
// - Dialer
// - Listener
// - Relay
func init() {
v0.BuildDialerWithWrappingTransport(&ReverseWrappingTransport{})
v0.BuildListenerWithWrappingTransport(&ReverseWrappingTransport{})
v0.BuildRelayWithWrappingTransport(&ReverseWrappingTransport{}, v0.RelayWrapRemote)
}
// main function is required for TinyGo to build the WATM.
// It can be empty or contain any code you want to execute
// when the WATM is loaded -- yes, this function will be
// executed right after the WATM is instantiated.
func main() {}
type ReverseWrappingTransport struct {
}
// Wrap implements the v0.WrappingTransport interface
func (rwt *ReverseWrappingTransport) Wrap(conn v0net.Conn) (v0net.Conn, error) {
return &ReverseConn{conn}, conn.SetNonBlock(true) // must set non-block, otherwise will block on read and lose fairness
}
// ReverseConn should implement the v0net.Conn interface
type ReverseConn struct {
v0net.Conn // embedded Conn
}
// Read implements the v0net.Conn interface
func (rc *ReverseConn) Read(b []byte) (n int, err error) {
tmpBuf := make([]byte, len(b))
n, err = rc.Conn.Read(tmpBuf)
if err != nil {
return 0, err
}
// reverse all bytes read successfully so far
for i := 0; i < n; i++ {
b[i] = tmpBuf[n-i-1]
}
return n, err
}
// Write implements the v0net.Conn interface
func (rc *ReverseConn) Write(b []byte) (n int, err error) {
tmpBuf := make([]byte, len(b))
// reverse the bytes to be written
for i := 0; i < len(b); i++ {
tmpBuf[i] = b[len(b)-i-1]
}
return rc.Conn.Write(tmpBuf[:len(b)])
}
Save as main.go
in your project directory.
Build
go mod tidy # to download the required packages
tinygo build -o reverse.wasm -target=wasi main.go
Release Build with Optimization
With more precise control over the build process, you can use the following command to fine-tune the behavior in a release-level standard.
tinygo build -o reverse.wasm -target=wasi -no-debug -scheduler=none -gc=conservative main.go
Now you should end up with a reverse.wasm
file in your project directory. It can be load into a WATER runtime and used as a transport module.