diff --git a/vendor.conf b/vendor.conf index 4ecc26735..a185cd7b7 100644 --- a/vendor.conf +++ b/vendor.conf @@ -40,4 +40,4 @@ github.com/boltdb/bolt e9cf4fae01b5a8ff89d0ec6b32f0d9c9f79aefdd google.golang.org/genproto d80a6e20e776b0b17a324d0ba1ab50a39c8e8944 golang.org/x/text 19e51611da83d6be54ddafce4a4af510cb3e9ea4 github.com/dmcgowan/go-tar go1.10 -github.com/stevvooe/ttrpc 76e68349ad9ab4d03d764c713826d31216715e4f +github.com/stevvooe/ttrpc d2710463e497617f16f26d1e715a3308609e7982 diff --git a/vendor/github.com/stevvooe/ttrpc/client.go b/vendor/github.com/stevvooe/ttrpc/client.go index ca76afe19..ae367f90e 100644 --- a/vendor/github.com/stevvooe/ttrpc/client.go +++ b/vendor/github.com/stevvooe/ttrpc/client.go @@ -2,8 +2,12 @@ package ttrpc import ( "context" + "io" "net" + "os" + "strings" "sync" + "syscall" "github.com/containerd/containerd/log" "github.com/gogo/protobuf/proto" @@ -11,6 +15,10 @@ import ( "google.golang.org/grpc/status" ) +// ErrClosed is returned by client methods when the underlying connection is +// closed. +var ErrClosed = errors.New("ttrpc: closed") + type Client struct { codec codec conn net.Conn @@ -91,7 +99,7 @@ func (c *Client) dispatch(ctx context.Context, req *Request, resp *Response) err select { case err := <-errs: - return err + return filterCloseErr(err) case <-c.done: return c.err } @@ -171,7 +179,14 @@ func (c *Client) run() { call.errs <- c.recv(call.resp, msg) delete(waiters, msg.StreamID) case <-shutdown: + if shutdownErr != nil { + shutdownErr = filterCloseErr(shutdownErr) + } else { + shutdownErr = ErrClosed + } + shutdownErr = errors.Wrapf(shutdownErr, "ttrpc: client shutting down") + c.err = shutdownErr for _, waiter := range waiters { waiter.errs <- shutdownErr @@ -179,6 +194,9 @@ func (c *Client) run() { c.Close() return case <-c.closed: + if c.err == nil { + c.err = ErrClosed + } // broadcast the shutdown error to the remaining waiters. for _, waiter := range waiters { waiter.errs <- shutdownErr @@ -209,3 +227,30 @@ func (c *Client) recv(resp *Response, msg *message) error { defer c.channel.putmbuf(msg.p) return proto.Unmarshal(msg.p, resp) } + +// filterCloseErr rewrites EOF and EPIPE errors to ErrClosed. Use when +// returning from call or handling errors from main read loop. +// +// This purposely ignores errors with a wrapped cause. +func filterCloseErr(err error) error { + if err == nil { + return nil + } + + if err == io.EOF { + return ErrClosed + } + + if strings.Contains(err.Error(), "use of closed network connection") { + return ErrClosed + } + + // if we have an epipe on a write, we cast to errclosed + if oerr, ok := err.(*net.OpError); ok && oerr.Op == "write" { + if serr, ok := oerr.Err.(*os.SyscallError); ok && serr.Err == syscall.EPIPE { + return ErrClosed + } + } + + return err +} diff --git a/vendor/github.com/stevvooe/ttrpc/server.go b/vendor/github.com/stevvooe/ttrpc/server.go index edfca0c52..5d688e5d5 100644 --- a/vendor/github.com/stevvooe/ttrpc/server.go +++ b/vendor/github.com/stevvooe/ttrpc/server.go @@ -16,7 +16,7 @@ import ( ) var ( - ErrServerClosed = errors.New("ttrpc: server close") + ErrServerClosed = errors.New("ttrpc: server closed") ) type Server struct {