go-ssb-room/muxrpc/handlers/gossip/ping.go

132 lines
2.9 KiB
Go

// SPDX-FileCopyrightText: 2021 The NGI Pointer Secure-Scuttlebutt Team of 2020/2021
//
// SPDX-License-Identifier: MIT
package gossip
import (
"context"
"encoding/json"
"io"
"time"
"github.com/ssbc/go-muxrpc/v2"
"go.mindeco.de/encodedTime"
)
// Ping implements the server side of gossip.ping.
// it's idea is mentioned here https://github.com/ssbc/ssb-gossip/#ping-duplex
// and implemented by https://github.com/dominictarr/pull-ping/
func Ping(ctx context.Context, req *muxrpc.Request, peerSrc *muxrpc.ByteSource, peerSnk *muxrpc.ByteSink) error {
type arg struct {
// The only argument is the delay between two pings.
// the Javascript code calls this "timeout", tho.
Delay int `json:"timeout"`
}
var args []arg
err := json.Unmarshal(req.RawArgs, &args)
if err != nil {
return err
}
// var timeout = time.Minute * 5
// if len(args) == 1 {
// timeout = time.Minute * time.Duration(args[0].Timeout/(60*1000))
// }
// return sillyPingPong(ctx, peerSrc, peerSnk)
return actualPingPong(ctx, peerSrc, peerSnk)
}
// actually just read and write whenever...
func sillyPingPong(ctx context.Context, peerSrc *muxrpc.ByteSource, peerSnk *muxrpc.ByteSink) error {
var (
sendErr = make(chan error)
receiveErr = make(chan error)
)
go func() {
peerSnk.SetEncoding(muxrpc.TypeJSON)
enc := json.NewEncoder(peerSnk)
tick := time.NewTicker(5 * time.Second)
defer tick.Stop()
defer close(sendErr)
for {
select {
case <-ctx.Done():
return
case <-tick.C:
}
var pong = encodedTime.Millisecs(time.Now())
if err := enc.Encode(pong); err != nil {
sendErr <- err
return
}
}
}()
go func() {
defer close(receiveErr)
for peerSrc.Next(ctx) {
var ping encodedTime.Millisecs
err := peerSrc.Reader(func(rd io.Reader) error {
return json.NewDecoder(rd).Decode(&ping)
})
if err != nil {
receiveErr <- err
return
}
}
return
}()
select {
case e := <-sendErr:
return e
case e := <-receiveErr:
return e
case <-ctx.Done():
return nil
}
}
// this is how it should work, i think, but it leads to disconnects...
// From the code it's hard to see but the client sends a timestamp in milliseconds (Date.now() in javascript/json)
// and the other side responds with it's own timestamp.
func actualPingPong(ctx context.Context, peerSrc *muxrpc.ByteSource, peerSnk *muxrpc.ByteSink) error {
peerSnk.SetEncoding(muxrpc.TypeJSON)
enc := json.NewEncoder(peerSnk)
for peerSrc.Next(ctx) {
var ping encodedTime.Millisecs
err := peerSrc.Reader(func(rd io.Reader) error {
return json.NewDecoder(rd).Decode(&ping)
})
if err != nil {
return err
}
//when := time.Time(ping)
//fmt.Printf("got ping: %s - age: %s\n", when.String(), time.Since(when))
pong := encodedTime.Millisecs(time.Now())
err = enc.Encode(pong)
if err != nil {
return err
}
// time.Sleep(timeout)
}
return peerSrc.Err()
}