MCPcopy
hub / github.com/moby/moby / doRequest

Method doRequest

client/request.go:130–219  ·  view source on GitHub ↗

doRequest sends an HTTP request and returns an HTTP response. It is a wrapper around [http.Client.Do] with extra handling to decorate errors. Otherwise, it behaves identical to [http.Client.Do]; an error is returned when failing to make a connection, On error, any Response can be ignored. A non-2xx

(req *http.Request)

Source from the content-addressed store, hash-verified

128// when failing to make a connection, On error, any Response can be ignored.
129// A non-2xx status code doesn't cause an error.
130func (cli *Client) doRequest(req *http.Request) (*http.Response, error) {
131 resp, err := cli.client.Do(req) // #nosec G704 -- ignore "SSRF via taint analysis"; API client intentionally sends caller-provided requests/URLs.
132 if err == nil {
133 return resp, nil
134 }
135
136 if cli.scheme != "https" && strings.Contains(err.Error(), "malformed HTTP response") {
137 return nil, errConnectionFailed{fmt.Errorf("%w.\n* Are you trying to connect to a TLS-enabled daemon without TLS?", err)}
138 }
139
140 const (
141 // Go 1.25 / TLS 1.3 may produce a generic "handshake failure"
142 // whereas TLS 1.2 may produce a "bad certificate" TLS alert.
143 // See https://github.com/golang/go/issues/56371
144 //
145 // > https://tip.golang.org/doc/go1.12#tls_1_3
146 // >
147 // > In TLS 1.3 the client is the last one to speak in the handshake, so if
148 // > it causes an error to occur on the server, it will be returned on the
149 // > client by the first Read, not by Handshake. For example, that will be
150 // > the case if the server rejects the client certificate.
151 //
152 // https://github.com/golang/go/blob/go1.25.1/src/crypto/tls/alert.go#L71-L72
153 alertBadCertificate = "bad certificate" // go1.24 / TLS 1.2
154 alertHandshakeFailure = "handshake failure" // go1.25 / TLS 1.3
155 )
156
157 // TODO(thaJeztah): see if we can use errors.As for a [crypto/tls.AlertError] instead of bare string matching.
158 if cli.scheme == "https" && (strings.Contains(err.Error(), alertHandshakeFailure) || strings.Contains(err.Error(), alertBadCertificate)) {
159 return nil, errConnectionFailed{fmt.Errorf("the server probably has client authentication (--tlsverify) enabled; check your TLS client certification settings: %w", err)}
160 }
161
162 // Don't decorate context sentinel errors; users may be comparing to
163 // them directly.
164 if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {
165 return nil, err
166 }
167
168 if errors.Is(err, os.ErrPermission) {
169 // Don't include request errors (Get "http://%2Fvar%2Frun%2Fdocker.sock/v1.51/version"),
170 // which are irrelevant if we weren't able to connect.
171 return nil, errConnectionFailed{fmt.Errorf("permission denied while trying to connect to the docker API at %v", cli.host)}
172 }
173 if errors.Is(err, os.ErrNotExist) {
174 // Unwrap the error to remove request errors (Get "http://%2Fvar%2Frun%2Fdocker.sock/v1.51/version"),
175 // which are irrelevant if we weren't able to connect.
176 err = errors.Unwrap(err)
177 return nil, errConnectionFailed{fmt.Errorf("failed to connect to the docker API at %v; check if the path is correct and if the daemon is running: %w", cli.host, err)}
178 }
179 var dnsErr *net.DNSError
180 if errors.As(err, &dnsErr) {
181 return nil, errConnectionFailed{fmt.Errorf("failed to connect to the docker API at %v: %w", cli.host, dnsErr)}
182 }
183
184 var nErr net.Error
185 if errors.As(err, &nErr) {
186 // FIXME(thaJeztah): any net.Error should be considered a connection error (but we should include the original error)?
187 if nErr.Timeout() {

Callers 2

pingMethod · 0.95
sendRequestMethod · 0.95

Calls 11

ErrorMethod · 0.95
connectionFailedFunction · 0.85
ErrorfMethod · 0.80
UnwrapMethod · 0.65
TimeoutMethod · 0.65
OpenMethod · 0.65
CloseMethod · 0.65
DoMethod · 0.45
ContainsMethod · 0.45
ErrorMethod · 0.45
IsMethod · 0.45

Tested by

no test coverage detected