| 422 | # `request` method's signature intentionally violates LSP. |
| 423 | # urllib3's API is different from `http.client.HTTPConnection` and the subclassing is only incidental. |
| 424 | def request( # type: ignore[override] |
| 425 | self, |
| 426 | method: str, |
| 427 | url: str, |
| 428 | body: _TYPE_BODY | None = None, |
| 429 | headers: typing.Mapping[str, str] | None = None, |
| 430 | *, |
| 431 | chunked: bool = False, |
| 432 | preload_content: bool = True, |
| 433 | decode_content: bool = True, |
| 434 | enforce_content_length: bool = True, |
| 435 | ) -> None: |
| 436 | # Update the inner socket's timeout value to send the request. |
| 437 | # This only triggers if the connection is re-used. |
| 438 | if self.sock is not None: |
| 439 | self.sock.settimeout(self.timeout) |
| 440 | |
| 441 | # Store these values to be fed into the HTTPResponse |
| 442 | # object later. TODO: Remove this in favor of a real |
| 443 | # HTTP lifecycle mechanism. |
| 444 | |
| 445 | # We have to store these before we call .request() |
| 446 | # because sometimes we can still salvage a response |
| 447 | # off the wire even if we aren't able to completely |
| 448 | # send the request body. |
| 449 | self._response_options = _ResponseOptions( |
| 450 | request_method=method, |
| 451 | request_url=url, |
| 452 | preload_content=preload_content, |
| 453 | decode_content=decode_content, |
| 454 | enforce_content_length=enforce_content_length, |
| 455 | ) |
| 456 | |
| 457 | if headers is None: |
| 458 | headers = {} |
| 459 | header_keys = frozenset(to_str(k.lower()) for k in headers) |
| 460 | skip_accept_encoding = "accept-encoding" in header_keys |
| 461 | skip_host = "host" in header_keys |
| 462 | self.putrequest( |
| 463 | method, url, skip_accept_encoding=skip_accept_encoding, skip_host=skip_host |
| 464 | ) |
| 465 | |
| 466 | # Transform the body into an iterable of sendall()-able chunks |
| 467 | # and detect if an explicit Content-Length is doable. |
| 468 | chunks_and_cl = body_to_chunks(body, method=method, blocksize=self.blocksize) |
| 469 | chunks = chunks_and_cl.chunks |
| 470 | content_length = chunks_and_cl.content_length |
| 471 | |
| 472 | # When chunked is explicit set to 'True' we respect that. |
| 473 | if chunked: |
| 474 | if "transfer-encoding" not in header_keys: |
| 475 | self.putheader("Transfer-Encoding", "chunked") |
| 476 | else: |
| 477 | # Detect whether a framing mechanism is already in use. If so |
| 478 | # we respect that value, otherwise we pick chunked vs content-length |
| 479 | # depending on the type of 'body'. |
| 480 | if "content-length" in header_keys: |
| 481 | chunked = False |