forked from TrueCloudLab/rclone
Retry errors which indicate the connection closed prematurely.
See discussion in #442
This commit is contained in:
parent
5c2d8ffe33
commit
1752ee3c8b
4 changed files with 64 additions and 4 deletions
|
@ -142,9 +142,6 @@ var retryErrorCodes = []int{
|
||||||
// shouldRetry returns a boolean as to whether this resp and err
|
// shouldRetry returns a boolean as to whether this resp and err
|
||||||
// deserve to be retried. It returns the err as a convenience
|
// deserve to be retried. It returns the err as a convenience
|
||||||
func shouldRetry(resp *http.Response, err error) (bool, error) {
|
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
|
return fs.ShouldRetry(err) || fs.ShouldRetryHTTP(resp, retryErrorCodes), err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
9
fs/closed_conn.go
Normal file
9
fs/closed_conn.go
Normal 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
27
fs/closed_conn_win.go
Normal 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
|
||||||
|
}
|
29
fs/error.go
29
fs/error.go
|
@ -4,8 +4,10 @@ package fs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Retry is an optional interface for error as to whether the
|
// Retry is an optional interface for error as to whether the
|
||||||
|
@ -56,14 +58,39 @@ func RetryError(err error) error {
|
||||||
return plainRetryError{err}
|
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
|
// 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
|
// 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 {
|
func ShouldRetry(err error) bool {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Look for premature closing of connection
|
||||||
|
if err == io.EOF || err == io.ErrUnexpectedEOF || isClosedConnError(err) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// Unwrap url.Error
|
// Unwrap url.Error
|
||||||
if urlErr, ok := err.(*url.Error); ok {
|
if urlErr, ok := err.(*url.Error); ok {
|
||||||
err = urlErr.Err
|
err = urlErr.Err
|
||||||
|
|
Loading…
Reference in a new issue