From cd40eb89aea1d5f4fdfe0b0baa5b37ac513f5f75 Mon Sep 17 00:00:00 2001 From: Kostadin Plachkov Date: Fri, 11 Dec 2020 00:13:37 +0100 Subject: [PATCH] Fix client request error handling Signed-off-by: Kostadin Plachkov --- client/request.go | 11 ++++++++--- client/request_test.go | 42 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 3 deletions(-) diff --git a/client/request.go b/client/request.go index 813eac2c9e..dc5a1dfeb7 100644 --- a/client/request.go +++ b/client/request.go @@ -110,11 +110,16 @@ func (cli *Client) sendRequest(ctx context.Context, method, path string, query u if err != nil { return serverResponse{}, err } + resp, err := cli.doRequest(ctx, req) - if err != nil { - return resp, errdefs.FromStatusCode(err, resp.statusCode) + switch { + case errors.Is(err, context.Canceled): + return serverResponse{}, errdefs.Cancelled(err) + case errors.Is(err, context.DeadlineExceeded): + return serverResponse{}, errdefs.Deadline(err) + case err == nil: + err = cli.checkResponseErr(resp) } - err = cli.checkResponseErr(resp) return resp, errdefs.FromStatusCode(err, resp.statusCode) } diff --git a/client/request_test.go b/client/request_test.go index e07c0ebcab..63df859557 100644 --- a/client/request_test.go +++ b/client/request_test.go @@ -3,12 +3,14 @@ package client // import "github.com/docker/docker/client" import ( "bytes" "context" + "errors" "fmt" "io/ioutil" "math/rand" "net/http" "strings" "testing" + "time" "github.com/docker/docker/api/types" "github.com/docker/docker/errdefs" @@ -105,3 +107,43 @@ func TestInfiniteError(t *testing.T) { _, err := client.Ping(context.Background()) assert.Check(t, is.ErrorContains(err, "request returned Internal Server Error")) } + +func TestCanceledContext(t *testing.T) { + testURL := "/test" + + client := &Client{ + client: newMockClient(func(req *http.Request) (*http.Response, error) { + assert.Equal(t, req.Context().Err(), context.Canceled) + + return &http.Response{}, context.Canceled + }), + } + + ctx, cancel := context.WithCancel(context.Background()) + cancel() + + _, err := client.sendRequest(ctx, http.MethodGet, testURL, nil, nil, nil) + assert.Equal(t, true, errdefs.IsCancelled(err)) + assert.Equal(t, true, errors.Is(err, context.Canceled)) +} + +func TestDeadlineExceededContext(t *testing.T) { + testURL := "/test" + + client := &Client{ + client: newMockClient(func(req *http.Request) (*http.Response, error) { + assert.Equal(t, req.Context().Err(), context.DeadlineExceeded) + + return &http.Response{}, context.DeadlineExceeded + }), + } + + ctx, cancel := context.WithDeadline(context.Background(), time.Now()) + defer cancel() + + <-ctx.Done() + + _, err := client.sendRequest(ctx, http.MethodGet, testURL, nil, nil, nil) + assert.Equal(t, true, errdefs.IsDeadline(err)) + assert.Equal(t, true, errors.Is(err, context.DeadlineExceeded)) +}