feat: better error handling #17
+20
@@ -0,0 +1,20 @@
|
||||
---
|
||||
kind: pipeline
|
||||
name: git.coopcloud.tech/decentral1se/iroh-go
|
||||
steps:
|
||||
- name: go build
|
||||
image: golang:1.26
|
||||
commands:
|
||||
- go build -v ./...
|
||||
|
||||
- name: examples
|
||||
image: golang:1.26
|
||||
commands:
|
||||
- for ex in $(find examples -mindepth 1 -maxdepth 1 -type d -not -path "*-desktop"); do make -C $ex; done
|
||||
depends_on:
|
||||
- go build
|
||||
|
||||
trigger:
|
||||
action:
|
||||
exclude:
|
||||
- synchronized
|
||||
@@ -3,6 +3,7 @@ examples/connect-desktop/build
|
||||
examples/connect-desktop/frontend/dist
|
||||
examples/connect-desktop/frontend/wailsjs
|
||||
examples/connect/connect
|
||||
examples/errors/errors
|
||||
examples/pairchat/pairchat
|
||||
examples/timeserve/timeserve
|
||||
iroh-go/target/
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# `iroh-go`
|
||||
|
||||
[](https://build.coopcloud.tech/decentral1se/iroh-go)
|
||||
|
||||
> **WARNING** Highly Experimental ™️
|
||||
|
||||
Go FFI bindings for [`iroh`](https://iroh.computer) via
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
package iroh_ffi
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// `As` casts an error to IrohError and back, extracting relevant error message
|
||||
// information. It is a single argument variant of the typical errors.As API
|
||||
// because we always expect an IrohError back from the iroh FFI bindings. The
|
||||
// bindings do not return an IrohError for reasons and will not for the
|
||||
// forseeable future due to API stability guarantees.
|
||||
func As(err error) error {
|
||||
if err, ok := errors.AsType[IrohError](err); ok {
|
||||
return fmt.Errorf("%s: %s", err.Message(), err.DebugMessage())
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// `Is` supports matching against the stable iroh error taxonomy using the
|
||||
// error sentinel construction below. The caller must pass in a valid error
|
||||
// sentinel specified by this library, e.g. iroh.BindError.
|
||||
func (e *IrohError) Is(target error) bool {
|
||||
if err, ok := errors.AsType[sentinel](target); ok {
|
||||
return uint(e.Kind()) == err.kind
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type sentinel struct {
|
||||
kind uint
|
||||
message string
|
||||
}
|
||||
|
||||
func (s sentinel) Error() string {
|
||||
return s.message
|
||||
}
|
||||
|
||||
func (s sentinel) String() string {
|
||||
return s.message
|
||||
}
|
||||
|
||||
var InvalidInputError error = sentinel{
|
||||
kind: uint(IrohErrorKindInvalidInput),
|
||||
message: "iroh: invalid input supplied by the caller",
|
||||
}
|
||||
|
||||
var BindError error = sentinel{
|
||||
kind: uint(IrohErrorKindBind),
|
||||
message: "iroh: failure while binding an endpoint",
|
||||
}
|
||||
|
||||
var ConnectError error = sentinel{
|
||||
kind: uint(IrohErrorKindConnect),
|
||||
message: "iroh: failure while initiating or completing an outgoing connection",
|
||||
}
|
||||
|
||||
var ConnectionError error = sentinel{
|
||||
kind: uint(IrohErrorKindConnection),
|
||||
message: "iroh: an established connection failed or closed unexpectedly",
|
||||
}
|
||||
|
||||
var AlpnError error = sentinel{
|
||||
kind: uint(IrohErrorKindAlpn),
|
||||
message: "iroh: ALPN negotiation or lookup failed",
|
||||
}
|
||||
|
||||
var KeyParsingError error = sentinel{
|
||||
kind: uint(IrohErrorKindKeyParsing),
|
||||
message: "iroh: endpoint id / public key parsing failed",
|
||||
}
|
||||
|
||||
var TicketParsingError error = sentinel{
|
||||
kind: uint(IrohErrorKindKeyParsing),
|
||||
message: "iroh: ticket parsing failed",
|
||||
}
|
||||
|
||||
var RelayError error = sentinel{
|
||||
kind: uint(IrohErrorKindRelay),
|
||||
message: "iroh: relay configuration or relay operation failed",
|
||||
}
|
||||
|
||||
var StreamError error = sentinel{
|
||||
kind: uint(IrohErrorKindStream),
|
||||
message: "iroh: stream read/write/control operation failed",
|
||||
}
|
||||
|
||||
var DatagramError error = sentinel{
|
||||
kind: uint(IrohErrorKindDatagram),
|
||||
message: "iroh: datagram send/receive operation failed",
|
||||
}
|
||||
|
||||
var FCallbackError error = sentinel{
|
||||
kind: uint(IrohErrorKindCallback),
|
||||
message: "iroh: foreign callback failed",
|
||||
}
|
||||
|
||||
var ClosedError error = sentinel{
|
||||
kind: uint(IrohErrorKindClosed),
|
||||
message: "iroh: operation was attempted on a closed stream/connection/resource",
|
||||
}
|
||||
|
||||
var TimeoutError error = sentinel{
|
||||
kind: uint(IrohErrorKindTimeout),
|
||||
message: "iroh: operation timed out",
|
||||
}
|
||||
|
||||
var InternalError error = sentinel{
|
||||
kind: uint(IrohErrorKindInternal),
|
||||
message: "iroh: unclassified internal error",
|
||||
}
|
||||
@@ -1,6 +1,10 @@
|
||||
# connect-desktop
|
||||
|
||||
A version of [`../connect`](../connect) as a desktop application. On running
|
||||
> **WARNING** Due to Wails requiring some additional system dependencies, we
|
||||
> are not testing this example regularly on CI/CD. Therefore, this example
|
||||
> might not build when you try it. Contributions are very welcome!
|
||||
|
||||
A version of [`../connect`](../connect) as a desktop application. When running
|
||||
the desktop application, an Iroh endpoint id will be displayed. This id can be
|
||||
plugged into a terminal running the [`../connect`](../connect) example as a
|
||||
sender. What the sender types in the terminal will appear in the
|
||||
@@ -8,28 +12,23 @@ connect-desktop application window.
|
||||
|
||||
## Dependencies
|
||||
|
||||
* Wails v2.10.2 (old version, haven't tried on a newer yet - hopefully works there too!)
|
||||
* Vite v3.0.7
|
||||
* Go v1.18
|
||||
* npm v10.2+
|
||||
* node v20.11+
|
||||
This example has been tested on the following versions.
|
||||
|
||||
**Getting setup**
|
||||
* Wails v2.10.2
|
||||
* Node v20.11+
|
||||
|
||||
* Install node and golang
|
||||
Please consult the [Wails
|
||||
docs](https://wails.io/docs/gettingstarted/installation) for everything you
|
||||
need to get up and running.
|
||||
|
||||
Install Wails:
|
||||
### Wails
|
||||
|
||||
```
|
||||
go install github.com/wailsapp/wails/v2/cmd/wails@v2.10.2 // for tested version
|
||||
// OR: go install github.com/wailsapp/wails/v2/cmd/wails@latest // for latest
|
||||
|
||||
// then install frontend dependency
|
||||
cd frontend
|
||||
npm i
|
||||
go install github.com/wailsapp/wails/v2/cmd/wails@v2.10.2
|
||||
cd frontend && npm i
|
||||
```
|
||||
|
||||
## Running in development mode
|
||||
## Development mode
|
||||
|
||||
```
|
||||
wails dev
|
||||
|
||||
@@ -2,11 +2,11 @@ package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
iroh "git.coopcloud.tech/decentral1se/iroh-go"
|
||||
"github.com/wailsapp/wails/v2/pkg/runtime"
|
||||
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -15,16 +15,9 @@ var (
|
||||
endpointPeer *string
|
||||
)
|
||||
|
||||
func check(err error) {
|
||||
if err != nil {
|
||||
irohErr := err.(*iroh.IrohError)
|
||||
panic(irohErr.Message())
|
||||
}
|
||||
}
|
||||
|
||||
// App struct
|
||||
type App struct {
|
||||
ctx context.Context
|
||||
ctx context.Context
|
||||
endpoint *iroh.Endpoint
|
||||
}
|
||||
|
||||
@@ -45,7 +38,9 @@ func (a *App) startup(ctx context.Context) {
|
||||
}
|
||||
|
||||
endpoint, err := iroh.EndpointBind(opts)
|
||||
check(err)
|
||||
if err := iroh.As(err); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
endpoint.Online()
|
||||
a.endpoint = endpoint
|
||||
|
||||
@@ -63,28 +58,32 @@ func listen(ctx context.Context, e *iroh.Endpoint) {
|
||||
}
|
||||
}
|
||||
|
||||
func handleIncoming (ctx context.Context, incoming *iroh.Incoming) {
|
||||
func handleIncoming(ctx context.Context, incoming *iroh.Incoming) {
|
||||
accepting, err := incoming.Accept()
|
||||
check(err)
|
||||
if err := iroh.As(err); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
conn, err := accepting.Connect()
|
||||
check(err)
|
||||
if err := iroh.As(err); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
stream, err := conn.AcceptBi()
|
||||
check(err)
|
||||
if err := iroh.As(err); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
recv := stream.Recv()
|
||||
for {
|
||||
frame, err := recv.Read(frameSize)
|
||||
// handle timeout
|
||||
if err != nil {
|
||||
irohErr := err.(*iroh.IrohError)
|
||||
errMsg := irohErr.Message()
|
||||
if strings.HasPrefix(errMsg, "ConnectionLost") {
|
||||
if errors.Is(err, iroh.TimeoutError) {
|
||||
fmt.Println("lost connection")
|
||||
break
|
||||
} else {
|
||||
check(err)
|
||||
}
|
||||
if err := iroh.As(err); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
runtime.EventsEmit(ctx, "data", map[string]interface{}{
|
||||
|
||||
@@ -16,7 +16,7 @@ func main() {
|
||||
app := NewApp()
|
||||
|
||||
// Create application with options
|
||||
err := wails.Run(&options.App{
|
||||
if err := wails.Run(&options.App{
|
||||
Title: "go-iroh-desktop-example",
|
||||
Width: 1024,
|
||||
Height: 768,
|
||||
@@ -28,9 +28,7 @@ func main() {
|
||||
Bind: []interface{}{
|
||||
app,
|
||||
},
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
println("Error:", err.Error())
|
||||
}); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
+31
-23
@@ -2,11 +2,11 @@ package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
iroh "git.coopcloud.tech/decentral1se/iroh-go"
|
||||
@@ -18,13 +18,6 @@ var (
|
||||
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)
|
||||
@@ -39,22 +32,30 @@ func parseFlags() {
|
||||
|
||||
func send(e *iroh.Endpoint, id string) {
|
||||
remote, err := iroh.EndpointIdFromString(id)
|
||||
check(err)
|
||||
if err := iroh.As(err); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
addr := iroh.NewEndpointAddr(remote, nil, nil)
|
||||
conn, err := e.Connect(addr, alpn)
|
||||
check(err)
|
||||
if err := iroh.As(err); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
stream, err := conn.OpenBi()
|
||||
check(err)
|
||||
if err := iroh.As(err); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fmt.Println("connected")
|
||||
fmt.Println("connection established")
|
||||
|
||||
scanner := bufio.NewScanner(os.Stdin)
|
||||
for scanner.Scan() {
|
||||
if line := scanner.Text(); line != "" {
|
||||
err = stream.Send().WriteAll([]byte(line))
|
||||
check(err)
|
||||
if err := iroh.As(err); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,26 +73,30 @@ func listen(e *iroh.Endpoint) {
|
||||
|
||||
func handleIncoming(incoming *iroh.Incoming) {
|
||||
accepting, err := incoming.Accept()
|
||||
check(err)
|
||||
if err := iroh.As(err); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
conn, err := accepting.Connect()
|
||||
check(err)
|
||||
if err := iroh.As(err); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
stream, err := conn.AcceptBi()
|
||||
check(err)
|
||||
if err := iroh.As(err); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
recv := stream.Recv()
|
||||
for {
|
||||
frame, err := recv.Read(frameSize)
|
||||
// handle timeout
|
||||
if err != nil {
|
||||
irohErr := err.(*iroh.IrohError)
|
||||
errMsg := irohErr.Message()
|
||||
if strings.HasPrefix(errMsg, "ConnectionLost") {
|
||||
if errors.Is(err, iroh.TimeoutError) {
|
||||
fmt.Println("lost connection")
|
||||
break
|
||||
} else {
|
||||
check(err)
|
||||
}
|
||||
if err := iroh.As(err); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
fmt.Println(string(frame))
|
||||
@@ -108,7 +113,10 @@ func main() {
|
||||
}
|
||||
|
||||
endpoint, err := iroh.EndpointBind(opts)
|
||||
check(err)
|
||||
if err := iroh.As(err); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
endpoint.Online()
|
||||
|
||||
if *endpointPeer == "" {
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
default:
|
||||
@go build -v ./...
|
||||
@@ -0,0 +1,9 @@
|
||||
# errors
|
||||
|
||||
This example shows a brief example of how to deal with error handling in a
|
||||
convenient way. Unfortunately, the error handling coming down the pipes in the
|
||||
FFI bindings is not ergonomic by default ends up creating extremely verbose
|
||||
error handling. We've created an error handling wrapper API which we've tried
|
||||
to make as intuitive as possible. See
|
||||
[`#263`](https://github.com/n0-computer/iroh-ffi/issues/263) for the juicy
|
||||
details.
|
||||
@@ -0,0 +1,33 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
iroh "git.coopcloud.tech/decentral1se/iroh-go"
|
||||
)
|
||||
|
||||
func main() {
|
||||
preset := iroh.PresetN0DisableRelay()
|
||||
opts := iroh.EndpointOptions{
|
||||
Preset: &preset,
|
||||
Alpns: &[][]byte{[]byte("iroh-go-errors/0")},
|
||||
}
|
||||
|
||||
endpoint, err := iroh.EndpointBind(opts)
|
||||
|
||||
// NOTE(d1): The main route: convert errors to IrohError with `iroh.As`. This
|
||||
// brings the `IrohError.Message`/`DebugMessage` back in a regular error
|
||||
// value. This ensures we maintain `err != nil` compatibility
|
||||
if err := iroh.As(err); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// NOTE(d1): If you need more fine grained error catching: no `iroh.As`
|
||||
// machinery is required. Sentinel checks with the usual `errors.Is` API are
|
||||
// supported. It works as expected with `err` values
|
||||
if err != nil && errors.Is(err, iroh.BindError) {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
endpoint.Online()
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
module git.coopcloud.tech/decentral1se/iroh-go/examples/errors
|
||||
|
||||
go 1.26.1
|
||||
|
||||
require git.coopcloud.tech/decentral1se/iroh-go v0.0.0-20260717110820-68ad06fe2a25
|
||||
|
||||
replace git.coopcloud.tech/decentral1se/iroh-go => ../../
|
||||
@@ -0,0 +1,2 @@
|
||||
default:
|
||||
@go build -v ./...
|
||||
@@ -0,0 +1,2 @@
|
||||
default:
|
||||
@go build -v ./...
|
||||
@@ -2,11 +2,11 @@ package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
iroh "git.coopcloud.tech/decentral1se/iroh-go"
|
||||
@@ -18,13 +18,6 @@ var (
|
||||
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)
|
||||
@@ -39,15 +32,21 @@ func parseFlags() {
|
||||
|
||||
func send(e *iroh.Endpoint, id string) {
|
||||
remote, err := iroh.EndpointIdFromString(id)
|
||||
check(err)
|
||||
if err := iroh.As(err); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
addr := iroh.NewEndpointAddr(remote, nil, nil)
|
||||
conn, err := e.Connect(addr, alpn)
|
||||
check(err)
|
||||
if err := iroh.As(err); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
stream, err := conn.OpenBi()
|
||||
check(err)
|
||||
fmt.Println("connected")
|
||||
if err := iroh.As(err); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println("connection established")
|
||||
recv := stream.Recv()
|
||||
done := make(chan struct{})
|
||||
go handleInput(stream, done)
|
||||
@@ -63,13 +62,19 @@ func listen(e *iroh.Endpoint) {
|
||||
|
||||
func handleIncoming(incoming *iroh.Incoming) {
|
||||
accepting, err := incoming.Accept()
|
||||
check(err)
|
||||
if err := iroh.As(err); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
conn, err := accepting.Connect()
|
||||
check(err)
|
||||
if err := iroh.As(err); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
stream, err := conn.AcceptBi()
|
||||
check(err)
|
||||
if err := iroh.As(err); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
recv := stream.Recv()
|
||||
done := make(chan struct{})
|
||||
@@ -82,7 +87,9 @@ func handleInput(stream *iroh.BiStream, done chan struct{}) {
|
||||
for scanner.Scan() {
|
||||
if line := scanner.Text(); line != "" {
|
||||
err := stream.Send().WriteAll([]byte(line))
|
||||
check(err)
|
||||
if err := iroh.As(err); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
select {
|
||||
@@ -102,16 +109,12 @@ func handleInput(stream *iroh.BiStream, done chan struct{}) {
|
||||
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)
|
||||
}
|
||||
if errors.Is(err, iroh.TimeoutError) {
|
||||
fmt.Println("lost connection")
|
||||
break
|
||||
}
|
||||
if err := iroh.As(err); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(string(frame))
|
||||
}
|
||||
@@ -128,7 +131,9 @@ func main() {
|
||||
}
|
||||
|
||||
endpoint, err := iroh.EndpointBind(opts)
|
||||
check(err)
|
||||
if err := iroh.As(err); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
endpoint.Online()
|
||||
|
||||
if *endpointPeer == "" {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
# timeserve
|
||||
|
||||
This example demonstrates that an endpoint can support multiple live connections at once. The
|
||||
endpoint aka listener writes the time, once every 2 seconds, to every connected peer.
|
||||
|
||||
Try connecting to a running listener with 3 (or more!) receiver peers at once.
|
||||
This example demonstrates that an endpoint can support multiple live
|
||||
connections at once. The endpoint aka listener writes the time, once every 2
|
||||
seconds, to every connected peer. Try connecting to a running listener with 3
|
||||
(or more!) receiver peers at once.
|
||||
|
||||
## Build a binary
|
||||
|
||||
@@ -39,5 +39,6 @@ go build -v -a -ldflags '-extldflags "-static"' ./timeserve.go
|
||||
./timeserve -endpoint <endpoint-id>
|
||||
```
|
||||
|
||||
You'll see a connection notice if a receiver peer manages to connect to the listener.
|
||||
Receivers (Terminal 2 and Terminal 3) will display the time sent by the listener (Terminal 1).
|
||||
You'll see a connection notice if a receiver peer manages to connect to the
|
||||
listener. Receivers (Terminal 2 and Terminal 3) will display the time sent by
|
||||
the listener (Terminal 1).
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
default:
|
||||
@go build -v ./...
|
||||
@@ -1,11 +1,11 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
@@ -18,13 +18,6 @@ var (
|
||||
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)
|
||||
@@ -39,51 +32,47 @@ func parseFlags() {
|
||||
|
||||
func receiver(e *iroh.Endpoint, id string) {
|
||||
remote, err := iroh.EndpointIdFromString(id)
|
||||
check(err)
|
||||
if err := iroh.As(err); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
addr := iroh.NewEndpointAddr(remote, nil, nil)
|
||||
conn, err := e.Connect(addr, alpn)
|
||||
check(err)
|
||||
if err := iroh.As(err); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// from docs:
|
||||
// "The peer that calls open_bi must write to its SendStream before the peer Connection is able to accept the stream
|
||||
// using accept_bi()"
|
||||
// from docs: "The peer that calls open_bi must write to its SendStream
|
||||
// before the peer Connection is able to accept the stream using accept_bi()"
|
||||
// https://docs.rs/iroh/latest/iroh/endpoint/struct.Connection.html#method.accept_bi
|
||||
stream, err := conn.OpenBi()
|
||||
check(err)
|
||||
// NOTE: for the other peer to realize the bidi stream is open, you have to send some data! the data sent cannot be
|
||||
// the empty string ""; empty string == no data
|
||||
if err := iroh.As(err); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// NOTE: for the other peer to realize the bidi stream is open, you have to
|
||||
// send some data! the data sent cannot be the empty string ""; empty string
|
||||
// == no data
|
||||
err = stream.Send().WriteAll([]byte{1})
|
||||
if err := iroh.As(err); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
check(err)
|
||||
handleReading(stream.Recv())
|
||||
}
|
||||
|
||||
func handleReading(recv *iroh.RecvStream) {
|
||||
for {
|
||||
frame, err := recv.Read(frameSize)
|
||||
if checkForTimeout(err) {
|
||||
if errors.Is(err, iroh.TimeoutError) {
|
||||
fmt.Println("lost connection")
|
||||
break
|
||||
}
|
||||
fmt.Println(string(frame))
|
||||
check(err)
|
||||
}
|
||||
}
|
||||
|
||||
func checkForTimeout(err error) bool {
|
||||
// handle timeout
|
||||
if err != nil {
|
||||
irohErr := err.(*iroh.IrohError)
|
||||
errMsg := irohErr.Message()
|
||||
if strings.HasPrefix(errMsg, "ConnectionLost") {
|
||||
fmt.Println("lost connection")
|
||||
return true
|
||||
} else {
|
||||
// panics and exits bc unhandled err
|
||||
check(err)
|
||||
if err := iroh.As(err); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(string(frame))
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func listen(e *iroh.Endpoint) {
|
||||
@@ -96,20 +85,32 @@ func listen(e *iroh.Endpoint) {
|
||||
|
||||
func handleIncoming(incoming *iroh.Incoming) {
|
||||
accepting, err := incoming.Accept()
|
||||
check(err)
|
||||
if err := iroh.As(err); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
conn, err := accepting.Connect()
|
||||
check(err)
|
||||
if err := iroh.As(err); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
stream, err := conn.AcceptBi()
|
||||
check(err)
|
||||
if err := iroh.As(err); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
for {
|
||||
err := stream.Send().WriteAll([]byte(time.Now().Format(time.DateTime)))
|
||||
if checkForTimeout(err) {
|
||||
if errors.Is(err, iroh.TimeoutError) {
|
||||
fmt.Println("lost connection")
|
||||
break
|
||||
}
|
||||
// prints every two seconds (not for any technical reason, 1/sec just feels hectic!)
|
||||
if err := iroh.As(err); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// prints every two seconds (not for any technical reason, 1/sec just feels
|
||||
// hectic!)
|
||||
<-time.After(2 * time.Second)
|
||||
}
|
||||
}
|
||||
@@ -124,7 +125,9 @@ func main() {
|
||||
}
|
||||
|
||||
endpoint, err := iroh.EndpointBind(opts)
|
||||
check(err)
|
||||
if err := iroh.As(err); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
endpoint.Online()
|
||||
|
||||
if *endpointPeer == "" {
|
||||
|
||||
Reference in New Issue
Block a user