|
@@ -6,6 +6,7 @@ import (
|
|
"fmt"
|
|
"fmt"
|
|
"io"
|
|
"io"
|
|
"io/ioutil"
|
|
"io/ioutil"
|
|
|
|
+ "net"
|
|
"net/http"
|
|
"net/http"
|
|
"net/url"
|
|
"net/url"
|
|
"strings"
|
|
"strings"
|
|
@@ -24,47 +25,47 @@ type serverResponse struct {
|
|
}
|
|
}
|
|
|
|
|
|
// head sends an http request to the docker API using the method HEAD.
|
|
// head sends an http request to the docker API using the method HEAD.
|
|
-func (cli *Client) head(ctx context.Context, path string, query url.Values, headers map[string][]string) (*serverResponse, error) {
|
|
|
|
|
|
+func (cli *Client) head(ctx context.Context, path string, query url.Values, headers map[string][]string) (serverResponse, error) {
|
|
return cli.sendRequest(ctx, "HEAD", path, query, nil, headers)
|
|
return cli.sendRequest(ctx, "HEAD", path, query, nil, headers)
|
|
}
|
|
}
|
|
|
|
|
|
// getWithContext sends an http request to the docker API using the method GET with a specific go context.
|
|
// getWithContext sends an http request to the docker API using the method GET with a specific go context.
|
|
-func (cli *Client) get(ctx context.Context, path string, query url.Values, headers map[string][]string) (*serverResponse, error) {
|
|
|
|
|
|
+func (cli *Client) get(ctx context.Context, path string, query url.Values, headers map[string][]string) (serverResponse, error) {
|
|
return cli.sendRequest(ctx, "GET", path, query, nil, headers)
|
|
return cli.sendRequest(ctx, "GET", path, query, nil, headers)
|
|
}
|
|
}
|
|
|
|
|
|
// postWithContext sends an http request to the docker API using the method POST with a specific go context.
|
|
// postWithContext sends an http request to the docker API using the method POST with a specific go context.
|
|
-func (cli *Client) post(ctx context.Context, path string, query url.Values, obj interface{}, headers map[string][]string) (*serverResponse, error) {
|
|
|
|
|
|
+func (cli *Client) post(ctx context.Context, path string, query url.Values, obj interface{}, headers map[string][]string) (serverResponse, error) {
|
|
return cli.sendRequest(ctx, "POST", path, query, obj, headers)
|
|
return cli.sendRequest(ctx, "POST", path, query, obj, headers)
|
|
}
|
|
}
|
|
|
|
|
|
-func (cli *Client) postRaw(ctx context.Context, path string, query url.Values, body io.Reader, headers map[string][]string) (*serverResponse, error) {
|
|
|
|
|
|
+func (cli *Client) postRaw(ctx context.Context, path string, query url.Values, body io.Reader, headers map[string][]string) (serverResponse, error) {
|
|
return cli.sendClientRequest(ctx, "POST", path, query, body, headers)
|
|
return cli.sendClientRequest(ctx, "POST", path, query, body, headers)
|
|
}
|
|
}
|
|
|
|
|
|
// put sends an http request to the docker API using the method PUT.
|
|
// put sends an http request to the docker API using the method PUT.
|
|
-func (cli *Client) put(ctx context.Context, path string, query url.Values, obj interface{}, headers map[string][]string) (*serverResponse, error) {
|
|
|
|
|
|
+func (cli *Client) put(ctx context.Context, path string, query url.Values, obj interface{}, headers map[string][]string) (serverResponse, error) {
|
|
return cli.sendRequest(ctx, "PUT", path, query, obj, headers)
|
|
return cli.sendRequest(ctx, "PUT", path, query, obj, headers)
|
|
}
|
|
}
|
|
|
|
|
|
// put sends an http request to the docker API using the method PUT.
|
|
// put sends an http request to the docker API using the method PUT.
|
|
-func (cli *Client) putRaw(ctx context.Context, path string, query url.Values, body io.Reader, headers map[string][]string) (*serverResponse, error) {
|
|
|
|
|
|
+func (cli *Client) putRaw(ctx context.Context, path string, query url.Values, body io.Reader, headers map[string][]string) (serverResponse, error) {
|
|
return cli.sendClientRequest(ctx, "PUT", path, query, body, headers)
|
|
return cli.sendClientRequest(ctx, "PUT", path, query, body, headers)
|
|
}
|
|
}
|
|
|
|
|
|
// delete sends an http request to the docker API using the method DELETE.
|
|
// delete sends an http request to the docker API using the method DELETE.
|
|
-func (cli *Client) delete(ctx context.Context, path string, query url.Values, headers map[string][]string) (*serverResponse, error) {
|
|
|
|
|
|
+func (cli *Client) delete(ctx context.Context, path string, query url.Values, headers map[string][]string) (serverResponse, error) {
|
|
return cli.sendRequest(ctx, "DELETE", path, query, nil, headers)
|
|
return cli.sendRequest(ctx, "DELETE", path, query, nil, headers)
|
|
}
|
|
}
|
|
|
|
|
|
-func (cli *Client) sendRequest(ctx context.Context, method, path string, query url.Values, obj interface{}, headers map[string][]string) (*serverResponse, error) {
|
|
|
|
|
|
+func (cli *Client) sendRequest(ctx context.Context, method, path string, query url.Values, obj interface{}, headers map[string][]string) (serverResponse, error) {
|
|
var body io.Reader
|
|
var body io.Reader
|
|
|
|
|
|
if obj != nil {
|
|
if obj != nil {
|
|
var err error
|
|
var err error
|
|
body, err = encodeData(obj)
|
|
body, err = encodeData(obj)
|
|
if err != nil {
|
|
if err != nil {
|
|
- return nil, err
|
|
|
|
|
|
+ return serverResponse{}, err
|
|
}
|
|
}
|
|
if headers == nil {
|
|
if headers == nil {
|
|
headers = make(map[string][]string)
|
|
headers = make(map[string][]string)
|
|
@@ -75,8 +76,8 @@ func (cli *Client) sendRequest(ctx context.Context, method, path string, query u
|
|
return cli.sendClientRequest(ctx, method, path, query, body, headers)
|
|
return cli.sendClientRequest(ctx, method, path, query, body, headers)
|
|
}
|
|
}
|
|
|
|
|
|
-func (cli *Client) sendClientRequest(ctx context.Context, method, path string, query url.Values, body io.Reader, headers map[string][]string) (*serverResponse, error) {
|
|
|
|
- serverResp := &serverResponse{
|
|
|
|
|
|
+func (cli *Client) sendClientRequest(ctx context.Context, method, path string, query url.Values, body io.Reader, headers map[string][]string) (serverResponse, error) {
|
|
|
|
+ serverResp := serverResponse{
|
|
body: nil,
|
|
body: nil,
|
|
statusCode: -1,
|
|
statusCode: -1,
|
|
}
|
|
}
|
|
@@ -105,10 +106,6 @@ func (cli *Client) sendClientRequest(ctx context.Context, method, path string, q
|
|
|
|
|
|
resp, err := cancellable.Do(ctx, cli.transport, req)
|
|
resp, err := cancellable.Do(ctx, cli.transport, req)
|
|
if err != nil {
|
|
if err != nil {
|
|
- if isTimeout(err) || strings.Contains(err.Error(), "connection refused") || strings.Contains(err.Error(), "dial unix") {
|
|
|
|
- return serverResp, ErrConnectionFailed
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
if !cli.transport.Secure() && strings.Contains(err.Error(), "malformed HTTP response") {
|
|
if !cli.transport.Secure() && strings.Contains(err.Error(), "malformed HTTP response") {
|
|
return serverResp, fmt.Errorf("%v.\n* Are you trying to connect to a TLS-enabled daemon without TLS?", err)
|
|
return serverResp, fmt.Errorf("%v.\n* Are you trying to connect to a TLS-enabled daemon without TLS?", err)
|
|
}
|
|
}
|
|
@@ -117,6 +114,23 @@ func (cli *Client) sendClientRequest(ctx context.Context, method, path string, q
|
|
return serverResp, fmt.Errorf("The server probably has client authentication (--tlsverify) enabled. Please check your TLS client certification settings: %v", err)
|
|
return serverResp, fmt.Errorf("The server probably has client authentication (--tlsverify) enabled. Please check your TLS client certification settings: %v", err)
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ // Don't decorate context sentinel errors; users may be comparing to
|
|
|
|
+ // them directly.
|
|
|
|
+ switch err {
|
|
|
|
+ case context.Canceled, context.DeadlineExceeded:
|
|
|
|
+ return serverResp, err
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if err, ok := err.(net.Error); ok {
|
|
|
|
+ if err.Timeout() {
|
|
|
|
+ return serverResp, ErrConnectionFailed
|
|
|
|
+ }
|
|
|
|
+ if !err.Temporary() {
|
|
|
|
+ if strings.Contains(err.Error(), "connection refused") || strings.Contains(err.Error(), "dial unix") {
|
|
|
|
+ return serverResp, ErrConnectionFailed
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
return serverResp, fmt.Errorf("An error occurred trying to connect: %v", err)
|
|
return serverResp, fmt.Errorf("An error occurred trying to connect: %v", err)
|
|
}
|
|
}
|
|
|
|
|
|
@@ -185,23 +199,10 @@ func encodeData(data interface{}) (*bytes.Buffer, error) {
|
|
return params, nil
|
|
return params, nil
|
|
}
|
|
}
|
|
|
|
|
|
-func ensureReaderClosed(response *serverResponse) {
|
|
|
|
- if response != nil && response.body != nil {
|
|
|
|
|
|
+func ensureReaderClosed(response serverResponse) {
|
|
|
|
+ if body := response.body; body != nil {
|
|
// Drain up to 512 bytes and close the body to let the Transport reuse the connection
|
|
// Drain up to 512 bytes and close the body to let the Transport reuse the connection
|
|
- io.CopyN(ioutil.Discard, response.body, 512)
|
|
|
|
|
|
+ io.CopyN(ioutil.Discard, body, 512)
|
|
response.body.Close()
|
|
response.body.Close()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
-
|
|
|
|
-func isTimeout(err error) bool {
|
|
|
|
- type timeout interface {
|
|
|
|
- Timeout() bool
|
|
|
|
- }
|
|
|
|
- e := err
|
|
|
|
- switch urlErr := err.(type) {
|
|
|
|
- case *url.Error:
|
|
|
|
- e = urlErr.Err
|
|
|
|
- }
|
|
|
|
- t, ok := e.(timeout)
|
|
|
|
- return ok && t.Timeout()
|
|
|
|
-}
|
|
|