From 8d0d10cce5a05a466437e779ca5398115b4e6f9f Mon Sep 17 00:00:00 2001 From: Gyu-Ho Lee Date: Tue, 29 Mar 2016 16:50:47 -0700 Subject: [PATCH] client: return original ctx error Fix https://github.com/coreos/etcd/issues/3209. --- client/client.go | 4 +++- client/client_test.go | 48 +++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 49 insertions(+), 3 deletions(-) diff --git a/client/client.go b/client/client.go index 0bef131f6..2aaa112ed 100644 --- a/client/client.go +++ b/client/client.go @@ -342,7 +342,9 @@ func (c *httpClusterClient) Do(ctx context.Context, act httpAction) (*http.Respo resp, body, err = hc.Do(ctx, action) if err != nil { cerr.Errors = append(cerr.Errors, err) - // mask previous errors with context error, which is controlled by user + if err == ctx.Err() { + return nil, nil, ctx.Err() + } if err == context.Canceled || err == context.DeadlineExceeded { return nil, nil, err } diff --git a/client/client_test.go b/client/client_test.go index 09f12f6d9..a4ebe1039 100644 --- a/client/client_test.go +++ b/client/client_test.go @@ -295,7 +295,7 @@ func TestSimpleHTTPClientDoHeaderTimeout(t *testing.T) { t.Fatalf("expected non-nil error, got nil") } case <-time.After(time.Second): - t.Fatalf("unexpected timeout when waitting for the test to finish") + t.Fatalf("unexpected timeout when waiting for the test to finish") } } @@ -444,7 +444,51 @@ func TestHTTPClusterClientDoDeadlineExceedContext(t *testing.T) { t.Errorf("err = %+v, want %+v", err, context.DeadlineExceeded) } case <-time.After(time.Second): - t.Fatalf("unexpected timeout when waitting for request to deadline exceed") + t.Fatalf("unexpected timeout when waiting for request to deadline exceed") + } +} + +type fakeCancelContext struct{} + +var fakeCancelContextError = errors.New("fake context canceled") + +func (f fakeCancelContext) Deadline() (time.Time, bool) { return time.Time{}, false } +func (f fakeCancelContext) Done() <-chan struct{} { + d := make(chan struct{}, 1) + d <- struct{}{} + return d +} +func (f fakeCancelContext) Err() error { return fakeCancelContextError } +func (f fakeCancelContext) Value(key interface{}) interface{} { return 1 } + +func withTimeout(parent context.Context, timeout time.Duration) (context.Context, context.CancelFunc) { + return parent, func() { parent = nil } +} + +func TestHTTPClusterClientDoCanceledContext(t *testing.T) { + fakeURL := url.URL{} + tr := newFakeTransport() + tr.finishCancel <- struct{}{} + c := &httpClusterClient{ + clientFactory: newHTTPClientFactory(tr, DefaultCheckRedirect, 0), + endpoints: []url.URL{fakeURL}, + } + + errc := make(chan error) + go func() { + ctx, cancel := withTimeout(fakeCancelContext{}, time.Millisecond) + cancel() + _, _, err := c.Do(ctx, &fakeAction{}) + errc <- err + }() + + select { + case err := <-errc: + if err != fakeCancelContextError { + t.Errorf("err = %+v, want %+v", err, fakeCancelContextError) + } + case <-time.After(time.Second): + t.Fatalf("unexpected timeout when waiting for request to fake context canceled") } }