Retry errors which indicate the connection closed prematurely.

See discussion in #442
This commit is contained in:
Nick Craig-Wood 2016-04-29 16:42:08 +01:00
parent 5c2d8ffe33
commit 1752ee3c8b
4 changed files with 64 additions and 4 deletions

View File

@ -142,9 +142,6 @@ var retryErrorCodes = []int{
// shouldRetry returns a boolean as to whether this resp and err
// deserve to be retried. It returns the err as a convenience
func shouldRetry(resp *http.Response, err error) (bool, error) {
if err == io.EOF {
return true, err
}
return fs.ShouldRetry(err) || fs.ShouldRetryHTTP(resp, retryErrorCodes), err
}

9
fs/closed_conn.go Normal file
View File

@ -0,0 +1,9 @@
// +build !windows
package fs
// isClosedConnErrorPlatform reports whether err is an error from use
// of a closed network connection using platform specific error codes.
func isClosedConnErrorPlatform(err error) bool {
return false
}

27
fs/closed_conn_win.go Normal file
View File

@ -0,0 +1,27 @@
// +build windows
package fs
import (
"net"
"os"
"syscall"
)
// isClosedConnErrorPlatform reports whether err is an error from use
// of a closed network connection using platform specific error codes.
//
// Code adapted from net/http
func isClosedConnErrorPlatform(err error) bool {
if oe, ok := err.(*net.OpError); ok && oe.Op == "read" {
if se, ok := oe.Err.(*os.SyscallError); ok && se.Syscall == "wsarecv" {
if errno, ok := se.Err.(syscall.Errno); ok {
const WSAECONNABORTED syscall.Errno = 10053
if errno == syscall.WSAECONNRESET || errno == WSAECONNABORTED {
return true
}
}
}
}
return false
}

View File

@ -4,8 +4,10 @@ package fs
import (
"fmt"
"io"
"net/http"
"net/url"
"strings"
)
// Retry is an optional interface for error as to whether the
@ -56,14 +58,39 @@ func RetryError(err error) error {
return plainRetryError{err}
}
// isClosedConnError reports whether err is an error from use of a closed
// network connection.
//
// Code adapted from net/http
func isClosedConnError(err error) bool {
if err == nil {
return false
}
// Note that this error isn't exported so we have to do a
// string comparison :-(
str := err.Error()
if strings.Contains(str, "use of closed network connection") {
return true
}
return isClosedConnErrorPlatform(err)
}
// ShouldRetry looks at an error and tries to work out if retrying the
// operation that caused it would be a good idea. It returns true if
// the error implements Timeout() or Temporary() and it returns true.
// the error implements Timeout() or Temporary() or if the error
// indicates a premature closing of the connection.
func ShouldRetry(err error) bool {
if err == nil {
return false
}
// Look for premature closing of connection
if err == io.EOF || err == io.ErrUnexpectedEOF || isClosedConnError(err) {
return true
}
// Unwrap url.Error
if urlErr, ok := err.(*url.Error); ok {
err = urlErr.Err