iroh.Pipe
#32
@@ -4,6 +4,12 @@ Please read the [README](./README.md) before diving in. Please [raise an issue
|
||||
before starting on large
|
||||
changes](https://git.coopcloud.tech/decentral1se/iroh-go/issues/new).
|
||||
|
||||
## LLM Policy
|
||||
|
||||
We adopt the [Strict No LLM / No AI
|
||||
Policy](https://ziglang.org/code-of-conduct/#strict-no-llm-no-ai-policy) of
|
||||
[Zig](https://ziglang.org).
|
||||
|
||||
## Conventions
|
||||
|
||||
* API wrappers (such as `endpoint.go`) should return upstream errors even
|
||||
|
||||
@@ -56,34 +56,6 @@ See [`examples`](./examples). See also the [upstream
|
||||
examples](https://github.com/n0-computer/iroh/tree/main/iroh/examples) (in
|
||||
Rust) for inspiration.
|
||||
|
||||
## Run your own `iroh-relay`
|
||||
|
||||
Each [`iroh` release](https://github.com/n0-computer/iroh/releases) features a
|
||||
relay binary. You can also make use of the upstream provided docker image:
|
||||
`n0computer/iroh-relay`.
|
||||
|
||||
### Local hacking
|
||||
|
||||
First, run `iroh-relay` in development mode with noisy logging.
|
||||
|
||||
```
|
||||
RUST_LOG=info,iroh_relay=debug ./iroh-relay --dev
|
||||
```
|
||||
|
||||
You can then use `http://localhost:3340` as the default location of your own
|
||||
relay in the construction of your endpoint as follows. Once you create an
|
||||
endpoint, you should see your relay respond locally with corresponding logs.
|
||||
|
||||
```go
|
||||
preset := iroh.PresetN0DisableRelay()
|
||||
relay, _ := iroh.RelayModeCustomFromUrls([]string{"http://localhost:3340"})
|
||||
opts := iroh.EndpointOptions{
|
||||
Preset: &preset,
|
||||
RelayMode: &relay,
|
||||
// ...moar config...
|
||||
}
|
||||
```
|
||||
|
||||
## Generate bindings
|
||||
|
||||
See [`iroh-go`](./iroh-go) for the Rust toolchain configuration. Run
|
||||
@@ -123,10 +95,4 @@ Apache 2.0 & MIT, following the upstream licensing choices in
|
||||
|
||||
## Contributing
|
||||
|
||||
[`CONTRIBUTING.md`](./CONTRIBUTING.md).
|
||||
|
||||
## LLM Policy
|
||||
|
||||
We adopt the [Strict No LLM / No AI
|
||||
Policy](https://ziglang.org/code-of-conduct/#strict-no-llm-no-ai-policy) of
|
||||
[Zig](https://ziglang.org).
|
||||
> [`CONTRIBUTING.md`](./CONTRIBUTING.md).
|
||||
|
||||
@@ -22,106 +22,6 @@ var (
|
||||
secretFlag *string
|
||||
)
|
||||
|
||||
type SimplePipe struct {
|
||||
recv *iroh.RecvStream
|
||||
send *iroh.SendStream
|
||||
totalBytesRead int
|
||||
}
|
||||
|
||||
func (sp *SimplePipe) setRecv(recv *iroh.RecvStream) {
|
||||
sp.recv = recv
|
||||
}
|
||||
|
||||
func (sp *SimplePipe) setSend(send *iroh.SendStream) {
|
||||
sp.send = send
|
||||
}
|
||||
|
||||
// (NOTE): cblgh: inlining go's docs for io.Reader and io.Writer in case of any
|
||||
// other future bugs with my implementations of the interface contract
|
||||
//
|
||||
// Read reads up to len(buf) bytes into buf. It returns the number of bytes read (0 <=
|
||||
// n <= len(buf)) and any error encountered.
|
||||
//
|
||||
// Even if Read returns n < len(buf), it may use all of buf as scratch space during the
|
||||
// call. If some data is available but not len(buf) bytes, Read conventionally returns
|
||||
// what is available instead of waiting for more.
|
||||
//
|
||||
// When Read encounters an error or end-of-file condition after successfully
|
||||
// reading n > 0 bytes, it returns the number of bytes read. It may return the
|
||||
// (non-nil) error from the same call or return the error (and n == 0) from a
|
||||
// subsequent call . An instance of this general case is that a Reader returning a
|
||||
// non-zero number of bytes at the end of the input stream may return either
|
||||
// err == EOF or err == nil. The next Read should return 0, EOF.
|
||||
//
|
||||
// Callers should always process the n > 0 bytes returned before considering the
|
||||
// error err. Doing so correctly handles I/O errors that happen after reading some
|
||||
// bytes and also both of the allowed EOF behaviors.
|
||||
//
|
||||
// If len(buf) == 0, Read should always return n == 0. It may return a non-nil error
|
||||
// if some error condition is known, such as EOF.
|
||||
//
|
||||
// Implementations of Read are discouraged from returning a zero byte count with a
|
||||
// nil error, except when len(buf) == 0. Callers should treat a return of 0 and nil a
|
||||
// s indicating that nothing happened; in particular it does not indicate EOF.
|
||||
//
|
||||
// Implementations must not retain buf.
|
||||
func (sp *SimplePipe) Read(buf []byte) (int, error) {
|
||||
// fmt.Println("READ size of buf", len(buf))
|
||||
if len(buf) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
retbuf, err := sp.recv.Read(uint32(len(buf)))
|
||||
|
||||
// fmt.Println("retbuf", retbuf, "len(retbuf)", len(retbuf), "err", err)
|
||||
|
||||
// signal end of file
|
||||
if sp.totalBytesRead > 0 && len(retbuf) == 0 {
|
||||
return 0, io.EOF
|
||||
}
|
||||
|
||||
sp.totalBytesRead += len(retbuf)
|
||||
|
||||
copyBytes := func() {
|
||||
for i, d := range retbuf {
|
||||
buf[i] = d
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
copyBytes()
|
||||
return len(retbuf), err
|
||||
}
|
||||
copyBytes()
|
||||
return len(retbuf), err
|
||||
}
|
||||
|
||||
// Write writes len(buf) bytes from buf to the underlying data stream. It
|
||||
// returns the number of bytes written from buf (0 <= n <= len(buf)) and any
|
||||
// error encountered that caused the write to stop early. Write must return a
|
||||
// non-nil error if it returns n < len(buf). Write must not modify the slice
|
||||
// data, even temporarily.
|
||||
//
|
||||
// Implementations must not retain buf.
|
||||
func (sp *SimplePipe) Write(buf []byte) (int, error) {
|
||||
// fmt.Println("WRITE size of buf", len(buf))
|
||||
n64, err := sp.send.Write(buf)
|
||||
n := int(n64)
|
||||
|
||||
if n < len(buf) {
|
||||
return n, errors.New("SimplePipe.Write exited before writing all bytes")
|
||||
}
|
||||
|
||||
return n, err
|
||||
}
|
||||
|
||||
// use as:
|
||||
// var sp SimplePipe
|
||||
// sp.setRecv(stream.Recv()) or sp.setSend(stream.Send())
|
||||
//
|
||||
// io.Copy(*sp, os.Stdin)
|
||||
// io.Copy(os.Stdout, *sp)
|
||||
|
||||
var done = make(chan os.Signal, 1)
|
||||
|
||||
func awaitInterrupt() {
|
||||
@@ -143,7 +43,7 @@ func log(s string) {
|
||||
}
|
||||
}
|
||||
|
||||
func send(e *iroh.Endpoint, id string, sp *SimplePipe) {
|
||||
func send(e *iroh.Endpoint, id string) {
|
||||
remote, err := iroh.EndpointIdFromString(id)
|
||||
if err := iroh.As(err); err != nil {
|
||||
panic(err)
|
||||
@@ -162,17 +62,20 @@ func send(e *iroh.Endpoint, id string, sp *SimplePipe) {
|
||||
|
||||
log("connection established")
|
||||
|
||||
sp.setSend(stream.Send())
|
||||
p := iroh.NewPipe(stream)
|
||||
|
||||
// write initial data to open the connection
|
||||
sp.Write([]byte{1})
|
||||
if err := p.Ready(); err != nil {
|
||||
panic(iroh.As(err))
|
||||
}
|
||||
|
||||
// copy stdin directly to opened connection's send stream
|
||||
n, err := io.Copy(sp, os.Stdin)
|
||||
n, err := io.Copy(p, os.Stdin)
|
||||
log(fmt.Sprintf("wrote %d", n))
|
||||
if err = iroh.As(err); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// mark send stream as done
|
||||
stream.Send().Finish()
|
||||
exit(conn)
|
||||
@@ -186,12 +89,12 @@ func exit(conn *iroh.Connection) {
|
||||
done <- os.Interrupt
|
||||
}
|
||||
|
||||
func listen(e *iroh.Endpoint, sp *SimplePipe) {
|
||||
func listen(e *iroh.Endpoint) {
|
||||
incoming := *e.AcceptNext()
|
||||
handleIncoming(incoming, sp)
|
||||
handleIncoming(incoming)
|
||||
}
|
||||
|
||||
func handleIncoming(incoming *iroh.Incoming, sp *SimplePipe) {
|
||||
func handleIncoming(incoming *iroh.Incoming) {
|
||||
accepting, err := incoming.Accept()
|
||||
if err := iroh.As(err); err != nil {
|
||||
panic(err)
|
||||
@@ -207,8 +110,9 @@ func handleIncoming(incoming *iroh.Incoming, sp *SimplePipe) {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
sp.setRecv(stream.Recv())
|
||||
n, err := io.Copy(os.Stdout, sp)
|
||||
p := iroh.NewPipe(stream)
|
||||
|
||||
n, err := io.Copy(os.Stdout, p)
|
||||
log(fmt.Sprintf("read n %d", n))
|
||||
if errors.Is(err, iroh.TimeoutError) ||
|
||||
errors.Is(err, iroh.StreamError) {
|
||||
@@ -220,7 +124,6 @@ func handleIncoming(incoming *iroh.Incoming, sp *SimplePipe) {
|
||||
}
|
||||
|
||||
func main() {
|
||||
var sp SimplePipe
|
||||
parseFlags()
|
||||
|
||||
preset := iroh.PresetN0()
|
||||
@@ -246,9 +149,9 @@ func main() {
|
||||
if *endpointPeer == "" {
|
||||
log("this endpoint is online and listening! use this id to connect via a peer:")
|
||||
os.Stderr.Write([]byte(fmt.Sprintf("simple-pipe -endpoint %s\n", endpoint.Id())))
|
||||
go func() { listen(endpoint, &sp) }()
|
||||
go func() { listen(endpoint) }()
|
||||
} else {
|
||||
go func() { send(endpoint, *endpointPeer, &sp) }()
|
||||
go func() { send(endpoint, *endpointPeer) }()
|
||||
}
|
||||
|
||||
awaitInterrupt()
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
package iroh_ffi
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
)
|
||||
|
||||
type Pipe struct {
|
||||
stream *BiStream
|
||||
recv *RecvStream
|
||||
send *SendStream
|
||||
totalBytesRead int
|
||||
}
|
||||
|
||||
func NewPipe(stream *BiStream) *Pipe {
|
||||
return &Pipe{
|
||||
stream: stream,
|
||||
recv: stream.Recv(),
|
||||
send: stream.Send(),
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Pipe) Ready() error {
|
||||
if err := p.stream.Ready(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Pipe) Read(buf []byte) (int, error) {
|
||||
if len(buf) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
retbuf, err := p.recv.Read(uint32(len(buf)))
|
||||
|
||||
if p.totalBytesRead > 0 && len(retbuf) == 0 {
|
||||
return 0, io.EOF
|
||||
}
|
||||
|
||||
p.totalBytesRead += len(retbuf)
|
||||
|
||||
copyBytes := func() {
|
||||
for i, d := range retbuf {
|
||||
buf[i] = d
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
copyBytes()
|
||||
return len(retbuf), err
|
||||
}
|
||||
|
||||
copyBytes()
|
||||
return len(retbuf), err
|
||||
}
|
||||
|
||||
func (p *Pipe) Write(buf []byte) (int, error) {
|
||||
n64, err := p.send.Write(buf)
|
||||
n := int(n64)
|
||||
|
||||
if n < len(buf) {
|
||||
return n, errors.New("Pipe.Write: exited before writing all bytes")
|
||||
}
|
||||
|
||||
return n, err
|
||||
}
|
||||
Reference in New Issue
Block a user