diff --git a/.gitignore b/.gitignore index 4b4311c..f31d10e 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ iroh-go/target/ +.*.sw[a-z] diff --git a/examples/connect/README.md b/examples/connect/README.md index 0b0ed79..8a782d2 100644 --- a/examples/connect/README.md +++ b/examples/connect/README.md @@ -1,9 +1,9 @@ -# connect +t connect ## Build a binary ``` -go build --ldflags '-linkmode external -extldflags=-static -s -w' ./main.go +go build --ldflags '-linkmode external -extldflags=-static -s -w' ./connect.go ``` If you run into `glibc` incompatibility, you can compile with `zig`/`musl`. diff --git a/examples/pairchat/README.md b/examples/pairchat/README.md new file mode 100644 index 0000000..2fafa9f --- /dev/null +++ b/examples/pairchat/README.md @@ -0,0 +1,37 @@ +# pairchat + +This is a more complicated version of `examples/connect` which allows the listener to also +write back to the sender. The result is a basic pairwise (only 2 peers can connect at once) +chat program. + +## Build a binary + +``` +go build --ldflags '-linkmode external -extldflags=-static -s -w' ./pairchat.go +``` + +If you run into `glibc` incompatibility, you can compile with `zig`/`musl`. + +``` +CC="zig cc -target x86_64-linux-musl -lunwind" \ +CGO_ENABLED=1 \ +CGO_LDFLAGS="-static" \ +GOOS=linux GOARCH=amd64 \ +go build -v -a -ldflags '-extldflags "-static"' ./pairchat.go +``` + +## Terminal 1 (listener) + +``` +./pairchat +``` + +## Terminal 2 (sender) + +``` +./pairchat -endpoint +``` + +You'll see a connection notice if the two endpoints manage to connect to each other. You can +type messages on both sides of the connection, and they will appear on the other end. So +Terminal 1 will see what Terminal 2 types, and Terminal 2 will see what Terminal 1 types. diff --git a/examples/pairchat/pairchat.go b/examples/pairchat/pairchat.go new file mode 100644 index 0000000..a927e34 --- /dev/null +++ b/examples/pairchat/pairchat.go @@ -0,0 +1,144 @@ +package main + +import ( + "bufio" + "flag" + "fmt" + "strings" + "os" + "os/signal" + "syscall" + + iroh "git.coopcloud.tech/decentral1se/iroh-go" +) + +var ( + alpn = []byte("iroh-go-pairchat/0") + frameSize uint32 = 256 + endpointPeer *string +) + +func check(err error) { + if err != nil { + irohErr := err.(*iroh.IrohError) + panic(irohErr.Message()) + } +} + +func awaitInterrupt() { + done := make(chan os.Signal, 1) + signal.Notify(done, syscall.SIGINT, syscall.SIGTERM) + <-done + os.Exit(0) +} + +func parseFlags() { + endpointPeer = flag.String("endpoint", "", "endpoint id of peer") + flag.Parse() +} + +func send(e *iroh.Endpoint, id string) { + remote, err := iroh.EndpointIdFromString(id) + check(err) + + addr := iroh.NewEndpointAddr(remote, nil, nil) + conn, err := e.Connect(addr, alpn) + check(err) + + stream, err := conn.OpenBi() + check(err) + fmt.Println("connected") + recv := stream.Recv() + done := make(chan struct{}) + go handleInput(stream, done) + go handleReading(recv, done) +} + +func listen(e *iroh.Endpoint) { + for { + incoming := *e.AcceptNext() + go handleIncoming(incoming) + } +} + +func handleIncoming (incoming *iroh.Incoming) { + accepting, err := incoming.Accept() + check(err) + + conn, err := accepting.Connect() + check(err) + + stream, err := conn.AcceptBi() + check(err) + + recv := stream.Recv() + done := make(chan struct{}) + go handleInput(stream, done) + go handleReading(recv, done) +} + +func handleInput (stream *iroh.BiStream, done chan struct{}) { + scanner := bufio.NewScanner(os.Stdin) + for scanner.Scan() { + if line := scanner.Text(); line != "" { + err := stream.Send().WriteAll([]byte(line)) + check(err) + } + + select { + // exit if channel done is closed + case <-done: + return + default: + // do nothing + } + } + + if err := scanner.Err(); err != nil { + panic(err) + } +} + +func handleReading(recv *iroh.RecvStream, done chan struct{}) { + for { + frame, err := recv.Read(frameSize) + // handle timeout + if err != nil { + irohErr := err.(*iroh.IrohError) + errMsg := irohErr.Message() + if strings.HasPrefix(errMsg, "ConnectionLost") { + fmt.Println("lost connection") + break + } else { + check(err) + } + } + fmt.Println(string(frame)) + } + close(done) +} + + +func main() { + parseFlags() + + preset := iroh.PresetN0() + opts := iroh.EndpointOptions{ + Preset: &preset, + Alpns: &[][]byte{alpn}, + } + + endpoint, err := iroh.EndpointBind(opts) + check(err) + endpoint.Online() + + if *endpointPeer == "" { + fmt.Println("this endpoint is online and listening! use this id to connect via a peer:") + fmt.Printf("pairchat -endpoint %s\n", endpoint.Id()) + go func() { listen(endpoint) }() + } else { + go func() { send(endpoint, *endpointPeer) }() + } + + awaitInterrupt() +}