diff --git a/cmd/rest/client_test.go b/cmd/rest/client_test.go new file mode 100644 index 000000000..83e096acb --- /dev/null +++ b/cmd/rest/client_test.go @@ -0,0 +1,68 @@ +/* + * MinIO Cloud Storage, (C) 2020 MinIO, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package rest + +import ( + "errors" + "net" + "net/url" + "testing" +) + +func TestNetworkError_Unwrap(t *testing.T) { + tests := []struct { + name string + err error + target interface{} + want bool + }{ + { + name: "url.Error", + err: &url.Error{Op: "PUT", URL: "http://localhost/1234", Err: restError("remote server offline")}, + target: &url.Error{}, + want: true, + }, + { + name: "net.Error", + err: &url.Error{Op: "PUT", URL: "http://localhost/1234", Err: restError("remote server offline")}, + want: true, + }, + { + name: "net.Error-unmatched", + err: errors.New("something"), + want: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Wrap error + n := &NetworkError{ + Err: tt.err, + } + if tt.target == nil { + var netErrInterface net.Error + if errors.As(n, &netErrInterface) != tt.want { + t.Errorf("errors.As(n, &tt.target) != tt.want, n: %#v, target: %#v, want:%v, got: %v", n, tt.target, tt.want, !tt.want) + } + } else { + if errors.As(n, &tt.target) != tt.want { + t.Errorf("errors.As(n, &tt.target) != tt.want, n: %#v, target: %#v, want:%v, got: %v", n, tt.target, tt.want, !tt.want) + } + } + }) + } +} diff --git a/pkg/net/url.go b/pkg/net/url.go index 39de2fae1..315a954a8 100644 --- a/pkg/net/url.go +++ b/pkg/net/url.go @@ -141,7 +141,7 @@ func ParseURL(s string) (u *URL, err error) { } // IsNetworkOrHostDown - if there was a network error or if the host is down. -// expectTimeouts indicates that context timeouts are expected and does not +// expectTimeouts indicates that *context* timeouts are expected and does not // indicate a downed host. Other timeouts still returns down. func IsNetworkOrHostDown(err error, expectTimeouts bool) bool { if err == nil { @@ -158,37 +158,37 @@ func IsNetworkOrHostDown(err error, expectTimeouts bool) bool { // We need to figure if the error either a timeout // or a non-temporary error. - e, ok := err.(net.Error) - if ok { - if urlErr, ok := e.(*url.Error); ok { - switch urlErr.Err.(type) { - case *net.DNSError, *net.OpError, net.UnknownNetworkError: - return true - } + urlErr := &url.Error{} + if errors.As(err, &urlErr) { + switch urlErr.Err.(type) { + case *net.DNSError, *net.OpError, net.UnknownNetworkError: + return true } + } + var e net.Error + if errors.As(err, &e) { if e.Timeout() { return true } - } - ok = false // Fallback to other mechanisms. - if strings.Contains(err.Error(), "Connection closed by foreign host") { - ok = true - } else if strings.Contains(err.Error(), "TLS handshake timeout") { + switch { + case strings.Contains(err.Error(), "Connection closed by foreign host"): + return true + case strings.Contains(err.Error(), "TLS handshake timeout"): // If error is - tlsHandshakeTimeoutError. - ok = true - } else if strings.Contains(err.Error(), "i/o timeout") { + return true + case strings.Contains(err.Error(), "i/o timeout"): // If error is - tcp timeoutError. - ok = true - } else if strings.Contains(err.Error(), "connection timed out") { + return true + case strings.Contains(err.Error(), "connection timed out"): // If err is a net.Dial timeout. - ok = true - } else if strings.Contains(strings.ToLower(err.Error()), "503 service unavailable") { + return true + case strings.Contains(strings.ToLower(err.Error()), "503 service unavailable"): // Denial errors - ok = true + return true } - return ok + return false }