From fb3f152c196269d7a0fc9246226de478866073c1 Mon Sep 17 00:00:00 2001 From: Dave Cheney Date: Sat, 10 Oct 2015 14:21:42 +1100 Subject: [PATCH] net/url: make *url.Error implement net.Error Fixes #12866 net/http.Client returns some errors wrapped in a *url.Error. To avoid the requirement to unwrap these errors to determine if the cause was temporary or a timeout, make *url.Error implement net.Error directly. Change-Id: I1ba84ecc7ad5147a40f056ff1254e60290152408 Reviewed-on: https://go-review.googlesource.com/15672 Reviewed-by: Brad Fitzpatrick Run-TryBot: Brad Fitzpatrick --- src/net/http/client_test.go | 7 ++-- src/net/url/url.go | 18 ++++++++ src/net/url/url_test.go | 83 +++++++++++++++++++++++++++++++++++++ 3 files changed, 104 insertions(+), 4 deletions(-) diff --git a/src/net/http/client_test.go b/src/net/http/client_test.go index 7b524d381b..86ec83add1 100644 --- a/src/net/http/client_test.go +++ b/src/net/http/client_test.go @@ -983,13 +983,12 @@ func TestClientTimeout_Headers(t *testing.T) { if err == nil { t.Fatal("got response from Get; expected error") } - ue, ok := err.(*url.Error) - if !ok { + if _, ok := err.(*url.Error); !ok { t.Fatalf("Got error of type %T; want *url.Error", err) } - ne, ok := ue.Err.(net.Error) + ne, ok := err.(net.Error) if !ok { - t.Fatalf("Got url.Error.Err of type %T; want some net.Error", err) + t.Fatalf("Got error of type %T; want some net.Error", err) } if !ne.Timeout() { t.Error("net.Error.Timeout = false; want true") diff --git a/src/net/url/url.go b/src/net/url/url.go index 8ffad663d5..7f648e3b39 100644 --- a/src/net/url/url.go +++ b/src/net/url/url.go @@ -24,6 +24,24 @@ type Error struct { func (e *Error) Error() string { return e.Op + " " + e.URL + ": " + e.Err.Error() } +type timeout interface { + Timeout() bool +} + +func (e *Error) Timeout() bool { + t, ok := e.Err.(timeout) + return ok && t.Timeout() +} + +type temporary interface { + Temporary() bool +} + +func (e *Error) Temporary() bool { + t, ok := e.Err.(temporary) + return ok && t.Temporary() +} + func ishex(c byte) bool { switch { case '0' <= c && c <= '9': diff --git a/src/net/url/url_test.go b/src/net/url/url_test.go index ff6e9e4541..dbac91b945 100644 --- a/src/net/url/url_test.go +++ b/src/net/url/url_test.go @@ -6,6 +6,8 @@ package url import ( "fmt" + "io" + "net" "reflect" "strings" "testing" @@ -1229,3 +1231,84 @@ func TestShouldEscape(t *testing.T) { } } } + +type timeoutError struct { + timeout bool +} + +func (e *timeoutError) Error() string { return "timeout error" } +func (e *timeoutError) Timeout() bool { return e.timeout } + +type temporaryError struct { + temporary bool +} + +func (e *temporaryError) Error() string { return "temporary error" } +func (e *temporaryError) Temporary() bool { return e.temporary } + +type timeoutTemporaryError struct { + timeoutError + temporaryError +} + +func (e *timeoutTemporaryError) Error() string { return "timeout/temporary error" } + +var netErrorTests = []struct { + err error + timeout bool + temporary bool +}{{ + err: &Error{"Get", "http://google.com/", &timeoutError{timeout: true}}, + timeout: true, + temporary: false, +}, { + err: &Error{"Get", "http://google.com/", &timeoutError{timeout: false}}, + timeout: false, + temporary: false, +}, { + err: &Error{"Get", "http://google.com/", &temporaryError{temporary: true}}, + timeout: false, + temporary: true, +}, { + err: &Error{"Get", "http://google.com/", &temporaryError{temporary: false}}, + timeout: false, + temporary: false, +}, { + err: &Error{"Get", "http://google.com/", &timeoutTemporaryError{timeoutError{timeout: true}, temporaryError{temporary: true}}}, + timeout: true, + temporary: true, +}, { + err: &Error{"Get", "http://google.com/", &timeoutTemporaryError{timeoutError{timeout: false}, temporaryError{temporary: true}}}, + timeout: false, + temporary: true, +}, { + err: &Error{"Get", "http://google.com/", &timeoutTemporaryError{timeoutError{timeout: true}, temporaryError{temporary: false}}}, + timeout: true, + temporary: false, +}, { + err: &Error{"Get", "http://google.com/", &timeoutTemporaryError{timeoutError{timeout: false}, temporaryError{temporary: false}}}, + timeout: false, + temporary: false, +}, { + err: &Error{"Get", "http://google.com/", io.EOF}, + timeout: false, + temporary: false, +}} + +// Test that url.Error implements net.Error and that it forwards +func TestURLErrorImplementsNetError(t *testing.T) { + for i, tt := range netErrorTests { + err, ok := tt.err.(net.Error) + if !ok { + t.Errorf("%d: %T does not implement net.Error", i+1, tt.err) + continue + } + if err.Timeout() != tt.timeout { + t.Errorf("%d: err.Timeout(): want %v, have %v", i+1, tt.timeout, err.Timeout()) + continue + } + if err.Temporary() != tt.temporary { + t.Errorf("%d: err.Temporary(): want %v, have %v", i+1, tt.temporary, err.Temporary()) + } + } +} -- 2.50.0