69 lines
1.3 KiB
Go
69 lines
1.3 KiB
Go
package command
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"time"
|
|
)
|
|
|
|
type Command interface {
|
|
String() string
|
|
ValidateResponse([]byte) ([]byte, error)
|
|
}
|
|
|
|
type Conn interface {
|
|
io.ReadWriter
|
|
SetDeadline(time.Time) error
|
|
}
|
|
|
|
const (
|
|
maxAttempts = 3
|
|
timeout = time.Second * 5
|
|
readBufferSizeBytes = 4_096
|
|
)
|
|
|
|
// Send writes the command to the provided Writer, and reads and validates the
|
|
// response.
|
|
func Send(cmd Command, conn Conn) ([]byte, error) {
|
|
var (
|
|
resp []byte
|
|
err error
|
|
attempts int
|
|
)
|
|
|
|
for {
|
|
if resp, err = tryRequest(cmd, conn); err != nil {
|
|
attempts++
|
|
log.Printf("error executing command (attempt %d): %s", attempts, err)
|
|
if attempts <= 3 {
|
|
continue
|
|
}
|
|
return nil, fmt.Errorf("error executing command: %s", err)
|
|
}
|
|
|
|
return resp, nil
|
|
}
|
|
}
|
|
|
|
func tryRequest(cmd Command, conn Conn) ([]byte, error) {
|
|
if err := conn.SetDeadline(time.Now().Add(timeout)); err != nil {
|
|
return nil, fmt.Errorf("error setting deadline: %s", err)
|
|
}
|
|
|
|
p := make([]byte, readBufferSizeBytes)
|
|
_, err := fmt.Fprint(conn, cmd.String())
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error writing to socket: %s", err)
|
|
}
|
|
|
|
r := bufio.NewReader(conn)
|
|
n, err := r.Read(p)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error reading from socket: %s", err)
|
|
}
|
|
|
|
return cmd.ValidateResponse(p[:n])
|
|
}
|