()
| 294 | } |
| 295 | |
| 296 | func (d *duplexHTTPCall) makeRequest() { |
| 297 | // This runs concurrently with Write and CloseWrite. Read and CloseRead wait |
| 298 | // on d.responseReady, so we can't race with them. |
| 299 | defer close(d.responseReady) |
| 300 | |
| 301 | // Promote the header Host to the request object. |
| 302 | if host := getHeaderCanonical(d.request.Header, headerHost); len(host) > 0 { |
| 303 | d.request.Host = host |
| 304 | } |
| 305 | if d.onRequestSend != nil { |
| 306 | d.onRequestSend(d.request) |
| 307 | } |
| 308 | // Once we send a message to the server, they send a message back and |
| 309 | // establish the receive side of the stream. |
| 310 | // On error, we close the request body using the Write side of the pipe. |
| 311 | // This ensures HTTP2 streams receive an io.EOF from the Read side of the |
| 312 | // pipe. Write's check for io.ErrClosedPipe and will convert this to io.EOF. |
| 313 | response, err := d.httpClient.Do(d.request) //nolint:bodyclose |
| 314 | if err != nil { |
| 315 | if errors.Is(err, io.EOF) { |
| 316 | // We use io.EOF as a sentinel in many places and don't want this |
| 317 | // transport error to be confused for those other situations. |
| 318 | err = io.ErrUnexpectedEOF |
| 319 | } |
| 320 | err = wrapIfContextError(err) |
| 321 | err = wrapIfLikelyH2CNotConfiguredError(d.request, err) |
| 322 | err = wrapIfLikelyWithGRPCNotUsedError(err) |
| 323 | err = wrapIfRSTError(d.ctx, err) |
| 324 | if _, ok := asError(err); !ok { |
| 325 | err = NewError(CodeUnavailable, err) |
| 326 | } |
| 327 | d.responseErr = err |
| 328 | _ = d.CloseWrite() |
| 329 | return |
| 330 | } |
| 331 | // We've got a response. We can now read from the response body. |
| 332 | // Closing the response body is delegated to the caller even on error. |
| 333 | d.response = response |
| 334 | if err := d.validateResponse(response); err != nil { |
| 335 | d.responseErr = err |
| 336 | _ = d.CloseWrite() |
| 337 | return |
| 338 | } |
| 339 | if (d.streamType&StreamTypeBidi) == StreamTypeBidi && response.ProtoMajor < 2 { |
| 340 | // If we somehow dialed an HTTP/1.x server, fail with an explicit message |
| 341 | // rather than returning a more cryptic error later on. |
| 342 | d.responseErr = errorf( |
| 343 | CodeUnimplemented, |
| 344 | "response from %v is HTTP/%d.%d: bidi streams require at least HTTP/2", |
| 345 | d.request.URL, |
| 346 | response.ProtoMajor, |
| 347 | response.ProtoMinor, |
| 348 | ) |
| 349 | _ = d.CloseWrite() |
| 350 | } |
| 351 | } |
| 352 | |
| 353 | // getNoBody is a GetBody function for http.NoBody. |
no test coverage detected