mirror of
https://github.com/zrepl/zrepl.git
synced 2025-01-09 15:58:33 +01:00
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
|
||
|
|
||
|
}
|