Skip to content

Commit

Permalink
update: adding descriptions and other details (#11)
Browse files Browse the repository at this point in the history
* update: add mobile platform support statement

Signed-off-by: Gaukas Wang <[email protected]>

* update: move usage directly into index page

Signed-off-by: Gaukas Wang <[email protected]>

* new: copy-paste ready example for runtime library

Signed-off-by: Gaukas Wang <[email protected]>

* update: minor details

Signed-off-by: Gaukas Wang <[email protected]>

* update: description for go watm library code

Signed-off-by: Gaukas Wang <[email protected]>

---------

Signed-off-by: Gaukas Wang <[email protected]>
  • Loading branch information
gaukas authored Mar 26, 2024
1 parent 32f1773 commit f5f5ba5
Show file tree
Hide file tree
Showing 8 changed files with 340 additions and 87 deletions.
6 changes: 5 additions & 1 deletion runtime/go/cross-platform.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,8 @@ Currently, `water` supports the following platforms:
| windows/arm64 |||
| others |||

Due to the absence of a machine in `windows/arm64`, we could not run tests on this platform. The compilation compatibility is tested by cross-compiling from `windows/amd64` to `windows/arm64`.
Due to the absence of a machine in `windows/arm64`, we could not run tests on this platform. The compilation compatibility is tested by cross-compiling from `windows/amd64` to `windows/arm64`.

## Mobile Platform Support

We are working on [WaterMob](https://github.com/gaukas/watermob) to bring `water` to mobile platforms. Currently, it is still in the early stage of development.
62 changes: 60 additions & 2 deletions runtime/go/go.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,65 @@ permalink: /runtime/go.html
# Runtime Library in Go: `water`
[![Go Doc](https://pkg.go.dev/badge/github.com/refraction-networking/water.svg)](https://pkg.go.dev/github.com/refraction-networking/water)

`water` (a.k.a. `water-go` in contrast to `water-rs`) is a WATER Runtime Library build in Go. It uses the WebAssembly runtime with WASI support from [wazero](https://github.com/tetratelabs/wazero), and implements an abstracted network programming interface that roughly ressembles the standard `net` package in Go.
`water` is a WATER Runtime Library build in Go. It uses the WebAssembly runtime with WASI support from [wazero](https://github.com/tetratelabs/wazero), and implements an abstracted network programming interface that roughly ressembles the standard `net` package in Go.

## Cross-platform Support
Project WATER is designed to be cross-platform and cross-architecture. See [Cross-platform Compatibility (Go)](./go/cross-platform.html) for more details.
Project WATER is designed to be cross-platform and cross-architecture. See [Cross-platform Compatibility (Go)](./go/cross-platform.html) for more details.

## Usage
This section introduces the basic usage of `water` in a Go project. For a fully working example, see [Quick Start with Runtime Library in Go](./go/quick-start.html).

### Importing `water`
To use `water` in a Go project, simply import it as a module.

By default, `water` does not recognize any transport modules as there can be many different
specifications of transport modules. To use a specific transport module, import its implementation
as well.

```go
import (
"github.com/refraction-networking/water"
_ "github.com/refraction-networking/water/transport/v0" // import the v0 transport module spec
)
```

### Dialer Mode
Dialer acts like a client. It actively creates connections to a remote server (and usually is the one who sends the first message).

```go
// Load the WebAssembly binary into wasm as []byte.
// The rest of the code on this page assumes that wasm is already loaded.
wasm, _ := os.ReadFile("./examples/v0/plain/plain.wasm")

config := &water.Config{
TransportModuleBin: wasm,
}

dialer, _ := water.NewDialerWithContext(context.Background(), config)
conn, _ := dialer.DialContext(context.Background(),"tcp", remoteAddr)
```

### Listener Mode
Listener acts like a server. It listens on a network address and wait for
incoming connections to accept.

```go
lis, _ := config.ListenContext(context.Background(), "tcp", localAddr)
defer lis.Close()
log.Printf("Listening on %s", lis.Addr().String())

for {
conn, err := lis.Accept()
handleConn(conn)
}
```

### Relay Mode
A relay combines the functionalities of both a dialer and a listener. It works
like a forward proxy, accepting connections from a client and forwarding them to a
remote server by dialing a connection to the remote server.

```go
relay, _ := water.NewRelayWithContext(context.Background(), config)
relay.ListenAndRelayTo("tcp", localAddr, "tcp", remoteAddr) // blocking
```
252 changes: 252 additions & 0 deletions runtime/go/quick-start.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,252 @@
---
layout: default
title: Quick Start with Runtime Library in Go
grand_parent: Runtime Library
parent: Runtime Library in Go
nav_order: 2
---

# Quick Start
This guide walks you through a few examples of using different modes of `water` Runtime Library.

## Dialer Mode
The code below demonstrates how to use `water` to create a dialer that connects to a remote server and send random bytes every 5 seconds.

```go
package main

import (
"context"
"crypto/rand"
"flag"
"fmt"
"log"
"net"
"os"
"time"

"github.com/refraction-networking/water" // import the water package
_ "github.com/refraction-networking/water/transport/v0" // explicitly enable WATM v0
)

var (
remoteAddr = flag.String("raddr", "", "remote address to dial")
wasmPath = flag.String("wasm", "", "path to wasm file")
remoteConn net.Conn
)

func main() {
flag.Parse()

wasm, err := os.ReadFile(*wasmPath)
if err != nil {
panic(fmt.Sprintf("failed to read wasm file: %v", err))
}

// start using W.A.T.E.R. API below this line, have fun!
config := &water.Config{
TransportModuleBin: wasm,
NetworkDialerFunc: net.Dial, // optional field, defaults to net.Dial
}
// configuring the standard out of the WebAssembly instance to inherit
// from the parent process
config.ModuleConfig().InheritStdout()
config.ModuleConfig().InheritStderr()

ctx := context.Background()
// // optional: enable wazero logging
// ctx = context.WithValue(ctx, experimental.FunctionListenerFactoryKey{},
// logging.NewHostLoggingListenerFactory(os.Stderr, logging.LogScopeFilesystem|logging.LogScopePoll|logging.LogScopeSock))

dialer, err := water.NewDialerWithContext(ctx, config)
if err != nil {
panic(fmt.Sprintf("failed to create dialer: %v", err))
}

conn, err := dialer.DialContext(ctx, "tcp", *remoteAddr)
if err != nil {
panic(fmt.Sprintf("failed to dial: %v", err))
}
defer conn.Close()
// conn is a net.Conn that you are familiar with.
// So effectively, W.A.T.E.R. API ends here and everything below
// this line is just how you treat a net.Conn.

remoteConn = conn

worker()
}

func worker() {
defer remoteConn.Close()

log.Printf("Connected to %s", remoteConn.RemoteAddr())
chanMsgRecv := make(chan []byte, 4) // up to 4 messages in the buffer
// start a goroutine to read data from the connection
go func() {
defer close(chanMsgRecv)
buf := make([]byte, 1024) // 1 KiB
for {
n, err := remoteConn.Read(buf)
if err != nil {
log.Printf("read remoteConn: error %v, tearing down connection...", err)
remoteConn.Close()
return
}
chanMsgRecv <- buf[:n]
}
}()

// start a ticker for sending message every 5 seconds
ticker := time.NewTicker(5 * time.Second)
defer ticker.Stop()

var sendBuf []byte = make([]byte, 4) // 4 bytes per message
for {
select {
case msg := <-chanMsgRecv:
if msg == nil {
return // connection closed
}
log.Printf("peer: %x\n", msg)
case <-ticker.C:
n, err := rand.Read(sendBuf)
if err != nil {
log.Printf("rand.Read: error %v, tearing down connection...", err)
return
}
// print the bytes sending as hex string
log.Printf("sending: %x\n", sendBuf[:n])

_, err = remoteConn.Write(sendBuf[:n])
if err != nil {
log.Printf("write: error %v, tearing down connection...", err)
return
}
}
}
}
```

## Listener Mode
The code below demonstrates how to use `water` to create a listener that listens on a local address and accepts incoming connections then sends random bytes every 5 seconds.

```go
package main

import (
"context"
"crypto/rand"
"flag"
"fmt"
"log"
"net"
"os"
"time"

"github.com/refraction-networking/water"
_ "github.com/refraction-networking/water/transport/v0"
)

var (
localAddr = flag.String("laddr", "", "local address to listen on")
wasmPath = flag.String("wasm", "", "path to wasm file")
)

func main() {
flag.Parse()

wasm, err := os.ReadFile(*wasmPath)
if err != nil {
panic(fmt.Sprintf("failed to read wasm file: %v", err))
}

// start using W.A.T.E.R. API below this line, have fun!
config := &water.Config{
TransportModuleBin: wasm,
}
// configuring the standard out of the WebAssembly instance to inherit
// from the parent process
config.ModuleConfig().InheritStdout()
config.ModuleConfig().InheritStderr()

ctx := context.Background()
// // optional: enable wazero logging
// ctx = context.WithValue(ctx, experimental.FunctionListenerFactoryKey{},
// logging.NewHostLoggingListenerFactory(os.Stderr, logging.LogScopeFilesystem|logging.LogScopePoll|logging.LogScopeSock))

lis, err := config.ListenContext(ctx, "tcp", *localAddr)
if err != nil {
panic(fmt.Sprintf("failed to listen: %v", err))
}
defer lis.Close()
log.Printf("Listening on %s:%s", lis.Addr().Network(), lis.Addr().String())
// lis is a net.Listener that you are familiar with.
// So effectively, W.A.T.E.R. API ends here and everything below
// this line is just how you treat a net.Listener.

clientCntr := 0
for {
conn, err := lis.Accept()
if err != nil {
panic(fmt.Sprintf("failed to accept: %v", err))
}

// start a goroutine to handle the connection
go handleConn(fmt.Sprintf("client#%d", clientCntr), conn)
clientCntr++
}
}

func handleConn(peer string, conn net.Conn) {
defer conn.Close()

log.Printf("handling connection from/to %s(%s)", peer, conn.RemoteAddr())
chanMsgRecv := make(chan []byte, 4) // up to 4 messages in the buffer
// start a goroutine to read data from the connection
go func() {
defer close(chanMsgRecv)
buf := make([]byte, 1024) // 1 KiB
for {
// conn.SetReadDeadline(time.Now().Add(5 * time.Second))
n, err := conn.Read(buf)
if err != nil {
log.Printf("read %s: error %v, tearing down connection...", peer, err)
conn.Close()
return
}
chanMsgRecv <- buf[:n]
}
}()

// start a ticker for sending message every 5 seconds
ticker := time.NewTicker(5 * time.Second)
defer ticker.Stop()

var sendBuf []byte = make([]byte, 4) // 4 bytes per message
for {
select {
case msg := <-chanMsgRecv:
if msg == nil {
log.Printf("read %s: connection closed, tearing down connection...", peer)
return // connection closed
}
log.Printf("%s: %x\n", peer, msg)
case <-ticker.C:
n, err := rand.Read(sendBuf)
if err != nil {
log.Printf("rand.Read: error %v, tearing down connection...", err)
return
}
// print the bytes sending as hex string
log.Printf("sending: %x\n", sendBuf[:n])

_, err = conn.Write(sendBuf[:n])
if err != nil {
log.Printf("write %s: error %v, tearing down connection...", peer, err)
return
}
}
}
}
```
9 changes: 6 additions & 3 deletions runtime/go/troubleshoot.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,19 @@ layout: default
title: Troubleshooting (Go)
grand_parent: Runtime Library
parent: Runtime Library in Go
nav_order: 2
nav_order: 3
---
# Troubleshooting
This page contains information on how to troubleshoot common issues when using `water` in a Go project.

## Enable `wazero` debug logs

`wazero` is the WebAssembly runtime with WASI support that `water` uses. To enable debug logs from `wazero`, pass the values below via the `context.Context`
`wazero` is the WebAssembly runtime with WASI support that `water` uses. To enable debug logs from `wazero`, pass the values below via the `context.Context`.

```go
// example of enabling FileSystem, Poll, and Sock logging scopes of wazero
ctx = context.WithValue(ctx, experimental.FunctionListenerFactoryKey{},
logging.NewHostLoggingListenerFactory(os.Stderr, logging.LogScopeFilesystem|logging.LogScopePoll|logging.LogScopeSock))
```
```

## To be continued...
Loading

0 comments on commit f5f5ba5

Please sign in to comment.