Repository: moby/moby
File: client/request.go (Go)

Code before:
}

func (cli *Client) doRequest(req *http.Request) (*http.Response, error) {
	resp, err := cli.client.Do(req)
	if err == nil {
		return resp, nil
	}

	if cli.scheme != "https" && strings.Contains(err.Error(), "malformed HTTP response") {
		return nil, errConnectionFailed{fmt.Errorf("%w.\n* Are you trying to connect to a TLS-enabled daemon without TLS?", err)}
	}

	if cli.scheme == "https" && strings.Contains(err.Error(), "bad certificate") {
		return nil, errConnectionFailed{errors.Wrap(err, "the server probably has client authentication (--tlsverify) enabled; check your TLS client certification settings")}
	}

	// Don't decorate context sentinel errors; users may be comparing to
	// them directly.
	if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {
		return nil, err
	}

	if errors.Is(err, os.ErrPermission) {
		// Unwrap the error to remove request errors ("Get "http://%2Fvar%2Frun%2Fdocker.sock/v1.51/version"),
		// which are irrelevant if we weren't able to connect.
		err = errors.Unwrap(err)
		return nil, errConnectionFailed{errors.Wrapf(err, "permission denied while trying to connect to the docker API at %v", cli.host)}
	}
	if errors.Is(err, os.ErrNotExist) {
		// Unwrap the error to remove request errors ("Get "http://%2Fvar%2Frun%2Fdocker.sock/v1.51/version"),
		// which are irrelevant if we weren't able to connect.
		err = errors.Unwrap(err)
		return nil, errConnectionFailed{errors.Wrapf(err, "failed to connect to the docker API at %v; check if the path is correct and if the daemon is running", cli.host)}
	}
	var dnsErr *net.DNSError
	if errors.As(err, &dnsErr) {
		return nil, errConnectionFailed{errors.Wrapf(dnsErr, "failed to connect to the docker API at %v", cli.host)}
	}

	var nErr net.Error
	if errors.As(err, &nErr) {
		// FIXME(thaJeztah): any net.Error should be considered a connection error (but we should include the original error)?
		if nErr.Timeout() {
			return nil, connectionFailed(cli.host)
		}
		if strings.Contains(nErr.Error(), "connection refused") || strings.Contains(nErr.Error(), "dial unix") {
			return nil, connectionFailed(cli.host)
		}
	}

	// Although there's not a strongly typed error for this in go-winio,

Review comment (suggestion):
Should the unwrapping be done conditionally? Or do the `As` and unwrap from that type.

Code after:

	resp, err := cli.doRequest(req)
	switch {
	case errors.Is(err, context.Canceled), errors.Is(err, context.DeadlineExceeded):
		return nil, err
	case err == nil:
		return resp, cli.checkResponseErr(resp)
	default:
		return resp, err
	}
}

func (cli *Client) doRequest(req *http.Request) (*http.Response, error) {
	resp, err := cli.client.Do(req)
	if err == nil {
		return resp, nil
	}

	if cli.scheme != "https" && strings.Contains(err.Error(), "malformed HTTP response") {
		return nil, errConnectionFailed{fmt.Errorf("%w.\n* Are you trying to connect to a TLS-enabled daemon without TLS?", err)}
	}

	if cli.scheme == "https" && strings.Contains(err.Error(), "bad certificate") {
		return nil, errConnectionFailed{errors.Wrap(err, "the server probably has client authentication (--tlsverify) enabled; check your TLS client certification settings")}
	}

	// Don't decorate context sentinel errors; users may be comparing to
	// them directly.
	if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {
		return nil, err
	}

	if errors.Is(err, os.ErrPermission) {
		// Don't include request errors ("Get "http://%2Fvar%2Frun%2Fdocker.sock/v1.51/version"),
		// which are irrelevant if we weren't able to connect.
		return nil, errConnectionFailed{fmt.Errorf("permission denied while trying to connect to the docker API at %v", cli.host)}
	}
	if errors.Is(err, os.ErrNotExist) {
		// Unwrap the error to remove request errors ("Get "http://%2Fvar%2Frun%2Fdocker.sock/v1.51/version"),
		// which are irrelevant if we weren't able to connect.
		err = errors.Unwrap(err)
		return nil, errConnectionFailed{errors.Wrapf(err, "failed to connect to the docker API at %v; check if the path is correct and if the daemon is running", cli.host)}
	}
	var dnsErr *net.DNSError
	if errors.As(err, &dnsErr) {
		return nil, errConnectionFailed{errors.Wrapf(dnsErr, "failed to connect to the docker API at %v", cli.host)}
	}

	var nErr net.Error
	if errors.As(err, &nErr) {
		// FIXME(thaJeztah): any net.Error should be considered a connection error (but we should include the original error)?