mirror of
https://github.com/zrepl/zrepl.git
synced 2025-01-08 23:39:04 +01:00
796c5ad42d
transport/ssh: update go-netssh to new version => supports CloseWrite and Deadlines => build: require Go 1.11 (netssh requires it)
139 lines
2.5 KiB
Go
139 lines
2.5 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"net"
|
|
"time"
|
|
|
|
"github.com/zrepl/zrepl/transport"
|
|
)
|
|
|
|
type DeadlineMode uint
|
|
|
|
const (
|
|
DeadlineModeClientTimeout DeadlineMode = 1 + iota
|
|
DeadlineModeServerTimeout
|
|
)
|
|
|
|
type Deadlines struct {
|
|
mode DeadlineMode
|
|
}
|
|
|
|
func (d Deadlines) Client(wire transport.Wire) {
|
|
switch d.mode {
|
|
case DeadlineModeClientTimeout:
|
|
d.sleepThenSend(wire)
|
|
case DeadlineModeServerTimeout:
|
|
d.sendThenRead(wire)
|
|
default:
|
|
panic(d.mode)
|
|
}
|
|
}
|
|
|
|
func (d Deadlines) Server(wire transport.Wire) {
|
|
switch d.mode {
|
|
case DeadlineModeClientTimeout:
|
|
d.sendThenRead(wire)
|
|
case DeadlineModeServerTimeout:
|
|
d.sleepThenSend(wire)
|
|
default:
|
|
panic(d.mode)
|
|
}
|
|
}
|
|
|
|
var deadlinesTimeout = 1 * time.Second
|
|
|
|
func (d Deadlines) sleepThenSend(wire transport.Wire) {
|
|
defer wire.Close()
|
|
|
|
log.Print("sleepThenSend")
|
|
|
|
// exceed timeout of peer (do not respond to their hi msg)
|
|
time.Sleep(3 * deadlinesTimeout)
|
|
// expect that the client has hung up on us by now
|
|
err := d.sendMsg(wire, "hi")
|
|
log.Printf("err=%s", err)
|
|
log.Printf("err=%#v", err)
|
|
if err == nil {
|
|
log.Panic("no error")
|
|
}
|
|
if _, ok := err.(net.Error); !ok {
|
|
log.Panic("not a net error")
|
|
}
|
|
|
|
}
|
|
|
|
func (d Deadlines) sendThenRead(wire transport.Wire) {
|
|
|
|
log.Print("sendThenRead")
|
|
|
|
err := d.sendMsg(wire, "hi")
|
|
noerror(err)
|
|
|
|
err = wire.SetReadDeadline(time.Now().Add(deadlinesTimeout))
|
|
noerror(err)
|
|
|
|
m, err := d.recvMsg(wire)
|
|
log.Printf("m=%q", m)
|
|
log.Printf("err=%s", err)
|
|
log.Printf("err=%#v", err)
|
|
|
|
// close asap so that the peer get's a 'connection reset by peer' error or similar
|
|
closeErr := wire.Close()
|
|
if closeErr != nil {
|
|
panic(closeErr)
|
|
}
|
|
|
|
var neterr net.Error
|
|
var ok bool
|
|
if err == nil {
|
|
goto unexpErr // works for nil, too
|
|
}
|
|
neterr, ok = err.(net.Error)
|
|
if !ok {
|
|
log.Println("not a net error")
|
|
goto unexpErr
|
|
}
|
|
if !neterr.Timeout() {
|
|
log.Println("not a timeout")
|
|
}
|
|
|
|
return
|
|
|
|
unexpErr:
|
|
panic(fmt.Sprintf("sendThenRead: client should have hung up but got error %T %s", err, err))
|
|
}
|
|
|
|
const deadlinesMsgLen = 40
|
|
|
|
func (d Deadlines) sendMsg(wire transport.Wire, msg string) error {
|
|
if len(msg) > deadlinesMsgLen {
|
|
panic(len(msg))
|
|
}
|
|
var buf [deadlinesMsgLen]byte
|
|
copy(buf[:], []byte(msg))
|
|
n, err := wire.Write(buf[:])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if n != len(buf) {
|
|
panic("short write not allowed")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (d Deadlines) recvMsg(wire transport.Wire) (string, error) {
|
|
|
|
var buf bytes.Buffer
|
|
r := io.LimitReader(wire, deadlinesMsgLen)
|
|
_, err := io.Copy(&buf, r)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return buf.String(), nil
|
|
|
|
}
|