restic/internal/backend/rclone/stdio_conn.go
Michael Eischer 3cd927d180 rclone: Give rclone time to finish before closing stdin pipe
Calling `Close()` on the rclone backend sometimes failed during test
execution with 'signal: Broken pipe'. The stdio connection closed both
the stdin and stdout file descriptors at the same moment, therefore
giving rclone no chance to properly send any final http2 data frames.

Now the stdin connection to rclone is closed first and will only be
forcefully closed after a timeout. In case rclone exits before the
timeout then the stdio connection will be closed normally.
2020-07-26 00:29:25 +02:00

100 lines
1.9 KiB
Go

package rclone
import (
"net"
"os"
"os/exec"
"sync"
"time"
"github.com/restic/restic/internal/debug"
)
// StdioConn implements a net.Conn via stdin/stdout.
type StdioConn struct {
stdin *os.File
stdout *os.File
cmd *exec.Cmd
closeIn sync.Once
closeOut sync.Once
}
func (s *StdioConn) Read(p []byte) (int, error) {
n, err := s.stdin.Read(p)
return n, err
}
func (s *StdioConn) Write(p []byte) (int, error) {
n, err := s.stdout.Write(p)
return n, err
}
// Close closes the stream to the child process.
func (s *StdioConn) Close() (err error) {
s.closeOut.Do(func() {
debug.Log("close stdio send connection")
err = s.stdout.Close()
})
return err
}
// CloseAll closes both streams.
func (s *StdioConn) CloseAll() (err error) {
err = s.Close()
s.closeIn.Do(func() {
debug.Log("close stdio receive connection")
err2 := s.stdin.Close()
if err == nil {
err = err2
}
})
return err
}
// LocalAddr returns nil.
func (s *StdioConn) LocalAddr() net.Addr {
return Addr{}
}
// RemoteAddr returns nil.
func (s *StdioConn) RemoteAddr() net.Addr {
return Addr{}
}
// SetDeadline sets the read/write deadline.
func (s *StdioConn) SetDeadline(t time.Time) error {
err1 := s.stdin.SetReadDeadline(t)
err2 := s.stdout.SetWriteDeadline(t)
if err1 != nil {
return err1
}
return err2
}
// SetReadDeadline sets the read/write deadline.
func (s *StdioConn) SetReadDeadline(t time.Time) error {
return s.stdin.SetReadDeadline(t)
}
// SetWriteDeadline sets the read/write deadline.
func (s *StdioConn) SetWriteDeadline(t time.Time) error {
return s.stdout.SetWriteDeadline(t)
}
// make sure StdioConn implements net.Conn
var _ net.Conn = &StdioConn{}
// Addr implements net.Addr for stdin/stdout.
type Addr struct{}
// Network returns the network type as a string.
func (a Addr) Network() string {
return "stdio"
}
func (a Addr) String() string {
return "stdio"
}