mirror of https://github.com/knative/pkg.git
Handle and retry 'connection reset by peer' errors. (#746)
This commit is contained in:
parent
9353a99265
commit
c3d4edcf0c
|
|
@ -40,13 +40,14 @@ func isDNSError(err error) bool {
|
||||||
return strings.Contains(msg, "no such host") || strings.Contains(msg, ":53")
|
return strings.Contains(msg, "no such host") || strings.Contains(msg, ":53")
|
||||||
}
|
}
|
||||||
|
|
||||||
func isTCPConnectRefuse(err error) bool {
|
func isConnectionRefused(err error) bool {
|
||||||
// The alternative for the string check is:
|
// The alternative for the string check is:
|
||||||
// errNo := (((err.(*url.Error)).Err.(*net.OpError)).Err.(*os.SyscallError).Err).(syscall.Errno)
|
// errNo := (((err.(*url.Error)).Err.(*net.OpError)).Err.(*os.SyscallError).Err).(syscall.Errno)
|
||||||
// if errNo == syscall.Errno(0x6f) {...}
|
// if errNo == syscall.Errno(0x6f) {...}
|
||||||
// But with assertions, of course.
|
// But with assertions, of course.
|
||||||
if err != nil && strings.Contains(err.Error(), "connect: connection refused") {
|
return err != nil && strings.Contains(err.Error(), "connect: connection refused")
|
||||||
return true
|
}
|
||||||
}
|
|
||||||
return false
|
func isConnectionReset(err error) bool {
|
||||||
|
return err != nil && strings.Contains(err.Error(), "connection reset by peer")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ limitations under the License.
|
||||||
package spoof
|
package spoof
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"net/http"
|
"net/http"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
@ -57,31 +58,57 @@ func TestDNSError(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTCPConnectRefuse(t *testing.T) {
|
func TestConnectionRefused(t *testing.T) {
|
||||||
client := &http.Client{}
|
client := &http.Client{}
|
||||||
|
|
||||||
for _, tt := range []struct {
|
for _, tt := range []struct {
|
||||||
name string
|
name string
|
||||||
url string
|
url string
|
||||||
tcpRefuse bool
|
connRefused bool
|
||||||
}{{
|
}{{
|
||||||
name: "nothing listening",
|
name: "nothing listening",
|
||||||
url: "http://localhost:60001",
|
url: "http://localhost:60001",
|
||||||
tcpRefuse: true,
|
connRefused: true,
|
||||||
}, {
|
}, {
|
||||||
name: "dns error",
|
name: "dns error",
|
||||||
url: "http://this.url.does.not.exist",
|
url: "http://this.url.does.not.exist",
|
||||||
tcpRefuse: false,
|
connRefused: false,
|
||||||
}, {
|
}, {
|
||||||
name: "google.com",
|
name: "google.com",
|
||||||
url: "https://google.com",
|
url: "https://google.com",
|
||||||
tcpRefuse: false,
|
connRefused: false,
|
||||||
}} {
|
}} {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
req, _ := http.NewRequest("GET", tt.url, nil)
|
req, _ := http.NewRequest("GET", tt.url, nil)
|
||||||
_, err := client.Do(req)
|
_, err := client.Do(req)
|
||||||
if tcpRefuse := isTCPConnectRefuse(err); tt.tcpRefuse != tcpRefuse {
|
if connRefused := isConnectionRefused(err); tt.connRefused != connRefused {
|
||||||
t.Errorf("Expected tcpRefuse=%v, got %v", tt.tcpRefuse, tcpRefuse)
|
t.Errorf("Expected connRefused=%v, got %v", tt.connRefused, connRefused)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConnectionReset(t *testing.T) {
|
||||||
|
for _, tt := range []struct {
|
||||||
|
name string
|
||||||
|
err error
|
||||||
|
connReset bool
|
||||||
|
}{{
|
||||||
|
name: "error matching",
|
||||||
|
err: errors.New("read tcp 10.60.2.57:47882->104.154.144.94:80: read: connection reset by peer"),
|
||||||
|
connReset: true,
|
||||||
|
}, {
|
||||||
|
name: "error not matching",
|
||||||
|
err: errors.New("dial tcp: lookup this.url.does.not.exist on 127.0.0.1:53: no such host"),
|
||||||
|
connReset: false,
|
||||||
|
}, {
|
||||||
|
name: "nil error",
|
||||||
|
err: nil,
|
||||||
|
connReset: false,
|
||||||
|
}} {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if connReset := isConnectionReset(tt.err); tt.connReset != connReset {
|
||||||
|
t.Errorf("Expected connReset=%v, got %v", tt.connReset, connReset)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -214,19 +214,22 @@ func (sc *SpoofingClient) Poll(req *http.Request, inState ResponseChecker) (*Res
|
||||||
resp, err = sc.Do(req)
|
resp, err = sc.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if isTCPTimeout(err) {
|
if isTCPTimeout(err) {
|
||||||
sc.Logf("Retrying %s for TCP timeout %v", req.URL.String(), err)
|
sc.Logf("Retrying %s for TCP timeout: %v", req.URL, err)
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
// Retrying on DNS error, since we may be using xip.io or nip.io in tests.
|
// Retrying on DNS error, since we may be using xip.io or nip.io in tests.
|
||||||
if isDNSError(err) {
|
if isDNSError(err) {
|
||||||
sc.Logf("Retrying %s for DNS error %v", req.URL.String(), err)
|
sc.Logf("Retrying %s for DNS error: %v", req.URL, err)
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
// Repeat the poll on `connection refused` errors, which are usually transient Istio errors.
|
// Repeat the poll on `connection refused` errors, which are usually transient Istio errors.
|
||||||
if isTCPConnectRefuse(err) {
|
if isConnectionRefused(err) {
|
||||||
sc.Logf("Retrying %s for connection refused %v", req.URL.String(), err)
|
sc.Logf("Retrying %s for connection refused: %v", req.URL, err)
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
if isConnectionReset(err) {
|
||||||
|
sc.Logf("Retrying %s for connection reset: %v", req.URL, err)
|
||||||
|
}
|
||||||
return true, err
|
return true, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue