]> Cypherpunks repositories - gostls13.git/commitdiff
net/url: make *url.Error implement net.Error
authorDave Cheney <dave@cheney.net>
Sat, 10 Oct 2015 03:21:42 +0000 (14:21 +1100)
committerDave Cheney <dave@cheney.net>
Sat, 10 Oct 2015 11:32:58 +0000 (11:32 +0000)
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 <bradfitz@golang.org>
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>

src/net/http/client_test.go
src/net/url/url.go
src/net/url/url_test.go

index 7b524d381bce9a5e2acc06b1f2cc1e437de26bb9..86ec83add10591fe4d731bb3e1d41a9147310f41 100644 (file)
@@ -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")
index 8ffad663d5cf062e96055131a48a5c51cab66434..7f648e3b396eb0eeb239868db3e75814cc33330d 100644 (file)
@@ -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':
index ff6e9e4541a7b6b524f74e02808bedfc5f464406..dbac91b9454472de02fa4bf75599005f68ef0121 100644 (file)
@@ -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())
+               }
+       }
+}