Skip to content

Commit f5f5ba5

Browse files
authored
update: adding descriptions and other details (#11)
* 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]>
1 parent 32f1773 commit f5f5ba5

File tree

8 files changed

+340
-87
lines changed

8 files changed

+340
-87
lines changed

runtime/go/cross-platform.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,8 @@ Currently, `water` supports the following platforms:
2727
| windows/arm64 |||
2828
| others |||
2929

30-
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`.
30+
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`.
31+
32+
## Mobile Platform Support
33+
34+
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.

runtime/go/go.md

Lines changed: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,65 @@ permalink: /runtime/go.html
1010
# Runtime Library in Go: `water`
1111
[![Go Doc](https://pkg.go.dev/badge/github.com/refraction-networking/water.svg)](https://pkg.go.dev/github.com/refraction-networking/water)
1212

13-
`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.
13+
`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.
1414

1515
## Cross-platform Support
16-
Project WATER is designed to be cross-platform and cross-architecture. See [Cross-platform Compatibility (Go)](./go/cross-platform.html) for more details.
16+
Project WATER is designed to be cross-platform and cross-architecture. See [Cross-platform Compatibility (Go)](./go/cross-platform.html) for more details.
17+
18+
## Usage
19+
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).
20+
21+
### Importing `water`
22+
To use `water` in a Go project, simply import it as a module.
23+
24+
By default, `water` does not recognize any transport modules as there can be many different
25+
specifications of transport modules. To use a specific transport module, import its implementation
26+
as well.
27+
28+
```go
29+
import (
30+
"github.com/refraction-networking/water"
31+
_ "github.com/refraction-networking/water/transport/v0" // import the v0 transport module spec
32+
)
33+
```
34+
35+
### Dialer Mode
36+
Dialer acts like a client. It actively creates connections to a remote server (and usually is the one who sends the first message).
37+
38+
```go
39+
// Load the WebAssembly binary into wasm as []byte.
40+
// The rest of the code on this page assumes that wasm is already loaded.
41+
wasm, _ := os.ReadFile("./examples/v0/plain/plain.wasm")
42+
43+
config := &water.Config{
44+
TransportModuleBin: wasm,
45+
}
46+
47+
dialer, _ := water.NewDialerWithContext(context.Background(), config)
48+
conn, _ := dialer.DialContext(context.Background(),"tcp", remoteAddr)
49+
```
50+
51+
### Listener Mode
52+
Listener acts like a server. It listens on a network address and wait for
53+
incoming connections to accept.
54+
55+
```go
56+
lis, _ := config.ListenContext(context.Background(), "tcp", localAddr)
57+
defer lis.Close()
58+
log.Printf("Listening on %s", lis.Addr().String())
59+
60+
for {
61+
conn, err := lis.Accept()
62+
handleConn(conn)
63+
}
64+
```
65+
66+
### Relay Mode
67+
A relay combines the functionalities of both a dialer and a listener. It works
68+
like a forward proxy, accepting connections from a client and forwarding them to a
69+
remote server by dialing a connection to the remote server.
70+
71+
```go
72+
relay, _ := water.NewRelayWithContext(context.Background(), config)
73+
relay.ListenAndRelayTo("tcp", localAddr, "tcp", remoteAddr) // blocking
74+
```

runtime/go/quick-start.md

Lines changed: 252 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
1+
---
2+
layout: default
3+
title: Quick Start with Runtime Library in Go
4+
grand_parent: Runtime Library
5+
parent: Runtime Library in Go
6+
nav_order: 2
7+
---
8+
9+
# Quick Start
10+
This guide walks you through a few examples of using different modes of `water` Runtime Library.
11+
12+
## Dialer Mode
13+
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.
14+
15+
```go
16+
package main
17+
18+
import (
19+
"context"
20+
"crypto/rand"
21+
"flag"
22+
"fmt"
23+
"log"
24+
"net"
25+
"os"
26+
"time"
27+
28+
"github.com/refraction-networking/water" // import the water package
29+
_ "github.com/refraction-networking/water/transport/v0" // explicitly enable WATM v0
30+
)
31+
32+
var (
33+
remoteAddr = flag.String("raddr", "", "remote address to dial")
34+
wasmPath = flag.String("wasm", "", "path to wasm file")
35+
remoteConn net.Conn
36+
)
37+
38+
func main() {
39+
flag.Parse()
40+
41+
wasm, err := os.ReadFile(*wasmPath)
42+
if err != nil {
43+
panic(fmt.Sprintf("failed to read wasm file: %v", err))
44+
}
45+
46+
// start using W.A.T.E.R. API below this line, have fun!
47+
config := &water.Config{
48+
TransportModuleBin: wasm,
49+
NetworkDialerFunc: net.Dial, // optional field, defaults to net.Dial
50+
}
51+
// configuring the standard out of the WebAssembly instance to inherit
52+
// from the parent process
53+
config.ModuleConfig().InheritStdout()
54+
config.ModuleConfig().InheritStderr()
55+
56+
ctx := context.Background()
57+
// // optional: enable wazero logging
58+
// ctx = context.WithValue(ctx, experimental.FunctionListenerFactoryKey{},
59+
// logging.NewHostLoggingListenerFactory(os.Stderr, logging.LogScopeFilesystem|logging.LogScopePoll|logging.LogScopeSock))
60+
61+
dialer, err := water.NewDialerWithContext(ctx, config)
62+
if err != nil {
63+
panic(fmt.Sprintf("failed to create dialer: %v", err))
64+
}
65+
66+
conn, err := dialer.DialContext(ctx, "tcp", *remoteAddr)
67+
if err != nil {
68+
panic(fmt.Sprintf("failed to dial: %v", err))
69+
}
70+
defer conn.Close()
71+
// conn is a net.Conn that you are familiar with.
72+
// So effectively, W.A.T.E.R. API ends here and everything below
73+
// this line is just how you treat a net.Conn.
74+
75+
remoteConn = conn
76+
77+
worker()
78+
}
79+
80+
func worker() {
81+
defer remoteConn.Close()
82+
83+
log.Printf("Connected to %s", remoteConn.RemoteAddr())
84+
chanMsgRecv := make(chan []byte, 4) // up to 4 messages in the buffer
85+
// start a goroutine to read data from the connection
86+
go func() {
87+
defer close(chanMsgRecv)
88+
buf := make([]byte, 1024) // 1 KiB
89+
for {
90+
n, err := remoteConn.Read(buf)
91+
if err != nil {
92+
log.Printf("read remoteConn: error %v, tearing down connection...", err)
93+
remoteConn.Close()
94+
return
95+
}
96+
chanMsgRecv <- buf[:n]
97+
}
98+
}()
99+
100+
// start a ticker for sending message every 5 seconds
101+
ticker := time.NewTicker(5 * time.Second)
102+
defer ticker.Stop()
103+
104+
var sendBuf []byte = make([]byte, 4) // 4 bytes per message
105+
for {
106+
select {
107+
case msg := <-chanMsgRecv:
108+
if msg == nil {
109+
return // connection closed
110+
}
111+
log.Printf("peer: %x\n", msg)
112+
case <-ticker.C:
113+
n, err := rand.Read(sendBuf)
114+
if err != nil {
115+
log.Printf("rand.Read: error %v, tearing down connection...", err)
116+
return
117+
}
118+
// print the bytes sending as hex string
119+
log.Printf("sending: %x\n", sendBuf[:n])
120+
121+
_, err = remoteConn.Write(sendBuf[:n])
122+
if err != nil {
123+
log.Printf("write: error %v, tearing down connection...", err)
124+
return
125+
}
126+
}
127+
}
128+
}
129+
```
130+
131+
## Listener Mode
132+
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.
133+
134+
```go
135+
package main
136+
137+
import (
138+
"context"
139+
"crypto/rand"
140+
"flag"
141+
"fmt"
142+
"log"
143+
"net"
144+
"os"
145+
"time"
146+
147+
"github.com/refraction-networking/water"
148+
_ "github.com/refraction-networking/water/transport/v0"
149+
)
150+
151+
var (
152+
localAddr = flag.String("laddr", "", "local address to listen on")
153+
wasmPath = flag.String("wasm", "", "path to wasm file")
154+
)
155+
156+
func main() {
157+
flag.Parse()
158+
159+
wasm, err := os.ReadFile(*wasmPath)
160+
if err != nil {
161+
panic(fmt.Sprintf("failed to read wasm file: %v", err))
162+
}
163+
164+
// start using W.A.T.E.R. API below this line, have fun!
165+
config := &water.Config{
166+
TransportModuleBin: wasm,
167+
}
168+
// configuring the standard out of the WebAssembly instance to inherit
169+
// from the parent process
170+
config.ModuleConfig().InheritStdout()
171+
config.ModuleConfig().InheritStderr()
172+
173+
ctx := context.Background()
174+
// // optional: enable wazero logging
175+
// ctx = context.WithValue(ctx, experimental.FunctionListenerFactoryKey{},
176+
// logging.NewHostLoggingListenerFactory(os.Stderr, logging.LogScopeFilesystem|logging.LogScopePoll|logging.LogScopeSock))
177+
178+
lis, err := config.ListenContext(ctx, "tcp", *localAddr)
179+
if err != nil {
180+
panic(fmt.Sprintf("failed to listen: %v", err))
181+
}
182+
defer lis.Close()
183+
log.Printf("Listening on %s:%s", lis.Addr().Network(), lis.Addr().String())
184+
// lis is a net.Listener that you are familiar with.
185+
// So effectively, W.A.T.E.R. API ends here and everything below
186+
// this line is just how you treat a net.Listener.
187+
188+
clientCntr := 0
189+
for {
190+
conn, err := lis.Accept()
191+
if err != nil {
192+
panic(fmt.Sprintf("failed to accept: %v", err))
193+
}
194+
195+
// start a goroutine to handle the connection
196+
go handleConn(fmt.Sprintf("client#%d", clientCntr), conn)
197+
clientCntr++
198+
}
199+
}
200+
201+
func handleConn(peer string, conn net.Conn) {
202+
defer conn.Close()
203+
204+
log.Printf("handling connection from/to %s(%s)", peer, conn.RemoteAddr())
205+
chanMsgRecv := make(chan []byte, 4) // up to 4 messages in the buffer
206+
// start a goroutine to read data from the connection
207+
go func() {
208+
defer close(chanMsgRecv)
209+
buf := make([]byte, 1024) // 1 KiB
210+
for {
211+
// conn.SetReadDeadline(time.Now().Add(5 * time.Second))
212+
n, err := conn.Read(buf)
213+
if err != nil {
214+
log.Printf("read %s: error %v, tearing down connection...", peer, err)
215+
conn.Close()
216+
return
217+
}
218+
chanMsgRecv <- buf[:n]
219+
}
220+
}()
221+
222+
// start a ticker for sending message every 5 seconds
223+
ticker := time.NewTicker(5 * time.Second)
224+
defer ticker.Stop()
225+
226+
var sendBuf []byte = make([]byte, 4) // 4 bytes per message
227+
for {
228+
select {
229+
case msg := <-chanMsgRecv:
230+
if msg == nil {
231+
log.Printf("read %s: connection closed, tearing down connection...", peer)
232+
return // connection closed
233+
}
234+
log.Printf("%s: %x\n", peer, msg)
235+
case <-ticker.C:
236+
n, err := rand.Read(sendBuf)
237+
if err != nil {
238+
log.Printf("rand.Read: error %v, tearing down connection...", err)
239+
return
240+
}
241+
// print the bytes sending as hex string
242+
log.Printf("sending: %x\n", sendBuf[:n])
243+
244+
_, err = conn.Write(sendBuf[:n])
245+
if err != nil {
246+
log.Printf("write %s: error %v, tearing down connection...", peer, err)
247+
return
248+
}
249+
}
250+
}
251+
}
252+
```

runtime/go/troubleshoot.md

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,19 @@ layout: default
33
title: Troubleshooting (Go)
44
grand_parent: Runtime Library
55
parent: Runtime Library in Go
6-
nav_order: 2
6+
nav_order: 3
77
---
88
# Troubleshooting
9+
This page contains information on how to troubleshoot common issues when using `water` in a Go project.
910

1011
## Enable `wazero` debug logs
1112

12-
`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`
13+
`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`.
1314

1415
```go
1516
// example of enabling FileSystem, Poll, and Sock logging scopes of wazero
1617
ctx = context.WithValue(ctx, experimental.FunctionListenerFactoryKey{},
1718
logging.NewHostLoggingListenerFactory(os.Stderr, logging.LogScopeFilesystem|logging.LogScopePoll|logging.LogScopeSock))
18-
```
19+
```
20+
21+
## To be continued...

0 commit comments

Comments
 (0)