Full Code of encode/httpx for AI

master b5addb64f016 cached
115 files
967.5 KB
256.7k tokens
1151 symbols
1 requests
Download .txt
Showing preview only (1,009K chars total). Download the full file or copy to clipboard to get everything.
Repository: encode/httpx
Branch: master
Commit: b5addb64f016
Files: 115
Total size: 967.5 KB

Directory structure:
gitextract_2vanwzu3/

├── .github/
│   ├── CONTRIBUTING.md
│   ├── FUNDING.yml
│   ├── ISSUE_TEMPLATE/
│   │   ├── 1-issue.md
│   │   └── config.yml
│   ├── PULL_REQUEST_TEMPLATE.md
│   ├── dependabot.yml
│   └── workflows/
│       ├── publish.yml
│       └── test-suite.yml
├── .gitignore
├── CHANGELOG.md
├── LICENSE.md
├── README.md
├── docs/
│   ├── CNAME
│   ├── advanced/
│   │   ├── authentication.md
│   │   ├── clients.md
│   │   ├── event-hooks.md
│   │   ├── extensions.md
│   │   ├── proxies.md
│   │   ├── resource-limits.md
│   │   ├── ssl.md
│   │   ├── text-encodings.md
│   │   ├── timeouts.md
│   │   └── transports.md
│   ├── api.md
│   ├── async.md
│   ├── code_of_conduct.md
│   ├── compatibility.md
│   ├── contributing.md
│   ├── css/
│   │   └── custom.css
│   ├── environment_variables.md
│   ├── exceptions.md
│   ├── http2.md
│   ├── index.md
│   ├── logging.md
│   ├── overrides/
│   │   └── partials/
│   │       └── nav.html
│   ├── quickstart.md
│   ├── third_party_packages.md
│   └── troubleshooting.md
├── httpx/
│   ├── __init__.py
│   ├── __version__.py
│   ├── _api.py
│   ├── _auth.py
│   ├── _client.py
│   ├── _config.py
│   ├── _content.py
│   ├── _decoders.py
│   ├── _exceptions.py
│   ├── _main.py
│   ├── _models.py
│   ├── _multipart.py
│   ├── _status_codes.py
│   ├── _transports/
│   │   ├── __init__.py
│   │   ├── asgi.py
│   │   ├── base.py
│   │   ├── default.py
│   │   ├── mock.py
│   │   └── wsgi.py
│   ├── _types.py
│   ├── _urlparse.py
│   ├── _urls.py
│   ├── _utils.py
│   └── py.typed
├── mkdocs.yml
├── pyproject.toml
├── requirements.txt
├── scripts/
│   ├── build
│   ├── check
│   ├── clean
│   ├── coverage
│   ├── docs
│   ├── install
│   ├── lint
│   ├── publish
│   ├── sync-version
│   └── test
└── tests/
    ├── __init__.py
    ├── client/
    │   ├── __init__.py
    │   ├── test_async_client.py
    │   ├── test_auth.py
    │   ├── test_client.py
    │   ├── test_cookies.py
    │   ├── test_event_hooks.py
    │   ├── test_headers.py
    │   ├── test_properties.py
    │   ├── test_proxies.py
    │   ├── test_queryparams.py
    │   └── test_redirects.py
    ├── common.py
    ├── concurrency.py
    ├── conftest.py
    ├── fixtures/
    │   ├── .netrc
    │   └── .netrc-nopassword
    ├── models/
    │   ├── __init__.py
    │   ├── test_cookies.py
    │   ├── test_headers.py
    │   ├── test_queryparams.py
    │   ├── test_requests.py
    │   ├── test_responses.py
    │   ├── test_url.py
    │   ├── test_whatwg.py
    │   └── whatwg.json
    ├── test_api.py
    ├── test_asgi.py
    ├── test_auth.py
    ├── test_config.py
    ├── test_content.py
    ├── test_decoders.py
    ├── test_exceptions.py
    ├── test_exported_members.py
    ├── test_main.py
    ├── test_multipart.py
    ├── test_status_codes.py
    ├── test_timeouts.py
    ├── test_utils.py
    └── test_wsgi.py

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/CONTRIBUTING.md
================================================
# Contributing

Thank you for being interested in contributing to HTTPX.
There are many ways you can contribute to the project:

- Try HTTPX and [report bugs/issues you find](https://github.com/encode/httpx/issues/new)
- [Implement new features](https://github.com/encode/httpx/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22)
- [Review Pull Requests of others](https://github.com/encode/httpx/pulls)
- Write documentation
- Participate in discussions

## Reporting Bugs or Other Issues

Found something that HTTPX should support?
Stumbled upon some unexpected behaviour?

Contributions should generally start out with [a discussion](https://github.com/encode/httpx/discussions).
Possible bugs may be raised as a "Potential Issue" discussion, feature requests may
be raised as an "Ideas" discussion. We can then determine if the discussion needs
to be escalated into an "Issue" or not, or if we'd consider a pull request.

Try to be more descriptive as you can and in case of a bug report,
provide as much information as possible like:

- OS platform
- Python version
- Installed dependencies and versions (`python -m pip freeze`)
- Code snippet
- Error traceback

You should always try to reduce any examples to the *simplest possible case*
that demonstrates the issue.

Some possibly useful tips for narrowing down potential issues...

- Does the issue exist on HTTP/1.1, or HTTP/2, or both?
- Does the issue exist with `Client`, `AsyncClient`, or both?
- When using `AsyncClient` does the issue exist when using `asyncio` or `trio`, or both?

## Development

To start developing HTTPX create a **fork** of the
[HTTPX repository](https://github.com/encode/httpx) on GitHub.

Then clone your fork with the following command replacing `YOUR-USERNAME` with
your GitHub username:

```shell
$ git clone https://github.com/YOUR-USERNAME/httpx
```

You can now install the project and its dependencies using:

```shell
$ cd httpx
$ scripts/install
```

## Testing and Linting

We use custom shell scripts to automate testing, linting,
and documentation building workflow.

To run the tests, use:

```shell
$ scripts/test
```

!!! warning
    The test suite spawns testing servers on ports **8000** and **8001**.
    Make sure these are not in use, so the tests can run properly.

You can run a single test script like this:

```shell
$ scripts/test -- tests/test_multipart.py
```

To run the code auto-formatting:

```shell
$ scripts/lint
```

Lastly, to run code checks separately (they are also run as part of `scripts/test`), run:

```shell
$ scripts/check
```

## Documenting

Documentation pages are located under the `docs/` folder.

To run the documentation site locally (useful for previewing changes), use:

```shell
$ scripts/docs
```

## Resolving Build / CI Failures

Once you've submitted your pull request, the test suite will automatically run, and the results will show up in GitHub.
If the test suite fails, you'll want to click through to the "Details" link, and try to identify why the test suite failed.

<p align="center" style="margin: 0 0 10px">
  <img src="https://raw.githubusercontent.com/encode/httpx/master/docs/img/gh-actions-fail.png" alt='Failing PR commit status'>
</p>

Here are some common ways the test suite can fail:

### Check Job Failed

<p align="center" style="margin: 0 0 10px">
  <img src="https://raw.githubusercontent.com/encode/httpx/master/docs/img/gh-actions-fail-check.png" alt='Failing GitHub action lint job'>
</p>

This job failing means there is either a code formatting issue or type-annotation issue.
You can look at the job output to figure out why it's failed or within a shell run:

```shell
$ scripts/check
```

It may be worth it to run `$ scripts/lint` to attempt auto-formatting the code
and if that job succeeds commit the changes.

### Docs Job Failed

This job failing means the documentation failed to build. This can happen for
a variety of reasons like invalid markdown or missing configuration within `mkdocs.yml`.

### Python 3.X Job Failed

<p align="center" style="margin: 0 0 10px">
  <img src="https://raw.githubusercontent.com/encode/httpx/master/docs/img/gh-actions-fail-test.png" alt='Failing GitHub action test job'>
</p>

This job failing means the unit tests failed or not all code paths are covered by unit tests.

If tests are failing you will see this message under the coverage report:

`=== 1 failed, 435 passed, 1 skipped, 1 xfailed in 11.09s ===`

If tests succeed but coverage doesn't reach our current threshold, you will see this
message under the coverage report:

`FAIL Required test coverage of 100% not reached. Total coverage: 99.00%`

## Releasing

*This section is targeted at HTTPX maintainers.*

Before releasing a new version, create a pull request that includes:

- **An update to the changelog**:
    - We follow the format from [keepachangelog](https://keepachangelog.com/en/1.0.0/).
    - [Compare](https://github.com/encode/httpx/compare/) `master` with the tag of the latest release, and list all entries that are of interest to our users:
        - Things that **must** go in the changelog: added, changed, deprecated or removed features, and bug fixes.
        - Things that **should not** go in the changelog: changes to documentation, tests or tooling.
        - Try sorting entries in descending order of impact / importance.
        - Keep it concise and to-the-point. 🎯
- **A version bump**: see `__version__.py`.

For an example, see [#1006](https://github.com/encode/httpx/pull/1006).

Once the release PR is merged, create a
[new release](https://github.com/encode/httpx/releases/new) including:

- Tag version like `0.13.3`.
- Release title `Version 0.13.3`
- Description copied from the changelog.

Once created this release will be automatically uploaded to PyPI.

If something goes wrong with the PyPI job the release can be published using the
`scripts/publish` script.

## Development proxy setup

To test and debug requests via a proxy it's best to run a proxy server locally.
Any server should do but HTTPCore's test suite uses
[`mitmproxy`](https://mitmproxy.org/) which is written in Python, it's fully
featured and has excellent UI and tools for introspection of requests.

You can install `mitmproxy` using `pip install mitmproxy` or [several
other ways](https://docs.mitmproxy.org/stable/overview-installation/).

`mitmproxy` does require setting up local TLS certificates for HTTPS requests,
as its main purpose is to allow developers to inspect requests that pass through
it. We can set them up follows:

1. [`pip install trustme-cli`](https://github.com/sethmlarson/trustme-cli/).
2. `trustme-cli -i example.org www.example.org`, assuming you want to test
connecting to that domain, this will create three files: `server.pem`,
`server.key` and `client.pem`.
3. `mitmproxy` requires a PEM file that includes the private key and the
certificate so we need to concatenate them:
`cat server.key server.pem > server.withkey.pem`.
4. Start the proxy server `mitmproxy --certs server.withkey.pem`, or use the
[other mitmproxy commands](https://docs.mitmproxy.org/stable/) with different
UI options.

At this point the server is ready to start serving requests, you'll need to
configure HTTPX as described in the
[proxy section](https://www.python-httpx.org/advanced/#http-proxying) and
the [SSL certificates section](https://www.python-httpx.org/advanced/#ssl-certificates),
this is where our previously generated `client.pem` comes in:

```
import httpx

ssl_context = httpx.SSLContext()
ssl_context.load_verify_locations("/path/to/client.pem")

with httpx.Client(proxy="http://127.0.0.1:8080/", ssl_context=ssl_context) as client:
    response = client.get("https://example.org")
    print(response.status_code)  # should print 200
```

Note, however, that HTTPS requests will only succeed to the host specified
in the SSL/TLS certificate we generated, HTTPS requests to other hosts will
raise an error like:

```
ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate
verify failed: Hostname mismatch, certificate is not valid for
'duckduckgo.com'. (_ssl.c:1108)
```

If you want to make requests to more hosts you'll need to regenerate the
certificates and include all the hosts you intend to connect to in the
seconds step, i.e.

`trustme-cli -i example.org www.example.org duckduckgo.com www.duckduckgo.com`


================================================
FILE: .github/FUNDING.yml
================================================
github: encode


================================================
FILE: .github/ISSUE_TEMPLATE/1-issue.md
================================================
---
name: Issue
about: Please only raise an issue if you've been advised to do so after discussion. Thanks! 🙏
---

The starting point for issues should usually be a discussion...

https://github.com/encode/httpx/discussions

Possible bugs may be raised as a "Potential Issue" discussion, feature requests may be raised as an "Ideas" discussion. We can then determine if the discussion needs to be escalated into an "Issue" or not.

This will help us ensure that the "Issues" list properly reflects ongoing or needed work on the project.

---

- [ ] Initially raised as discussion #...


================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
# Ref: https://help.github.com/en/github/building-a-strong-community/configuring-issue-templates-for-your-repository#configuring-the-template-chooser
blank_issues_enabled: false
contact_links:
- name: Discussions
  url: https://github.com/encode/httpx/discussions
  about: >
    The "Discussions" forum is where you want to start. 💖
- name: Chat
  url: https://gitter.im/encode/community
  about: >
    Our community chat forum.


================================================
FILE: .github/PULL_REQUEST_TEMPLATE.md
================================================
<!-- Thanks for contributing to HTTPX! 💚
Given this is a project maintained by volunteers, please read this template to not waste your time, or ours! 😁 -->

# Summary

<!-- Write a small summary about what is happening here. -->

# Checklist

- [ ] I understand that this PR may be closed in case there was no previous discussion. (This doesn't apply to typos!)
- [ ] I've added a test for each change that was introduced, and I tried as much as possible to make a single atomic change.
- [ ] I've updated the documentation accordingly.


================================================
FILE: .github/dependabot.yml
================================================
version: 2
updates:
  - package-ecosystem: "pip"
    directory: "/"
    schedule:
      interval: "monthly"
    groups:
      python-packages:
        patterns:
          - "*"
  - package-ecosystem: "github-actions"
    directory: "/"
    schedule: 
      interval: monthly


================================================
FILE: .github/workflows/publish.yml
================================================
name: Publish

on:
  push:
    tags:
      - '*'

jobs:
  publish:
    name: "Publish release"
    runs-on: "ubuntu-latest"

    environment:
       name: deploy

    steps:
      - uses: "actions/checkout@v4"
      - uses: "actions/setup-python@v6"
        with:
          python-version: 3.9
      - name: "Install dependencies"
        run: "scripts/install"
      - name: "Build package & docs"
        run: "scripts/build"
      - name: "Publish to PyPI & deploy docs"
        run: "scripts/publish"
        env:
          TWINE_USERNAME: __token__
          TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }}


================================================
FILE: .github/workflows/test-suite.yml
================================================
---
name: Test Suite

on:
  push:
    branches: ["master"]
  pull_request:
    branches: ["master", "version-*"]

jobs:
  tests:
    name: "Python ${{ matrix.python-version }}"
    runs-on: "ubuntu-latest"

    strategy:
      matrix:
        python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]

    steps:
      - uses: "actions/checkout@v4"
      - uses: "actions/setup-python@v6"
        with:
          python-version: "${{ matrix.python-version }}"
          allow-prereleases: true
      - name: "Install dependencies"
        run: "scripts/install"
      - name: "Run linting checks"
        run: "scripts/check"
      - name: "Build package & docs"
        run: "scripts/build"
      - name: "Run tests"
        run: "scripts/test"
      - name: "Enforce coverage"
        run: "scripts/coverage"


================================================
FILE: .gitignore
================================================
*.pyc
.coverage
.pytest_cache/
.mypy_cache/
__pycache__/
htmlcov/
site/
*.egg-info/
venv*/
.python-version
build/
dist/


================================================
FILE: CHANGELOG.md
================================================
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).

## [UNRELEASED]

### Removed

* Drop support for Python 3.8

### Added

* Expose `FunctionAuth` from the public API. (#3699)

## 0.28.1 (6th December, 2024)

* Fix SSL case where `verify=False` together with client side certificates.
 
## 0.28.0 (28th November, 2024)

Be aware that the default *JSON request bodies now use a more compact representation*. This is generally considered a prefered style, tho may require updates to test suites.

The 0.28 release includes a limited set of deprecations...

**Deprecations**:

We are working towards a simplified SSL configuration API.

*For users of the standard `verify=True` or `verify=False` cases, or `verify=<ssl_context>` case this should require no changes. The following cases have been deprecated...*

* The `verify` argument as a string argument is now deprecated and will raise warnings.
* The `cert` argument is now deprecated and will raise warnings.

Our revised [SSL documentation](docs/advanced/ssl.md) covers how to implement the same behaviour with a more constrained API.

**The following changes are also included**:

* The deprecated `proxies` argument has now been removed.
* The deprecated `app` argument has now been removed.
* JSON request bodies use a compact representation. (#3363)
* Review URL percent escape sets, based on WHATWG spec. (#3371, #3373)
* Ensure `certifi` and `httpcore` are only imported if required. (#3377)
* Treat `socks5h` as a valid proxy scheme. (#3178)
* Cleanup `Request()` method signature in line with `client.request()` and `httpx.request()`. (#3378)
* Bugfix: When passing `params={}`, always strictly update rather than merge with an existing querystring. (#3364)

## 0.27.2 (27th August, 2024)

### Fixed

* Reintroduced supposedly-private `URLTypes` shortcut. (#2673)

## 0.27.1 (27th August, 2024)

### Added

* Support for `zstd` content decoding using the python `zstandard` package is added. Installable using `httpx[zstd]`. (#3139)

### Fixed

* Improved error messaging for `InvalidURL` exceptions. (#3250)
* Fix `app` type signature in `ASGITransport`. (#3109)

## 0.27.0 (21st February, 2024)

### Deprecated

* The `app=...` shortcut has been deprecated. Use the explicit style of `transport=httpx.WSGITransport()` or `transport=httpx.ASGITransport()` instead.

### Fixed

* Respect the `http1` argument while configuring proxy transports. (#3023)
* Fix RFC 2069 mode digest authentication. (#3045)

## 0.26.0 (20th December, 2023)

### Added

* The `proxy` argument was added. You should use the `proxy` argument instead of the deprecated `proxies`, or use `mounts=` for more complex configurations. (#2879)

### Deprecated

* The `proxies` argument is now deprecated. It will still continue to work, but it will be removed in the future. (#2879)

### Fixed

* Fix cases of double escaping of URL path components. Allow / as a safe character in the query portion. (#2990)
* Handle `NO_PROXY` envvar cases when a fully qualified URL is supplied as the value. (#2741)
* Allow URLs where username or password contains unescaped '@'. (#2986)
* Ensure ASGI `raw_path` does not include URL query component. (#2999)
* Ensure `Response.iter_text()` cannot yield empty strings. (#2998)

## 0.25.2 (24th November, 2023)

### Added

* Add missing type hints to few `__init__()` methods. (#2938)

## 0.25.1 (3rd November, 2023)

### Added

* Add support for Python 3.12. (#2854)
* Add support for httpcore 1.0 (#2885)

### Fixed

* Raise `ValueError` on `Response.encoding` being set after `Response.text` has been accessed. (#2852)

## 0.25.0 (11th September, 2023)

### Removed

* Drop support for Python 3.7. (#2813)

### Added

* Support HTTPS proxies. (#2845)
* Change the type of `Extensions` from `Mapping[Str, Any]` to `MutableMapping[Str, Any]`. (#2803)
* Add `socket_options` argument to `httpx.HTTPTransport` and `httpx.AsyncHTTPTransport` classes. (#2716)
* The `Response.raise_for_status()` method now returns the response instance. For example: `data = httpx.get('...').raise_for_status().json()`. (#2776)

### Fixed

* Return `500` error response instead of exceptions when `raise_app_exceptions=False` is set on `ASGITransport`. (#2669)
* Ensure all `WSGITransport` environs have a `SERVER_PROTOCOL`. (#2708)
* Always encode forward slashes as `%2F` in query parameters (#2723)
* Use Mozilla documentation instead of `httpstatuses.com` for HTTP error reference (#2768)

## 0.24.1 (17th May, 2023)

### Added

* Provide additional context in some `InvalidURL` exceptions. (#2675)

### Fixed

* Fix optional percent-encoding behaviour. (#2671)
* More robust checking for opening upload files in binary mode. (#2630)
* Properly support IP addresses in `NO_PROXY` environment variable. (#2659)
* Set default file for `NetRCAuth()` to `None` to use the stdlib default. (#2667)
* Set logging request lines to INFO level for async requests, in line with sync requests. (#2656)
* Fix which gen-delims need to be escaped for path/query/fragment components in URL. (#2701)

## 0.24.0 (6th April, 2023)

### Changed

* The logging behaviour has been changed to be more in-line with other standard Python logging usages. We no longer have a custom `TRACE` log level, and we no longer use the `HTTPX_LOG_LEVEL` environment variable to auto-configure logging. We now have a significant amount of `DEBUG` logging available at the network level. Full documentation is available at https://www.python-httpx.org/logging/ (#2547, encode/httpcore#648)
* The `Response.iter_lines()` method now matches the stdlib behaviour and does not include the newline characters. It also resolves a performance issue. (#2423)
* Query parameter encoding switches from using + for spaces and %2F for forward slash, to instead using %20 for spaces and treating forward slash as a safe, unescaped character. This differs from `requests`, but is in line with browser behavior in Chrome, Safari, and Firefox. Both options are RFC valid. (#2543)
* NetRC authentication is no longer automatically handled, but is instead supported by an explicit `httpx.NetRCAuth()` authentication class. See the documentation at https://www.python-httpx.org/advanced/authentication/#netrc-authentication (#2525)

### Removed

* The `rfc3986` dependancy has been removed. (#2252)

## 0.23.3 (4th January, 2023)

### Fixed

* Version 0.23.2 accidentally included stricter type checking on query parameters. This shouldn've have been included in a minor version bump, and is now reverted. (#2523, #2539)

## 0.23.2 (2nd January, 2023)

### Added

* Support digest auth nonce counting to avoid multiple auth requests. (#2463)

### Fixed

* Multipart file uploads where the file length cannot be determine now use chunked transfer encoding, rather than loading the entire file into memory in order to determine the `Content-Length`. (#2382)
* Raise `TypeError` if content is passed a dict-instance. (#2495)
* Partially revert the API breaking change in 0.23.1, which removed `RawURL`. We continue to expose a `url.raw` property which is now a plain named-tuple. This API is still expected to be deprecated, but we will do so with a major version bump. (#2481)

## 0.23.1 (18th November, 2022)

**Note**: The 0.23.1 release should have used a proper version bump, rather than a minor point release.
There are API surface area changes that may affect some users.
See the "Removed" section of these release notes for details.

### Added

* Support for Python 3.11. (#2420)
* Allow setting an explicit multipart boundary in `Content-Type` header. (#2278)
* Allow `tuple` or `list` for multipart values, not just `list`. (#2355)
* Allow `str` content for multipart upload files. (#2400)
* Support connection upgrades. See https://www.encode.io/httpcore/extensions/#upgrade-requests

### Fixed

* Don't drop empty query parameters. (#2354)

### Removed

* Upload files *must* always be opened in binary mode. (#2400)
* Drop `.read`/`.aread` from `SyncByteStream`/`AsyncByteStream`. (#2407)
* Drop `RawURL`. (#2241)

## 0.23.0 (23rd May, 2022)

### Changed

* Drop support for Python 3.6. (#2097)
* Use `utf-8` as the default character set, instead of falling back to `charset-normalizer` for auto-detection. To enable automatic character set detection, see [the documentation](https://www.python-httpx.org/advanced/text-encodings/#using-auto-detection). (#2165)

### Fixed

* Fix `URL.copy_with` for some oddly formed URL cases. (#2185)
* Digest authentication should use case-insensitive comparison for determining which algorithm is being used. (#2204)
* Fix console markup escaping in command line client. (#1866)
* When files are used in multipart upload, ensure we always seek to the start of the file. (#2065)
* Ensure that `iter_bytes` never yields zero-length chunks. (#2068)
* Preserve `Authorization` header for redirects that are to the same origin, but are an `http`-to-`https` upgrade. (#2074)
* When responses have binary output, don't print the output to the console in the command line client. Use output like `<16086 bytes of binary data>` instead. (#2076)
* Fix display of `--proxies` argument in the command line client help. (#2125)
* Close responses when task cancellations occur during stream reading. (#2156)
* Fix type error on accessing `.request` on `HTTPError` exceptions. (#2158)

## 0.22.0 (26th January, 2022)

### Added

* Support for [the SOCKS5 proxy protocol](https://www.python-httpx.org/advanced/proxies/#socks) via [the `socksio` package](https://github.com/sethmlarson/socksio). (#2034)
* Support for custom headers in multipart/form-data requests (#1936)

### Fixed

* Don't perform unreliable close/warning on `__del__` with unclosed clients. (#2026)
* Fix `Headers.update(...)` to correctly handle repeated headers (#2038)

## 0.21.3 (6th January, 2022)

### Fixed

* Fix streaming uploads using `SyncByteStream` or `AsyncByteStream`. Regression in 0.21.2. (#2016)

## 0.21.2 (5th January, 2022)

### Fixed

* HTTP/2 support for tunnelled proxy cases. (#2009)
* Improved the speed of large file uploads. (#1948)

## 0.21.1 (16th November, 2021)

### Fixed

* The `response.url` property is now correctly annotated as `URL`, instead of `Optional[URL]`. (#1940)

## 0.21.0 (15th November, 2021)

The 0.21.0 release integrates against a newly redesigned `httpcore` backend.

Both packages ought to automatically update to the required versions, but if you are
seeing any issues, you should ensure that you have `httpx==0.21.*` and `httpcore==0.14.*` installed.

### Added

* The command-line client will now display connection information when `-v/--verbose` is used.
* The command-line client will now display server certificate information when `-v/--verbose` is used.
* The command-line client is now able to properly detect if the outgoing request
should be formatted as HTTP/1.1 or HTTP/2, based on the result of the HTTP/2 negotiation.

### Removed

* Curio support is no longer currently included. Please get in touch if you require this, so that we can assess priorities.

## 0.20.0 (13th October, 2021)

The 0.20.0 release adds an integrated command-line client, and also includes some
design changes. The most notable of these is that redirect responses are no longer
automatically followed, unless specifically requested.

This design decision prioritises a more explicit approach to redirects, in order
to avoid code that unintentionally issues multiple requests as a result of
misconfigured URLs.

For example, previously a client configured to send requests to `http://api.github.com/`
would end up sending every API request twice, as each request would be redirected to `https://api.github.com/`.

If you do want auto-redirect behaviour, you can enable this either by configuring
the client instance with `Client(follow_redirects=True)`, or on a per-request
basis, with `.get(..., follow_redirects=True)`.

This change is a classic trade-off between convenience and precision, with no "right"
answer. See [discussion #1785](https://github.com/encode/httpx/discussions/1785) for more
context.

The other major design change is an update to the Transport API, which is the low-level
interface against which requests are sent. Previously this interface used only primitive
datastructures, like so...

```python
(status_code, headers, stream, extensions) = transport.handle_request(method, url, headers, stream, extensions)
try
    ...
finally:
    stream.close()
```

Now the interface is much simpler...

```python
response = transport.handle_request(request)
try
    ...
finally:
    response.close()
```

### Changed

* The `allow_redirects` flag is now `follow_redirects` and defaults to `False`.
* The `raise_for_status()` method will now raise an exception for any responses
  except those with 2xx status codes. Previously only 4xx and 5xx status codes
  would result in an exception.
* The low-level transport API changes to the much simpler `response = transport.handle_request(request)`.
* The `client.send()` method no longer accepts a `timeout=...` argument, but the
  `client.build_request()` does. This required by the signature change of the
  Transport API. The request timeout configuration is now stored on the request
  instance, as `request.extensions['timeout']`.

### Added

* Added the `httpx` command-line client.
* Response instances now include `.is_informational`, `.is_success`, `.is_redirect`, `.is_client_error`, and `.is_server_error`
  properties for checking 1xx, 2xx, 3xx, 4xx, and 5xx response types. Note that the behaviour of `.is_redirect` is slightly different in that it now returns True for all 3xx responses, in order to allow for a consistent set of properties onto the different HTTP status code types. The `response.has_redirect_location` location may be used to determine responses with properly formed URL redirects.

### Fixed

* `response.iter_bytes()` no longer raises a ValueError when called on a response with no content. (Pull #1827)
* The `'wsgi.error'` configuration now defaults to `sys.stderr`, and is corrected to be a `TextIO` interface, not a `BytesIO` interface. Additionally, the WSGITransport now accepts a `wsgi_error` configuration. (Pull #1828)
* Follow the WSGI spec by properly closing the iterable returned by the application. (Pull #1830)

## 0.19.0 (19th August, 2021)

### Added

* Add support for `Client(allow_redirects=<bool>)`. (Pull #1790)
* Add automatic character set detection, when no `charset` is included in the response `Content-Type` header. (Pull #1791)

### Changed

* Event hooks are now also called for any additional redirect or auth requests/responses. (Pull #1806)
* Strictly enforce that upload files must be opened in binary mode. (Pull #1736)
* Strictly enforce that client instances can only be opened and closed once, and cannot be re-opened. (Pull #1800)
* Drop `mode` argument from `httpx.Proxy(..., mode=...)`. (Pull #1795)

## 0.18.2 (17th June, 2021)

### Added

* Support for Python 3.10. (Pull #1687)
* Expose `httpx.USE_CLIENT_DEFAULT`, used as the default to `auth` and `timeout` parameters in request methods. (Pull #1634)
* Support [HTTP/2 "prior knowledge"](https://python-hyper.org/projects/hyper-h2/en/v2.3.1/negotiating-http2.html#prior-knowledge), using `httpx.Client(http1=False, http2=True)`. (Pull #1624)

### Fixed

* Clean up some cases where warnings were being issued. (Pull #1687)
* Prefer Content-Length over Transfer-Encoding: chunked for content=<file-like> cases. (Pull #1619)

## 0.18.1 (29th April, 2021)

### Changed

* Update brotli support to use the `brotlicffi` package (Pull #1605)
* Ensure that `Request(..., stream=...)` does not auto-generate any headers on the request instance. (Pull #1607)

### Fixed

* Pass through `timeout=...` in top-level httpx.stream() function. (Pull #1613)
* Map httpcore transport close exceptions to httpx exceptions. (Pull #1606)

## 0.18.0 (27th April, 2021)

The 0.18.x release series formalises our low-level Transport API, introducing the base classes `httpx.BaseTransport` and `httpx.AsyncBaseTransport`.

See the "[Custom transports](https://www.python-httpx.org/advanced/transports/#custom-transports)" documentation and the [`httpx.BaseTransport.handle_request()`](https://github.com/encode/httpx/blob/397aad98fdc8b7580a5fc3e88f1578b4302c6382/httpx/_transports/base.py#L77-L147) docstring for more complete details on implementing custom transports.

Pull request #1522 includes a checklist of differences from the previous `httpcore` transport API, for developers implementing custom transports.

The following API changes have been issuing deprecation warnings since 0.17.0 onwards, and are now fully deprecated...

* You should now use httpx.codes consistently instead of httpx.StatusCodes.
* Use limits=... instead of pool_limits=....
* Use proxies={"http://": ...} instead of proxies={"http": ...} for scheme-specific mounting.

### Changed

* Transport instances now inherit from `httpx.BaseTransport` or `httpx.AsyncBaseTransport`,
  and should implement either the `handle_request` method or `handle_async_request` method. (Pull #1522, #1550)
* The `response.ext` property and `Response(ext=...)` argument are now named `extensions`. (Pull #1522)
* The recommendation to not use `data=<bytes|str|bytes (a)iterator>` in favour of `content=<bytes|str|bytes (a)iterator>` has now been escalated to a deprecation warning. (Pull #1573)
* Drop `Response(on_close=...)` from API, since it was a bit of leaking implementation detail. (Pull #1572)
* When using a client instance, cookies should always be set on the client, rather than on a per-request basis. We prefer enforcing a stricter API here because it provides clearer expectations around cookie persistence, particularly when redirects occur. (Pull #1574)
* The runtime exception `httpx.ResponseClosed` is now named `httpx.StreamClosed`. (#1584)
* The `httpx.QueryParams` model now presents an immutable interface. There is a discussion on [the design and motivation here](https://github.com/encode/httpx/discussions/1599). Use `client.params = client.params.merge(...)` instead of `client.params.update(...)`. The basic query manipulation methods are `query.set(...)`, `query.add(...)`, and `query.remove()`. (#1600)

### Added

* The `Request` and `Response` classes can now be serialized using pickle. (#1579)
* Handle `data={"key": [None|int|float|bool]}` cases. (Pull #1539)
* Support `httpx.URL(**kwargs)`, for example `httpx.URL(scheme="https", host="www.example.com", path="/')`, or `httpx.URL("https://www.example.com/", username="tom@gmail.com", password="123 456")`. (Pull #1601)
* Support `url.copy_with(params=...)`. (Pull #1601)
* Add `url.params` parameter, returning an immutable `QueryParams` instance. (Pull #1601)
* Support query manipulation methods on the URL class. These are `url.copy_set_param()`, `url.copy_add_param()`, `url.copy_remove_param()`, `url.copy_merge_params()`. (Pull #1601)
* The `httpx.URL` class now performs port normalization, so `:80` ports are stripped from `http` URLs and `:443` ports are stripped from `https` URLs. (Pull #1603)
* The `URL.host` property returns unicode strings for internationalized domain names. The `URL.raw_host` property returns byte strings with IDNA escaping applied. (Pull #1590)

### Fixed

* Fix Content-Length for cases of `files=...` where unicode string is used as the file content. (Pull #1537)
* Fix some cases of merging relative URLs against `Client(base_url=...)`. (Pull #1532)
* The `request.content` attribute is now always available except for streaming content, which requires an explicit `.read()`. (Pull #1583)

## 0.17.1 (March 15th, 2021)

### Fixed

* Type annotation on `CertTypes` allows `keyfile` and `password` to be optional. (Pull #1503)
* Fix httpcore pinned version. (Pull #1495)

## 0.17.0 (February 28th, 2021)

### Added

* Add `httpx.MockTransport()`, allowing to mock out a transport using pre-determined responses. (Pull #1401, Pull #1449)
* Add `httpx.HTTPTransport()` and `httpx.AsyncHTTPTransport()` default transports. (Pull #1399)
* Add mount API support, using `httpx.Client(mounts=...)`. (Pull #1362)
* Add `chunk_size` parameter to `iter_raw()`, `iter_bytes()`, `iter_text()`. (Pull #1277)
* Add `keepalive_expiry` parameter to `httpx.Limits()` configuration. (Pull #1398)
* Add repr to `httpx.Cookies` to display available cookies. (Pull #1411)
* Add support for `params=<tuple>` (previously only `params=<list>` was supported). (Pull #1426)

### Fixed

* Add missing `raw_path` to ASGI scope. (Pull #1357)
* Tweak `create_ssl_context` defaults to use `trust_env=True`. (Pull #1447)
* Properly URL-escape WSGI `PATH_INFO`. (Pull #1391)
* Properly set default ports in WSGI transport. (Pull #1469)
* Properly encode slashes when using `base_url`. (Pull #1407)
* Properly map exceptions in `request.aclose()`. (Pull #1465)

## 0.16.1 (October 8th, 2020)

### Fixed

* Support literal IPv6 addresses in URLs. (Pull #1349)
* Force lowercase headers in ASGI scope dictionaries. (Pull #1351)

## 0.16.0 (October 6th, 2020)

### Changed

* Preserve HTTP header casing. (Pull #1338, encode/httpcore#216, python-hyper/h11#104)
* Drop `response.next()` and `response.anext()` methods in favour of `response.next_request` attribute. (Pull #1339)
* Closed clients now raise a runtime error if attempting to send a request. (Pull #1346)

### Added

* Add Python 3.9 to officially supported versions.
* Type annotate `__enter__`/`__exit__`/`__aenter__`/`__aexit__` in a way that supports subclasses of `Client` and `AsyncClient`. (Pull #1336)

## 0.15.5 (October 1st, 2020)

### Added

* Add `response.next_request` (Pull #1334)

## 0.15.4 (September 25th, 2020)

### Added

* Support direct comparisons between `Headers` and dicts or lists of two-tuples. Eg. `assert response.headers == {"Content-Length": 24}` (Pull #1326)

### Fixed

* Fix automatic `.read()` when `Response` instances are created with `content=<str>` (Pull #1324)

## 0.15.3 (September 24th, 2020)

### Fixed

* Fixed connection leak in async client due to improper closing of response streams. (Pull #1316)

## 0.15.2 (September 23nd, 2020)

### Fixed

* Fixed `response.elapsed` property. (Pull #1313)
* Fixed client authentication interaction with `.stream()`. (Pull #1312)

## 0.15.1 (September 23nd, 2020)

### Fixed

* ASGITransport now properly applies URL decoding to the `path` component, as-per the ASGI spec. (Pull #1307)

## 0.15.0 (September 22nd, 2020)

### Added

* Added support for curio. (Pull https://github.com/encode/httpcore/pull/168)
* Added support for event hooks. (Pull #1246)
* Added support for authentication flows which require either sync or async I/O. (Pull #1217)
* Added support for monitoring download progress with `response.num_bytes_downloaded`. (Pull #1268)
* Added `Request(content=...)` for byte content, instead of overloading `Request(data=...)` (Pull #1266)
* Added support for all URL components as parameter names when using `url.copy_with(...)`. (Pull #1285)
* Neater split between automatically populated headers on `Request` instances, vs default `client.headers`. (Pull #1248)
* Unclosed `AsyncClient` instances will now raise warnings if garbage collected. (Pull #1197)
* Support `Response(content=..., text=..., html=..., json=...)` for creating usable response instances in code. (Pull #1265, #1297)
* Support instantiating requests from the low-level transport API. (Pull #1293)
* Raise errors on invalid URL types. (Pull #1259)

### Changed

* Cleaned up expected behaviour for URL escaping. `url.path` is now URL escaped. (Pull #1285)
* Cleaned up expected behaviour for bytes vs str in URL components. `url.userinfo` and `url.query` are not URL escaped, and so return bytes. (Pull #1285)
* Drop `url.authority` property in favour of `url.netloc`, since "authority" was semantically incorrect. (Pull #1285)
* Drop `url.full_path` property in favour of `url.raw_path`, for better consistency with other parts of the API. (Pull #1285)
* No longer use the `chardet` library for auto-detecting charsets, instead defaulting to a simpler approach when no charset is specified. (#1269)

### Fixed

* Swapped ordering of redirects and authentication flow. (Pull #1267)
* `.netrc` lookups should use host, not host+port. (Pull #1298)

### Removed

* The `URLLib3Transport` class no longer exists. We've published it instead as an example of [a custom transport class](https://gist.github.com/florimondmanca/d56764d78d748eb9f73165da388e546e). (Pull #1182)
* Drop `request.timer` attribute, which was being used internally to set `response.elapsed`. (Pull #1249)
* Drop `response.decoder` attribute, which was being used internally. (Pull #1276)
* `Request.prepare()` is now a private method. (Pull #1284)
* The `Headers.getlist()` method had previously been deprecated in favour of `Headers.get_list()`. It is now fully removed.
* The `QueryParams.getlist()` method had previously been deprecated in favour of `QueryParams.get_list()`. It is now fully removed.
* The `URL.is_ssl` property had previously been deprecated in favour of `URL.scheme == "https"`. It is now fully removed.
* The `httpx.PoolLimits` class had previously been deprecated in favour of `httpx.Limits`. It is now fully removed.
* The `max_keepalive` setting had previously been deprecated in favour of the more explicit `max_keepalive_connections`. It is now fully removed.
* The verbose `httpx.Timeout(5.0, connect_timeout=60.0)` style had previously been deprecated in favour of `httpx.Timeout(5.0, connect=60.0)`. It is now fully removed.
* Support for instantiating a timeout config missing some defaults, such as `httpx.Timeout(connect=60.0)`, had previously been deprecated in favour of enforcing a more explicit style, such as `httpx.Timeout(5.0, connect=60.0)`. This is now strictly enforced.

## 0.14.3 (September 2nd, 2020)

### Added

* `http.Response()` may now be instantiated without a `request=...` parameter. Useful for some unit testing cases. (Pull #1238)
* Add `103 Early Hints` and `425 Too Early` status codes. (Pull #1244)

### Fixed

* `DigestAuth` now handles responses that include multiple 'WWW-Authenticate' headers. (Pull #1240)
* Call into transport `__enter__`/`__exit__` or `__aenter__`/`__aexit__` when client is used in a context manager style. (Pull #1218)

## 0.14.2 (August 24th, 2020)

### Added

* Support `client.get(..., auth=None)` to bypass the default authentication on a clients. (Pull #1115)
* Support `client.auth = ...` property setter. (Pull #1185)
* Support `httpx.get(..., proxies=...)` on top-level request functions. (Pull #1198)
* Display instances with nicer import styles. (Eg. <httpx.ReadTimeout ...>) (Pull #1155)
* Support `cookies=[(key, value)]` list-of-two-tuples style usage. (Pull #1211)

### Fixed

* Ensure that automatically included headers on a request may be modified. (Pull #1205)
* Allow explicit `Content-Length` header on streaming requests. (Pull #1170)
* Handle URL quoted usernames and passwords properly. (Pull #1159)
* Use more consistent default for `HEAD` requests, setting `allow_redirects=True`. (Pull #1183)
* If a transport error occurs while streaming the response, raise an `httpx` exception, not the underlying `httpcore` exception. (Pull #1190)
* Include the underlying `httpcore` traceback, when transport exceptions occur. (Pull #1199)

## 0.14.1 (August 11th, 2020)

### Added

* The `httpx.URL(...)` class now raises `httpx.InvalidURL` on invalid URLs, rather than exposing the underlying `rfc3986` exception. If a redirect response includes an invalid 'Location' header, then a `RemoteProtocolError` exception is raised, which will be associated with the request that caused it. (Pull #1163)

### Fixed

* Handling multiple `Set-Cookie` headers became broken in the 0.14.0 release, and is now resolved. (Pull #1156)

## 0.14.0 (August 7th, 2020)

The 0.14 release includes a range of improvements to the public API, intended on preparing for our upcoming 1.0 release.

* Our HTTP/2 support is now fully optional. **You now need to use `pip install httpx[http2]` if you want to include the HTTP/2 dependencies.**
* Our HSTS support has now been removed. Rewriting URLs from `http` to `https` if the host is on the HSTS list can be beneficial in avoiding roundtrips to incorrectly formed URLs, but on balance we've decided to remove this feature, on the principle of least surprise. Most programmatic clients do not include HSTS support, and for now we're opting to remove our support for it.
* Our exception hierarchy has been overhauled. Most users will want to stick with their existing `httpx.HTTPError` usage, but we've got a clearer overall structure now. See https://www.python-httpx.org/exceptions/ for more details.

When upgrading you should be aware of the following public API changes. Note that deprecated usages will currently continue to function, but will issue warnings.

* You should now use `httpx.codes` consistently instead of `httpx.StatusCodes`.
* Usage of `httpx.Timeout()` should now always include an explicit default. Eg. `httpx.Timeout(None, pool=5.0)`.
* When using `httpx.Timeout()`, we now have more concisely named keyword arguments. Eg. `read=5.0`, instead of `read_timeout=5.0`.
* Use `httpx.Limits()` instead of `httpx.PoolLimits()`, and `limits=...` instead of `pool_limits=...`.
* The `httpx.Limits(max_keepalive=...)` argument is now deprecated in favour of a more explicit `httpx.Limits(max_keepalive_connections=...)`.
* Keys used with `Client(proxies={...})` should now be in the style of `{"http://": ...}`, rather than `{"http": ...}`.
* The multidict methods `Headers.getlist()` and `QueryParams.getlist()` are deprecated in favour of more consistent `.get_list()` variants.
* The `URL.is_ssl` property is deprecated in favour of `URL.scheme == "https"`.
* The `URL.join(relative_url=...)` method is now `URL.join(url=...)`. This change does not support warnings for the deprecated usage style.

One notable aspect of the 0.14.0 release is that it tightens up the public API for `httpx`, by ensuring that several internal attributes and methods have now become strictly private.

The following previously had nominally public names on the client, but were all undocumented and intended solely for internal usage. They are all now replaced with underscored names, and should not be relied on or accessed.

These changes should not affect users who have been working from the `httpx` documentation.

* `.merge_url()`, `.merge_headers()`, `.merge_cookies()`, `.merge_queryparams()`
* `.build_auth()`, `.build_redirect_request()`
* `.redirect_method()`, `.redirect_url()`, `.redirect_headers()`, `.redirect_stream()`
* `.send_handling_redirects()`, `.send_handling_auth()`, `.send_single_request()`
* `.init_transport()`, `.init_proxy_transport()`
* `.proxies`, `.transport`, `.netrc`, `.get_proxy_map()`

See pull requests #997, #1065, #1071.

Some areas of API which were already on the deprecation path, and were raising warnings or errors in 0.13.x have now been escalated to being fully removed.

* Drop `ASGIDispatch`, `WSGIDispatch`, which have been replaced by `ASGITransport`, `WSGITransport`.
* Drop `dispatch=...`` on client, which has been replaced by `transport=...``
* Drop `soft_limit`, `hard_limit`, which have been replaced by `max_keepalive` and `max_connections`.
* Drop `Response.stream` and` `Response.raw`, which have been replaced by ``.aiter_bytes` and `.aiter_raw`.
* Drop `proxies=<transport instance>` in favor of `proxies=httpx.Proxy(...)`.

See pull requests #1057, #1058.

### Added

* Added dedicated exception class `httpx.HTTPStatusError` for `.raise_for_status()` exceptions. (Pull #1072)
* Added `httpx.create_ssl_context()` helper function. (Pull #996)
* Support for proxy exclusions like `proxies={"https://www.example.com": None}`. (Pull #1099)
* Support `QueryParams(None)` and `client.params = None`. (Pull #1060)

### Changed

* Use `httpx.codes` consistently in favour of `httpx.StatusCodes` which is placed into deprecation. (Pull #1088)
* Usage of `httpx.Timeout()` should now always include an explicit default. Eg. `httpx.Timeout(None, pool=5.0)`. (Pull #1085)
* Switch to more concise `httpx.Timeout()` keyword arguments. Eg. `read=5.0`, instead of `read_timeout=5.0`. (Pull #1111)
* Use `httpx.Limits()` instead of `httpx.PoolLimits()`, and `limits=...` instead of `pool_limits=...`. (Pull #1113)
* Keys used with `Client(proxies={...})` should now be in the style of `{"http://": ...}`, rather than `{"http": ...}`. (Pull #1127)
* The multidict methods `Headers.getlist` and `QueryParams.getlist` are deprecated in favour of more consistent `.get_list()` variants. (Pull #1089)
* `URL.port` becomes `Optional[int]`. Now only returns a port if one is explicitly included in the URL string. (Pull #1080)
* The `URL(..., allow_relative=[bool])` parameter no longer exists. All URL instances may be relative. (Pull #1073)
* Drop unnecessary `url.full_path = ...` property setter. (Pull #1069)
* The `URL.join(relative_url=...)` method is now `URL.join(url=...)`. (Pull #1129)
* The `URL.is_ssl` property is deprecated in favour of `URL.scheme == "https"`. (Pull #1128)

### Fixed

* Add missing `Response.next()` method. (Pull #1055)
* Ensure all exception classes are exposed as public API. (Pull #1045)
* Support multiple items with an identical field name in multipart encodings. (Pull #777)
* Skip HSTS preloading on single-label domains. (Pull #1074)
* Fixes for `Response.iter_lines()`. (Pull #1033, #1075)
* Ignore permission errors when accessing `.netrc` files. (Pull #1104)
* Allow bare hostnames in `HTTP_PROXY` etc... environment variables. (Pull #1120)
* Settings `app=...` or `transport=...` bypasses any environment based proxy defaults. (Pull #1122)
* Fix handling of `.base_url` when a path component is included in the base URL. (Pull #1130)

---

## 0.13.3 (May 29th, 2020)

### Fixed

* Include missing keepalive expiry configuration. (Pull #1005)
* Improved error message when URL redirect has a custom scheme. (Pull #1002)

## 0.13.2 (May 27th, 2020)

### Fixed

* Include explicit "Content-Length: 0" on POST, PUT, PATCH if no request body is used. (Pull #995)
* Add `http2` option to `httpx.Client`. (Pull #982)
* Tighten up API typing in places. (Pull #992, #999)

## 0.13.1 (May 22nd, 2020)

### Fixed

* Fix pool options deprecation warning. (Pull #980)
* Include `httpx.URLLib3ProxyTransport` in top-level API. (Pull #979)

## 0.13.0 (May 22nd, 2020)

This release switches to `httpcore` for all the internal networking, which means:

* We're using the same codebase for both our sync and async clients.
* HTTP/2 support is now available with the sync client.
* We no longer have a `urllib3` dependency for our sync client, although there is still an *optional* `URLLib3Transport` class.

It also means we've had to remove our UDS support, since maintaining that would have meant having to push back our work towards a 1.0 release, which isn't a trade-off we wanted to make.

We also now have [a public "Transport API"](https://www.python-httpx.org/advanced/transports/#custom-transports), which you can use to implement custom transport implementations against. This formalises and replaces our previously private "Dispatch API".

### Changed

* Use `httpcore` for underlying HTTP transport. Drop `urllib3` requirement. (Pull #804, #967)
* Rename pool limit options from `soft_limit`/`hard_limit` to `max_keepalive`/`max_connections`. (Pull #968)
* The previous private "Dispatch API" has now been promoted to a public "Transport API". When customizing the transport use `transport=...`. The `ASGIDispatch` and `WSGIDispatch` class naming is deprecated in favour of `ASGITransport` and `WSGITransport`. (Pull #963)

### Added

* Added `URLLib3Transport` class for optional `urllib3` transport support. (Pull #804, #963)
* Streaming multipart uploads. (Pull #857)
* Logging via HTTPCORE_LOG_LEVEL and HTTPX_LOG_LEVEL environment variables
and TRACE level logging. (Pull encode/httpcore#79)

### Fixed

* Performance improvement in brotli decoder. (Pull #906)
* Proper warning level of deprecation notice in `Response.stream` and `Response.raw`. (Pull #908)
* Fix support for generator based WSGI apps. (Pull #887)
* Reuse of connections on HTTP/2 in close concurrency situations. (Pull encode/httpcore#81)
* Honor HTTP/2 max concurrent streams settings (Pull encode/httpcore#89, encode/httpcore#90)
* Fix bytes support in multipart uploads. (Pull #974)
* Improve typing support for `files=...`. (Pull #976)

### Removed

* Dropped support for `Client(uds=...)` (Pull #804)

## 0.13.0.dev2 (May 12th, 2020)

The 0.13.0.dev2 is a *pre-release* version. To install it, use `pip install httpx --pre`.

### Added

* Logging via HTTPCORE_LOG_LEVEL and HTTPX_LOG_LEVEL environment variables
and TRACE level logging. (HTTPCore Pull #79)

### Fixed

* Reuse of connections on HTTP/2 in close concurrency situations. (HTTPCore Pull #81)
* When using an `app=<ASGI app>` observe neater disconnect behaviour instead of sending empty body messages. (Pull #919)

## 0.13.0.dev1 (May 6th, 2020)

The 0.13.0.dev1 is a *pre-release* version. To install it, use `pip install httpx --pre`.

### Fixed

* Passing `http2` flag to proxy dispatchers. (Pull #934)
* Use [`httpcore` v0.8.3](https://github.com/encode/httpcore/releases/tag/0.8.3)
which addresses problems in handling of headers when using proxies.

## 0.13.0.dev0 (April 30th, 2020)

The 0.13.0.dev0 is a *pre-release* version. To install it, use `pip install httpx --pre`.

This release switches to `httpcore` for all the internal networking, which means:

* We're using the same codebase for both our sync and async clients.
* HTTP/2 support is now available with the sync client.
* We no longer have a `urllib3` dependency for our sync client, although there is still an *optional* `URLLib3Dispatcher` class.

It also means we've had to remove our UDS support, since maintaining that would have meant having to push back our work towards a 1.0 release, which isn't a trade-off we wanted to make.

### Changed

* Use `httpcore` for underlying HTTP transport. Drop `urllib3` requirement. (Pull #804)

### Added

* Added `URLLib3Dispatcher` class for optional `urllib3` transport support. (Pull #804)
* Streaming multipart uploads. (Pull #857)

### Fixed

* Performance improvement in brotli decoder. (Pull #906)
* Proper warning level of deprecation notice in `Response.stream` and `Response.raw`. (Pull #908)
* Fix support for generator based WSGI apps. (Pull #887)

### Removed

* Dropped support for `Client(uds=...)` (Pull #804)

---

## 0.12.1 (March 19th, 2020)

### Fixed

* Resolved packaging issue, where additional files were being included.

## 0.12.0 (March 9th, 2020)

The 0.12 release tightens up the API expectations for `httpx` by switching to private module names to enforce better clarity around public API.

All imports of `httpx` should import from the top-level package only, such as `from httpx import Request`, rather than importing from privately namespaced modules such as `from httpx._models import Request`.

### Added

* Support making response body available to auth classes with `.requires_response_body`. (Pull #803)
* Export `NetworkError` exception. (Pull #814)
* Add support for `NO_PROXY` environment variable. (Pull #835)

### Changed

* Switched to private module names. (Pull #785)
* Drop redirect looping detection and the `RedirectLoop` exception, instead using `TooManyRedirects`. (Pull #819)
* Drop `backend=...` parameter on `AsyncClient`, in favour of always autodetecting `trio`/`asyncio`. (Pull #791)

### Fixed

* Support basic auth credentials in proxy URLs. (Pull #780)
* Fix `httpx.Proxy(url, mode="FORWARD_ONLY")` configuration. (Pull #788)
* Fallback to setting headers as UTF-8 if no encoding is specified. (Pull #820)
* Close proxy dispatches classes on client close. (Pull #826)
* Support custom `cert` parameters even if `verify=False`. (Pull #796)
* Don't support invalid dict-of-dicts form data in `data=...`. (Pull #811)

---

## 0.11.1 (January 17th, 2020)

### Fixed

* Fixed usage of `proxies=...` on `Client()`. (Pull #763)
* Support both `zlib` and `deflate` style encodings on `Content-Encoding: deflate`. (Pull #758)
* Fix for streaming a redirect response body with `allow_redirects=False`. (Pull #766)
* Handle redirect with malformed Location headers missing host. (Pull #774)

## 0.11.0 (January 9th, 2020)

The 0.11 release reintroduces our sync support, so that `httpx` now supports both a standard thread-concurrency API, and an async API.

Existing async `httpx` users that are upgrading to 0.11 should ensure that:

* Async codebases should always use a client instance to make requests, instead of the top-level API.
* The async client is named as `httpx.AsyncClient()`, instead of `httpx.Client()`.
* When instantiating proxy configurations use the `httpx.Proxy()` class, instead of the previous `httpx.HTTPProxy()`. This new configuration class works for configuring both sync and async clients.

We believe the API is now pretty much stable, and are aiming for a 1.0 release sometime on or before April 2020.

### Changed

- Top level API such as `httpx.get(url, ...)`, `httpx.post(url, ...)`, `httpx.request(method, url, ...)` becomes synchronous.
- Added `httpx.Client()` for synchronous clients, with `httpx.AsyncClient` being used for async clients.
- Switched to `proxies=httpx.Proxy(...)` for proxy configuration.
- Network connection errors are wrapped in `httpx.NetworkError`, rather than exposing lower-level exception types directly.

### Removed

- The `request.url.origin` property and `httpx.Origin` class are no longer available.
- The per-request `cert`, `verify`, and `trust_env` arguments are escalated from raising errors if used, to no longer being available. These arguments should be used on a per-client instance instead, or in the top-level API.
- The `stream` argument has escalated from raising an error when used, to no longer being available. Use the `client.stream(...)` or `httpx.stream()` streaming API instead.

### Fixed

- Redirect loop detection matches against `(method, url)` rather than `url`. (Pull #734)

---

## 0.10.1 (December 31st, 2019)

### Fixed

- Fix issue with concurrent connection acquisition. (Pull #700)
- Fix write error on closing HTTP/2 connections. (Pull #699)

## 0.10.0 (December 29th, 2019)

The 0.10.0 release makes some changes that will allow us to support both sync and async interfaces.

In particular with streaming responses the `response.read()` method becomes `response.aread()`, and the `response.close()` method becomes `response.aclose()`.

If following redirects explicitly the `response.next()` method becomes `response.anext()`.

### Fixed

- End HTTP/2 streams immediately on no-body requests, rather than sending an empty body message. (Pull #682)
- Improve typing for `Response.request`: switch from `Optional[Request]` to `Request`. (Pull #666)
- `Response.elapsed` now reflects the entire download time. (Pull #687, #692)

### Changed

- Added `AsyncClient` as a synonym for `Client`. (Pull #680)
- Switch to `response.aread()` for conditionally reading streaming responses. (Pull #674)
- Switch to `response.aclose()` and `client.aclose()` for explicit closing. (Pull #674, #675)
- Switch to `response.anext()` for resolving the next redirect response. (Pull #676)

### Removed

- When using a client instance, the per-request usage of `verify`, `cert`, and `trust_env` have now escalated from raising a warning to raising an error. You should set these arguments on the client instead. (Pull #617)
- Removed the undocumented `request.read()`, since end users should not require it.

---

## 0.9.5 (December 20th, 2019)

### Fixed

- Fix Host header and HSTS rewrites when an explicit `:80` port is included in URL. (Pull #649)
- Query Params on the URL string are merged with any `params=...` argument. (Pull #653)
- More robust behavior when closing connections. (Pull #640)
- More robust behavior when handling HTTP/2 headers with trailing whitespace. (Pull #637)
- Allow any explicit `Content-Type` header to take precedence over the encoding default. (Pull #633)

## 0.9.4 (December 12th, 2019)

### Fixed

- Added expiry to Keep-Alive connections, resolving issues with acquiring connections. (Pull #627)
- Increased flow control windows on HTTP/2, resolving download speed issues. (Pull #629)

## 0.9.3 (December 7th, 2019)

### Fixed

- Fixed HTTP/2 with autodetection backend. (Pull #614)

## 0.9.2 (December 7th, 2019)

* Released due to packaging build artifact.

## 0.9.1 (December 6th, 2019)

* Released due to packaging build artifact.

## 0.9.0 (December 6th, 2019)

The 0.9 releases brings some major new features, including:

* A new streaming API.
* Autodetection of either asyncio or trio.
* Nicer timeout configuration.
* HTTP/2 support off by default, but can be enabled.

We've also removed all private types from the top-level package export.

In order to ensure you are only ever working with public API you should make
sure to only import the top-level package eg. `import httpx`, rather than
importing modules within the package.

### Added

- Added concurrency backend autodetection. (Pull #585)
- Added `Client(backend='trio')` and `Client(backend='asyncio')` API. (Pull #585)
- Added `response.stream_lines()` API. (Pull #575)
- Added `response.is_error` API. (Pull #574)
- Added support for `timeout=Timeout(5.0, connect_timeout=60.0)` styles. (Pull #593)

### Fixed

- Requests or Clients with `timeout=None` now correctly always disable timeouts. (Pull #592)
- Request 'Authorization' headers now have priority over `.netrc` authentication info. (Commit 095b691)
- Files without a filename no longer set a Content-Type in multipart data. (Commit ed94950)

### Changed

- Added `httpx.stream()` API. Using `stream=True` now results in a warning. (Pull #600, #610)
- HTTP/2 support is switched to "off by default", but can be enabled explicitly. (Pull #584)
- Switched to `Client(http2=True)` API from `Client(http_versions=["HTTP/1.1", "HTTP/2"])`. (Pull #586)
- Removed all private types from the top-level package export. (Pull #608)
- The SSL configuration settings of `verify`, `cert`, and `trust_env` now raise warnings if used per-request when using a Client instance. They should always be set on the Client instance itself. (Pull #597)
- Use plain strings "TUNNEL_ONLY" or "FORWARD_ONLY" on the HTTPProxy `proxy_mode` argument. The `HTTPProxyMode` enum still exists, but its usage will raise warnings. (#610)
- Pool timeouts are now on the timeout configuration, not the pool limits configuration. (Pull #563)
- The timeout configuration is now named `httpx.Timeout(...)`, not `httpx.TimeoutConfig(...)`. The old version currently remains as a synonym for backwards compatibility.  (Pull #591)

---

## 0.8.0 (November 27, 2019)

### Removed

- The synchronous API has been removed, in order to allow us to fundamentally change how we approach supporting both sync and async variants. (See #588 for more details.)

---

## 0.7.8 (November 17, 2019)

### Added

- Add support for proxy tunnels for Python 3.6 + asyncio. (Pull #521)

## 0.7.7 (November 15, 2019)

### Fixed

- Resolve an issue with cookies behavior on redirect requests. (Pull #529)

### Added

- Add request/response DEBUG logs. (Pull #502)
- Use TRACE log level for low level info. (Pull #500)

## 0.7.6 (November 2, 2019)

### Removed

- Drop `proxies` parameter from the high-level API. (Pull #485)

### Fixed

- Tweak multipart files: omit null filenames, add support for `str` file contents. (Pull #482)
- Cache NETRC authentication per-client. (Pull #400)
- Rely on `getproxies` for all proxy environment variables. (Pull #470)
- Wait for the `asyncio` stream to close when closing a connection. (Pull #494)

## 0.7.5 (October 10, 2019)

### Added

- Allow lists of values to be passed to `params`. (Pull #386)
- `ASGIDispatch`, `WSGIDispatch` are now available in the `httpx.dispatch` namespace. (Pull #407)
- `HTTPError` is now available in the `httpx` namespace.  (Pull #421)
- Add support for `start_tls()` to the Trio concurrency backend. (Pull #467)

### Fixed

- Username and password are no longer included in the `Host` header when basic authentication
  credentials are supplied via the URL. (Pull #417)

### Removed

- The `.delete()` function no longer has `json`, `data`, or `files` parameters
  to match the expected semantics of the `DELETE` method. (Pull #408)
- Removed the `trio` extra. Trio support is detected automatically. (Pull #390)

## 0.7.4 (September 25, 2019)

### Added

- Add Trio concurrency backend. (Pull #276)
- Add `params` parameter to `Client` for setting default query parameters. (Pull #372)
- Add support for `SSL_CERT_FILE` and `SSL_CERT_DIR` environment variables. (Pull #307)
- Add debug logging to calls into ASGI apps. (Pull #371)
- Add debug logging to SSL configuration. (Pull #378)

### Fixed

- Fix a bug when using `Client` without timeouts in Python 3.6. (Pull #383)
- Propagate `Client` configuration to HTTP proxies. (Pull #377)

## 0.7.3 (September 20, 2019)

### Added

- HTTP Proxy support. (Pulls #259, #353)
- Add Digest authentication. (Pull #332)
- Add `.build_request()` method to `Client` and `AsyncClient`. (Pull #319)
- Add `.elapsed` property on responses. (Pull #351)
- Add support for `SSLKEYLOGFILE` in Python 3.8b4+. (Pull #301)

### Removed

- Drop NPN support for HTTP version negotiation. (Pull #314)

### Fixed

- Fix distribution of type annotations for mypy (Pull #361).
- Set `Host` header when redirecting cross-origin. (Pull #321)
- Drop `Content-Length` headers on `GET` redirects. (Pull #310)
- Raise `KeyError` if header isn't found in `Headers`. (Pull #324)
- Raise `NotRedirectResponse` in `response.next()` if there is no redirection to perform. (Pull #297)
- Fix bug in calculating the HTTP/2 maximum frame size. (Pull #153)

## 0.7.2 (August 28, 2019)

- Enforce using `httpx.AsyncioBackend` for the synchronous client. (Pull #232)
- `httpx.ConnectionPool` will properly release a dropped connection. (Pull #230)
- Remove the `raise_app_exceptions` argument from `Client`. (Pull #238)
- `DecodeError` will no longer be raised for an empty body encoded with Brotli. (Pull #237)
- Added `http_versions` parameter to `Client`. (Pull #250)
- Only use HTTP/1.1 on short-lived connections like `httpx.get()`. (Pull #284)
- Convert `Client.cookies` and `Client.headers` when set as a property. (Pull #274)
- Setting `HTTPX_DEBUG=1` enables debug logging on all requests. (Pull #277)

## 0.7.1 (August 18, 2019)

- Include files with source distribution to be installable. (Pull #233)

## 0.7.0 (August 17, 2019)

- Add the `trust_env` property to `BaseClient`. (Pull #187)
- Add the `links` property to `BaseResponse`. (Pull #211)
- Accept `ssl.SSLContext` instances into `SSLConfig(verify=...)`. (Pull #215)
- Add `Response.stream_text()` with incremental encoding detection. (Pull #183)
- Properly updated the `Host` header when a redirect changes the origin. (Pull #199)
- Ignore invalid `Content-Encoding` headers. (Pull #196)
- Use `~/.netrc` and `~/_netrc` files by default when `trust_env=True`. (Pull #189)
- Create exception base class `HTTPError` with `request` and `response` properties. (Pull #162)
- Add HSTS preload list checking within `BaseClient` to upgrade HTTP URLs to HTTPS. (Pull #184)
- Switch IDNA encoding from IDNA 2003 to IDNA 2008. (Pull #161)
- Expose base classes for alternate concurrency backends. (Pull #178)
- Improve Multipart parameter encoding. (Pull #167)
- Add the `headers` property to `BaseClient`. (Pull #159)
- Add support for Google's `brotli` library. (Pull #156)
- Remove deprecated TLS versions (TLSv1 and TLSv1.1) from default `SSLConfig`. (Pull #155)
- Fix `URL.join(...)` to work similarly to RFC 3986 URL joining. (Pull #144)

---

## 0.6.8 (July 25, 2019)

- Check for disconnections when searching for an available
  connection in `ConnectionPool.keepalive_connections` (Pull #145)
- Allow string comparison for `URL` objects (Pull #139)
- Add HTTP status codes 418 and 451 (Pull #135)
- Add support for client certificate passwords (Pull #118)
- Enable post-handshake client cert authentication for TLSv1.3 (Pull #118)
- Disable using `commonName` for hostname checking for OpenSSL 1.1.0+ (Pull #118)
- Detect encoding for `Response.json()` (Pull #116)

## 0.6.7 (July 8, 2019)

- Check for connection aliveness on re-acquisition (Pull #111)

## 0.6.6 (July 3, 2019)

- Improve `USER_AGENT` (Pull #110)
- Add `Connection: keep-alive` by default to HTTP/1.1 connections. (Pull #110)

## 0.6.5 (June 27, 2019)

- Include `Host` header by default. (Pull #109)
- Improve HTTP protocol detection. (Pull #107)

## 0.6.4 (June 25, 2019)

- Implement read and write timeouts (Pull #104)

## 0.6.3 (June 24, 2019)

- Handle early connection closes (Pull #103)

## 0.6.2 (June 23, 2019)

- Use urllib3's `DEFAULT_CIPHERS` for the `SSLConfig` object. (Pull #100)

## 0.6.1 (June 21, 2019)

- Add support for setting a `base_url` on the `Client`.

## 0.6.0 (June 21, 2019)

- Honor `local_flow_control_window` for HTTP/2 connections (Pull #98)


================================================
FILE: LICENSE.md
================================================
Copyright © 2019, [Encode OSS Ltd](https://www.encode.io/).
All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

* Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


================================================
FILE: README.md
================================================
<p align="center">
  <a href="https://www.python-httpx.org/"><img width="350" height="208" src="https://raw.githubusercontent.com/encode/httpx/master/docs/img/butterfly.png" alt='HTTPX'></a>
</p>

<p align="center"><strong>HTTPX</strong> <em>- A next-generation HTTP client for Python.</em></p>

<p align="center">
<a href="https://github.com/encode/httpx/actions">
    <img src="https://github.com/encode/httpx/workflows/Test%20Suite/badge.svg" alt="Test Suite">
</a>
<a href="https://pypi.org/project/httpx/">
    <img src="https://badge.fury.io/py/httpx.svg" alt="Package version">
</a>
</p>

HTTPX is a fully featured HTTP client library for Python 3. It includes **an integrated command line client**, has support for both **HTTP/1.1 and HTTP/2**, and provides both **sync and async APIs**.

---

Install HTTPX using pip:

```shell
$ pip install httpx
```

Now, let's get started:

```pycon
>>> import httpx
>>> r = httpx.get('https://www.example.org/')
>>> r
<Response [200 OK]>
>>> r.status_code
200
>>> r.headers['content-type']
'text/html; charset=UTF-8'
>>> r.text
'<!doctype html>\n<html>\n<head>\n<title>Example Domain</title>...'
```

Or, using the command-line client.

```shell
$ pip install 'httpx[cli]'  # The command line client is an optional dependency.
```

Which now allows us to use HTTPX directly from the command-line...

<p align="center">
  <img width="700" src="docs/img/httpx-help.png" alt='httpx --help'>
</p>

Sending a request...

<p align="center">
  <img width="700" src="docs/img/httpx-request.png" alt='httpx http://httpbin.org/json'>
</p>

## Features

HTTPX builds on the well-established usability of `requests`, and gives you:

* A broadly [requests-compatible API](https://www.python-httpx.org/compatibility/).
* An integrated command-line client.
* HTTP/1.1 [and HTTP/2 support](https://www.python-httpx.org/http2/).
* Standard synchronous interface, but with [async support if you need it](https://www.python-httpx.org/async/).
* Ability to make requests directly to [WSGI applications](https://www.python-httpx.org/advanced/transports/#wsgi-transport) or [ASGI applications](https://www.python-httpx.org/advanced/transports/#asgi-transport).
* Strict timeouts everywhere.
* Fully type annotated.
* 100% test coverage.

Plus all the standard features of `requests`...

* International Domains and URLs
* Keep-Alive & Connection Pooling
* Sessions with Cookie Persistence
* Browser-style SSL Verification
* Basic/Digest Authentication
* Elegant Key/Value Cookies
* Automatic Decompression
* Automatic Content Decoding
* Unicode Response Bodies
* Multipart File Uploads
* HTTP(S) Proxy Support
* Connection Timeouts
* Streaming Downloads
* .netrc Support
* Chunked Requests

## Installation

Install with pip:

```shell
$ pip install httpx
```

Or, to include the optional HTTP/2 support, use:

```shell
$ pip install httpx[http2]
```

HTTPX requires Python 3.9+.

## Documentation

Project documentation is available at [https://www.python-httpx.org/](https://www.python-httpx.org/).

For a run-through of all the basics, head over to the [QuickStart](https://www.python-httpx.org/quickstart/).

For more advanced topics, see the [Advanced Usage](https://www.python-httpx.org/advanced/) section, the [async support](https://www.python-httpx.org/async/) section, or the [HTTP/2](https://www.python-httpx.org/http2/) section.

The [Developer Interface](https://www.python-httpx.org/api/) provides a comprehensive API reference.

To find out about tools that integrate with HTTPX, see [Third Party Packages](https://www.python-httpx.org/third_party_packages/).

## Contribute

If you want to contribute with HTTPX check out the [Contributing Guide](https://www.python-httpx.org/contributing/) to learn how to start.

## Dependencies

The HTTPX project relies on these excellent libraries:

* `httpcore` - The underlying transport implementation for `httpx`.
  * `h11` - HTTP/1.1 support.
* `certifi` - SSL certificates.
* `idna` - Internationalized domain name support.
* `sniffio` - Async library autodetection.

As well as these optional installs:

* `h2` - HTTP/2 support. *(Optional, with `httpx[http2]`)*
* `socksio` - SOCKS proxy support. *(Optional, with `httpx[socks]`)*
* `rich` - Rich terminal support. *(Optional, with `httpx[cli]`)*
* `click` - Command line client support. *(Optional, with `httpx[cli]`)*
* `brotli` or `brotlicffi` - Decoding for "brotli" compressed responses. *(Optional, with `httpx[brotli]`)*
* `zstandard` - Decoding for "zstd" compressed responses. *(Optional, with `httpx[zstd]`)*

A huge amount of credit is due to `requests` for the API layout that
much of this work follows, as well as to `urllib3` for plenty of design
inspiration around the lower-level networking details.

---

<p align="center"><i>HTTPX is <a href="https://github.com/encode/httpx/blob/master/LICENSE.md">BSD licensed</a> code.<br/>Designed & crafted with care.</i><br/>&mdash; 🦋 &mdash;</p>


================================================
FILE: docs/CNAME
================================================
www.python-httpx.org


================================================
FILE: docs/advanced/authentication.md
================================================
Authentication can either be included on a per-request basis...

```pycon
>>> auth = httpx.BasicAuth(username="username", password="secret")
>>> client = httpx.Client()
>>> response = client.get("https://www.example.com/", auth=auth)
```

Or configured on the client instance, ensuring that all outgoing requests will include authentication credentials...

```pycon
>>> auth = httpx.BasicAuth(username="username", password="secret")
>>> client = httpx.Client(auth=auth)
>>> response = client.get("https://www.example.com/")
```

## Basic authentication

HTTP basic authentication is an unencrypted authentication scheme that uses a simple encoding of the username and password in the request `Authorization` header. Since it is unencrypted it should typically only be used over `https`, although this is not strictly enforced.

```pycon
>>> auth = httpx.BasicAuth(username="finley", password="secret")
>>> client = httpx.Client(auth=auth)
>>> response = client.get("https://httpbin.org/basic-auth/finley/secret")
>>> response
<Response [200 OK]>
```

## Digest authentication

HTTP digest authentication is a challenge-response authentication scheme. Unlike basic authentication it provides encryption, and can be used over unencrypted `http` connections. It requires an additional round-trip in order to negotiate the authentication. 

```pycon
>>> auth = httpx.DigestAuth(username="olivia", password="secret")
>>> client = httpx.Client(auth=auth)
>>> response = client.get("https://httpbin.org/digest-auth/auth/olivia/secret")
>>> response
<Response [200 OK]>
>>> response.history
[<Response [401 UNAUTHORIZED]>]
```

## NetRC authentication

HTTPX can be configured to use [a `.netrc` config file](https://everything.curl.dev/usingcurl/netrc) for authentication.

The `.netrc` config file allows authentication credentials to be associated with specified hosts. When a request is made to a host that is found in the netrc file, the username and password will be included using HTTP basic authentication.

Example `.netrc` file:

```
machine example.org
login example-username
password example-password

machine python-httpx.org
login other-username
password other-password
```

Some examples of configuring `.netrc` authentication with `httpx`.

Use the default `.netrc` file in the users home directory:

```pycon
>>> auth = httpx.NetRCAuth()
>>> client = httpx.Client(auth=auth)
```

Use an explicit path to a `.netrc` file:

```pycon
>>> auth = httpx.NetRCAuth(file="/path/to/.netrc")
>>> client = httpx.Client(auth=auth)
```

Use the `NETRC` environment variable to configure a path to the `.netrc` file,
or fallback to the default.

```pycon
>>> auth = httpx.NetRCAuth(file=os.environ.get("NETRC"))
>>> client = httpx.Client(auth=auth)
```

The `NetRCAuth()` class uses [the `netrc.netrc()` function from the Python standard library](https://docs.python.org/3/library/netrc.html). See the documentation there for more details on exceptions that may be raised if the `.netrc` file is not found, or cannot be parsed.

## Custom authentication schemes

When issuing requests or instantiating a client, the `auth` argument can be used to pass an authentication scheme to use. The `auth` argument may be one of the following...

* A two-tuple of `username`/`password`, to be used with basic authentication.
* An instance of `httpx.BasicAuth()`, `httpx.DigestAuth()`, or `httpx.NetRCAuth()`.
* A callable, accepting a request and returning an authenticated request instance.
* An instance of subclasses of `httpx.Auth`.

The most involved of these is the last, which allows you to create authentication flows involving one or more requests. A subclass of `httpx.Auth` should implement `def auth_flow(request)`, and yield any requests that need to be made...

```python
class MyCustomAuth(httpx.Auth):
    def __init__(self, token):
        self.token = token

    def auth_flow(self, request):
        # Send the request, with a custom `X-Authentication` header.
        request.headers['X-Authentication'] = self.token
        yield request
```

If the auth flow requires more than one request, you can issue multiple yields, and obtain the response in each case...

```python
class MyCustomAuth(httpx.Auth):
    def __init__(self, token):
        self.token = token

    def auth_flow(self, request):
      response = yield request
      if response.status_code == 401:
          # If the server issues a 401 response then resend the request,
          # with a custom `X-Authentication` header.
          request.headers['X-Authentication'] = self.token
          yield request
```

Custom authentication classes are designed to not perform any I/O, so that they may be used with both sync and async client instances. If you are implementing an authentication scheme that requires the request body, then you need to indicate this on the class using a `requires_request_body` property.

You will then be able to access `request.content` inside the `.auth_flow()` method.

```python
class MyCustomAuth(httpx.Auth):
    requires_request_body = True

    def __init__(self, token):
        self.token = token

    def auth_flow(self, request):
      response = yield request
      if response.status_code == 401:
          # If the server issues a 401 response then resend the request,
          # with a custom `X-Authentication` header.
          request.headers['X-Authentication'] = self.sign_request(...)
          yield request

    def sign_request(self, request):
        # Create a request signature, based on `request.method`, `request.url`,
        # `request.headers`, and `request.content`.
        ...
```

Similarly, if you are implementing a scheme that requires access to the response body, then use the `requires_response_body` property.   You will then be able to access response body properties and methods such as `response.content`, `response.text`, `response.json()`, etc.

```python
class MyCustomAuth(httpx.Auth):
    requires_response_body = True

    def __init__(self, access_token, refresh_token, refresh_url):
        self.access_token = access_token
        self.refresh_token = refresh_token
        self.refresh_url = refresh_url

    def auth_flow(self, request):
        request.headers["X-Authentication"] = self.access_token
        response = yield request

        if response.status_code == 401:
            # If the server issues a 401 response, then issue a request to
            # refresh tokens, and resend the request.
            refresh_response = yield self.build_refresh_request()
            self.update_tokens(refresh_response)

            request.headers["X-Authentication"] = self.access_token
            yield request

    def build_refresh_request(self):
        # Return an `httpx.Request` for refreshing tokens.
        ...

    def update_tokens(self, response):
        # Update the `.access_token` and `.refresh_token` tokens
        # based on a refresh response.
        data = response.json()
        ...
```

If you _do_ need to perform I/O other than HTTP requests, such as accessing a disk-based cache, or you need to use concurrency primitives, such as locks, then you should override `.sync_auth_flow()` and `.async_auth_flow()` (instead of `.auth_flow()`). The former will be used by `httpx.Client`, while the latter will be used by `httpx.AsyncClient`.

```python
import asyncio
import threading
import httpx


class MyCustomAuth(httpx.Auth):
    def __init__(self):
        self._sync_lock = threading.RLock()
        self._async_lock = asyncio.Lock()

    def sync_get_token(self):
        with self._sync_lock:
            ...

    def sync_auth_flow(self, request):
        token = self.sync_get_token()
        request.headers["Authorization"] = f"Token {token}"
        yield request

    async def async_get_token(self):
        async with self._async_lock:
            ...

    async def async_auth_flow(self, request):
        token = await self.async_get_token()
        request.headers["Authorization"] = f"Token {token}"
        yield request
```

If you only want to support one of the two methods, then you should still override it, but raise an explicit `RuntimeError`.

```python
import httpx
import sync_only_library


class MyCustomAuth(httpx.Auth):
    def sync_auth_flow(self, request):
        token = sync_only_library.get_token(...)
        request.headers["Authorization"] = f"Token {token}"
        yield request

    async def async_auth_flow(self, request):
        raise RuntimeError("Cannot use a sync authentication class with httpx.AsyncClient")
```

================================================
FILE: docs/advanced/clients.md
================================================
!!! hint
    If you are coming from Requests, `httpx.Client()` is what you can use instead of `requests.Session()`.

## Why use a Client?

!!! note "TL;DR"
    If you do anything more than experimentation, one-off scripts, or prototypes, then you should use a `Client` instance.

**More efficient usage of network resources**

When you make requests using the top-level API as documented in the [Quickstart](../quickstart.md) guide, HTTPX has to establish a new connection _for every single request_ (connections are not reused). As the number of requests to a host increases, this quickly becomes inefficient.

On the other hand, a `Client` instance uses [HTTP connection pooling](https://en.wikipedia.org/wiki/HTTP_persistent_connection). This means that when you make several requests to the same host, the `Client` will reuse the underlying TCP connection, instead of recreating one for every single request.

This can bring **significant performance improvements** compared to using the top-level API, including:

- Reduced latency across requests (no handshaking).
- Reduced CPU usage and round-trips.
- Reduced network congestion.

**Extra features**

`Client` instances also support features that aren't available at the top-level API, such as:

- Cookie persistence across requests.
- Applying configuration across all outgoing requests.
- Sending requests through HTTP proxies.
- Using [HTTP/2](../http2.md).

The other sections on this page go into further detail about what you can do with a `Client` instance.

## Usage

The recommended way to use a `Client` is as a context manager. This will ensure that connections are properly cleaned up when leaving the `with` block:

```python
with httpx.Client() as client:
    ...
```

Alternatively, you can explicitly close the connection pool without block-usage using `.close()`:

```python
client = httpx.Client()
try:
    ...
finally:
    client.close()
```

## Making requests

Once you have a `Client`, you can send requests using `.get()`, `.post()`, etc. For example:

```pycon
>>> with httpx.Client() as client:
...     r = client.get('https://example.com')
...
>>> r
<Response [200 OK]>
```

These methods accept the same arguments as `httpx.get()`, `httpx.post()`, etc. This means that all features documented in the [Quickstart](../quickstart.md) guide are also available at the client level.

For example, to send a request with custom headers:

```pycon
>>> with httpx.Client() as client:
...     headers = {'X-Custom': 'value'}
...     r = client.get('https://example.com', headers=headers)
...
>>> r.request.headers['X-Custom']
'value'
```

## Sharing configuration across requests

Clients allow you to apply configuration to all outgoing requests by passing parameters to the `Client` constructor.

For example, to apply a set of custom headers _on every request_:

```pycon
>>> url = 'http://httpbin.org/headers'
>>> headers = {'user-agent': 'my-app/0.0.1'}
>>> with httpx.Client(headers=headers) as client:
...     r = client.get(url)
...
>>> r.json()['headers']['User-Agent']
'my-app/0.0.1'
```

## Merging of configuration

When a configuration option is provided at both the client-level and request-level, one of two things can happen:

- For headers, query parameters and cookies, the values are combined together. For example:

```pycon
>>> headers = {'X-Auth': 'from-client'}
>>> params = {'client_id': 'client1'}
>>> with httpx.Client(headers=headers, params=params) as client:
...     headers = {'X-Custom': 'from-request'}
...     params = {'request_id': 'request1'}
...     r = client.get('https://example.com', headers=headers, params=params)
...
>>> r.request.url
URL('https://example.com?client_id=client1&request_id=request1')
>>> r.request.headers['X-Auth']
'from-client'
>>> r.request.headers['X-Custom']
'from-request'
```

- For all other parameters, the request-level value takes priority. For example:

```pycon
>>> with httpx.Client(auth=('tom', 'mot123')) as client:
...     r = client.get('https://example.com', auth=('alice', 'ecila123'))
...
>>> _, _, auth = r.request.headers['Authorization'].partition(' ')
>>> import base64
>>> base64.b64decode(auth)
b'alice:ecila123'
```

If you need finer-grained control on the merging of client-level and request-level parameters, see [Request instances](#request-instances).

## Other Client-only configuration options

Additionally, `Client` accepts some configuration options that aren't available at the request level.

For example, `base_url` allows you to prepend an URL to all outgoing requests:

```pycon
>>> with httpx.Client(base_url='http://httpbin.org') as client:
...     r = client.get('/headers')
...
>>> r.request.url
URL('http://httpbin.org/headers')
```

For a list of all available client parameters, see the [`Client`](../api.md#client) API reference.

---

## Request instances

For maximum control on what gets sent over the wire, HTTPX supports building explicit [`Request`](../api.md#request) instances:

```python
request = httpx.Request("GET", "https://example.com")
```

To dispatch a `Request` instance across to the network, create a [`Client` instance](#client-instances) and use `.send()`:

```python
with httpx.Client() as client:
    response = client.send(request)
    ...
```

If you need to mix client-level and request-level options in a way that is not supported by the default [Merging of parameters](#merging-of-parameters), you can use `.build_request()` and then make arbitrary modifications to the `Request` instance. For example:

```python
headers = {"X-Api-Key": "...", "X-Client-ID": "ABC123"}

with httpx.Client(headers=headers) as client:
    request = client.build_request("GET", "https://api.example.com")

    print(request.headers["X-Client-ID"])  # "ABC123"

    # Don't send the API key for this particular request.
    del request.headers["X-Api-Key"]

    response = client.send(request)
    ...
```

## Monitoring download progress

If you need to monitor download progress of large responses, you can use response streaming and inspect the `response.num_bytes_downloaded` property.

This interface is required for properly determining download progress, because the total number of bytes returned by `response.content` or `response.iter_content()` will not always correspond with the raw content length of the response if HTTP response compression is being used.

For example, showing a progress bar using the [`tqdm`](https://github.com/tqdm/tqdm) library while a response is being downloaded could be done like this…

```python
import tempfile

import httpx
from tqdm import tqdm

with tempfile.NamedTemporaryFile() as download_file:
    url = "https://speed.hetzner.de/100MB.bin"
    with httpx.stream("GET", url) as response:
        total = int(response.headers["Content-Length"])

        with tqdm(total=total, unit_scale=True, unit_divisor=1024, unit="B") as progress:
            num_bytes_downloaded = response.num_bytes_downloaded
            for chunk in response.iter_bytes():
                download_file.write(chunk)
                progress.update(response.num_bytes_downloaded - num_bytes_downloaded)
                num_bytes_downloaded = response.num_bytes_downloaded
```

![tqdm progress bar](../img/tqdm-progress.gif)

Or an alternate example, this time using the [`rich`](https://github.com/willmcgugan/rich) library…

```python
import tempfile
import httpx
import rich.progress

with tempfile.NamedTemporaryFile() as download_file:
    url = "https://speed.hetzner.de/100MB.bin"
    with httpx.stream("GET", url) as response:
        total = int(response.headers["Content-Length"])

        with rich.progress.Progress(
            "[progress.percentage]{task.percentage:>3.0f}%",
            rich.progress.BarColumn(bar_width=None),
            rich.progress.DownloadColumn(),
            rich.progress.TransferSpeedColumn(),
        ) as progress:
            download_task = progress.add_task("Download", total=total)
            for chunk in response.iter_bytes():
                download_file.write(chunk)
                progress.update(download_task, completed=response.num_bytes_downloaded)
```

![rich progress bar](../img/rich-progress.gif)

## Monitoring upload progress

If you need to monitor upload progress of large responses, you can use request content generator streaming.

For example, showing a progress bar using the [`tqdm`](https://github.com/tqdm/tqdm) library.

```python
import io
import random

import httpx
from tqdm import tqdm


def gen():
    """
    this is a complete example with generated random bytes.
    you can replace `io.BytesIO` with real file object.
    """
    total = 32 * 1024 * 1024  # 32m
    with tqdm(ascii=True, unit_scale=True, unit='B', unit_divisor=1024, total=total) as bar:
        with io.BytesIO(random.randbytes(total)) as f:
            while data := f.read(1024):
                yield data
                bar.update(len(data))


httpx.post("https://httpbin.org/post", content=gen())
```

![tqdm progress bar](../img/tqdm-progress.gif)

## Multipart file encoding

As mentioned in the [quickstart](../quickstart.md#sending-multipart-file-uploads)
multipart file encoding is available by passing a dictionary with the
name of the payloads as keys and either tuple of elements or a file-like object or a string as values.

```pycon
>>> with open('report.xls', 'rb') as report_file:
...     files = {'upload-file': ('report.xls', report_file, 'application/vnd.ms-excel')}
...     r = httpx.post("https://httpbin.org/post", files=files)
>>> print(r.text)
{
  ...
  "files": {
    "upload-file": "<... binary content ...>"
  },
  ...
}
```

More specifically, if a tuple is used as a value, it must have between 2 and 3 elements:

- The first element is an optional file name which can be set to `None`.
- The second element may be a file-like object or a string which will be automatically
encoded in UTF-8.
- An optional third element can be used to specify the
[MIME type](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_Types)
of the file being uploaded. If not specified HTTPX will attempt to guess the MIME type based
on the file name, with unknown file extensions defaulting to "application/octet-stream".
If the file name is explicitly set to `None` then HTTPX will not include a content-type
MIME header field.

```pycon
>>> files = {'upload-file': (None, 'text content', 'text/plain')}
>>> r = httpx.post("https://httpbin.org/post", files=files)
>>> print(r.text)
{
  ...
  "files": {},
  "form": {
    "upload-file": "text-content"
  },
  ...
}
```

!!! tip
    It is safe to upload large files this way. File uploads are streaming by default, meaning that only one chunk will be loaded into memory at a time.

 Non-file data fields can be included in the multipart form using by passing them to `data=...`.

You can also send multiple files in one go with a multiple file field form.
To do that, pass a list of `(field, <file>)` items instead of a dictionary, allowing you to pass multiple items with the same `field`.
For instance this request sends 2 files, `foo.png` and `bar.png` in one request on the `images` form field:

```pycon
>>> with open('foo.png', 'rb') as foo_file, open('bar.png', 'rb') as bar_file:
...     files = [
...         ('images', ('foo.png', foo_file, 'image/png')),
...         ('images', ('bar.png', bar_file, 'image/png')),
...     ]
...     r = httpx.post("https://httpbin.org/post", files=files)
```


================================================
FILE: docs/advanced/event-hooks.md
================================================
HTTPX allows you to register "event hooks" with the client, that are called
every time a particular type of event takes place.

There are currently two event hooks:

* `request` - Called after a request is fully prepared, but before it is sent to the network. Passed the `request` instance.
* `response` - Called after the response has been fetched from the network, but before it is returned to the caller. Passed the `response` instance.

These allow you to install client-wide functionality such as logging, monitoring or tracing.

```python
def log_request(request):
    print(f"Request event hook: {request.method} {request.url} - Waiting for response")

def log_response(response):
    request = response.request
    print(f"Response event hook: {request.method} {request.url} - Status {response.status_code}")

client = httpx.Client(event_hooks={'request': [log_request], 'response': [log_response]})
```

You can also use these hooks to install response processing code, such as this
example, which creates a client instance that always raises `httpx.HTTPStatusError`
on 4xx and 5xx responses.

```python
def raise_on_4xx_5xx(response):
    response.raise_for_status()

client = httpx.Client(event_hooks={'response': [raise_on_4xx_5xx]})
```

!!! note
    Response event hooks are called before determining if the response body
    should be read or not.

    If you need access to the response body inside an event hook, you'll
    need to call `response.read()`, or for AsyncClients, `response.aread()`.

The hooks are also allowed to modify `request` and `response` objects.

```python
def add_timestamp(request):
    request.headers['x-request-timestamp'] = datetime.now(tz=datetime.utc).isoformat()

client = httpx.Client(event_hooks={'request': [add_timestamp]})
```

Event hooks must always be set as a **list of callables**, and you may register
multiple event hooks for each type of event.

As well as being able to set event hooks on instantiating the client, there
is also an `.event_hooks` property, that allows you to inspect and modify
the installed hooks.

```python
client = httpx.Client()
client.event_hooks['request'] = [log_request]
client.event_hooks['response'] = [log_response, raise_on_4xx_5xx]
```

!!! note
    If you are using HTTPX's async support, then you need to be aware that
    hooks registered with `httpx.AsyncClient` MUST be async functions,
    rather than plain functions.


================================================
FILE: docs/advanced/extensions.md
================================================
# Extensions

Request and response extensions provide a untyped space where additional information may be added.

Extensions should be used for features that may not be available on all transports, and that do not fit neatly into [the simplified request/response model](https://www.encode.io/httpcore/extensions/) that the underlying `httpcore` package uses as its API.

Several extensions are supported on the request:

```python
# Request timeouts actually implemented as an extension on
# the request, ensuring that they are passed throughout the
# entire call stack.
client = httpx.Client()
response = client.get(
    "https://www.example.com",
    extensions={"timeout": {"connect": 5.0}}
)
response.request.extensions["timeout"]
{"connect": 5.0}
```

And on the response:

```python
client = httpx.Client()
response = client.get("https://www.example.com")
print(response.extensions["http_version"])  # b"HTTP/1.1"
# Other server responses could have been
# b"HTTP/0.9", b"HTTP/1.0", or b"HTTP/1.1"
```

## Request Extensions

### `"trace"`

The trace extension allows a callback handler to be installed to monitor the internal
flow of events within the underlying `httpcore` transport.

The simplest way to explain this is with an example:

```python
import httpx

def log(event_name, info):
    print(event_name, info)

client = httpx.Client()
response = client.get("https://www.example.com/", extensions={"trace": log})
# connection.connect_tcp.started {'host': 'www.example.com', 'port': 443, 'local_address': None, 'timeout': None}
# connection.connect_tcp.complete {'return_value': <httpcore.backends.sync.SyncStream object at 0x1093f94d0>}
# connection.start_tls.started {'ssl_context': <ssl.SSLContext object at 0x1093ee750>, 'server_hostname': b'www.example.com', 'timeout': None}
# connection.start_tls.complete {'return_value': <httpcore.backends.sync.SyncStream object at 0x1093f9450>}
# http11.send_request_headers.started {'request': <Request [b'GET']>}
# http11.send_request_headers.complete {'return_value': None}
# http11.send_request_body.started {'request': <Request [b'GET']>}
# http11.send_request_body.complete {'return_value': None}
# http11.receive_response_headers.started {'request': <Request [b'GET']>}
# http11.receive_response_headers.complete {'return_value': (b'HTTP/1.1', 200, b'OK', [(b'Age', b'553715'), (b'Cache-Control', b'max-age=604800'), (b'Content-Type', b'text/html; charset=UTF-8'), (b'Date', b'Thu, 21 Oct 2021 17:08:42 GMT'), (b'Etag', b'"3147526947+ident"'), (b'Expires', b'Thu, 28 Oct 2021 17:08:42 GMT'), (b'Last-Modified', b'Thu, 17 Oct 2019 07:18:26 GMT'), (b'Server', b'ECS (nyb/1DCD)'), (b'Vary', b'Accept-Encoding'), (b'X-Cache', b'HIT'), (b'Content-Length', b'1256')])}
# http11.receive_response_body.started {'request': <Request [b'GET']>}
# http11.receive_response_body.complete {'return_value': None}
# http11.response_closed.started {}
# http11.response_closed.complete {'return_value': None}
```

The `event_name` and `info` arguments here will be one of the following:

* `{event_type}.{event_name}.started`, `<dictionary of keyword arguments>`
* `{event_type}.{event_name}.complete`, `{"return_value": <...>}`
* `{event_type}.{event_name}.failed`, `{"exception": <...>}`

Note that when using async code the handler function passed to `"trace"` must be an `async def ...` function.

The following event types are currently exposed...

**Establishing the connection**

* `"connection.connect_tcp"`
* `"connection.connect_unix_socket"`
* `"connection.start_tls"`

**HTTP/1.1 events**

* `"http11.send_request_headers"`
* `"http11.send_request_body"`
* `"http11.receive_response"`
* `"http11.receive_response_body"`
* `"http11.response_closed"`

**HTTP/2 events**

* `"http2.send_connection_init"`
* `"http2.send_request_headers"`
* `"http2.send_request_body"`
* `"http2.receive_response_headers"`
* `"http2.receive_response_body"`
* `"http2.response_closed"`

The exact set of trace events may be subject to change across different versions of `httpcore`. If you need to rely on a particular set of events it is recommended that you pin installation of the package to a fixed version.

### `"sni_hostname"`

The server's hostname, which is used to confirm the hostname supplied by the SSL certificate.

If you want to connect to an explicit IP address rather than using the standard DNS hostname lookup, then you'll need to use this request extension.

For example:

``` python
# Connect to '185.199.108.153' but use 'www.encode.io' in the Host header,
# and use 'www.encode.io' when SSL verifying the server hostname.
client = httpx.Client()
headers = {"Host": "www.encode.io"}
extensions = {"sni_hostname": "www.encode.io"}
response = client.get(
    "https://185.199.108.153/path",
    headers=headers,
    extensions=extensions
)
```

### `"timeout"`

A dictionary of `str: Optional[float]` timeout values.

May include values for `'connect'`, `'read'`, `'write'`, or `'pool'`.

For example:

```python
# Timeout if a connection takes more than 5 seconds to established, or if
# we are blocked waiting on the connection pool for more than 10 seconds.
client = httpx.Client()
response = client.get(
    "https://www.example.com",
    extensions={"timeout": {"connect": 5.0, "pool": 10.0}}
)
```

This extension is how the `httpx` timeouts are implemented, ensuring that the timeout values are associated with the request instance and passed throughout the stack. You shouldn't typically be working with this extension directly, but use the higher level `timeout` API instead.

### `"target"`

The target that is used as [the HTTP target instead of the URL path](https://datatracker.ietf.org/doc/html/rfc2616#section-5.1.2).

This enables support constructing requests that would otherwise be unsupported.

* URL paths with non-standard escaping applied.
* Forward proxy requests using an absolute URI.
* Tunneling proxy requests using `CONNECT` with hostname as the target.
* Server-wide `OPTIONS *` requests.

Some examples:

Using the 'target' extension to send requests without the standard path escaping rules...

```python
# Typically a request to "https://www.example.com/test^path" would
# connect to "www.example.com" and send an HTTP/1.1 request like...
#
# GET /test%5Epath HTTP/1.1
#
# Using the target extension we can include the literal '^'...
#
# GET /test^path HTTP/1.1
#
# Note that requests must still be valid HTTP requests.
# For example including whitespace in the target will raise a `LocalProtocolError`.
extensions = {"target": b"/test^path"}
response = httpx.get("https://www.example.com", extensions=extensions)
```

The `target` extension also allows server-wide `OPTIONS *` requests to be constructed...

```python
# This will send the following request...
#
# CONNECT * HTTP/1.1
extensions = {"target": b"*"}
response = httpx.request("CONNECT", "https://www.example.com", extensions=extensions)
```

## Response Extensions

### `"http_version"`

The HTTP version, as bytes. Eg. `b"HTTP/1.1"`.

When using HTTP/1.1 the response line includes an explicit version, and the value of this key could feasibly be one of `b"HTTP/0.9"`, `b"HTTP/1.0"`, or `b"HTTP/1.1"`.

When using HTTP/2 there is no further response versioning included in the protocol, and the value of this key will always be `b"HTTP/2"`.

### `"reason_phrase"`

The reason-phrase of the HTTP response, as bytes. For example `b"OK"`. Some servers may include a custom reason phrase, although this is not recommended.

HTTP/2 onwards does not include a reason phrase on the wire.

When no key is included, a default based on the status code may be used.

### `"stream_id"`

When HTTP/2 is being used the `"stream_id"` response extension can be accessed to determine the ID of the data stream that the response was sent on.

### `"network_stream"`

The `"network_stream"` extension allows developers to handle HTTP `CONNECT` and `Upgrade` requests, by providing an API that steps outside the standard request/response model, and can directly read or write to the network.

The interface provided by the network stream:

* `read(max_bytes, timeout = None) -> bytes`
* `write(buffer, timeout = None)`
* `close()`
* `start_tls(ssl_context, server_hostname = None, timeout = None) -> NetworkStream`
* `get_extra_info(info) -> Any`

This API can be used as the foundation for working with HTTP proxies, WebSocket upgrades, and other advanced use-cases.

See the [network backends documentation](https://www.encode.io/httpcore/network-backends/) for more information on working directly with network streams.

**Extra network information**

The network stream abstraction also allows access to various low-level information that may be exposed by the underlying socket:

```python
response = httpx.get("https://www.example.com")
network_stream = response.extensions["network_stream"]

client_addr = network_stream.get_extra_info("client_addr")
server_addr = network_stream.get_extra_info("server_addr")
print("Client address", client_addr)
print("Server address", server_addr)
```

The socket SSL information is also available through this interface, although you need to ensure that the underlying connection is still open, in order to access it...

```python
with httpx.stream("GET", "https://www.example.com") as response:
    network_stream = response.extensions["network_stream"]

    ssl_object = network_stream.get_extra_info("ssl_object")
    print("TLS version", ssl_object.version())
```


================================================
FILE: docs/advanced/proxies.md
================================================
HTTPX supports setting up [HTTP proxies](https://en.wikipedia.org/wiki/Proxy_server#Web_proxy_servers) via the `proxy` parameter to be passed on client initialization or top-level API functions like `httpx.get(..., proxy=...)`.

<div align="center">
    <img src="https://upload.wikimedia.org/wikipedia/commons/thumb/2/27/Open_proxy_h2g2bob.svg/480px-Open_proxy_h2g2bob.svg.png"/>
    <figcaption><em>Diagram of how a proxy works (source: Wikipedia). The left hand side "Internet" blob may be your HTTPX client requesting <code>example.com</code> through a proxy.</em></figcaption>
</div>

## HTTP Proxies

To route all traffic (HTTP and HTTPS) to a proxy located at `http://localhost:8030`, pass the proxy URL to the client...

```python
with httpx.Client(proxy="http://localhost:8030") as client:
    ...
```

For more advanced use cases, pass a mounts `dict`. For example, to route HTTP and HTTPS requests to 2 different proxies, respectively located at `http://localhost:8030`, and `http://localhost:8031`, pass a `dict` of proxy URLs:

```python
proxy_mounts = {
    "http://": httpx.HTTPTransport(proxy="http://localhost:8030"),
    "https://": httpx.HTTPTransport(proxy="http://localhost:8031"),
}

with httpx.Client(mounts=proxy_mounts) as client:
    ...
```

For detailed information about proxy routing, see the [Routing](#routing) section.

!!! tip "Gotcha"
    In most cases, the proxy URL for the `https://` key _should_ use the `http://` scheme (that's not a typo!).

    This is because HTTP proxying requires initiating a connection with the proxy server. While it's possible that your proxy supports doing it via HTTPS, most proxies only support doing it via HTTP.

    For more information, see [FORWARD vs TUNNEL](#forward-vs-tunnel).

## Authentication

Proxy credentials can be passed as the `userinfo` section of the proxy URL. For example:

```python
with httpx.Client(proxy="http://username:password@localhost:8030") as client:
    ...
```

## Proxy mechanisms

!!! note
    This section describes **advanced** proxy concepts and functionality.

### FORWARD vs TUNNEL

In general, the flow for making an HTTP request through a proxy is as follows:

1. The client connects to the proxy (initial connection request).
2. The proxy transfers data to the server on your behalf.

How exactly step 2/ is performed depends on which of two proxying mechanisms is used:

* **Forwarding**: the proxy makes the request for you, and sends back the response it obtained from the server.
* **Tunnelling**: the proxy establishes a TCP connection to the server on your behalf, and the client reuses this connection to send the request and receive the response. This is known as an [HTTP Tunnel](https://en.wikipedia.org/wiki/HTTP_tunnel). This mechanism is how you can access websites that use HTTPS from an HTTP proxy (the client "upgrades" the connection to HTTPS by performing the TLS handshake with the server over the TCP connection provided by the proxy).

### Troubleshooting proxies

If you encounter issues when setting up proxies, please refer to our [Troubleshooting guide](../troubleshooting.md#proxies).

## SOCKS

In addition to HTTP proxies, `httpcore` also supports proxies using the SOCKS protocol.
This is an optional feature that requires an additional third-party library be installed before use.

You can install SOCKS support using `pip`:

```shell
$ pip install httpx[socks]
```

You can now configure a client to make requests via a proxy using the SOCKS protocol:

```python
httpx.Client(proxy='socks5://user:pass@host:port')
```


================================================
FILE: docs/advanced/resource-limits.md
================================================
You can control the connection pool size using the `limits` keyword
argument on the client. It takes instances of `httpx.Limits` which define:

- `max_keepalive_connections`, number of allowable keep-alive connections, or `None` to always
allow. (Defaults 20)
- `max_connections`, maximum number of allowable connections, or `None` for no limits.
(Default 100)
- `keepalive_expiry`, time limit on idle keep-alive connections in seconds, or `None` for no limits. (Default 5)

```python
limits = httpx.Limits(max_keepalive_connections=5, max_connections=10)
client = httpx.Client(limits=limits)
```

================================================
FILE: docs/advanced/ssl.md
================================================
When making a request over HTTPS, HTTPX needs to verify the identity of the requested host. To do this, it uses a bundle of SSL certificates (a.k.a. CA bundle) delivered by a trusted certificate authority (CA).

### Enabling and disabling verification

By default httpx will verify HTTPS connections, and raise an error for invalid SSL cases...

```pycon
>>> httpx.get("https://expired.badssl.com/")
httpx.ConnectError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: certificate has expired (_ssl.c:997)
```

You can disable SSL verification completely and allow insecure requests...

```pycon
>>> httpx.get("https://expired.badssl.com/", verify=False)
<Response [200 OK]>
```

### Configuring client instances

If you're using a `Client()` instance you should pass any `verify=<...>` configuration when instantiating the client.

By default the [certifi CA bundle](https://certifiio.readthedocs.io/en/latest/) is used for SSL verification.

For more complex configurations you can pass an [SSL Context](https://docs.python.org/3/library/ssl.html) instance...

```python
import certifi
import httpx
import ssl

# This SSL context is equivalent to the default `verify=True`.
ctx = ssl.create_default_context(cafile=certifi.where())
client = httpx.Client(verify=ctx)
```

Using [the `truststore` package](https://truststore.readthedocs.io/) to support system certificate stores...

```python
import ssl
import truststore
import httpx

# Use system certificate stores.
ctx = truststore.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
client = httpx.Client(verify=ctx)
```

Loding an alternative certificate verification store using [the standard SSL context API](https://docs.python.org/3/library/ssl.html)...

```python
import httpx
import ssl

# Use an explicitly configured certificate store.
ctx = ssl.create_default_context(cafile="path/to/certs.pem")  # Either cafile or capath.
client = httpx.Client(verify=ctx)
```

### Client side certificates

Client side certificates allow a remote server to verify the client. They tend to be used within private organizations to authenticate requests to remote servers.

You can specify client-side certificates, using the [`.load_cert_chain()`](https://docs.python.org/3/library/ssl.html#ssl.SSLContext.load_cert_chain) API...

```python
ctx = ssl.create_default_context()
ctx.load_cert_chain(certfile="path/to/client.pem")  # Optionally also keyfile or password.
client = httpx.Client(verify=ctx)
```

### Working with `SSL_CERT_FILE` and `SSL_CERT_DIR`

`httpx` does respect the `SSL_CERT_FILE` and `SSL_CERT_DIR` environment variables by default. For details, refer to [the section on the environment variables page](../environment_variables.md#ssl_cert_file).

### Making HTTPS requests to a local server

When making requests to local servers, such as a development server running on `localhost`, you will typically be using unencrypted HTTP connections.

If you do need to make HTTPS connections to a local server, for example to test an HTTPS-only service, you will need to create and use your own certificates. Here's one way to do it...

1. Use [trustme](https://github.com/python-trio/trustme) to generate a pair of server key/cert files, and a client cert file.
2. Pass the server key/cert files when starting your local server. (This depends on the particular web server you're using. For example, [Uvicorn](https://www.uvicorn.org) provides the `--ssl-keyfile` and `--ssl-certfile` options.)
3. Configure `httpx` to use the certificates stored in `client.pem`.

```python
ctx = ssl.create_default_context(cafile="client.pem")
client = httpx.Client(verify=ctx)
```


================================================
FILE: docs/advanced/text-encodings.md
================================================
When accessing `response.text`, we need to decode the response bytes into a unicode text representation.

By default `httpx` will use `"charset"` information included in the response `Content-Type` header to determine how the response bytes should be decoded into text.

In cases where no charset information is included on the response, the default behaviour is to assume "utf-8" encoding, which is by far the most widely used text encoding on the internet.

## Using the default encoding

To understand this better let's start by looking at the default behaviour for text decoding...

```python
import httpx
# Instantiate a client with the default configuration.
client = httpx.Client()
# Using the client...
response = client.get(...)
print(response.encoding)  # This will either print the charset given in
                          # the Content-Type charset, or else "utf-8".
print(response.text)  # The text will either be decoded with the Content-Type
                      # charset, or using "utf-8".
```

This is normally absolutely fine. Most servers will respond with a properly formatted Content-Type header, including a charset encoding. And in most cases where no charset encoding is included, UTF-8 is very likely to be used, since it is so widely adopted.

## Using an explicit encoding

In some cases we might be making requests to a site where no character set information is being set explicitly by the server, but we know what the encoding is. In this case it's best to set the default encoding explicitly on the client.

```python
import httpx
# Instantiate a client with a Japanese character set as the default encoding.
client = httpx.Client(default_encoding="shift-jis")
# Using the client...
response = client.get(...)
print(response.encoding)  # This will either print the charset given in
                          # the Content-Type charset, or else "shift-jis".
print(response.text)  # The text will either be decoded with the Content-Type
                      # charset, or using "shift-jis".
```

## Using auto-detection

In cases where the server is not reliably including character set information, and where we don't know what encoding is being used, we can enable auto-detection to make a best-guess attempt when decoding from bytes to text.

To use auto-detection you need to set the `default_encoding` argument to a callable instead of a string. This callable should be a function which takes the input bytes as an argument and returns the character set to use for decoding those bytes to text.

There are two widely used Python packages which both handle this functionality:

* [`chardet`](https://chardet.readthedocs.io/) - This is a well established package, and is a port of [the auto-detection code in Mozilla](https://www-archive.mozilla.org/projects/intl/chardet.html).
* [`charset-normalizer`](https://charset-normalizer.readthedocs.io/) - A newer package, motivated by `chardet`, with a different approach.

Let's take a look at installing autodetection using one of these packages...

```shell
$ pip install httpx
$ pip install chardet
```

Once `chardet` is installed, we can configure a client to use character-set autodetection.

```python
import httpx
import chardet

def autodetect(content):
    return chardet.detect(content).get("encoding")

# Using a client with character-set autodetection enabled.
client = httpx.Client(default_encoding=autodetect)
response = client.get(...)
print(response.encoding)  # This will either print the charset given in
                          # the Content-Type charset, or else the auto-detected
                          # character set.
print(response.text)
```


================================================
FILE: docs/advanced/timeouts.md
================================================
HTTPX is careful to enforce timeouts everywhere by default.

The default behavior is to raise a `TimeoutException` after 5 seconds of
network inactivity.

## Setting and disabling timeouts

You can set timeouts for an individual request:

```python
# Using the top-level API:
httpx.get('http://example.com/api/v1/example', timeout=10.0)

# Using a client instance:
with httpx.Client() as client:
    client.get("http://example.com/api/v1/example", timeout=10.0)
```

Or disable timeouts for an individual request:

```python
# Using the top-level API:
httpx.get('http://example.com/api/v1/example', timeout=None)

# Using a client instance:
with httpx.Client() as client:
    client.get("http://example.com/api/v1/example", timeout=None)
```

## Setting a default timeout on a client

You can set a timeout on a client instance, which results in the given
`timeout` being used as the default for requests made with this client:

```python
client = httpx.Client()              # Use a default 5s timeout everywhere.
client = httpx.Client(timeout=10.0)  # Use a default 10s timeout everywhere.
client = httpx.Client(timeout=None)  # Disable all timeouts by default.
```

## Fine tuning the configuration

HTTPX also allows you to specify the timeout behavior in more fine grained detail.

There are four different types of timeouts that may occur. These are **connect**,
**read**, **write**, and **pool** timeouts.

* The **connect** timeout specifies the maximum amount of time to wait until
a socket connection to the requested host is established. If HTTPX is unable to connect
within this time frame, a `ConnectTimeout` exception is raised.
* The **read** timeout specifies the maximum duration to wait for a chunk of
data to be received (for example, a chunk of the response body). If HTTPX is
unable to receive data within this time frame, a `ReadTimeout` exception is raised.
* The **write** timeout specifies the maximum duration to wait for a chunk of
data to be sent (for example, a chunk of the request body). If HTTPX is unable
to send data within this time frame, a `WriteTimeout` exception is raised.
* The **pool** timeout specifies the maximum duration to wait for acquiring
a connection from the connection pool. If HTTPX is unable to acquire a connection
within this time frame, a `PoolTimeout` exception is raised. A related
configuration here is the maximum number of allowable connections in the
connection pool, which is configured by the `limits` argument.

You can configure the timeout behavior for any of these values...

```python
# A client with a 60s timeout for connecting, and a 10s timeout elsewhere.
timeout = httpx.Timeout(10.0, connect=60.0)
client = httpx.Client(timeout=timeout)

response = client.get('http://example.com/')
```

================================================
FILE: docs/advanced/transports.md
================================================
HTTPX's `Client` also accepts a `transport` argument. This argument allows you
to provide a custom Transport object that will be used to perform the actual
sending of the requests.

## HTTP Transport

For some advanced configuration you might need to instantiate a transport
class directly, and pass it to the client instance. One example is the
`local_address` configuration which is only available via this low-level API.

```pycon
>>> import httpx
>>> transport = httpx.HTTPTransport(local_address="0.0.0.0")
>>> client = httpx.Client(transport=transport)
```

Connection retries are also available via this interface. Requests will be retried the given number of times in case an `httpx.ConnectError` or an `httpx.ConnectTimeout` occurs, allowing smoother operation under flaky networks. If you need other forms of retry behaviors, such as handling read/write errors or reacting to `503 Service Unavailable`, consider general-purpose tools such as [tenacity](https://github.com/jd/tenacity).

```pycon
>>> import httpx
>>> transport = httpx.HTTPTransport(retries=1)
>>> client = httpx.Client(transport=transport)
```

Similarly, instantiating a transport directly provides a `uds` option for
connecting via a Unix Domain Socket that is only available via this low-level API:

```pycon
>>> import httpx
>>> # Connect to the Docker API via a Unix Socket.
>>> transport = httpx.HTTPTransport(uds="/var/run/docker.sock")
>>> client = httpx.Client(transport=transport)
>>> response = client.get("http://docker/info")
>>> response.json()
{"ID": "...", "Containers": 4, "Images": 74, ...}
```

## WSGI Transport

You can configure an `httpx` client to call directly into a Python web application using the WSGI protocol.

This is particularly useful for two main use-cases:

* Using `httpx` as a client inside test cases.
* Mocking out external services during tests or in dev or staging environments.

### Example

Here's an example of integrating against a Flask application:

```python
from flask import Flask
import httpx


app = Flask(__name__)

@app.route("/")
def hello():
    return "Hello World!"

transport = httpx.WSGITransport(app=app)
with httpx.Client(transport=transport, base_url="http://testserver") as client:
    r = client.get("/")
    assert r.status_code == 200
    assert r.text == "Hello World!"
```

### Configuration

For some more complex cases you might need to customize the WSGI transport. This allows you to:

* Inspect 500 error responses rather than raise exceptions by setting `raise_app_exceptions=False`.
* Mount the WSGI application at a subpath by setting `script_name` (WSGI).
* Use a given client address for requests by setting `remote_addr` (WSGI).

For example:

```python
# Instantiate a client that makes WSGI requests with a client IP of "1.2.3.4".
transport = httpx.WSGITransport(app=app, remote_addr="1.2.3.4")
with httpx.Client(transport=transport, base_url="http://testserver") as client:
    ...
```

## ASGI Transport

You can configure an `httpx` client to call directly into an async Python web application using the ASGI protocol.

This is particularly useful for two main use-cases:

* Using `httpx` as a client inside test cases.
* Mocking out external services during tests or in dev or staging environments.

### Example

Let's take this Starlette application as an example:

```python
from starlette.applications import Starlette
from starlette.responses import HTMLResponse
from starlette.routing import Route


async def hello(request):
    return HTMLResponse("Hello World!")


app = Starlette(routes=[Route("/", hello)])
```

We can make requests directly against the application, like so:

```python
transport = httpx.ASGITransport(app=app)

async with httpx.AsyncClient(transport=transport, base_url="http://testserver") as client:
    r = await client.get("/")
    assert r.status_code == 200
    assert r.text == "Hello World!"
```

### Configuration

For some more complex cases you might need to customise the ASGI transport. This allows you to:

* Inspect 500 error responses rather than raise exceptions by setting `raise_app_exceptions=False`.
* Mount the ASGI application at a subpath by setting `root_path`.
* Use a given client address for requests by setting `client`.

For example:

```python
# Instantiate a client that makes ASGI requests with a client IP of "1.2.3.4",
# on port 123.
transport = httpx.ASGITransport(app=app, client=("1.2.3.4", 123))
async with httpx.AsyncClient(transport=transport, base_url="http://testserver") as client:
    ...
```

See [the ASGI documentation](https://asgi.readthedocs.io/en/latest/specs/www.html#connection-scope) for more details on the `client` and `root_path` keys.

### ASGI startup and shutdown

It is not in the scope of HTTPX to trigger ASGI lifespan events of your app.

However it is suggested to use `LifespanManager` from [asgi-lifespan](https://github.com/florimondmanca/asgi-lifespan#usage) in pair with `AsyncClient`.

## Custom transports

A transport instance must implement the low-level Transport API which deals
with sending a single request, and returning a response. You should either
subclass `httpx.BaseTransport` to implement a transport to use with `Client`,
or subclass `httpx.AsyncBaseTransport` to implement a transport to
use with `AsyncClient`.

At the layer of the transport API we're using the familiar `Request` and
`Response` models.

See the `handle_request` and `handle_async_request` docstrings for more details
on the specifics of the Transport API.

A complete example of a custom transport implementation would be:

```python
import json
import httpx

class HelloWorldTransport(httpx.BaseTransport):
    """
    A mock transport that always returns a JSON "Hello, world!" response.
    """

    def handle_request(self, request):
        return httpx.Response(200, json={"text": "Hello, world!"})
```

Or this example, which uses a custom transport and `httpx.Mounts` to always redirect `http://` requests.

```python
class HTTPSRedirect(httpx.BaseTransport):
    """
    A transport that always redirects to HTTPS.
    """
    def handle_request(self, request):
        url = request.url.copy_with(scheme="https")
        return httpx.Response(303, headers={"Location": str(url)})

# A client where any `http` requests are always redirected to `https`
transport = httpx.Mounts({
    'http://': HTTPSRedirect()
    'https://': httpx.HTTPTransport()
})
client = httpx.Client(transport=transport)
```

A useful pattern here is custom transport classes that wrap the default HTTP implementation. For example...

```python
class DebuggingTransport(httpx.BaseTransport):
    def __init__(self, **kwargs):
        self._wrapper = httpx.HTTPTransport(**kwargs)

    def handle_request(self, request):
        print(f">>> {request}")
        response = self._wrapper.handle_request(request)
        print(f"<<< {response}")
        return response

    def close(self):
        self._wrapper.close()

transport = DebuggingTransport()
client = httpx.Client(transport=transport)
```

Here's another case, where we're using a round-robin across a number of different proxies...

```python
class ProxyRoundRobin(httpx.BaseTransport):
    def __init__(self, proxies, **kwargs):
        self._transports = [
            httpx.HTTPTransport(proxy=proxy, **kwargs)
            for proxy in proxies
        ]
        self._idx = 0

    def handle_request(self, request):
        transport = self._transports[self._idx]
        self._idx = (self._idx + 1) % len(self._transports)
        return transport.handle_request(request)

    def close(self):
        for transport in self._transports:
            transport.close()

proxies = [
    httpx.Proxy("http://127.0.0.1:8081"),
    httpx.Proxy("http://127.0.0.1:8082"),
    httpx.Proxy("http://127.0.0.1:8083"),
]
transport = ProxyRoundRobin(proxies=proxies)
client = httpx.Client(transport=transport)
```

## Mock transports

During testing it can often be useful to be able to mock out a transport,
and return pre-determined responses, rather than making actual network requests.

The `httpx.MockTransport` class accepts a handler function, which can be used
to map requests onto pre-determined responses:

```python
def handler(request):
    return httpx.Response(200, json={"text": "Hello, world!"})


# Switch to a mock transport, if the TESTING environment variable is set.
if os.environ.get('TESTING', '').upper() == "TRUE":
    transport = httpx.MockTransport(handler)
else:
    transport = httpx.HTTPTransport()

client = httpx.Client(transport=transport)
```

For more advanced use-cases you might want to take a look at either [the third-party
mocking library, RESPX](https://lundberg.github.io/respx/), or the [pytest-httpx library](https://github.com/Colin-b/pytest_httpx).

## Mounting transports

You can also mount transports against given schemes or domains, to control
which transport an outgoing request should be routed via, with [the same style
used for specifying proxy routing](#routing).

```python
import httpx

class HTTPSRedirectTransport(httpx.BaseTransport):
    """
    A transport that always redirects to HTTPS.
    """

    def handle_request(self, method, url, headers, stream, extensions):
        scheme, host, port, path = url
        if port is None:
            location = b"https://%s%s" % (host, path)
        else:
            location = b"https://%s:%d%s" % (host, port, path)
        stream = httpx.ByteStream(b"")
        headers = [(b"location", location)]
        extensions = {}
        return 303, headers, stream, extensions


# A client where any `http` requests are always redirected to `https`
mounts = {'http://': HTTPSRedirectTransport()}
client = httpx.Client(mounts=mounts)
```

A couple of other sketches of how you might take advantage of mounted transports...

Disabling HTTP/2 on a single given domain...

```python
mounts = {
    "all://": httpx.HTTPTransport(http2=True),
    "all://*example.org": httpx.HTTPTransport()
}
client = httpx.Client(mounts=mounts)
```

Mocking requests to a given domain:

```python
# All requests to "example.org" should be mocked out.
# Other requests occur as usual.
def handler(request):
    return httpx.Response(200, json={"text": "Hello, World!"})

mounts = {"all://example.org": httpx.MockTransport(handler)}
client = httpx.Client(mounts=mounts)
```

Adding support for custom schemes:

```python
# Support URLs like "file:///Users/sylvia_green/websites/new_client/index.html"
mounts = {"file://": FileSystemTransport()}
client = httpx.Client(mounts=mounts)
```

### Routing

HTTPX provides a powerful mechanism for routing requests, allowing you to write complex rules that specify which transport should be used for each request.

The `mounts` dictionary maps URL patterns to HTTP transports. HTTPX matches requested URLs against URL patterns to decide which transport should be used, if any. Matching is done from most specific URL patterns (e.g. `https://<domain>:<port>`) to least specific ones (e.g. `https://`).

HTTPX supports routing requests based on **scheme**, **domain**, **port**, or a combination of these.

### Wildcard routing

Route everything through a transport...

```python
mounts = {
    "all://": httpx.HTTPTransport(proxy="http://localhost:8030"),
}
```

### Scheme routing

Route HTTP requests through one transport, and HTTPS requests through another...

```python
mounts = {
    "http://": httpx.HTTPTransport(proxy="http://localhost:8030"),
    "https://": httpx.HTTPTransport(proxy="http://localhost:8031"),
}
```

### Domain routing

Proxy all requests on domain "example.com", let other requests pass through...

```python
mounts = {
    "all://example.com": httpx.HTTPTransport(proxy="http://localhost:8030"),
}
```

Proxy HTTP requests on domain "example.com", let HTTPS and other requests pass through...

```python
mounts = {
    "http://example.com": httpx.HTTPTransport(proxy="http://localhost:8030"),
}
```

Proxy all requests to "example.com" and its subdomains, let other requests pass through...

```python
mounts = {
    "all://*example.com": httpx.HTTPTransport(proxy="http://localhost:8030"),
}
```

Proxy all requests to strict subdomains of "example.com", let "example.com" and other requests pass through...

```python
mounts = {
    "all://*.example.com": httpx.HTTPTransport(proxy="http://localhost:8030"),
}
```

### Port routing

Proxy HTTPS requests on port 1234 to "example.com"...

```python
mounts = {
    "https://example.com:1234": httpx.HTTPTransport(proxy="http://localhost:8030"),
}
```

Proxy all requests on port 1234...

```python
mounts = {
    "all://*:1234": httpx.HTTPTransport(proxy="http://localhost:8030"),
}
```

### No-proxy support

It is also possible to define requests that _shouldn't_ be routed through the transport.

To do so, pass `None` as the proxy URL. For example...

```python
mounts = {
    # Route requests through a proxy by default...
    "all://": httpx.HTTPTransport(proxy="http://localhost:8031"),
    # Except those for "example.com".
    "all://example.com": None,
}
```

### Complex configuration example

You can combine the routing features outlined above to build complex proxy routing configurations. For example...

```python
mounts = {
    # Route all traffic through a proxy by default...
    "all://": httpx.HTTPTransport(proxy="http://localhost:8030"),
    # But don't use proxies for HTTPS requests to "domain.io"...
    "https://domain.io": None,
    # And use another proxy for requests to "example.com" and its subdomains...
    "all://*example.com": httpx.HTTPTransport(proxy="http://localhost:8031"),
    # And yet another proxy if HTTP is used,
    # and the "internal" subdomain on port 5550 is requested...
    "http://internal.example.com:5550": httpx.HTTPTransport(proxy="http://localhost:8032"),
}
```

### Environment variables

There are also environment variables that can be used to control the dictionary of the client mounts. 
They can be used to configure HTTP proxying for clients.

See documentation on [`HTTP_PROXY`, `HTTPS_PROXY`, `ALL_PROXY`](../environment_variables.md#http_proxy-https_proxy-all_proxy)
and [`NO_PROXY`](../environment_variables.md#no_proxy) for more information.


================================================
FILE: docs/api.md
================================================
# Developer Interface

## Helper Functions

!!! note
    Only use these functions if you're testing HTTPX in a console
    or making a small number of requests. Using a `Client` will
    enable HTTP/2 and connection pooling for more efficient and
    long-lived connections.

::: httpx.request
    :docstring:

::: httpx.get
    :docstring:

::: httpx.options
    :docstring:

::: httpx.head
    :docstring:

::: httpx.post
    :docstring:

::: httpx.put
    :docstring:

::: httpx.patch
    :docstring:

::: httpx.delete
    :docstring:

::: httpx.stream
    :docstring:

## `Client`

::: httpx.Client
    :docstring:
    :members: headers cookies params auth request get head options post put patch delete stream build_request send close

## `AsyncClient`

::: httpx.AsyncClient
    :docstring:
    :members: headers cookies params auth request get head options post put patch delete stream build_request send aclose


## `Response`

*An HTTP response.*

* `def __init__(...)`
* `.status_code` - **int**
* `.reason_phrase` - **str**
* `.http_version` - `"HTTP/2"` or `"HTTP/1.1"`
* `.url` - **URL**
* `.headers` - **Headers**
* `.content` - **bytes**
* `.text` - **str**
* `.encoding` - **str**
* `.is_redirect` - **bool**
* `.request` - **Request**
* `.next_request` - **Optional[Request]**
* `.cookies` - **Cookies**
* `.history` - **List[Response]**
* `.elapsed` - **[timedelta](https://docs.python.org/3/library/datetime.html)**
  * The amount of time elapsed between sending the request and calling `close()` on the corresponding response received for that request.
  [total_seconds()](https://docs.python.org/3/library/datetime.html#datetime.timedelta.total_seconds) to correctly get
  the total elapsed seconds.
* `def .raise_for_status()` - **Response**
* `def .json()` - **Any**
* `def .read()` - **bytes**
* `def .iter_raw([chunk_size])` - **bytes iterator**
* `def .iter_bytes([chunk_size])` - **bytes iterator**
* `def .iter_text([chunk_size])` - **text iterator**
* `def .iter_lines()` - **text iterator**
* `def .close()` - **None**
* `def .next()` - **Response**
* `def .aread()` - **bytes**
* `def .aiter_raw([chunk_size])` - **async bytes iterator**
* `def .aiter_bytes([chunk_size])` - **async bytes iterator**
* `def .aiter_text([chunk_size])` - **async text iterator**
* `def .aiter_lines()` - **async text iterator**
* `def .aclose()` - **None**
* `def .anext()` - **Response**

## `Request`

*An HTTP request. Can be constructed explicitly for more control over exactly
what gets sent over the wire.*

```pycon
>>> request = httpx.Request("GET", "https://example.org", headers={'host': 'example.org'})
>>> response = client.send(request)
```

* `def __init__(method, url, [params], [headers], [cookies], [content], [data], [files], [json], [stream])`
* `.method` - **str**
* `.url` - **URL**
* `.content` - **byte**, **byte iterator**, or **byte async iterator**
* `.headers` - **Headers**
* `.cookies` - **Cookies**

## `URL`

*A normalized, IDNA supporting URL.*

```pycon
>>> url = URL("https://example.org/")
>>> url.host
'example.org'
```

* `def __init__(url, **kwargs)`
* `.scheme` - **str**
* `.authority` - **str**
* `.host` - **str**
* `.port` - **int**
* `.path` - **str**
* `.query` - **str**
* `.raw_path` - **str**
* `.fragment` - **str**
* `.is_ssl` - **bool**
* `.is_absolute_url` - **bool**
* `.is_relative_url` - **bool**
* `def .copy_with([scheme], [authority], [path], [query], [fragment])` - **URL**

## `Headers`

*A case-insensitive multi-dict.*

```pycon
>>> headers = Headers({'Content-Type': 'application/json'})
>>> headers['content-type']
'application/json'
```

* `def __init__(self, headers, encoding=None)`
* `def copy()` - **Headers**

## `Cookies`

*A dict-like cookie store.*

```pycon
>>> cookies = Cookies()
>>> cookies.set("name", "value", domain="example.org")
```

* `def __init__(cookies: [dict, Cookies, CookieJar])`
* `.jar` - **CookieJar**
* `def extract_cookies(response)`
* `def set_cookie_header(request)`
* `def set(name, value, [domain], [path])`
* `def get(name, [domain], [path])`
* `def delete(name, [domain], [path])`
* `def clear([domain], [path])`
* *Standard mutable mapping interface*

## `Proxy`

*A configuration of the proxy server.*

```pycon
>>> proxy = Proxy("http://proxy.example.com:8030")
>>> client = Client(proxy=proxy)
```

* `def __init__(url, [ssl_context], [auth], [headers])`
* `.url` - **URL**
* `.auth` - **tuple[str, str]**
* `.headers` - **Headers**
* `.ssl_context` - **SSLContext**


================================================
FILE: docs/async.md
================================================
# Async Support

HTTPX offers a standard synchronous API by default, but also gives you
the option of an async client if you need it.

Async is a concurrency model that is far more efficient than multi-threading,
and can provide significant performance benefits and enable the use of
long-lived network connections such as WebSockets.

If you're working with an async web framework then you'll also want to use an
async client for sending outgoing HTTP requests.

## Making Async requests

To make asynchronous requests, you'll need an `AsyncClient`.

```pycon
>>> async with httpx.AsyncClient() as client:
...     r = await client.get('https://www.example.com/')
...
>>> r
<Response [200 OK]>
```

!!! tip
    Use [IPython](https://ipython.readthedocs.io/en/stable/) or Python 3.9+ with `python -m asyncio` to try this code interactively, as they support executing `async`/`await` expressions in the console.

## API Differences

If you're using an async client then there are a few bits of API that
use async methods.

### Making requests

The request methods are all async, so you should use `response = await client.get(...)` style for all of the following:

* `AsyncClient.get(url, ...)`
* `AsyncClient.options(url, ...)`
* `AsyncClient.head(url, ...)`
* `AsyncClient.post(url, ...)`
* `AsyncClient.put(url, ...)`
* `AsyncClient.patch(url, ...)`
* `AsyncClient.delete(url, ...)`
* `AsyncClient.request(method, url, ...)`
* `AsyncClient.send(request, ...)`

### Opening and closing clients

Use `async with httpx.AsyncClient()` if you want a context-managed client...

```python
async with httpx.AsyncClient() as client:
    ...
```

!!! warning
    In order to get the most benefit from connection pooling, make sure you're not instantiating multiple client instances - for example by using `async with` inside a "hot loop". This can be achieved either by having a single scoped client that's passed throughout wherever it's needed, or by having a single global client instance.

Alternatively, use `await client.aclose()` if you want to close a client explicitly:

```python
client = httpx.AsyncClient()
...
await client.aclose()
```

### Streaming responses

The `AsyncClient.stream(method, url, ...)` method is an async context block.

```pycon
>>> client = httpx.AsyncClient()
>>> async with client.stream('GET', 'https://www.example.com/') as response:
...     async for chunk in response.aiter_bytes():
...         ...
```

The async response streaming methods are:

* `Response.aread()` - For conditionally reading a response inside a stream block.
* `Response.aiter_bytes()` - For streaming the response content as bytes.
* `Response.aiter_text()` - For streaming the response content as text.
* `Response.aiter_lines()` - For streaming the response content as lines of text.
* `Response.aiter_raw()` - For streaming the raw response bytes, without applying content decoding.
* `Response.aclose()` - For closing the response. You don't usually need this, since `.stream` block closes the response automatically on exit.

For situations when context block usage is not practical, it is possible to enter "manual mode" by sending a [`Request` instance](advanced/clients.md#request-instances) using `client.send(..., stream=True)`.

Example in the context of forwarding the response to a streaming web endpoint with [Starlette](https://www.starlette.io):

```python
import httpx
from starlette.background import BackgroundTask
from starlette.responses import StreamingResponse

client = httpx.AsyncClient()

async def home(request):
    req = client.build_request("GET", "https://www.example.com/")
    r = await client.send(req, stream=True)
    return StreamingResponse(r.aiter_text(), background=BackgroundTask(r.aclose))
```

!!! warning
    When using this "manual streaming mode", it is your duty as a developer to make sure that `Response.aclose()` is called eventually. Failing to do so would leave connections open, most likely resulting in resource leaks down the line.

### Streaming requests

When sending a streaming request body with an `AsyncClient` instance, you should use an async bytes generator instead of a bytes generator:

```python
async def upload_bytes():
    ...  # yield byte content

await client.post(url, content=upload_bytes())
```

### Explicit transport instances

When instantiating a transport instance directly, you need to use `httpx.AsyncHTTPTransport`.

For instance:

```pycon
>>> import httpx
>>> transport = httpx.AsyncHTTPTransport(retries=1)
>>> async with httpx.AsyncClient(transport=transport) as client:
>>>     ...
```

## Supported async environments

HTTPX supports either `asyncio` or `trio` as an async environment.

It will auto-detect which of those two to use as the backend
for socket operations and concurrency primitives.

### [AsyncIO](https://docs.python.org/3/library/asyncio.html)

AsyncIO is Python's [built-in library](https://docs.python.org/3/library/asyncio.html)
for writing concurrent code with the async/await syntax.

```python
import asyncio
import httpx

async def main():
    async with httpx.AsyncClient() as client:
        response = await client.get('https://www.example.com/')
        print(response)

asyncio.run(main())
```

### [Trio](https://github.com/python-trio/trio)

Trio is [an alternative async library](https://trio.readthedocs.io/en/stable/),
designed around the [the principles of structured concurrency](https://en.wikipedia.org/wiki/Structured_concurrency).

```python
import httpx
import trio

async def main():
    async with httpx.AsyncClient() as client:
        response = await client.get('https://www.example.com/')
        print(response)

trio.run(main)
```

!!! important
    The `trio` package must be installed to use the Trio backend.


### [AnyIO](https://github.com/agronholm/anyio)

AnyIO is an [asynchronous networking and concurrency library](https://anyio.readthedocs.io/) that works on top of either `asyncio` or `trio`. It blends in with native libraries of your chosen backend (defaults to `asyncio`).

```python
import httpx
import anyio

async def main():
    async with httpx.AsyncClient() as client:
        response = await client.get('https://www.example.com/')
        print(response)

anyio.run(main, backend='trio')
```

## Calling into Python Web Apps

For details on calling directly into ASGI applications, see [the `ASGITransport` docs](../advanced/transports#asgitransport).

================================================
FILE: docs/code_of_conduct.md
================================================
# Code of Conduct

We expect contributors to our projects and online spaces to follow [the Python Software Foundation’s Code of Conduct](https://www.python.org/psf/conduct/).

The Python community is made up of members from around the globe with a diverse set of skills, personalities, and experiences. It is through these differences that our community experiences great successes and continued growth. When you're working with members of the community, this Code of Conduct will help steer your interactions and keep Python a positive, successful, and growing community.

## Our Community

Members of the Python community are **open, considerate, and respectful**. Behaviours that reinforce these values contribute to a positive environment, and include:

* **Being open.** Members of the community are open to collaboration, whether it's on PEPs, patches, problems, or otherwise.
* **Focusing on what is best for the community.** We're respectful of the processes set forth in the community, and we work within them.
* **Acknowledging time and effort.** We're respectful of the volunteer efforts that permeate the Python community. We're thoughtful when addressing the efforts of others, keeping in mind that often times the labor was completed simply for the good of the community.
* **Being respectful of differing viewpoints and experiences.** We're receptive to constructive comments and criticism, as the experiences and skill sets of other members contribute to the whole of our efforts.
* **Showing empathy towards other community members.** We're attentive in our communications, whether in person or online, and we're tactful when approaching differing views.
* **Being considerate.** Members of the community are considerate of their peers -- other Python users.
* **Being respectful.** We're respectful of others, their positions, their skills, their commitments, and their efforts.
* **Gracefully accepting constructive criticism.** When we disagree, we are courteous in raising our issues.
* **Using welcoming and inclusive language.** We're accepting of all who wish to take part in our activities, fostering an environment where anyone can participate and everyone can make a difference.

## Our Standards

Every member of our community has the right to have their identity respected. The Python community is dedicated to providing a positive experience for everyone, regardless of age, gender identity and expression, sexual orientation, disability, physical appearance, body size, ethnicity, nationality, race, or religion (or lack thereof), education, or socio-economic status.

## Inappropriate Behavior

Examples of unacceptable behavior by participants include:

* Harassment of any participants in any form
* Deliberate intimidation, stalking, or following
* Logging or taking screenshots of online activity for harassment purposes
* Publishing others' private information, such as a physical or electronic address, without explicit permission
* Violent threats or language directed against another person
* Incitement of violence or harassment towards any individual, including encouraging a person to commit suicide or to engage in self-harm
* Creating additional online accounts in order to harass another person or circumvent a ban
* Sexual language and imagery in online communities or in any conference venue, including talks
* Insults, put downs, or jokes that are based upon stereotypes, that are exclusionary, or that hold others up for ridicule
* Excessive swearing
* Unwelcome sexual attention or advances
* Unwelcome physical contact, including simulated physical contact (eg, textual descriptions like "hug" or "backrub") without consent or after a request to stop
* Pattern of inappropriate social contact, such as requesting/assuming inappropriate levels of intimacy with others
* Sustained disruption of online community discussions, in-person presentations, or other in-person events
* Continued one-on-one communication after requests to cease
* Other conduct that is inappropriate for a professional audience including people of many different backgrounds

Community members asked to stop any inappropriate behavior are expected to comply immediately.

## Enforcement

We take Code of Conduct violations seriously, and will act to ensure our spaces are welcoming, inclusive, and professional environments to communicate in.

If you need to raise a Code of Conduct report, you may do so privately by email to tom@tomchristie.com.

Reports will be treated confidentially.

Alternately you may [make a report to the Python Software Foundation](https://www.python.org/psf/conduct/reporting/).


================================================
FILE: docs/compatibility.md
================================================
# Requests Compatibility Guide

HTTPX aims to be broadly compatible with the `requests` API, although there are a
few design differences in places.

This documentation outlines places where the API differs...

## Redirects

Unlike `requests`, HTTPX does **not follow redirects by default**.

We differ in behaviour here [because auto-redirects can easily mask unnecessary network
calls being made](https://github.com/encode/httpx/discussions/1785).

You can still enable behaviour to automatically follow redirects, but you need to
do so explicitly...

```python
response = client.get(url, follow_redirects=True)
```

Or else instantiate a client, with redirect following enabled by default...

```python
client = httpx.Client(follow_redirects=True)
```

## Client instances

The HTTPX equivalent of `requests.Session` is `httpx.Client`.

```python
session = requests.Session(**kwargs)
```

is generally equivalent to

```python
client = httpx.Client(**kwargs)
```

## Request URLs

Accessing `response.url` will return a `URL` instance, rather than a string.

Use `str(response.url)` if you need a string instance.

## Determining the next redirect request

The `requests` library exposes an attribute `response.next`, which can be used to obtain the next redirect request.

```python
session = requests.Session()
request = requests.Request("GET", ...).prepare()
while request is not None:
    response = session.send(request, allow_redirects=False)
    request = response.next
```

In HTTPX, this attribute is instead named `response.next_request`. For example:

```python
client = httpx.Client()
request = client.build_request("GET", ...)
while request is not None:
    response = client.send(request)
    request = response.next_request
```

## Request Content

For uploading raw text or binary content we prefer to use a `content` parameter,
in order to better separate this usage from the case of uploading form data.

For example, using `content=...` to upload raw content:

```python
# Uploading text, bytes, or a bytes iterator.
httpx.post(..., content=b"Hello, world")
```

And using `data=...` to send form data:

```python
# Uploading form data.
httpx.post(..., data={"message": "Hello, world"})
```

Using the `data=<text/byte content>` will raise a deprecation warning,
and is expected to be fully removed with the HTTPX 1.0 release.

## Upload files

HTTPX strictly enforces that upload files must be opened in binary mode, in order
to avoid character encoding issues that can result from attempting to upload files
opened in text mode.

## Content encoding

HTTPX uses `utf-8` for encoding `str` request bodies. For example, when using `content=<str>` the request body will be encoded to `utf-8` before being sent over the wire. This differs from Requests which uses `latin1`. If you need an explicit encoding, pass encoded bytes explicitly, e.g. `content=<str>.encode("latin1")`.
For response bodies, assuming the server didn't send an explicit encoding then HTTPX will do its best to figure out an appropriate encoding. HTTPX makes a guess at the encoding to use for decoding the response using `charset_normalizer`. Fallback to that or any content with less than 32 octets will be decoded using `utf-8` with the `error="replace"` decoder strategy.

## Cookies

If using a client instance, then cookies should always be set on the client rather than on a per-request basis.

This usage is supported:

```python
client = httpx.Client(cookies=...)
client.post(...)
```

This usage is **not** supported:

```python
client = httpx.Client()
client.post(..., cookies=...)
```

We prefer enforcing a stricter API here because it provides clearer expectations around cookie persistence, particularly when redirects occur.

## Status Codes

In our documentation we prefer the uppercased versions, such as `codes.NOT_FOUND`, but also provide lower-cased versions for API compatibility with `requests`.

Requests includes various synonyms for status codes that HTTPX does not support.

## Streaming responses

HTTPX provides a `.stream()` interface rather than using `stream=True`. This ensures that streaming responses are always properly closed outside of the stream block, and makes it visually clearer at which points streaming I/O APIs may be used with a response.

For example:

```python
with httpx.stream("GET", "https://www.example.com") as response:
    ...
```

Within a `stream()` block request data is made available with:

* `.iter_bytes()` - Instead of `response.iter_content()`
* `.iter_text()` - Instead of `response.iter_content(decode_unicode=True)`
* `.iter_lines()` - Corresponding to `response.iter_lines()`
* `.iter_raw()` - Use this instead of `response.raw`
* `.read()` - Read the entire response body, making `response.text` and `response.content` available.

## Timeouts

HTTPX defaults to including reasonable [timeouts](quickstart.md#timeouts) for all network operations, while Requests has no timeouts by default.

To get the same behavior as Requests, set the `timeout` parameter to `None`:

```python
httpx.get('https://www.example.com', timeout=None)
```

## Proxy keys

HTTPX uses the mounts argument for HTTP proxying and transport routing.
It can do much more than proxies and allows you to configure more than just the proxy route.
For more detailed documentation, see [Mounting Transports](advanced/transports.md#mounting-transports).

When using `httpx.Client(mounts={...})` to map to a selection of different transports, we use full URL schemes, such as `mounts={"http://": ..., "https://": ...}`.

This is different to the `requests` usage of `proxies={"http": ..., "https": ...}`.

This change is for better consistency with more complex mappings, that might also include domain names, such as `mounts={"all://": ..., httpx.HTTPTransport(proxy="all://www.example.com": None})` which maps all requests onto a proxy, except for requests to "www.example.com" which have an explicit exclusion.

Also note that `requests.Session.request(...)` allows a `proxies=...` parameter, whereas `httpx.Client.request(...)` does not allow `mounts=...`.

## SSL configuration

When using a `Client` instance, the ssl configurations should always be passed on client instantiation, rather than passed to the request method.

If you need more than one different SSL configuration, you should use different client instances for each SSL configuration.

## Request body on HTTP methods

The HTTP `GET`, `DELETE`, `HEAD`, and `OPTIONS` methods are specified as not supporting a request body. To stay in line with this, the `.get`, `.delete`, `.head` and `.options` functions do not support `content`, `files`, `data`, or `json` arguments.

If you really do need to send request data using these http methods you should use the generic `.request` function instead.

```python
httpx.request(
  method="DELETE",
  url="https://www.example.com/",
  content=b'A request body on a DELETE request.'
)
```

## Checking for success and failure responses

We don't support `response.is_ok` since the naming is ambiguous there, and might incorrectly imply an equivalence to `response.status_code == codes.OK`. Instead we provide the `response.is_success` property, which can be used to check for a 2xx response.

## Request instantiation

There is no notion of [prepared requests](https://requests.readthedocs.io/en/stable/user/advanced/#prepared-requests) in HTTPX. If you need to customize request instantiation, see [Request instances](advanced/clients.md#request-instances).

Besides, `httpx.Request()` does not support the `auth`, `timeout`, `follow_redirects`, `mounts`, `verify` and `cert` parameters. However these are available in `httpx.request`, `httpx.get`, `httpx.post` etc., as well as on [`Client` instances](advanced/clients.md#client-instances).

## Mocking

If you need to mock HTTPX the same way that test utilities like `responses` and `requests-mock` does for `requests`, see [RESPX](https://github.com/lundberg/respx).

## Caching

If you use `cachecontrol` or `requests-cache` to add HTTP Caching support to the `requests` library, you can use [Hishel](https://hishel.com) for HTTPX.

## Networking layer

`requests` defers most of its HTTP networking code to the excellent [`urllib3` library](https://urllib3.readthedocs.io/en/latest/).

On the other hand, HTTPX uses [HTTPCore](https://github.com/encode/httpcore) as its core HTTP networking layer, which is a different project than `urllib3`.

## Query Parameters

`requests` omits `params` whose values are `None` (e.g. `requests.get(..., params={"foo": None})`). This is not supported by HTTPX.

For both query params (`params=`) and form data (`data=`), `requests` supports sending a list of tuples (e.g. `requests.get(..., params=[('key1', 'value1'), ('key1', 'value2')])`). This is not supported by HTTPX. Instead, use a dictionary with lists as values. E.g.: `httpx.get(..., params={'key1': ['value1', 'value2']})` or with form data: `httpx.post(..., data={'key1': ['value1', 'value2']})`.

## Event Hooks

`requests` allows event hooks to mutate `Request` and `Response` objects. See [examples](https://requests.readthedocs.io/en/master/user/advanced/#event-hooks) given in the documentation for `requests`.

In HTTPX, event hooks may access properties of requests and responses, but event hook callbacks cannot mutate the original request/response.

If you are looking for more control, consider checking out [Custom Transports](advanced/transports.md#custom-transports).

## Exceptions and Errors

`requests` exception hierarchy is slightly different to the `httpx` exception hierarchy. `requests` exposes a top level `RequestException`, where as `httpx` exposes a top level `HTTPError`. see the exceptions exposes in requests [here](https://requests.readthedocs.io/en/latest/_modules/requests/exceptions/). See the `httpx` error hierarchy [here](https://www.python-httpx.org/exceptions/).


================================================
FILE: docs/contributing.md
================================================
# Contributing

Thank you for being interested in contributing to HTTPX.
There are many ways you can contribute to the project:

- Try HTTPX and [report bugs/issues you find](https://github.com/encode/httpx/issues/new)
- [Implement new features](https://github.com/encode/httpx/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22)
- [Review Pull Requests of others](https://github.com/encode/httpx/pulls)
- Write documentation
- Participate in discussions

## Reporting Bugs or Other Issues

Found something that HTTPX should support?
Stumbled upon some unexpected behaviour?

Contributions should generally start out with [a discussion](https://github.com/encode/httpx/discussions).
Possible bugs may be raised as a "Potential Issue" discussion, feature requests may
be raised as an "Ideas" discussion. We can then determine if the discussion needs
to be escalated into an "Issue" or not, or if we'd consider a pull request.

Try to be more descriptive as you can and in case of a bug report,
provide as much information as possible like:

- OS platform
- Python version
- Installed dependencies and versions (`python -m pip freeze`)
- Code snippet
- Error traceback

You should always try to reduce any examples to the *simplest possible case*
that demonstrates the issue.

Some possibly useful tips for narrowing down potential issues...

- Does the issue exist on HTTP/1.1, or HTTP/2, or both?
- Does the issue exist with `Client`, `AsyncClient`, or both?
- When using `AsyncClient` does the issue exist when using `asyncio` or `trio`, or both?

## Development

To start developing HTTPX create a **fork** of the
[HTTPX repository](https://github.com/encode/httpx) on GitHub.

Then clone your fork with the following command replacing `YOUR-USERNAME` with
your GitHub username:

```shell
$ git clone https://github.com/YOUR-USERNAME/httpx
```

You can now install the project and its dependencies using:

```shell
$ cd httpx
$ scripts/install
```

## Testing and Linting

We use custom shell scripts to automate testing, linting,
and documentation building workflow.

To run the tests, use:

```shell
$ scripts/test
```

!!! warning
    The test suite spawns testing servers on ports **8000** and **8001**.
    Make sure these are not in use, so the tests can run properly.

Any additional arguments will be passed to `pytest`. See the [pytest documentation](https://docs.pytest.org/en/latest/how-to/usage.html) for more information.

For example, to run a single test script:

```shell
$ scripts/test tests/test_multipart.py
```

To run the code auto-formatting:

```shell
$ scripts/lint
```

Lastly, to run code checks separately (they are also run as part of `scripts/test`), run:

```shell
$ scripts/check
```

## Documenting

Documentation pages are located under the `docs/` folder.

To run the documentation site locally (useful for previewing changes), use:

```shell
$ scripts/docs
```

## Resolving Build / CI Failures

Once you've submitted your pull request, the test suite will automatically run, and the results will show up in GitHub.
If the test suite fails, you'll want to click through to the "Details" link, and try to identify why the test suite failed.

<p align="center" style="margin: 0 0 10px">
  <img src="https://raw.githubusercontent.com/encode/httpx/master/docs/img/gh-actions-fail.png" alt='Failing PR commit status'>
</p>

Here are some common ways the test suite can fail:

### Check Job Failed

<p align="center" style="margin: 0 0 10px">
  <img src="https://raw.githubusercontent.com/encode/httpx/master/docs/img/gh-actions-fail-check.png" alt='Failing GitHub action lint job'>
</p>

This job failing means there is either a code formatting issue or type-annotation issue.
You can look at the job output to figure out why it's failed or within a shell run:

```shell
$ scripts/check
```

It may be worth it to run `$ scripts/lint` to attempt auto-formatting the code
and if that job succeeds commit the changes.

### Docs Job Failed

This job failing means the documentation failed to build. This can happen for
a variety of reasons like invalid markdown or missing configuration within `mkdocs.yml`.

### Python 3.X Job Failed

<p align="center" style="margin: 0 0 10px">
  <img src="https://raw.githubusercontent.com/encode/httpx/master/docs/img/gh-actions-fail-test.png" alt='Failing GitHub action test job'>
</p>

This job failing means the unit tests failed or not all code paths are covered by unit tests.

If tests are failing you will see this message under the coverage report:

`=== 1 failed, 435 passed, 1 skipped, 1 xfailed in 11.09s ===`

If tests succeed but coverage doesn't reach our current threshold, you will see this
message under the coverage report:

`FAIL Required test coverage of 100% not reached. Total coverage: 99.00%`

## Releasing

*This section is targeted at HTTPX maintainers.*

Before releasing a new version, create a pull request that includes:

- **An update to the changelog**:
    - We follow the format from [keepachangelog](https://keepachangelog.com/en/1.0.0/).
    - [Compare](https://github.com/encode/httpx/compare/) `master` with the tag of the latest release, and list all entries that are of interest to our users:
        - Things that **must** go in the changelog: added, changed, deprecated or removed features, and bug fixes.
        - Things that **should not** go in the changelog: changes to documentation, tests or tooling.
        - Try sorting entries in descending order of impact / importance.
        - Keep it concise and to-the-point. 🎯
- **A version bump**: see `__version__.py`.

For an example, see [#1006](https://github.com/encode/httpx/pull/1006).

Once the release PR is merged, create a
[new release](https://github.com/encode/httpx/releases/new) including:

- Tag version like `0.13.3`.
- Release title `Version 0.13.3`
- Description copied from the changelog.

Once created this release will be automatically uploaded to PyPI.

If something goes wrong with the PyPI job the release can be published using the
`scripts/publish` script.

## Development proxy setup

To test and debug requests via a proxy it's best to run a proxy server locally.
Any server should do but HTTPCore's test suite uses
[`mitmproxy`](https://mitmproxy.org/) which is written in Python, it's fully
featured and has excellent UI and tools for introspection of requests.

You can install `mitmproxy` using `pip install mitmproxy` or [several
other ways](https://docs.mitmproxy.org/stable/overview-installation/).

`mitmproxy` does require setting up local TLS certificates for HTTPS requests,
as its main purpose is to allow developers to inspect requests that pass through
it. We can set them up follows:

1. [`pip install trustme-cli`](https://github.com/sethmlarson/trustme-cli/).
2. `trustme-cli -i example.org www.example.org`, assuming you want to test
connecting to that domain, this will create three files: `server.pem`,
`server.key` and `client.pem`.
3. `mitmproxy` requires a PEM file that includes the private key and the
certificate so we need to concatenate them:
`cat server.key server.pem > server.withkey.pem`.
4. Start the proxy server `mitmproxy --certs server.withkey.pem`, or use the
[other mitmproxy commands](https://docs.mitmproxy.org/stable/) with different
UI options.

At this point the server is ready to start serving requests, you'll need to
configure HTTPX as described in the
[proxy section](https://www.python-httpx.org/advanced/proxies/#http-proxies) and
the [SSL certificates section](https://www.python-httpx.org/advanced/ssl/),
this is where our previously generated `client.pem` comes in:

```python
ctx = ssl.create_default_context(cafile="/path/to/client.pem")
client = httpx.Client(proxy="http://127.0.0.1:8080/", verify=ctx)
```

Note, however, that HTTPS requests will only succeed to the host specified
in the SSL/TLS certificate we generated, HTTPS requests to other hosts will
raise an error like:

```
ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate
verify failed: Hostname mismatch, certificate is not valid for
'duckduckgo.com'. (_ssl.c:1108)
```

If you want to make requests to more hosts you'll need to regenerate the
certificates and include all the hosts you intend to connect to in the
seconds step, i.e.

`trustme-cli -i example.org www.example.org duckduckgo.com www.duckduckgo.com`


================================================
FILE: docs/css/custom.css
================================================
div.autodoc-docstring {
  padding-left: 20px;
  margin-bottom: 30px;
  border-left: 5px solid rgba(230, 230, 230);
}

div.autodoc-members {
  padding-left: 20px;
  margin-bottom: 15px;
}


================================================
FILE: docs/environment_variables.md
================================================
# Environment Variables

The HTTPX library can be configured via environment variables.
Environment variables are used by default. To ignore environment variables, `trust_env` has to be set `False`. There are two ways to set `trust_env` to disable environment variables:

* On the client via `httpx.Client(trust_env=False)`.
* Using the top-level API, such as `httpx.get("<url>", trust_env=False)`.

Here is a list of environment variables that HTTPX recognizes and what function they serve:

## Proxies

The environment variables documented below are used as a convention by various HTTP tooling, including:

* [cURL](https://github.com/curl/curl/blob/master/docs/MANUAL.md#environment-variables)
* [requests](https://github.com/psf/requests/blob/master/docs/user/advanced.rst#proxies)

For more information on using proxies in HTTPX, see [HTTP Proxying](advanced/proxies.md#http-proxying).

### `HTTP_PROXY`, `HTTPS_PROXY`, `ALL_PROXY`

Valid values: A URL to a proxy

`HTTP_PROXY`, `HTTPS_PROXY`, `ALL_PROXY` set the proxy to be used for `http`, `https`, or all requests respectively.

```bash
export HTTP_PROXY=http://my-external-proxy.com:1234

# This request will be sent through the proxy
python -c "import httpx; httpx.get('http://example.com')"

# This request will be sent directly, as we set `trust_env=False`
python -c "import httpx; httpx.get('http://example.com', trust_env=False)"

```

### `NO_PROXY`

Valid values: a comma-separated list of hostnames/urls

`NO_PROXY` disables the proxy for specific urls

```bash
export HTTP_PROXY=http://my-external-proxy.com:1234
export NO_PROXY=http://127.0.0.1,python-httpx.org

# As in the previous example, this request will be sent through the proxy
python -c "import httpx; httpx.get('http://example.com')"

# These requests will be sent directly, bypassing the proxy
python -c "import httpx; httpx.get('http://127.0.0.1:5000/my-api')"
python -c "import httpx; httpx.get('https://www.python-httpx.org')"
```

## `SSL_CERT_FILE`

Valid values: a filename

If this environment variable is set then HTTPX will load
CA certificate from the specified file instead of the default
location.

Example:

```console
SSL_CERT_FILE=/path/to/ca-certs/ca-bundle.crt python -c "import httpx; httpx.get('https://example.com')"
```

## `SSL_CERT_DIR`

Valid values: a directory following an [OpenSSL specific layout](https://www.openssl.org/docs/manmaster/man3/SSL_CTX_load_verify_locations.html).

If this environment variable is set and the directory follows an [OpenSSL specific layout](https://www.openssl.org/docs/manmaster/man3/SSL_CTX_load_verify_locations.html) (ie. you ran `c_rehash`) then HTTPX will load CA certificates from this directory instead of the default location.

Example:

```console
SSL_CERT_DIR=/path/to/ca-certs/ python -c "import httpx; httpx.get('https://example.com')"
```


================================================
FILE: docs/exceptions.md
================================================
# Exceptions

This page lists exceptions that may be raised when using HTTPX.

For an overview of how to work with HTTPX exceptions, see [Exceptions (Quickstart)](quickstart.md#exceptions).

## The exception hierarchy

* HTTPError
    * RequestError
        * TransportError
            * TimeoutException
                * ConnectTimeout
                * ReadTimeout
                * WriteTimeout
                * PoolTimeout
            * NetworkError
                * ConnectError
                * ReadError
                * WriteError
                * CloseError
            * ProtocolError
                * LocalProtocolError
                * RemoteProtocolError
            * ProxyError
            * UnsupportedProtocol
        * DecodingError
        * TooManyRedirects
    * HTTPStatusError
* InvalidURL
* CookieConflict
* StreamError
    * StreamConsumed
    * ResponseNotRead
    * RequestNotRead
    * StreamClosed

---

## Exception classes

::: httpx.HTTPError
    :docstring:

::: httpx.RequestError
    :docstring:

::: httpx.TransportError
    :docstring:

::: httpx.TimeoutException
    :docstring:

::: httpx.ConnectTimeout
    :docstring:

::: httpx.ReadTimeout
    :docstring:

::: httpx.WriteTimeout
    :docstring:

::: httpx.PoolTimeout
    :docstring:

::: httpx.NetworkError
    :docstring:

::: httpx.ConnectError
    :docstring:

::: httpx.ReadError
    :docstring:

::: httpx.WriteError
    :docstring:

::: httpx.CloseError
    :docstring:

::: httpx.ProtocolError
    :docstring:

::: httpx.LocalProtocolError
    :docstring:

::: httpx.RemoteProtocolError
    :docstring:

::: httpx.ProxyError
    :docstring:

::: httpx.UnsupportedProtocol
    :docstring:

::: httpx.DecodingError
    :docstring:

::: httpx.TooManyRedirects
    :docstring:

::: httpx.HTTPStatusError
    :docstring:

::: httpx.InvalidURL
    :docstring:

::: httpx.CookieConflict
    :docstring:

::: httpx.StreamError
    :docstring:

::: httpx.StreamConsumed
    :docstring:

::: httpx.StreamClosed
    :docstring:

::: httpx.ResponseNotRead
    :docstring:

::: httpx.RequestNotRead
    :docstring:


================================================
FILE: docs/http2.md
================================================
# HTTP/2

HTTP/2 is a major new iteration of the HTTP protocol, that provides a far more
efficient transport, with potential performance benefits. HTTP/2 does not change
the core semantics of the request or response, but alters the way that data is
sent to and from the server.

Rather than the text format that HTTP/1.1 uses, HTTP/2 is a binary format.
The binary format provides full request and response multiplexing, and efficient
compression of HTTP headers. The stream multiplexing means that where HTTP/1.1
requires one TCP stream for each concurrent request, HTTP/2 allows a single TCP
stream to handle multiple concurrent requests.

HTTP/2 also provides support for functionality such as response prioritization,
and server push.

For a comprehensive guide to HTTP/2 you may want to check out "[http2 explained](https://http2-explained.haxx.se/)".

## Enabling HTTP/2

When using the `httpx` client, HTTP/2 support is not enabled by default, because
HTTP/1.1 is a mature, battle-hardened transport layer, and our HTTP/1.1
implementation may be considered the more robust option at this point in time.
It is possible that a future version of `httpx` may enable HTTP/2 support by default.

If you're issuing highly concurrent requests you might want to consider
trying out our HTTP/2 support. You can do so by first making sure to install
the optional HTTP/2 dependencies...

```shell
$ pip install httpx[http2]
```

And then instantiating a client with HTTP/2 support enabled:

```python
client = httpx.AsyncClient(http2=True)
...
```

You can also instantiate a client as a context manager, to ensure that all
HTTP connections are nicely scoped, and will be closed once the context block
is exited.

```python
async with httpx.AsyncClient(http2=True) as client:
    ...
```

HTTP/2 support is available on both `Client` and `AsyncClient`, although it's
typically more useful in async contexts if you're issuing lots of concurrent
requests.

## Inspecting the HTTP version

Enabling HTTP/2 support on the client does not *necessarily* mean that your
requests and responses will be transported over HTTP/2, since both the client
*and* the server need to support HTTP/2. If you connect to a server that only
supports HTTP/1.1 the client will use a standard HTTP/1.1 connection instead.

You can determine which version of the HTTP protocol was used by examining
the `.http_version` property on the response.

```python
client = httpx.AsyncClient(http2=True)
response = await client.get(...)
print(response.http_version)  # "HTTP/1.0", "HTTP/1.1", or "HTTP/2".
```


================================================
FILE: docs/index.md
================================================
<p align="center" style="margin: 0 0 10px">
  <img width="350" height="208" src="https://raw.githubusercontent.com/encode/httpx/master/docs/img/butterfly.png" alt='HTTPX'>
</p>

<h1 align="center" style="font-size: 3rem; margin: -15px 0">
HTTPX
</h1>

---

<div align="center">
<p>
<a href="https://github.com/encode/httpx/actions">
    <img src="https://github.com/encode/httpx/workflows/Test%20Suite/badge.svg" alt="Test Suite">
</a>
<a href="https://pypi.org/project/httpx/">
    <img src="https://badge.fury.io/py/httpx.svg" alt="Package version">
</a>
</p>

<em>A next-generation HTTP client for Python.</em>
</div>

HTTPX is a fully featured HTTP client for Python 3, which provides sync and async APIs, and support for both HTTP/1.1 and HTTP/2.

---

Install HTTPX using pip:

```shell
$ pip install httpx
```

Now, let's get started:

```pycon
>>> import httpx
>>> r = httpx.get('https://www.example.org/')
>>> r
<Response [200 OK]>
>>> r.status_code
200
>>> r.headers['content-type']
'text/html; charset=UTF-8'
>>> r.text
'<!doctype html>\n<html>\n<head>\n<title>Example Domain</title>...'
```

Or, using the command-line client.

```shell
# The command line client is an optional dependency.
$ pip install 'httpx[cli]'
```

Which now allows us to use HTTPX directly from the command-line...

![httpx --help](img/httpx-help.png)

Sending a request...

![httpx http://httpbin.org/json](img/httpx-request.png)

## Features

HTTPX builds on the well-established usability of `requests`, and gives you:

* A broadly [requests-compatible API](compatibility.md).
* Standard synchronous interface, but with [async support if you need it](async.md).
* HTTP/1.1 [and HTTP/2 support](http2.md).
* Ability to make requests directly to [WSGI applications](advanced/transports.md#wsgi-transport) or [ASGI applications](advanced/transports.md#asgi-transport).
* Strict timeouts everywhere.
* Fully type annotated.
* 100% test coverage.

Plus all the standard features of `requests`...

* International Domains and URLs
* Keep-Alive & Connection Pooling
* Sessions with Cookie Persistence
* Browser-style SSL Verification
* Basic/Digest Authentication
* Elegant Key/Value Cookies
* Automatic Decompression
* Automatic Content Decoding
* Unicode Response Bodies
* Multipart File Uploads
* HTTP(S) Proxy Support
* Connection Timeouts
* Streaming Downloads
* .netrc Support
* Chunked Requests

## Documentation

For a run-through of all the basics, head over to the [QuickStart](quickstart.md).

For more advanced topics, see the **Advanced** section,
the [async support](async.md) section, or the [HTTP/2](http2.md) section.

The [Developer Interface](api.md) provides a comprehensive API reference.

To find out about tools that integrate with HTTPX, see [Third Party Packages](third_party_packages.md).

## Dependencies

The HTTPX project relies on these excellent libraries:

* `httpcore` - The underlying transport implementation for `httpx`.
  * `h11` - HTTP/1.1 support.
* `certifi` - SSL certificates.
* `idna` - Internationalized domain name support.
* `sniffio` - Async library autodetection.

As well as these optional installs:

* `h2` - HTTP/2 support. *(Optional, with `httpx[http2]`)*
* `socksio` - SOCKS proxy support. *(Optional, with `httpx[socks]`)*
* `rich` - Rich terminal support. *(Optional, with `httpx[cli]`)*
* `click` - Command line client support. *(Optional, with `httpx[cli]`)*
* `brotli` or `brotlicffi` - Decoding for "brotli" compressed responses. *(Optional, with `httpx[brotli]`)*
* `zstandard` - Decoding for "zstd" compressed responses. *(Optional, with `httpx[zstd]`)*

A huge amount of credit is due to `requests` for the API layout that
much of this work follows, as well as to `urllib3` for plenty of design
inspiration around the lower-level networking details.

## Installation

Install with pip:

```shell
$ pip install httpx
```

Or, to include the optional HTTP/2 support, use:

```shell
$ pip install httpx[http2]
```

To include the optional brotli and zstandard decoders support, use:

```shell
$ pip install httpx[brotli,zstd]
```

HTTPX requires Python 3.9+

[sync-support]: https://github.com/encode/httpx/issues/572


================================================
FILE: docs/logging.md
================================================
# Logging

If you need to inspect the internal behaviour of `httpx`, you can use Python's standard logging to output information about the underlying network behaviour.

For example, the following configuration...

```python
import logging
import httpx

logging.basicConfig(
    format="%(levelname)s [%(asctime)s] %(name)s - %(message)s",
    datefmt="%Y-%m-%d %H:%M:%S",
    level=logging.DEBUG
)

httpx.get("https://www.example.com")
```

Will send debug level output to the console, or wherever `stdout` is directed too...

```
DEBUG [2024-09-28 17:27:40] httpcore.connection - connect_tcp.started host='www.example.com' port=443 local_address=None timeout=5.0 socket_options=None
DEBUG [2024-09-28 17:27:41] httpcore.connection - connect_tcp.complete return_value=<httpcore._backends.sync.SyncStream object at 0x101f1e8e0>
DEBUG [2024-09-28 17:27:41] httpcore.connection - start_tls.started ssl_context=SSLContext(verify=True) server_hostname='www.example.com' timeout=5.0
DEBUG [2024-09-28 17:27:41] httpcore.connection - start_tls.complete return_value=<httpcore._backends.sync.SyncStream object at 0x1020f49a0>
DEBUG [2024-09-28 17:27:41] httpcore.http11 - send_request_headers.started request=<Request [b'GET']>
DEBUG [2024-09-28 17:27:41] httpcore.http11 - send_request_headers.complete
DEBUG [2024-09-28 17:27:41] httpcore.http11 - send_request_body.started request=<Request [b'GET']>
DEBUG [2024-09-28 17:27:41] httpcore.http11 - send_request_body.complete
DEBUG [2024-09-28 17:27:41] httpcore.http11 - receive_response_headers.started request=<Request [b'GET']>
DEBUG [2024-09-28 17:27:41] httpcore.http11 - receive_response_headers.complete return_value=(b'HTTP/1.1', 200, b'OK', [(b'Content-Encoding', b'gzip'), (b'Accept-Ranges', b'bytes'), (b'Age', b'407727'), (b'Cache-Control', b'max-age=604800'), (b'Content-Type', b'text/html; charset=UTF-8'), (b'Date', b'Sat, 28 Sep 2024 13:27:42 GMT'), (b'Etag', b'"3147526947+gzip"'), (b'Expires', b'Sat, 05 Oct 2024 13:27:42 GMT'), (b'Last-Modified', b'Thu, 17 Oct 2019 07:18:26 GMT'), (b'Server', b'ECAcc (dcd/7D43)'), (b'Vary', b'Accept-Encoding'), (b'X-Cache', b'HIT'), (b'Content-Length', b'648')])
INFO [2024-09-28 17:27:41] httpx - HTTP Request: GET https://www.example.com "HTTP/1.1 200 OK"
DEBUG [2024-09-28 17:27:41] httpcore.http11 - receive_response_body.started request=<Request [b'GET']>
DEBUG [2024-09-28 17:27:41] httpcore.http11 - receive_response_body.complete
DEBUG [2024-09-28 17:27:41] httpcore.http11 - response_closed.started
DEBUG [2024-09-28 17:27:41] httpcore.http11 - response_closed.complete
DEBUG [2024-09-28 17:27:41] httpcore.connection - close.started
DEBUG [2024-09-28 17:27:41] httpcore.connection - close.complete
```

Logging output includes information from both the high-level `httpx` logger, and the network-level `httpcore` logger, which can be configured separately.

For handling more complex logging configurations you might want to use the dictionary configuration style...

```python
import logging.config
import httpx

LOGGING_CONFIG = {
    "version": 1,
    "handlers": {
        "default": {
            "class": "logging.StreamHandler",
            "formatter": "http",
            "stream": "ext://sys.stderr"
        }
    },
    "formatters": {
        "http": {
            "format": "%(levelname)s [%(asctime)s] %(name)s - %(message)s",
            "datefmt": "%Y-%m-%d %H:%M:%S",
        }
    },
    'loggers': {
        'httpx': {
            'handlers': ['default'],
            'level': 'DEBUG',
        },
        'httpcore': {
            'handlers': ['default'],
            'level': 'DEBUG',
        },
    }
}

logging.config.dictConfig(LOGGING_CONFIG)
httpx.get('https://www.example.com')
```

The exact formatting of the debug logging may be subject to change across different versions of `httpx` and `httpcore`. If you need to rely on a particular format it is recommended that you pin installation of these packages to fixed versions.


================================================
FILE: docs/overrides/partials/nav.html
================================================
{% import "partials/nav-item.html" as item with context %}

<!-- Determine class according to configuration -->
 {% set class = "md-nav md-nav--primary" %}
 {% if "navigation.tabs" in features %}
   {% set class = class ~ " md-nav--lifted" %}
 {% endif %}
 {% if "toc.integrate" in features %}
   {% set class = class ~ " md-nav--integrated" %}
 {% endif %}

 <!-- Main navigation -->
 <nav
   class="{{ class }}"
   aria-label="{{ lang.t('nav.title') }}"
   data-md-level="0"
 >

   <!-- Site title -->
   <label class="md-nav__title" for="__drawer">
     <a
       href="{{ config.extra.homepage | d(nav.homepage.url, true) | url }}"
       title="{{ config.site_name | e }}"
       class="md-nav__button md-logo"
       aria-label="{{ config.site_name }}"
       data-md-component="logo"
     >
       {% include "partials/logo.html" %}
     </a>
     {{ config.site_name }}
   </label>

   <!-- Repository information -->
   {% if config.repo_url %}
     <div class="md-nav__source">
       {% include "partials/source.html" %}
     </div>
   {% endif %}

   <!-- Navigation list -->
   <ul class="md-nav__list" data-md-scrollfix>
     {% for nav_item in nav %}
       {% set path = "__nav_" ~ loop.index %}
       {{ item.render(nav_item, path, 1) }}
     {% endfor %}
   </ul>

   <ul class="md-nav__list" data-md-scrollfix style="padding-top: 15px; padding-left: 10px">
     <div>
       <a href="https://speakeasy.com"><img src="/img/speakeasy.png" width=150px style=></img></a>
     </div>
   </ul>
 </nav>
 

================================================
FILE: docs/quickstart.md
================================================
# QuickStart

First, start by importing HTTPX:

```pycon
>>> import httpx
```

Now, let’s try to get a webpage.

```pycon
>>> r = httpx.get('https://httpbin.org/get')
>>> r
<Response [200 OK]>
```

Similarly, to make an HTTP POST request:

```pycon
>>> r = httpx.post('https://httpbin.org/post', data={'key': 'value'})
```

The PUT, DELETE, HEAD, and OPTIONS requests all follow the same style:

```pycon
>>> r = httpx.put('https://httpbin.org/put', data={'key': 'value'})
>>> r = httpx.delete('https://httpbin.org/delete')
>>> r = httpx.head('https://httpbin.org/get')
>>> r = httpx.options('https://httpbin.org/get')
```

## Passing Parameters in URLs

To include URL query parameters in the request, use the `params` keyword:

```pycon
>>> params = {'key1': 'value1', 'key2': 'value2'}
>>> r = httpx.get('https://httpbin.org/get', params=params)
```

To see how the values get encoding into the URL string, we can inspect the
resulting URL that was used to make the request:

```pycon
>>> r.url
URL('https://httpbin.org/get?key2=value2&key1=value1')
```

You can also pass a list of items as a value:

```pycon
>>> params = {'key1': 'value1', 'key2': ['value2', 'value3']}
>>> r = httpx.get('https://httpbin.org/get', params=params)
>>> r.url
URL('https://httpbin.org/get?key1=value1&key2=value2&key2=value3')
```

## Response Content

HTTPX will automatically handle decoding the response content into Unicode text.

```pycon
>>> r = httpx.get('https://www.example.org/')
>>> r.text
'<!doctype html>\n<html>\n<head>\n<title>Example Domain</title>...'
```

You can inspect what encoding will be used to decode the response.

```pycon
>>> r.encoding
'UTF-8'
```

In some cases the response may not contain an explicit encoding, in which case HTTPX
will attempt to automatically determine an encoding to use.

```pycon
>>> r.encoding
None
>>> r.text
'<!doctype html>\n<html>\n<head>\n<title>Example Domain</title>...'
```

If you need to override the standard behaviour and explicitly set the encoding to
use, then you can do that too.

```pycon
>>> r.encoding = 'ISO-8859-1'
```

## Binary Response Content

The response content can also be accessed as bytes, for non-text responses:

```pycon
>>> r.content
b'<!doctype html>\n<html>\n<head>\n<title>Example Domain</title>...'
```

Any `gzip` and `deflate` HTTP response encodings will automatically
be decoded for you. If `brotlipy` is installed, then the `brotli` response
encoding will be supported. If `zstandard` is installed, then `zstd`
response encodings will also be supported.

For example, to create an image from binary data returned by a request, you can use the following code:

```pycon
>>> from PIL import Image
>>> from io import BytesIO
>>> i = Image.open(BytesIO(r.content))
```

## JSON Response Content

Often Web API responses will be encoded as JSON.

```pycon
>>> r = httpx.get('https://api.github.com/events')
>>> r.json()
[{u'repository': {u'open_issues': 0, u'url': 'https://github.com/...' ...  }}]
```

## Custom Headers

To include additional headers in the outgoing request, use the `headers` keyword argument:

```pycon
>>> url = 'https://httpbin.org/headers'
>>> headers = {'user-agent': 'my-app/0.0.1'}
>>> r = httpx.get(url, headers=headers)
```

## Sending Form Encoded Data

Some types of HTTP requests, such as `POST` and `PUT` requests, can include data
in the request body. One common way of including that is as form-encoded data,
which is used for HTML forms.

```pycon
>>> data = {'key1': 'value1', 'key2': 'value2'}
>>> r = httpx.post("https://httpbin.org/post", data=data)
>>> print(r.text)
{
  ...
  "form": {
    "key2": "value2",
    "key1": "value1"
  },
  ...
}
```

Form encoded data can also include multiple values from a given key.

```pycon
>>> data = {'key1': ['value1', 'value2']}
>>> r = httpx.post("https://httpbin.org/post", data=data)
>>> print(r.text)
{
  ...
  "form": {
    "key1": [
      "value1",
      "value2"
    ]
  },
  ...
}
```

## Sending Multipart File Uploads

You can also upload files, using HTTP multipart encoding:

```pycon
>>> with open('report.xls', 'rb') as report_file:
...     files = {'upload-file': report_file}
...     r = httpx.post("https://httpbin.org/post", files=files)
>>> print(r.text)
{
  ...
  "files": {
    "upload-file": "<... binary content ...>"
  },
  ...
}
```

You can also explicitly set the filename and content type, by using a tuple
of items for the file value:

```pycon
>>> with open('report.xls', 'rb') as report_file:
...     files = {'upload-file': ('report.xls', report_file, 'application/vnd.ms-excel')}
...     r = httpx.post("https://httpbin.org/post", files=files)
>>> print(r.text)
{
  ...
  "files": {
    "upload-file": "<... binary content ...>"
  },
  ...
}
```

If you need to include non-file data fields in the multipart form, use the `data=...` parameter:

```pycon
>>> data = {'message': 'Hello, world!'}
>>> with open('report.xls', 'rb') as report_file:
...     files = {'file': report_file}
...     r = httpx.post("https://httpbin.org/post", data=data, files=files)
>>> print(r.text)
{
  ...
  "files": {
    "file": "<... binary content ...>"
  },
  "form": {
    "message": "Hello, world!",
  },
  ...
}
```

## Sending JSON Encoded Data

Form encoded data is okay if all you need is a simple key-value data structure.
For more complicated data structures you'll often want to use JSON encoding instead.

```pycon
>>> data = {'integer': 123, 'boolean': True, 'list': ['a', 'b', 'c']}
>>> r = httpx.post("https://httpbin.org/post", json=data)
>>> print(r.text)
{
  ...
  "json": {
    "boolean": true,
    "integer": 123,
    "list": [
      "a",
      "b",
      "c"
    ]
  },
  ...
}
```

## Sending Binary Request Data

For other encodings, you should use the `content=...` parameter, passing
either a `bytes` type or a generator that yields `bytes`.

```pycon
>>> content = b'Hello, world'
>>> r = httpx.post("https://httpbin.org/post", content=content)
```

You may also want to set a custom `Content-Type` header when uploading
binary data.

## Response Status Codes

We can inspect the HTTP status code of the response:

```pycon
>>> r = httpx.get('https://httpbin.org/get')
>>> r.status_code
200
```

HTTPX also includes an easy shortcut for accessing status codes by their text phrase.

```pycon
>>> r.status_code == httpx.codes.OK
True
```

We can raise an exception for any responses which are not a 2xx success code:

```pycon
>>> not_found = httpx.get('https://httpbin.org/status/404')
>>> not_found.status_code
404
>>> not_found.raise_for_status()
Traceback (most recent call last):
  File "/Users/tomchristie/GitHub/encode/httpcore/httpx/models.py", line 837, in raise_for_status
    raise HTTPStatusError(message, response=self)
httpx._exceptions.HTTPStatusError: 404 Client Error: Not Found for url: https://httpbin.org/status/404
For more information check: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/404
```

Any successful response codes will return the `Response` instance rather than raising an exception.

```pycon
>>> r.raise_for_status()
```

The method returns the response instance, allowing you to use it inline. For example:

```pycon
>>> r = httpx.get('...').raise_for_status()
>>> data = httpx.get('...').raise_for_status().json()
```

## Response Headers

The response headers are available as a dictionary-like interface.

```pycon
>>> r.headers
Headers({
    'content-encoding': 'gzip',
    'transfer-encoding': 'chunked',
    'connection': 'close',
    'server': 'nginx/1.0.4',
    'x-runtime': '148ms',
    'etag': '"e1ca502697e5c9317743dc078f67693f"',
    'content-type': 'application/json'
})
```

The `Headers` data type is case-insensitive, so you can use any capitalization.

```pycon
>>> r.headers['Content-Type']
'application/json'

>>> r.headers.get('content-type')
'application/json'
```

Multiple values for a single response header are represented as a single comma-separated value, as per [RFC 7230](https://tools.ietf.org/html/rfc7230#section-3.2):

> A recipient MAY combine multiple header fields with the same field name into one “field-name: field-value” pair, without changing the semantics of the message, by appending each subsequent field-value to the combined field value in order, separated by a comma.

## Streaming Responses

For large downloads you may want to use streaming responses that do not load the entire response body into memory at once.

You can stream the binary content of the response...

```pycon
>>> with httpx.stream("GET", "https://www.example.com") as r:
...     for data in r.iter_bytes():
...         print(data)
```

Or the text of the response...

```pycon
>>> with httpx.stream("GET", "https://www.example.com") as r:
...     for text in r.iter_text():
...         print(text)
```

Or stream the text, on a line-by-line basis...

```pycon
>>> with httpx.stream("GET", "https://www.example.com") as r:
...     for line in r.iter_lines():
...         print(line)
```

HTTPX will use universal line endings, normalising all cases to `\n`.

In some cases you might want to access the raw bytes on the response without applying any HTTP content decoding. In this case any content encoding that the web server has applied such as `gzip`, `deflate`, `brotli`, or `zstd` will
not be automatically decoded.

```pycon
>>> with httpx.stream("GET", "https://www.example.com") as r:
...     for chunk in r.iter_raw():
...         print(chunk)
```

If you're using streaming responses in any of these ways then the `response.content` and `response.text` attributes will not be available, and will raise errors if accessed. However you can also use the response streaming functionality to conditionally load the response body:

```pycon
>>> with httpx.stream("GET", "https://www.example.com") as r:
...     if int(r.headers['Content-Length']) < TOO_LONG:
...         r.read()
...         print(r.text)
```

## Cookies

Any cookies that are set on the response can be easily accessed:

```pycon
>>> r = httpx.get('https://httpbin.org/cookies/set?chocolate=chip')
>>> r.cookies['chocolate']
'chip'
```

To include cookies in an outgoing request, use the `cookies` parameter:

```pycon
>>> cookies = {"peanut": "butter"}
>>> r = httpx.get('https://httpbin.org/cookies', cookies=cookies)
>>> r.json()
{'cookies': {'peanut': 'butter'}}
```

Cookies are returned in a `Cookies` instance, which is a dict-like data structure
with additional API for accessing cookies by their domain or path.

```pycon
>>> cookies = httpx.Cookies()
>>> cookies.set('cookie_on_domain', 'hello, there!', domain='httpbin.org')
>>> cookies.set('cookie_off_domain', 'nope.', domain='example.org')
>>> r = httpx.get('http://httpbin.org/cookies', cookies=cookies)
>>> r.json()
{'cookies': {'cookie_on_domain': 'hello, there!'}}
```

## Redirection and History

By default, HTTPX will **not** follow redirects for all HTTP methods, although
this can be explicitly enabled.

For example, GitHub redirects all HTTP requests to HTTPS.

```pycon
>>> r = httpx.get('http://github.com/')
>>> r.status_code
301
>>> r.history
[]
>>> r.next_request
<Request('GET', 'https://github.com/')>
```

You can modify the default redirection handling with the `follow_redirects` parameter:

```pycon
>>> r = httpx.get('http://github.com/', follow_redirects=True)
>>> r.url
URL('https://github.com/')
>>> r.status_code
200
>>> r.history
[<Response [301 Moved Permanently]>]
```

The `history` property of the response can be used to inspect any followed redirects.
It contains a list of any redirect responses that were followed, in the order
in which they were made.

## Timeouts

HTTPX defaults to including reasonable timeouts for all network operations,
meaning that if a connection is not properly established then it should always
raise an error rather than hanging indefinitely.

The default timeout for network inactivity is five seconds. You can modify the
value to be more or less strict:

```pycon
>>> httpx.get('https://github.com/', timeout=0.001)
```

You can also disable the timeout behavior completely...

```pycon
>>> httpx.get('https://github.com/', timeout=None)
```

For advanced timeout management, see [Timeout fine-tuning](advanced/timeouts.md#fine-tuning-the-configuration).

## Authentication

HTTPX supports Basic and Digest HTTP authentication.

To provide Basic authentication credentials, pass a 2-tuple of
plaintext `str` or `bytes` objects as the `auth` argument to the request
functions:

```pycon
>>> httpx.get("htt
Download .txt
gitextract_2vanwzu3/

├── .github/
│   ├── CONTRIBUTING.md
│   ├── FUNDING.yml
│   ├── ISSUE_TEMPLATE/
│   │   ├── 1-issue.md
│   │   └── config.yml
│   ├── PULL_REQUEST_TEMPLATE.md
│   ├── dependabot.yml
│   └── workflows/
│       ├── publish.yml
│       └── test-suite.yml
├── .gitignore
├── CHANGELOG.md
├── LICENSE.md
├── README.md
├── docs/
│   ├── CNAME
│   ├── advanced/
│   │   ├── authentication.md
│   │   ├── clients.md
│   │   ├── event-hooks.md
│   │   ├── extensions.md
│   │   ├── proxies.md
│   │   ├── resource-limits.md
│   │   ├── ssl.md
│   │   ├── text-encodings.md
│   │   ├── timeouts.md
│   │   └── transports.md
│   ├── api.md
│   ├── async.md
│   ├── code_of_conduct.md
│   ├── compatibility.md
│   ├── contributing.md
│   ├── css/
│   │   └── custom.css
│   ├── environment_variables.md
│   ├── exceptions.md
│   ├── http2.md
│   ├── index.md
│   ├── logging.md
│   ├── overrides/
│   │   └── partials/
│   │       └── nav.html
│   ├── quickstart.md
│   ├── third_party_packages.md
│   └── troubleshooting.md
├── httpx/
│   ├── __init__.py
│   ├── __version__.py
│   ├── _api.py
│   ├── _auth.py
│   ├── _client.py
│   ├── _config.py
│   ├── _content.py
│   ├── _decoders.py
│   ├── _exceptions.py
│   ├── _main.py
│   ├── _models.py
│   ├── _multipart.py
│   ├── _status_codes.py
│   ├── _transports/
│   │   ├── __init__.py
│   │   ├── asgi.py
│   │   ├── base.py
│   │   ├── default.py
│   │   ├── mock.py
│   │   └── wsgi.py
│   ├── _types.py
│   ├── _urlparse.py
│   ├── _urls.py
│   ├── _utils.py
│   └── py.typed
├── mkdocs.yml
├── pyproject.toml
├── requirements.txt
├── scripts/
│   ├── build
│   ├── check
│   ├── clean
│   ├── coverage
│   ├── docs
│   ├── install
│   ├── lint
│   ├── publish
│   ├── sync-version
│   └── test
└── tests/
    ├── __init__.py
    ├── client/
    │   ├── __init__.py
    │   ├── test_async_client.py
    │   ├── test_auth.py
    │   ├── test_client.py
    │   ├── test_cookies.py
    │   ├── test_event_hooks.py
    │   ├── test_headers.py
    │   ├── test_properties.py
    │   ├── test_proxies.py
    │   ├── test_queryparams.py
    │   └── test_redirects.py
    ├── common.py
    ├── concurrency.py
    ├── conftest.py
    ├── fixtures/
    │   ├── .netrc
    │   └── .netrc-nopassword
    ├── models/
    │   ├── __init__.py
    │   ├── test_cookies.py
    │   ├── test_headers.py
    │   ├── test_queryparams.py
    │   ├── test_requests.py
    │   ├── test_responses.py
    │   ├── test_url.py
    │   ├── test_whatwg.py
    │   └── whatwg.json
    ├── test_api.py
    ├── test_asgi.py
    ├── test_auth.py
    ├── test_config.py
    ├── test_content.py
    ├── test_decoders.py
    ├── test_exceptions.py
    ├── test_exported_members.py
    ├── test_main.py
    ├── test_multipart.py
    ├── test_status_codes.py
    ├── test_timeouts.py
    ├── test_utils.py
    └── test_wsgi.py
Download .txt
SYMBOL INDEX (1151 symbols across 54 files)

FILE: httpx/__init__.py
  function main (line 18) | def main() -> None:  # type: ignore

FILE: httpx/_api.py
  function request (line 39) | def request(
  function stream (line 124) | def stream(
  function get (line 174) | def get(
  function options (line 210) | def options(
  function head (line 246) | def head(
  function post (line 282) | def post(
  function put (line 323) | def put(
  function patch (line 364) | def patch(
  function delete (line 405) | def delete(

FILE: httpx/_auth.py
  class Auth (line 22) | class Auth:
    method auth_flow (line 38) | def auth_flow(self, request: Request) -> typing.Generator[Request, Res...
    method sync_auth_flow (line 62) | def sync_auth_flow(
    method async_auth_flow (line 87) | async def async_auth_flow(
  class FunctionAuth (line 113) | class FunctionAuth(Auth):
    method __init__ (line 119) | def __init__(self, func: typing.Callable[[Request], Request]) -> None:
    method auth_flow (line 122) | def auth_flow(self, request: Request) -> typing.Generator[Request, Res...
  class BasicAuth (line 126) | class BasicAuth(Auth):
    method __init__ (line 132) | def __init__(self, username: str | bytes, password: str | bytes) -> None:
    method auth_flow (line 135) | def auth_flow(self, request: Request) -> typing.Generator[Request, Res...
    method _build_auth_header (line 139) | def _build_auth_header(self, username: str | bytes, password: str | by...
  class NetRCAuth (line 145) | class NetRCAuth(Auth):
    method __init__ (line 150) | def __init__(self, file: str | None = None) -> None:
    method auth_flow (line 157) | def auth_flow(self, request: Request) -> typing.Generator[Request, Res...
    method _build_auth_header (line 169) | def _build_auth_header(self, username: str | bytes, password: str | by...
  class DigestAuth (line 175) | class DigestAuth(Auth):
    method __init__ (line 187) | def __init__(self, username: str | bytes, password: str | bytes) -> None:
    method auth_flow (line 193) | def auth_flow(self, request: Request) -> typing.Generator[Request, Res...
    method _parse_challenge (line 224) | def _parse_challenge(
    method _build_auth_header (line 255) | def _build_auth_header(
    method _get_client_nonce (line 303) | def _get_client_nonce(self, nonce_count: int, nonce: bytes) -> bytes:
    method _get_header_value (line 311) | def _get_header_value(self, header_fields: dict[str, bytes]) -> str:
    method _resolve_qop (line 329) | def _resolve_qop(self, qop: bytes | None, request: Request) -> bytes |...
  class _DigestAuthChallenge (line 343) | class _DigestAuthChallenge(typing.NamedTuple):

FILE: httpx/_client.py
  function _is_https_redirect (line 62) | def _is_https_redirect(url: URL, location: URL) -> bool:
  function _port_or_default (line 77) | def _port_or_default(url: URL) -> int | None:
  function _same_origin (line 83) | def _same_origin(url: URL, other: URL) -> bool:
  class UseClientDefault (line 94) | class UseClientDefault:
  class ClientState (line 125) | class ClientState(enum.Enum):
  class BoundSyncStream (line 139) | class BoundSyncStream(SyncByteStream):
    method __init__ (line 145) | def __init__(
    method __iter__ (line 152) | def __iter__(self) -> typing.Iterator[bytes]:
    method close (line 156) | def close(self) -> None:
  class BoundAsyncStream (line 162) | class BoundAsyncStream(AsyncByteStream):
    method __init__ (line 168) | def __init__(
    method __aiter__ (line 175) | async def __aiter__(self) -> typing.AsyncIterator[bytes]:
    method aclose (line 179) | async def aclose(self) -> None:
  class BaseClient (line 188) | class BaseClient:
    method __init__ (line 189) | def __init__(
    method is_closed (line 224) | def is_closed(self) -> bool:
    method trust_env (line 231) | def trust_env(self) -> bool:
    method _enforce_trailing_slash (line 234) | def _enforce_trailing_slash(self, url: URL) -> URL:
    method _get_proxy_map (line 239) | def _get_proxy_map(
    method timeout (line 254) | def timeout(self) -> Timeout:
    method timeout (line 258) | def timeout(self, timeout: TimeoutTypes) -> None:
    method event_hooks (line 262) | def event_hooks(self) -> dict[str, list[EventHook]]:
    method event_hooks (line 266) | def event_hooks(self, event_hooks: dict[str, list[EventHook]]) -> None:
    method auth (line 273) | def auth(self) -> Auth | None:
    method auth (line 284) | def auth(self, auth: AuthTypes) -> None:
    method base_url (line 288) | def base_url(self) -> URL:
    method base_url (line 295) | def base_url(self, url: URL | str) -> None:
    method headers (line 299) | def headers(self) -> Headers:
    method headers (line 306) | def headers(self, headers: HeaderTypes) -> None:
    method cookies (line 319) | def cookies(self) -> Cookies:
    method cookies (line 326) | def cookies(self, cookies: CookieTypes) -> None:
    method params (line 330) | def params(self) -> QueryParams:
    method params (line 337) | def params(self, params: QueryParamTypes) -> None:
    method build_request (line 340) | def build_request(
    method _merge_url (line 391) | def _merge_url(self, url: URL | str) -> URL:
    method _merge_cookies (line 413) | def _merge_cookies(self, cookies: CookieTypes | None = None) -> Cookie...
    method _merge_headers (line 424) | def _merge_headers(self, headers: HeaderTypes | None = None) -> Header...
    method _merge_queryparams (line 433) | def _merge_queryparams(
    method _build_auth (line 445) | def _build_auth(self, auth: AuthTypes | None) -> Auth | None:
    method _build_request_auth (line 457) | def _build_request_auth(
    method _build_redirect_request (line 475) | def _build_redirect_request(self, request: Request, response: Response...
    method _redirect_method (line 494) | def _redirect_method(self, request: Request, response: Response) -> str:
    method _redirect_url (line 517) | def _redirect_url(self, request: Request, response: Response) -> URL:
    method _redirect_headers (line 546) | def _redirect_headers(self, request: Request, url: URL, method: str) -...
    method _redirect_stream (line 573) | def _redirect_stream(
    method _set_timeout (line 584) | def _set_timeout(self, request: Request) -> None:
  class Client (line 594) | class Client(BaseClient):
    method __init__ (line 639) | def __init__(
    method _init_transport (line 718) | def _init_transport(
    method _init_proxy_transport (line 740) | def _init_proxy_transport(
    method _transport_for_url (line 760) | def _transport_for_url(self, url: URL) -> BaseTransport:
    method request (line 771) | def request(
    method stream (line 828) | def stream(
    method send (line 879) | def send(
    method _send_handling_auth (line 930) | def _send_handling_auth(
    method _send_handling_redirects (line 964) | def _send_handling_redirects(
    method _send_single_request (line 1001) | def _send_single_request(self, request: Request) -> Response:
    method get (line 1036) | def get(
    method options (line 1065) | def options(
    method head (line 1094) | def head(
    method post (line 1123) | def post(
    method put (line 1160) | def put(
    method patch (line 1197) | def patch(
    method delete (line 1234) | def delete(
    method close (line 1263) | def close(self) -> None:
    method __enter__ (line 1275) | def __enter__(self: T) -> T:
    method __exit__ (line 1293) | def __exit__(
  class AsyncClient (line 1307) | class AsyncClient(BaseClient):
    method __init__ (line 1353) | def __init__(
    method _init_transport (line 1432) | def _init_transport(
    method _init_proxy_transport (line 1454) | def _init_proxy_transport(
    method _transport_for_url (line 1474) | def _transport_for_url(self, url: URL) -> AsyncBaseTransport:
    method request (line 1485) | async def request(
    method stream (line 1543) | async def stream(
    method send (line 1594) | async def send(
    method _send_handling_auth (line 1645) | async def _send_handling_auth(
    method _send_handling_redirects (line 1679) | async def _send_handling_redirects(
    method _send_single_request (line 1717) | async def _send_single_request(self, request: Request) -> Response:
    method get (line 1751) | async def get(
    method options (line 1780) | async def options(
    method head (line 1809) | async def head(
    method post (line 1838) | async def post(
    method put (line 1875) | async def put(
    method patch (line 1912) | async def patch(
    method delete (line 1949) | async def delete(
    method aclose (line 1978) | async def aclose(self) -> None:
    method __aenter__ (line 1990) | async def __aenter__(self: U) -> U:
    method __aexit__ (line 2008) | async def __aexit__(

FILE: httpx/_config.py
  class UnsetType (line 16) | class UnsetType:
  function create_ssl_context (line 23) | def create_ssl_context(
  class Timeout (line 72) | class Timeout:
    method __init__ (line 86) | def __init__(
    method as_dict (line 132) | def as_dict(self) -> dict[str, float | None]:
    method __eq__ (line 140) | def __eq__(self, other: typing.Any) -> bool:
    method __repr__ (line 149) | def __repr__(self) -> str:
  class Limits (line 159) | class Limits:
    method __init__ (line 173) | def __init__(
    method __eq__ (line 184) | def __eq__(self, other: typing.Any) -> bool:
    method __repr__ (line 192) | def __repr__(self) -> str:
  class Proxy (line 201) | class Proxy:
    method __init__ (line 202) | def __init__(
    method raw_auth (line 227) | def raw_auth(self) -> tuple[bytes, bytes] | None:
    method __repr__ (line 235) | def __repr__(self) -> str:

FILE: httpx/_content.py
  class ByteStream (line 31) | class ByteStream(AsyncByteStream, SyncByteStream):
    method __init__ (line 32) | def __init__(self, stream: bytes) -> None:
    method __iter__ (line 35) | def __iter__(self) -> Iterator[bytes]:
    method __aiter__ (line 38) | async def __aiter__(self) -> AsyncIterator[bytes]:
  class IteratorByteStream (line 42) | class IteratorByteStream(SyncByteStream):
    method __init__ (line 45) | def __init__(self, stream: Iterable[bytes]) -> None:
    method __iter__ (line 50) | def __iter__(self) -> Iterator[bytes]:
  class AsyncIteratorByteStream (line 67) | class AsyncIteratorByteStream(AsyncByteStream):
    method __init__ (line 70) | def __init__(self, stream: AsyncIterable[bytes]) -> None:
    method __aiter__ (line 75) | async def __aiter__(self) -> AsyncIterator[bytes]:
  class UnattachedStream (line 92) | class UnattachedStream(AsyncByteStream, SyncByteStream):
    method __iter__ (line 99) | def __iter__(self) -> Iterator[bytes]:
    method __aiter__ (line 102) | async def __aiter__(self) -> AsyncIterator[bytes]:
  function encode_content (line 107) | def encode_content(
  function encode_urlencoded_data (line 136) | def encode_urlencoded_data(
  function encode_multipart_data (line 152) | def encode_multipart_data(
  function encode_text (line 160) | def encode_text(text: str) -> tuple[dict[str, str], ByteStream]:
  function encode_html (line 168) | def encode_html(html: str) -> tuple[dict[str, str], ByteStream]:
  function encode_json (line 176) | def encode_json(json: Any) -> tuple[dict[str, str], ByteStream]:
  function encode_request (line 186) | def encode_request(
  function encode_response (line 221) | def encode_response(

FILE: httpx/_decoders.py
  class ContentDecoder (line 36) | class ContentDecoder:
    method decode (line 37) | def decode(self, data: bytes) -> bytes:
    method flush (line 40) | def flush(self) -> bytes:
  class IdentityDecoder (line 44) | class IdentityDecoder(ContentDecoder):
    method decode (line 49) | def decode(self, data: bytes) -> bytes:
    method flush (line 52) | def flush(self) -> bytes:
  class DeflateDecoder (line 56) | class DeflateDecoder(ContentDecoder):
    method __init__ (line 63) | def __init__(self) -> None:
    method decode (line 67) | def decode(self, data: bytes) -> bytes:
    method flush (line 78) | def flush(self) -> bytes:
  class GZipDecoder (line 85) | class GZipDecoder(ContentDecoder):
    method __init__ (line 92) | def __init__(self) -> None:
    method decode (line 95) | def decode(self, data: bytes) -> bytes:
    method flush (line 101) | def flush(self) -> bytes:
  class BrotliDecoder (line 108) | class BrotliDecoder(ContentDecoder):
    method __init__ (line 118) | def __init__(self) -> None:
    method decode (line 136) | def decode(self, data: bytes) -> bytes:
    method flush (line 145) | def flush(self) -> bytes:
  class ZStandardDecoder (line 161) | class ZStandardDecoder(ContentDecoder):
    method __init__ (line 170) | def __init__(self) -> None:
    method decode (line 180) | def decode(self, data: bytes) -> bytes:
    method flush (line 194) | def flush(self) -> bytes:
  class MultiDecoder (line 203) | class MultiDecoder(ContentDecoder):
    method __init__ (line 208) | def __init__(self, children: typing.Sequence[ContentDecoder]) -> None:
    method decode (line 216) | def decode(self, data: bytes) -> bytes:
    method flush (line 221) | def flush(self) -> bytes:
  class ByteChunker (line 228) | class ByteChunker:
    method __init__ (line 233) | def __init__(self, chunk_size: int | None = None) -> None:
    method decode (line 237) | def decode(self, content: bytes) -> list[bytes]:
    method flush (line 260) | def flush(self) -> list[bytes]:
  class TextChunker (line 267) | class TextChunker:
    method __init__ (line 272) | def __init__(self, chunk_size: int | None = None) -> None:
    method decode (line 276) | def decode(self, content: str) -> list[str]:
    method flush (line 299) | def flush(self) -> list[str]:
  class TextDecoder (line 306) | class TextDecoder:
    method __init__ (line 311) | def __init__(self, encoding: str = "utf-8") -> None:
    method decode (line 314) | def decode(self, data: bytes) -> str:
    method flush (line 317) | def flush(self) -> str:
  class LineDecoder (line 321) | class LineDecoder:
    method __init__ (line 329) | def __init__(self) -> None:
    method decode (line 333) | def decode(self, text: str) -> list[str]:
    method flush (line 371) | def flush(self) -> list[str]:

FILE: httpx/_exceptions.py
  class HTTPError (line 74) | class HTTPError(Exception):
    method __init__ (line 92) | def __init__(self, message: str) -> None:
    method request (line 97) | def request(self) -> Request:
    method request (line 103) | def request(self, request: Request) -> None:
  class RequestError (line 107) | class RequestError(HTTPError):
    method __init__ (line 112) | def __init__(self, message: str, *, request: Request | None = None) ->...
  class TransportError (line 123) | class TransportError(RequestError):
  class TimeoutException (line 132) | class TimeoutException(TransportError):
  class ConnectTimeout (line 140) | class ConnectTimeout(TimeoutException):
  class ReadTimeout (line 146) | class ReadTimeout(TimeoutException):
  class WriteTimeout (line 152) | class WriteTimeout(TimeoutException):
  class PoolTimeout (line 158) | class PoolTimeout(TimeoutException):
  class NetworkError (line 167) | class NetworkError(TransportError):
  class ReadError (line 175) | class ReadError(NetworkError):
  class WriteError (line 181) | class WriteError(NetworkError):
  class ConnectError (line 187) | class ConnectError(NetworkError):
  class CloseError (line 193) | class CloseError(NetworkError):
  class ProxyError (line 202) | class ProxyError(TransportError):
  class UnsupportedProtocol (line 208) | class UnsupportedProtocol(TransportError):
  class ProtocolError (line 216) | class ProtocolError(TransportError):
  class LocalProtocolError (line 222) | class LocalProtocolError(ProtocolError):
  class RemoteProtocolError (line 232) | class RemoteProtocolError(ProtocolError):
  class DecodingError (line 243) | class DecodingError(RequestError):
  class TooManyRedirects (line 249) | class TooManyRedirects(RequestError):
  class HTTPStatusError (line 258) | class HTTPStatusError(HTTPError):
    method __init__ (line 265) | def __init__(self, message: str, *, request: Request, response: Respon...
  class InvalidURL (line 271) | class InvalidURL(Exception):
    method __init__ (line 276) | def __init__(self, message: str) -> None:
  class CookieConflict (line 280) | class CookieConflict(Exception):
    method __init__ (line 287) | def __init__(self, message: str) -> None:
  class StreamError (line 297) | class StreamError(RuntimeError):
    method __init__ (line 305) | def __init__(self, message: str) -> None:
  class StreamConsumed (line 309) | class StreamConsumed(StreamError):
    method __init__ (line 315) | def __init__(self) -> None:
  class StreamClosed (line 327) | class StreamClosed(StreamError):
    method __init__ (line 333) | def __init__(self) -> None:
  class ResponseNotRead (line 338) | class ResponseNotRead(StreamError):
    method __init__ (line 343) | def __init__(self) -> None:
  class RequestNotRead (line 351) | class RequestNotRead(StreamError):
    method __init__ (line 356) | def __init__(self) -> None:
  function request_context (line 365) | def request_context(

FILE: httpx/_main.py
  function print_help (line 26) | def print_help() -> None:
  function get_lexer_for_response (line 103) | def get_lexer_for_response(response: Response) -> str:
  function format_request_headers (line 116) | def format_request_headers(request: httpcore.Request, http2: bool = Fals...
  function format_response_headers (line 129) | def format_response_headers(
  function print_request_headers (line 147) | def print_request_headers(request: httpcore.Request, http2: bool = False...
  function print_response_headers (line 156) | def print_response_headers(
  function print_response (line 170) | def print_response(response: Response) -> None:
  function format_certificate (line 194) | def format_certificate(cert: _PeerCertRetDictType) -> str:  # pragma: no...
  function trace (line 212) | def trace(
  function download_response (line 251) | def download_response(response: Response, download: typing.BinaryIO) -> ...
  function validate_json (line 273) | def validate_json(
  function validate_auth (line 287) | def validate_auth(
  function handle_help (line 301) | def handle_help(
  function main (line 452) | def main(

FILE: httpx/_models.py
  function _is_known_encoding (line 56) | def _is_known_encoding(encoding: str) -> bool:
  function _normalize_header_key (line 67) | def _normalize_header_key(key: str | bytes, encoding: str | None = None)...
  function _normalize_header_value (line 74) | def _normalize_header_value(value: str | bytes, encoding: str | None = N...
  function _parse_content_type_charset (line 85) | def _parse_content_type_charset(content_type: str) -> str | None:
  function _parse_header_links (line 93) | def _parse_header_links(value: str) -> list[dict[str, str]]:
  function _obfuscate_sensitive_headers (line 130) | def _obfuscate_sensitive_headers(
  class Headers (line 139) | class Headers(typing.MutableMapping[str, str]):
    method __init__ (line 144) | def __init__(
    method encoding (line 167) | def encoding(self) -> str:
    method encoding (line 192) | def encoding(self, value: str) -> None:
    method raw (line 196) | def raw(self) -> list[tuple[bytes, bytes]]:
    method keys (line 202) | def keys(self) -> typing.KeysView[str]:
    method values (line 205) | def values(self) -> typing.ValuesView[str]:
    method items (line 216) | def items(self) -> typing.ItemsView[str, str]:
    method multi_items (line 231) | def multi_items(self) -> list[tuple[str, str]]:
    method get (line 242) | def get(self, key: str, default: typing.Any = None) -> typing.Any:
    method get_list (line 252) | def get_list(self, key: str, split_commas: bool = False) -> list[str]:
    method update (line 274) | def update(self, headers: HeaderTypes | None = None) -> None:  # type:...
    method copy (line 281) | def copy(self) -> Headers:
    method __getitem__ (line 284) | def __getitem__(self, key: str) -> str:
    method __setitem__ (line 304) | def __setitem__(self, key: str, value: str) -> None:
    method __delitem__ (line 328) | def __delitem__(self, key: str) -> None:
    method __contains__ (line 346) | def __contains__(self, key: typing.Any) -> bool:
    method __iter__ (line 350) | def __iter__(self) -> typing.Iterator[typing.Any]:
    method __len__ (line 353) | def __len__(self) -> int:
    method __eq__ (line 356) | def __eq__(self, other: typing.Any) -> bool:
    method __repr__ (line 366) | def __repr__(self) -> str:
  class Request (line 382) | class Request:
    method __init__ (line 383) | def __init__(
    method _prepare (line 441) | def _prepare(self, default_headers: dict[str, str]) -> None:
    method content (line 463) | def content(self) -> bytes:
    method read (line 468) | def read(self) -> bytes:
    method aread (line 482) | async def aread(self) -> bytes:
    method __repr__ (line 496) | def __repr__(self) -> str:
    method __getstate__ (line 501) | def __getstate__(self) -> dict[str, typing.Any]:
    method __setstate__ (line 508) | def __setstate__(self, state: dict[str, typing.Any]) -> None:
  class Response (line 515) | class Response:
    method __init__ (line 516) | def __init__(
    method _prepare (line 571) | def _prepare(self, default_headers: dict[str, str]) -> None:
    method elapsed (line 579) | def elapsed(self) -> datetime.timedelta:
    method elapsed (line 592) | def elapsed(self, elapsed: datetime.timedelta) -> None:
    method request (line 596) | def request(self) -> Request:
    method request (line 607) | def request(self, value: Request) -> None:
    method http_version (line 611) | def http_version(self) -> str:
    method reason_phrase (line 620) | def reason_phrase(self) -> str:
    method url (line 629) | def url(self) -> URL:
    method content (line 636) | def content(self) -> bytes:
    method text (line 642) | def text(self) -> str:
    method encoding (line 653) | def encoding(self) -> str | None:
    method encoding (line 675) | def encoding(self, value: str) -> None:
    method charset_encoding (line 689) | def charset_encoding(self) -> str | None:
    method _get_content_decoder (line 699) | def _get_content_decoder(self) -> ContentDecoder:
    method is_informational (line 725) | def is_informational(self) -> bool:
    method is_success (line 732) | def is_success(self) -> bool:
    method is_redirect (line 739) | def is_redirect(self) -> bool:
    method is_client_error (line 751) | def is_client_error(self) -> bool:
    method is_server_error (line 758) | def is_server_error(self) -> bool:
    method is_error (line 765) | def is_error(self) -> bool:
    method has_redirect_location (line 772) | def has_redirect_location(self) -> bool:
    method raise_for_status (line 794) | def raise_for_status(self) -> Response:
    method json (line 831) | def json(self, **kwargs: typing.Any) -> typing.Any:
    method cookies (line 835) | def cookies(self) -> Cookies:
    method links (line 842) | def links(self) -> dict[str | None, dict[str, str]]:
    method num_bytes_downloaded (line 856) | def num_bytes_downloaded(self) -> int:
    method __repr__ (line 859) | def __repr__(self) -> str:
    method __getstate__ (line 862) | def __getstate__(self) -> dict[str, typing.Any]:
    method __setstate__ (line 869) | def __setstate__(self, state: dict[str, typing.Any]) -> None:
    method read (line 876) | def read(self) -> bytes:
    method iter_bytes (line 884) | def iter_bytes(self, chunk_size: int | None = None) -> typing.Iterator...
    method iter_text (line 907) | def iter_text(self, chunk_size: int | None = None) -> typing.Iterator[...
    method iter_lines (line 926) | def iter_lines(self) -> typing.Iterator[str]:
    method iter_raw (line 935) | def iter_raw(self, chunk_size: int | None = None) -> typing.Iterator[b...
    method close (line 961) | def close(self) -> None:
    method aread (line 974) | async def aread(self) -> bytes:
    method aiter_bytes (line 982) | async def aiter_bytes(
    method aiter_text (line 1007) | async def aiter_text(
    method aiter_lines (line 1028) | async def aiter_lines(self) -> typing.AsyncIterator[str]:
    method aiter_raw (line 1037) | async def aiter_raw(
    method aclose (line 1065) | async def aclose(self) -> None:
  class Cookies (line 1079) | class Cookies(typing.MutableMapping[str, str]):
    method __init__ (line 1084) | def __init__(self, cookies: CookieTypes | None = None) -> None:
    method extract_cookies (line 1101) | def extract_cookies(self, response: Response) -> None:
    method set_cookie_header (line 1110) | def set_cookie_header(self, request: Request) -> None:
    method set (line 1117) | def set(self, name: str, value: str, domain: str = "", path: str = "/"...
    method get (line 1143) | def get(  # type: ignore
    method delete (line 1168) | def delete(
    method clear (line 1192) | def clear(self, domain: str | None = None, path: str | None = None) ->...
    method update (line 1205) | def update(self, cookies: CookieTypes | None = None) -> None:  # type:...
    method __setitem__ (line 1210) | def __setitem__(self, name: str, value: str) -> None:
    method __getitem__ (line 1213) | def __getitem__(self, name: str) -> str:
    method __delitem__ (line 1219) | def __delitem__(self, name: str) -> None:
    method __len__ (line 1222) | def __len__(self) -> int:
    method __iter__ (line 1225) | def __iter__(self) -> typing.Iterator[str]:
    method __bool__ (line 1228) | def __bool__(self) -> bool:
    method __repr__ (line 1233) | def __repr__(self) -> str:
    class _CookieCompatRequest (line 1243) | class _CookieCompatRequest(urllib.request.Request):
      method __init__ (line 1249) | def __init__(self, request: Request) -> None:
      method add_unredirected_header (line 1257) | def add_unredirected_header(self, key: str, value: str) -> None:
    class _CookieCompatResponse (line 1261) | class _CookieCompatResponse:
      method __init__ (line 1267) | def __init__(self, response: Response) -> None:
      method info (line 1270) | def info(self) -> email.message.Message:

FILE: httpx/_multipart.py
  function _format_form_param (line 33) | def _format_form_param(name: str, value: str) -> bytes:
  function _guess_content_type (line 45) | def _guess_content_type(filename: str | None) -> str | None:
  function get_multipart_boundary_from_content_type (line 56) | def get_multipart_boundary_from_content_type(
  class DataField (line 70) | class DataField:
    method __init__ (line 75) | def __init__(self, name: str, value: str | bytes | int | float | None)...
    method render_headers (line 90) | def render_headers(self) -> bytes:
    method render_data (line 99) | def render_data(self) -> bytes:
    method get_length (line 105) | def get_length(self) -> int:
    method render (line 110) | def render(self) -> typing.Iterator[bytes]:
  class FileField (line 115) | class FileField:
    method __init__ (line 122) | def __init__(self, name: str, value: FileTypes) -> None:
    method get_length (line 171) | def get_length(self) -> int | None:
    method render_headers (line 186) | def render_headers(self) -> bytes:
    method render_data (line 203) | def render_data(self) -> typing.Iterator[bytes]:
    method render (line 219) | def render(self) -> typing.Iterator[bytes]:
  class MultipartStream (line 224) | class MultipartStream(SyncByteStream, AsyncByteStream):
    method __init__ (line 229) | def __init__(
    method _iter_fields (line 244) | def _iter_fields(
    method iter_chunks (line 258) | def iter_chunks(self) -> typing.Iterator[bytes]:
    method get_content_length (line 265) | def get_content_length(self) -> int | None:
    method get_headers (line 287) | def get_headers(self) -> dict[str, str]:
    method __iter__ (line 294) | def __iter__(self) -> typing.Iterator[bytes]:
    method __aiter__ (line 298) | async def __aiter__(self) -> typing.AsyncIterator[bytes]:

FILE: httpx/_status_codes.py
  class codes (line 8) | class codes(IntEnum):
    method __new__ (line 28) | def __new__(cls, value: int, phrase: str = "") -> codes:
    method __str__ (line 35) | def __str__(self) -> str:
    method get_reason_phrase (line 39) | def get_reason_phrase(cls, value: int) -> str:
    method is_informational (line 46) | def is_informational(cls, value: int) -> bool:
    method is_success (line 53) | def is_success(cls, value: int) -> bool:
    method is_redirect (line 60) | def is_redirect(cls, value: int) -> bool:
    method is_client_error (line 67) | def is_client_error(cls, value: int) -> bool:
    method is_server_error (line 74) | def is_server_error(cls, value: int) -> bool:
    method is_error (line 81) | def is_error(cls, value: int) -> bool:

FILE: httpx/_transports/asgi.py
  function is_running_trio (line 29) | def is_running_trio() -> bool:
  function create_event (line 44) | def create_event() -> Event:
  class ASGIResponseStream (line 55) | class ASGIResponseStream(AsyncByteStream):
    method __init__ (line 56) | def __init__(self, body: list[bytes]) -> None:
    method __aiter__ (line 59) | async def __aiter__(self) -> typing.AsyncIterator[bytes]:
  class ASGITransport (line 63) | class ASGITransport(AsyncBaseTransport):
    method __init__ (line 87) | def __init__(
    method handle_async_request (line 99) | async def handle_async_request(

FILE: httpx/_transports/base.py
  class BaseTransport (line 14) | class BaseTransport:
    method __enter__ (line 15) | def __enter__(self: T) -> T:
    method __exit__ (line 18) | def __exit__(
    method handle_request (line 26) | def handle_request(self, request: Request) -> Response:
    method close (line 61) | def close(self) -> None:
  class AsyncBaseTransport (line 65) | class AsyncBaseTransport:
    method __aenter__ (line 66) | async def __aenter__(self: A) -> A:
    method __aexit__ (line 69) | async def __aexit__(
    method handle_async_request (line 77) | async def handle_async_request(
    method aclose (line 85) | async def aclose(self) -> None:

FILE: httpx/_transports/default.py
  function _load_httpcore_exceptions (line 74) | def _load_httpcore_exceptions() -> dict[type[Exception], type[httpx.HTTP...
  function map_httpcore_exceptions (line 96) | def map_httpcore_exceptions() -> typing.Iterator[None]:
  class ResponseStream (line 121) | class ResponseStream(SyncByteStream):
    method __init__ (line 122) | def __init__(self, httpcore_stream: typing.Iterable[bytes]) -> None:
    method __iter__ (line 125) | def __iter__(self) -> typing.Iterator[bytes]:
    method close (line 130) | def close(self) -> None:
  class HTTPTransport (line 135) | class HTTPTransport(BaseTransport):
    method __init__ (line 136) | def __init__(
    method __enter__ (line 217) | def __enter__(self: T) -> T:  # Use generics for subclass support.
    method __exit__ (line 221) | def __exit__(
    method handle_request (line 230) | def handle_request(
    method close (line 261) | def close(self) -> None:
  class AsyncResponseStream (line 265) | class AsyncResponseStream(AsyncByteStream):
    method __init__ (line 266) | def __init__(self, httpcore_stream: typing.AsyncIterable[bytes]) -> None:
    method __aiter__ (line 269) | async def __aiter__(self) -> typing.AsyncIterator[bytes]:
    method aclose (line 274) | async def aclose(self) -> None:
  class AsyncHTTPTransport (line 279) | class AsyncHTTPTransport(AsyncBaseTransport):
    method __init__ (line 280) | def __init__(
    method __aenter__ (line 361) | async def __aenter__(self: A) -> A:  # Use generics for subclass support.
    method __aexit__ (line 365) | async def __aexit__(
    method handle_async_request (line 374) | async def handle_async_request(
    method aclose (line 405) | async def aclose(self) -> None:

FILE: httpx/_transports/mock.py
  class MockTransport (line 15) | class MockTransport(AsyncBaseTransport, BaseTransport):
    method __init__ (line 16) | def __init__(self, handler: SyncHandler | AsyncHandler) -> None:
    method handle_request (line 19) | def handle_request(
    method handle_async_request (line 29) | async def handle_async_request(

FILE: httpx/_transports/wsgi.py
  function _skip_leading_empty_chunks (line 22) | def _skip_leading_empty_chunks(body: typing.Iterable[_T]) -> typing.Iter...
  class WSGIByteStream (line 30) | class WSGIByteStream(SyncByteStream):
    method __init__ (line 31) | def __init__(self, result: typing.Iterable[bytes]) -> None:
    method __iter__ (line 35) | def __iter__(self) -> typing.Iterator[bytes]:
    method close (line 39) | def close(self) -> None:
  class WSGITransport (line 44) | class WSGITransport(BaseTransport):
    method __init__ (line 77) | def __init__(
    method handle_request (line 91) | def handle_request(self, request: Request) -> Response:

FILE: httpx/_types.py
  class SyncByteStream (line 92) | class SyncByteStream:
    method __iter__ (line 93) | def __iter__(self) -> Iterator[bytes]:
    method close (line 99) | def close(self) -> None:
  class AsyncByteStream (line 106) | class AsyncByteStream:
    method __aiter__ (line 107) | async def __aiter__(self) -> AsyncIterator[bytes]:
    method aclose (line 113) | async def aclose(self) -> None:

FILE: httpx/_urlparse.py
  class ParseResult (line 158) | class ParseResult(typing.NamedTuple):
    method authority (line 168) | def authority(self) -> str:
    method netloc (line 178) | def netloc(self) -> str:
    method copy_with (line 186) | def copy_with(self, **kwargs: str | None) -> ParseResult:
    method __str__ (line 200) | def __str__(self) -> str:
  function urlparse (line 213) | def urlparse(url: str = "", **kwargs: str | None) -> ParseResult:
  function encode_host (line 348) | def encode_host(host: str) -> str:
  function normalize_port (line 395) | def normalize_port(port: str | int | None, scheme: str) -> int | None:
  function validate_path (line 422) | def validate_path(path: str, has_scheme: bool, has_authority: bool) -> N...
  function normalize_path (line 447) | def normalize_path(path: str) -> str:
  function PERCENT (line 478) | def PERCENT(string: str) -> str:
  function percent_encoded (line 482) | def percent_encoded(string: str, safe: str) -> str:
  function quote (line 497) | def quote(string: str, safe: str) -> str:

FILE: httpx/_urls.py
  class URL (line 15) | class URL:
    method __init__ (line 77) | def __init__(self, url: URL | str = "", **kwargs: typing.Any) -> None:
    method scheme (line 127) | def scheme(self) -> str:
    method raw_scheme (line 135) | def raw_scheme(self) -> bytes:
    method userinfo (line 143) | def userinfo(self) -> bytes:
    method username (line 151) | def username(self) -> str:
    method password (line 160) | def password(self) -> str:
    method host (line 169) | def host(self) -> str:
    method raw_host (line 196) | def raw_host(self) -> bytes:
    method port (line 218) | def port(self) -> int | None:
    method netloc (line 234) | def netloc(self) -> bytes:
    method path (line 245) | def path(self) -> str:
    method query (line 258) | def query(self) -> bytes:
    method params (line 275) | def params(self) -> QueryParams:
    method raw_path (line 283) | def raw_path(self) -> bytes:
    method fragment (line 300) | def fragment(self) -> str:
    method is_absolute_url (line 308) | def is_absolute_url(self) -> bool:
    method is_relative_url (line 320) | def is_relative_url(self) -> bool:
    method copy_with (line 327) | def copy_with(self, **kwargs: typing.Any) -> URL:
    method copy_set_param (line 342) | def copy_set_param(self, key: str, value: typing.Any = None) -> URL:
    method copy_add_param (line 345) | def copy_add_param(self, key: str, value: typing.Any = None) -> URL:
    method copy_remove_param (line 348) | def copy_remove_param(self, key: str) -> URL:
    method copy_merge_params (line 351) | def copy_merge_params(self, params: QueryParamTypes) -> URL:
    method join (line 354) | def join(self, url: URL | str) -> URL:
    method __hash__ (line 368) | def __hash__(self) -> int:
    method __eq__ (line 371) | def __eq__(self, other: typing.Any) -> bool:
    method __str__ (line 374) | def __str__(self) -> str:
    method __repr__ (line 377) | def __repr__(self) -> str:
    method raw (line 404) | def raw(self) -> tuple[bytes, bytes, int, bytes]:  # pragma: nocover
  class QueryParams (line 420) | class QueryParams(typing.Mapping[str, str]):
    method __init__ (line 425) | def __init__(self, *args: QueryParamTypes | None, **kwargs: typing.Any...
    method keys (line 463) | def keys(self) -> typing.KeysView[str]:
    method values (line 474) | def values(self) -> typing.ValuesView[str]:
    method items (line 486) | def items(self) -> typing.ItemsView[str, str]:
    method multi_items (line 498) | def multi_items(self) -> list[tuple[str, str]]:
    method get (line 512) | def get(self, key: typing.Any, default: typing.Any = None) -> typing.Any:
    method get_list (line 526) | def get_list(self, key: str) -> list[str]:
    method set (line 537) | def set(self, key: str, value: typing.Any = None) -> QueryParams:
    method add (line 552) | def add(self, key: str, value: typing.Any = None) -> QueryParams:
    method remove (line 567) | def remove(self, key: str) -> QueryParams:
    method merge (line 582) | def merge(self, params: QueryParamTypes | None = None) -> QueryParams:
    method __getitem__ (line 600) | def __getitem__(self, key: typing.Any) -> str:
    method __contains__ (line 603) | def __contains__(self, key: typing.Any) -> bool:
    method __iter__ (line 606) | def __iter__(self) -> typing.Iterator[typing.Any]:
    method __len__ (line 609) | def __len__(self) -> int:
    method __bool__ (line 612) | def __bool__(self) -> bool:
    method __hash__ (line 615) | def __hash__(self) -> int:
    method __eq__ (line 618) | def __eq__(self, other: typing.Any) -> bool:
    method __str__ (line 623) | def __str__(self) -> str:
    method __repr__ (line 626) | def __repr__(self) -> str:
    method update (line 631) | def update(self, params: QueryParamTypes | None = None) -> None:
    method __setitem__ (line 637) | def __setitem__(self, key: str, value: str) -> None:

FILE: httpx/_utils.py
  function primitive_value_to_str (line 15) | def primitive_value_to_str(value: PrimitiveData) -> str:
  function get_environment_proxies (line 30) | def get_environment_proxies() -> dict[str, str | None]:
  function to_bytes (line 79) | def to_bytes(value: str | bytes, encoding: str = "utf-8") -> bytes:
  function to_str (line 83) | def to_str(value: str | bytes, encoding: str = "utf-8") -> str:
  function to_bytes_or_str (line 87) | def to_bytes_or_str(value: str, match_type_of: typing.AnyStr) -> typing....
  function unquote (line 91) | def unquote(value: str) -> str:
  function peek_filelike_length (line 95) | def peek_filelike_length(stream: typing.Any) -> int | None:
  class URLPattern (line 120) | class URLPattern:
    method __init__ (line 162) | def __init__(self, pattern: str) -> None:
    method matches (line 192) | def matches(self, other: URL) -> bool:
    method priority (line 206) | def priority(self) -> tuple[int, int, int]:
    method __hash__ (line 219) | def __hash__(self) -> int:
    method __lt__ (line 222) | def __lt__(self, other: URLPattern) -> bool:
    method __eq__ (line 225) | def __eq__(self, other: typing.Any) -> bool:
  function is_ipv4_hostname (line 229) | def is_ipv4_hostname(hostname: str) -> bool:
  function is_ipv6_hostname (line 237) | def is_ipv6_hostname(hostname: str) -> bool:

FILE: tests/client/test_async_client.py
  function test_get (line 12) | async def test_get(server):
  function test_get_invalid_url (line 33) | async def test_get_invalid_url(server, url):
  function test_build_request (line 40) | async def test_build_request(server):
  function test_post (line 55) | async def test_post(server):
  function test_post_json (line 63) | async def test_post_json(server):
  function test_stream_response (line 71) | async def test_stream_response(server):
  function test_access_content_stream_response (line 82) | async def test_access_content_stream_response(server):
  function test_stream_request (line 93) | async def test_stream_request(server):
  function test_cannot_stream_sync_request (line 104) | async def test_cannot_stream_sync_request(server):
  function test_raise_for_status (line 115) | async def test_raise_for_status(server):
  function test_options (line 131) | async def test_options(server):
  function test_head (line 139) | async def test_head(server):
  function test_put (line 147) | async def test_put(server):
  function test_patch (line 154) | async def test_patch(server):
  function test_delete (line 161) | async def test_delete(server):
  function test_100_continue (line 169) | async def test_100_continue(server):
  function test_context_managed_transport (line 183) | async def test_context_managed_transport():
  function test_context_managed_transport_and_mount (line 215) | async def test_context_managed_transport_and_mount():
  function hello_world (line 255) | def hello_world(request):
  function test_client_closed_state_using_implicit_open (line 260) | async def test_client_closed_state_using_implicit_open():
  function test_client_closed_state_using_with_block (line 281) | async def test_client_closed_state_using_with_block():
  function unmounted (line 291) | def unmounted(request: httpx.Request) -> httpx.Response:
  function mounted (line 296) | def mounted(request: httpx.Request) -> httpx.Response:
  function test_mounted_transport (line 302) | async def test_mounted_transport():
  function test_async_mock_transport (line 317) | async def test_async_mock_transport():
  function test_cancellation_during_stream (line 330) | async def test_cancellation_during_stream():
  function test_server_extensions (line 370) | async def test_server_extensions(server):

FILE: tests/client/test_auth.py
  class App (line 23) | class App:
    method __init__ (line 28) | def __init__(self, auth_header: str = "", status_code: int = 200) -> N...
    method __call__ (line 32) | def __call__(self, request: httpx.Request) -> httpx.Response:
  class DigestApp (line 38) | class DigestApp:
    method __init__ (line 39) | def __init__(
    method __call__ (line 52) | def __call__(self, request: httpx.Request) -> httpx.Response:
    method challenge_send (line 59) | def challenge_send(self, request: httpx.Request) -> httpx.Response:
  class RepeatAuth (line 87) | class RepeatAuth(httpx.Auth):
    method __init__ (line 97) | def __init__(self, repeat: int) -> None:
    method auth_flow (line 100) | def auth_flow(
  class ResponseBodyAuth (line 115) | class ResponseBodyAuth(httpx.Auth):
    method __init__ (line 124) | def __init__(self, token: str) -> None:
    method auth_flow (line 127) | def auth_flow(
  class SyncOrAsyncAuth (line 137) | class SyncOrAsyncAuth(httpx.Auth):
    method __init__ (line 143) | def __init__(self) -> None:
    method sync_auth_flow (line 147) | def sync_auth_flow(
    method async_auth_flow (line 154) | async def async_auth_flow(
  function test_basic_auth (line 163) | async def test_basic_auth() -> None:
  function test_basic_auth_with_stream (line 176) | async def test_basic_auth_with_stream() -> None:
  function test_basic_auth_in_url (line 195) | async def test_basic_auth_in_url() -> None:
  function test_basic_auth_on_session (line 207) | async def test_basic_auth_on_session() -> None:
  function test_custom_auth (line 222) | async def test_custom_auth() -> None:
  function test_netrc_auth_credentials_exist (line 237) | def test_netrc_auth_credentials_exist() -> None:
  function test_netrc_auth_credentials_do_not_exist (line 256) | def test_netrc_auth_credentials_do_not_exist() -> None:
  function test_netrc_auth_nopassword_parse_error (line 277) | def test_netrc_auth_nopassword_parse_error() -> None:  # pragma: no cover
  function test_auth_disable_per_request (line 289) | async def test_auth_disable_per_request() -> None:
  function test_auth_hidden_url (line 303) | def test_auth_hidden_url() -> None:
  function test_auth_hidden_header (line 311) | async def test_auth_hidden_header() -> None:
  function test_auth_property (line 323) | async def test_auth_property() -> None:
  function test_auth_invalid_type (line 339) | async def test_auth_invalid_type() -> None:
  function test_digest_auth_returns_no_auth_if_no_digest_header_in_response (line 357) | async def test_digest_auth_returns_no_auth_if_no_digest_header_in_respon...
  function test_digest_auth_returns_no_auth_if_alternate_auth_scheme (line 370) | def test_digest_auth_returns_no_auth_if_alternate_auth_scheme() -> None:
  function test_digest_auth_200_response_including_digest_auth_header (line 385) | async def test_digest_auth_200_response_including_digest_auth_header() -...
  function test_digest_auth_401_response_without_digest_auth_header (line 400) | async def test_digest_auth_401_response_without_digest_auth_header() -> ...
  function test_digest_auth (line 427) | async def test_digest_auth(
  function test_digest_auth_no_specified_qop (line 460) | async def test_digest_auth_no_specified_qop() -> None:
  function test_digest_auth_qop_including_spaces_and_auth_returns_auth (line 492) | async def test_digest_auth_qop_including_spaces_and_auth_returns_auth(qo...
  function test_digest_auth_qop_auth_int_not_implemented (line 505) | async def test_digest_auth_qop_auth_int_not_implemented() -> None:
  function test_digest_auth_qop_must_be_auth_or_auth_int (line 516) | async def test_digest_auth_qop_must_be_auth_or_auth_int() -> None:
  function test_digest_auth_incorrect_credentials (line 527) | async def test_digest_auth_incorrect_credentials() -> None:
  function test_digest_auth_reuses_challenge (line 540) | async def test_digest_auth_reuses_challenge() -> None:
  function test_digest_auth_resets_nonce_count_after_401 (line 557) | async def test_digest_auth_resets_nonce_count_after_401() -> None:
  function test_async_digest_auth_raises_protocol_error_on_malformed_header (line 604) | async def test_async_digest_auth_raises_protocol_error_on_malformed_header(
  function test_sync_digest_auth_raises_protocol_error_on_malformed_header (line 623) | def test_sync_digest_auth_raises_protocol_error_on_malformed_header(
  function test_async_auth_history (line 636) | async def test_async_auth_history() -> None:
  function test_sync_auth_history (line 662) | def test_sync_auth_history() -> None:
  class ConsumeBodyTransport (line 688) | class ConsumeBodyTransport(httpx.MockTransport):
    method handle_async_request (line 689) | async def handle_async_request(self, request: httpx.Request) -> httpx....
  function test_digest_auth_unavailable_streaming_body (line 696) | async def test_digest_auth_unavailable_streaming_body():
  function test_async_auth_reads_response_body (line 710) | async def test_async_auth_reads_response_body() -> None:
  function test_sync_auth_reads_response_body (line 726) | def test_sync_auth_reads_response_body() -> None:
  function test_async_auth (line 743) | async def test_async_auth() -> None:
  function test_sync_auth (line 760) | def test_sync_auth() -> None:

FILE: tests/client/test_client.py
  function autodetect (line 12) | def autodetect(content):
  function test_get (line 16) | def test_get(server):
  function test_get_invalid_url (line 41) | def test_get_invalid_url(server, url):
  function test_build_request (line 47) | def test_build_request(server):
  function test_build_post_request (line 62) | def test_build_post_request(server):
  function test_post (line 78) | def test_post(server):
  function test_post_json (line 85) | def test_post_json(server):
  function test_stream_response (line 92) | def test_stream_response(server):
  function test_stream_iterator (line 100) | def test_stream_iterator(server):
  function test_raw_iterator (line 112) | def test_raw_iterator(server):
  function test_cannot_stream_async_request (line 124) | def test_cannot_stream_async_request(server):
  function test_raise_for_status (line 134) | def test_raise_for_status(server):
  function test_options (line 149) | def test_options(server):
  function test_head (line 156) | def test_head(server):
  function test_put (line 163) | def test_put(server):
  function test_patch (line 170) | def test_patch(server):
  function test_delete (line 177) | def test_delete(server):
  function test_base_url (line 184) | def test_base_url(server):
  function test_merge_absolute_url (line 192) | def test_merge_absolute_url():
  function test_merge_relative_url (line 198) | def test_merge_relative_url():
  function test_merge_relative_url_with_path (line 204) | def test_merge_relative_url_with_path():
  function test_merge_relative_url_with_dotted_path (line 210) | def test_merge_relative_url_with_dotted_path():
  function test_merge_relative_url_with_path_including_colon (line 216) | def test_merge_relative_url_with_path_including_colon():
  function test_merge_relative_url_with_encoded_slashes (line 222) | def test_merge_relative_url_with_encoded_slashes():
  function test_context_managed_transport (line 232) | def test_context_managed_transport():
  function test_context_managed_transport_and_mount (line 263) | def test_context_managed_transport_and_mount():
  function hello_world (line 301) | def hello_world(request):
  function test_client_closed_state_using_implicit_open (line 305) | def test_client_closed_state_using_implicit_open():
  function test_client_closed_state_using_with_block (line 326) | def test_client_closed_state_using_with_block():
  function echo_raw_headers (line 336) | def echo_raw_headers(request: httpx.Request) -> httpx.Response:
  function test_raw_client_header (line 344) | def test_raw_client_header():
  function unmounted (line 367) | def unmounted(request: httpx.Request) -> httpx.Response:
  function mounted (line 372) | def mounted(request: httpx.Request) -> httpx.Response:
  function test_mounted_transport (line 377) | def test_mounted_transport():
  function test_all_mounted_transport (line 392) | def test_all_mounted_transport():
  function test_server_extensions (line 402) | def test_server_extensions(server):
  function test_client_decode_text_using_autodetect (line 410) | def test_client_decode_text_using_autodetect():
  function test_client_decode_text_using_explicit_encoding (line 438) | def test_client_decode_text_using_explicit_encoding():

FILE: tests/client/test_cookies.py
  function get_and_set_cookies (line 8) | def get_and_set_cookies(request: httpx.Request) -> httpx.Response:
  function test_set_cookie (line 18) | def test_set_cookie() -> None:
  function test_set_per_request_cookie_is_deprecated (line 34) | def test_set_per_request_cookie_is_deprecated() -> None:
  function test_set_cookie_with_cookiejar (line 49) | def test_set_cookie_with_cookiejar() -> None:
  function test_setting_client_cookies_to_cookiejar (line 86) | def test_setting_client_cookies_to_cookiejar() -> None:
  function test_set_cookie_with_cookies_model (line 123) | def test_set_cookie_with_cookies_model() -> None:
  function test_get_cookie (line 140) | def test_get_cookie() -> None:
  function test_cookie_persistence (line 151) | def test_cookie_persistence() -> None:

FILE: tests/client/test_event_hooks.py
  function app (line 6) | def app(request: httpx.Request) -> httpx.Response:
  function test_event_hooks (line 16) | def test_event_hooks():
  function test_event_hooks_raising_exception (line 51) | def test_event_hooks_raising_exception(server):
  function test_async_event_hooks (line 67) | async def test_async_event_hooks():
  function test_async_event_hooks_raising_exception (line 103) | async def test_async_event_hooks_raising_exception():
  function test_event_hooks_with_redirect (line 118) | def test_event_hooks_with_redirect():
  function test_async_event_hooks_with_redirect (line 175) | async def test_async_event_hooks_with_redirect():

FILE: tests/client/test_headers.py
  function echo_headers (line 8) | def echo_headers(request: httpx.Request) -> httpx.Response:
  function echo_repeated_headers_multi_items (line 13) | def echo_repeated_headers_multi_items(request: httpx.Request) -> httpx.R...
  function echo_repeated_headers_items (line 18) | def echo_repeated_headers_items(request: httpx.Request) -> httpx.Response:
  function test_client_header (line 23) | def test_client_header():
  function test_header_merge (line 46) | def test_header_merge():
  function test_header_merge_conflicting_headers (line 68) | def test_header_merge_conflicting_headers():
  function test_header_update (line 90) | def test_header_update():
  function test_header_repeated_items (line 123) | def test_header_repeated_items():
  function test_header_repeated_multi_items (line 138) | def test_header_repeated_multi_items():
  function test_remove_default_header (line 152) | def test_remove_default_header():
  function test_header_does_not_exist (line 174) | def test_header_does_not_exist():
  function test_header_with_incorrect_value (line 180) | def test_header_with_incorrect_value():
  function test_host_with_auth_and_port_in_url (line 188) | def test_host_with_auth_and_port_in_url():
  function test_host_with_non_default_port_in_url (line 212) | def test_host_with_non_default_port_in_url():
  function test_request_auto_headers (line 235) | def test_request_auto_headers():
  function test_same_origin (line 240) | def test_same_origin():
  function test_not_same_origin (line 250) | def test_not_same_origin():
  function test_is_https_redirect (line 260) | def test_is_https_redirect():
  function test_is_not_https_redirect (line 272) | def test_is_not_https_redirect():
  function test_is_not_https_redirect_if_not_default_ports (line 284) | def test_is_not_https_redirect_if_not_default_ports():

FILE: tests/client/test_properties.py
  function test_client_base_url (line 4) | def test_client_base_url():
  function test_client_base_url_without_trailing_slash (line 11) | def test_client_base_url_without_trailing_slash():
  function test_client_base_url_with_trailing_slash (line 18) | def test_client_base_url_with_trailing_slash():
  function test_client_headers (line 25) | def test_client_headers():
  function test_client_cookies (line 32) | def test_client_cookies():
  function test_client_timeout (line 41) | def test_client_timeout():
  function test_client_event_hooks (line 54) | def test_client_event_hooks():
  function test_client_trust_env (line 63) | def test_client_trust_env():

FILE: tests/client/test_proxies.py
  function url_to_origin (line 7) | def url_to_origin(url: str) -> httpcore.URL:
  function test_socks_proxy (line 16) | def test_socks_proxy():
  function test_transport_for_request (line 88) | def test_transport_for_request(url, proxies, expected):
  function test_async_proxy_close (line 104) | async def test_async_proxy_close():
  function test_sync_proxy_close (line 114) | def test_sync_proxy_close():
  function test_unsupported_proxy_scheme (line 123) | def test_unsupported_proxy_scheme():
  function test_proxies_environ (line 226) | def test_proxies_environ(monkeypatch, client_class, url, env, expected):
  function test_for_deprecated_proxy_params (line 250) | def test_for_deprecated_proxy_params(proxies, is_valid):
  function test_proxy_with_mounts (line 260) | def test_proxy_with_mounts():

FILE: tests/client/test_queryparams.py
  function hello_world (line 4) | def hello_world(request: httpx.Request) -> httpx.Response:
  function test_client_queryparams (line 8) | def test_client_queryparams():
  function test_client_queryparams_string (line 14) | def test_client_queryparams_string():
  function test_client_queryparams_echo (line 25) | def test_client_queryparams_echo():

FILE: tests/client/test_redirects.py
  function redirects (line 8) | def redirects(request: httpx.Request) -> httpx.Response:
  function test_redirect_301 (line 116) | def test_redirect_301():
  function test_redirect_302 (line 124) | def test_redirect_302():
  function test_redirect_303 (line 132) | def test_redirect_303():
  function test_next_request (line 140) | def test_next_request():
  function test_async_next_request (line 155) | async def test_async_next_request():
  function test_head_redirect (line 169) | def test_head_redirect():
  function test_relative_redirect (line 182) | def test_relative_redirect():
  function test_malformed_redirect (line 192) | def test_malformed_redirect():
  function test_invalid_redirect (line 203) | def test_invalid_redirect():
  function test_no_scheme_redirect (line 209) | def test_no_scheme_redirect():
  function test_fragment_redirect (line 219) | def test_fragment_redirect():
  function test_multiple_redirects (line 229) | def test_multiple_redirects():
  function test_async_too_many_redirects (line 244) | async def test_async_too_many_redirects():
  function test_sync_too_many_redirects (line 252) | def test_sync_too_many_redirects():
  function test_redirect_loop (line 260) | def test_redirect_loop():
  function test_cross_domain_redirect_with_auth_header (line 266) | def test_cross_domain_redirect_with_auth_header():
  function test_cross_domain_https_redirect_with_auth_header (line 275) | def test_cross_domain_https_redirect_with_auth_header():
  function test_cross_domain_redirect_with_auth (line 284) | def test_cross_domain_redirect_with_auth():
  function test_same_domain_redirect (line 292) | def test_same_domain_redirect():
  function test_same_domain_https_redirect_with_auth_header (line 301) | def test_same_domain_https_redirect_with_auth_header():
  function test_body_redirect (line 310) | def test_body_redirect():
  function test_no_body_redirect (line 323) | def test_no_body_redirect():
  function test_can_stream_if_no_redirect (line 336) | def test_can_stream_if_no_redirect():
  class ConsumeBodyTransport (line 345) | class ConsumeBodyTransport(httpx.MockTransport):
    method handle_request (line 346) | def handle_request(self, request: httpx.Request) -> httpx.Response:
  function test_cannot_redirect_streaming_body (line 352) | def test_cannot_redirect_streaming_body():
  function test_cross_subdomain_redirect (line 363) | def test_cross_subdomain_redirect():
  function cookie_sessions (line 370) | def cookie_sessions(request: httpx.Request) -> httpx.Response:
  function test_redirect_cookie_behavior (line 403) | def test_redirect_cookie_behavior():
  function test_redirect_custom_scheme (line 434) | def test_redirect_custom_scheme():
  function test_async_invalid_redirect (line 442) | async def test_async_invalid_redirect():

FILE: tests/concurrency.py
  function sleep (line 11) | async def sleep(seconds: float) -> None:

FILE: tests/conftest.py
  function clean_environ (line 35) | def clean_environ():
  function app (line 59) | async def app(scope: Scope, receive: Receive, send: Send) -> None:
  function hello_world (line 79) | async def hello_world(scope: Scope, receive: Receive, send: Send) -> None:
  function hello_world_json (line 90) | async def hello_world_json(scope: Scope, receive: Receive, send: Send) -...
  function slow_response (line 101) | async def slow_response(scope: Scope, receive: Receive, send: Send) -> N...
  function status_code (line 113) | async def status_code(scope: Scope, receive: Receive, send: Send) -> None:
  function echo_body (line 125) | async def echo_body(scope: Scope, receive: Receive, send: Send) -> None:
  function echo_binary (line 144) | async def echo_binary(scope: Scope, receive: Receive, send: Send) -> None:
  function echo_headers (line 163) | async def echo_headers(scope: Scope, receive: Receive, send: Send) -> None:
  function redirect_301 (line 178) | async def redirect_301(scope: Scope, receive: Receive, send: Send) -> None:
  function cert_authority (line 186) | def cert_authority():
  function localhost_cert (line 191) | def localhost_cert(cert_authority):
  function cert_pem_file (line 196) | def cert_pem_file(localhost_cert):
  function cert_private_key_file (line 202) | def cert_private_key_file(localhost_cert):
  function cert_encrypted_private_key_file (line 208) | def cert_encrypted_private_key_file(localhost_cert):
  class TestServer (line 224) | class TestServer(Server):
    method url (line 226) | def url(self) -> httpx.URL:
    method install_signal_handlers (line 230) | def install_signal_handlers(self) -> None:
    method serve (line 235) | async def serve(self, sockets=None):
    method restart (line 245) | async def restart(self) -> None:  # pragma: no cover
    method watch_restarts (line 256) | async def watch_restarts(self) -> None:  # pragma: no cover
  function serve_in_thread (line 271) | def serve_in_thread(server: TestServer) -> typing.Iterator[TestServer]:
  function server (line 284) | def server() -> typing.Iterator[TestServer]:

FILE: tests/models/test_cookies.py
  function test_cookies (line 8) | def test_cookies():
  function test_cookies_update (line 23) | def test_cookies_update():
  function test_cookies_with_domain (line 33) | def test_cookies_with_domain():
  function test_cookies_with_domain_and_path (line 45) | def test_cookies_with_domain_and_path():
  function test_multiple_set_cookie (line 55) | def test_multiple_set_cookie():
  function test_cookies_can_be_a_list_of_tuples (line 80) | def test_cookies_can_be_a_list_of_tuples():
  function test_cookies_repr (line 90) | def test_cookies_repr():

FILE: tests/models/test_headers.py
  function test_headers (line 6) | def test_headers():
  function test_header_mutations (line 37) | def test_header_mutations():
  function test_copy_headers_method (line 53) | def test_copy_headers_method():
  function test_copy_headers_init (line 60) | def test_copy_headers_init():
  function test_headers_insert_retains_ordering (line 66) | def test_headers_insert_retains_ordering():
  function test_headers_insert_appends_if_new (line 72) | def test_headers_insert_appends_if_new():
  function test_headers_insert_removes_all_existing (line 78) | def test_headers_insert_removes_all_existing():
  function test_headers_delete_removes_all_existing (line 84) | def test_headers_delete_removes_all_existing():
  function test_headers_dict_repr (line 90) | def test_headers_dict_repr():
  function test_headers_encoding_in_repr (line 98) | def test_headers_encoding_in_repr():
  function test_headers_list_repr (line 106) | def test_headers_list_repr():
  function test_headers_decode_ascii (line 116) | def test_headers_decode_ascii():
  function test_headers_decode_utf_8 (line 126) | def test_headers_decode_utf_8():
  function test_headers_decode_iso_8859_1 (line 136) | def test_headers_decode_iso_8859_1():
  function test_headers_decode_explicit_encoding (line 146) | def test_headers_decode_explicit_encoding():
  function test_multiple_headers (line 158) | def test_multiple_headers():
  function test_sensitive_headers (line 170) | def test_sensitive_headers(header):
  function test_obfuscate_sensitive_headers (line 187) | def test_obfuscate_sensitive_headers(headers, output):
  function test_parse_header_links (line 212) | def test_parse_header_links(value, expected):
  function test_parse_header_links_no_link (line 217) | def test_parse_header_links_no_link():

FILE: tests/models/test_queryparams.py
  function test_queryparams (line 16) | def test_queryparams(source):
  function test_queryparam_types (line 53) | def test_queryparam_types():
  function test_empty_query_params (line 79) | def test_empty_query_params():
  function test_queryparam_update_is_hard_deprecated (line 90) | def test_queryparam_update_is_hard_deprecated():
  function test_queryparam_setter_is_hard_deprecated (line 96) | def test_queryparam_setter_is_hard_deprecated():
  function test_queryparam_set (line 102) | def test_queryparam_set():
  function test_queryparam_add (line 108) | def test_queryparam_add():
  function test_queryparam_remove (line 114) | def test_queryparam_remove():
  function test_queryparam_merge (line 120) | def test_queryparam_merge():
  function test_queryparams_are_hashable (line 128) | def test_queryparams_are_hashable():

FILE: tests/models/test_requests.py
  function test_request_repr (line 9) | def test_request_repr():
  function test_no_content (line 14) | def test_no_content():
  function test_content_length_header (line 19) | def test_content_length_header():
  function test_iterable_content (line 24) | def test_iterable_content():
  function test_generator_with_transfer_encoding_header (line 33) | def test_generator_with_transfer_encoding_header():
  function test_generator_with_content_length_header (line 41) | def test_generator_with_content_length_header():
  function test_url_encoded_data (line 52) | def test_url_encoded_data():
  function test_json_encoded_data (line 60) | def test_json_encoded_data():
  function test_headers (line 68) | def test_headers():
  function test_read_and_stream_data (line 78) | def test_read_and_stream_data():
  function test_aread_and_stream_data (line 90) | async def test_aread_and_stream_data():
  function test_cannot_access_streaming_content_without_read (line 101) | def test_cannot_access_streaming_content_without_read():
  function test_transfer_encoding_header (line 111) | def test_transfer_encoding_header():
  function test_ignore_transfer_encoding_header_if_content_length_exists (line 122) | def test_ignore_transfer_encoding_header_if_content_length_exists():
  function test_override_host_header (line 139) | def test_override_host_header():
  function test_override_accept_encoding_header (line 146) | def test_override_accept_encoding_header():
  function test_override_content_length_header (line 153) | def test_override_content_length_header():
  function test_url (line 164) | def test_url():
  function test_request_picklable (line 180) | def test_request_picklable():
  function test_request_async_streaming_content_picklable (line 196) | async def test_request_async_streaming_content_picklable():
  function test_request_generator_content_picklable (line 214) | def test_request_generator_content_picklable():
  function test_request_params (line 231) | def test_request_params():

FILE: tests/models/test_responses.py
  class StreamingBody (line 11) | class StreamingBody:
    method __iter__ (line 12) | def __iter__(self):
  function streaming_body (line 17) | def streaming_body() -> typing.Iterator[bytes]:
  function async_streaming_body (line 22) | async def async_streaming_body() -> typing.AsyncIterator[bytes]:
  function autodetect (line 27) | def autodetect(content):
  function test_response (line 31) | def test_response():
  function test_response_content (line 46) | def test_response_content():
  function test_response_text (line 55) | def test_response_text():
  function test_response_html (line 67) | def test_response_html():
  function test_response_json (line 79) | def test_response_json():
  function test_raise_for_status (line 91) | def test_raise_for_status():
  function test_response_repr (line 149) | def test_response_repr():
  function test_response_content_type_encoding (line 157) | def test_response_content_type_encoding():
  function test_response_default_to_utf8_encoding (line 172) | def test_response_default_to_utf8_encoding():
  function test_response_fallback_to_utf8_encoding (line 185) | def test_response_fallback_to_utf8_encoding():
  function test_response_no_charset_with_ascii_content (line 200) | def test_response_no_charset_with_ascii_content():
  function test_response_no_charset_with_utf8_content (line 217) | def test_response_no_charset_with_utf8_content():
  function test_response_no_charset_with_iso_8859_1_content (line 233) | def test_response_no_charset_with_iso_8859_1_content():
  function test_response_no_charset_with_cp_1252_content (line 247) | def test_response_no_charset_with_cp_1252_content():
  function test_response_non_text_encoding (line 261) | def test_response_non_text_encoding():
  function test_response_set_explicit_encoding (line 275) | def test_response_set_explicit_encoding():
  function test_response_force_encoding (line 289) | def test_response_force_encoding():
  function test_response_force_encoding_after_text_accessed (line 301) | def test_response_force_encoding_after_text_accessed():
  function test_read (line 318) | def test_read():
  function test_empty_read (line 336) | def test_empty_read():
  function test_aread (line 352) | async def test_aread():
  function test_empty_aread (line 371) | async def test_empty_aread():
  function test_iter_raw (line 386) | def test_iter_raw():
  function test_iter_raw_with_chunksize (line 398) | def test_iter_raw_with_chunksize():
  function test_iter_raw_doesnt_return_empty_chunks (line 416) | def test_iter_raw_doesnt_return_empty_chunks():
  function test_iter_raw_on_iterable (line 429) | def test_iter_raw_on_iterable():
  function test_iter_raw_on_async (line 441) | def test_iter_raw_on_async():
  function test_close_on_async (line 451) | def test_close_on_async():
  function test_iter_raw_increments_updates_counter (line 461) | def test_iter_raw_increments_updates_counter():
  function test_aiter_raw (line 471) | async def test_aiter_raw():
  function test_aiter_raw_with_chunksize (line 481) | async def test_aiter_raw_with_chunksize():
  function test_aiter_raw_on_sync (line 499) | async def test_aiter_raw_on_sync():
  function test_aclose_on_sync (line 510) | async def test_aclose_on_sync():
  function test_aiter_raw_increments_updates_counter (line 521) | async def test_aiter_raw_increments_updates_counter():
  function test_iter_bytes (line 530) | def test_iter_bytes():
  function test_iter_bytes_with_chunk_size (line 539) | def test_iter_bytes_with_chunk_size():
  function test_iter_bytes_with_empty_response (line 553) | def test_iter_bytes_with_empty_response():
  function test_iter_bytes_doesnt_return_empty_chunks (line 559) | def test_iter_bytes_doesnt_return_empty_chunks():
  function test_aiter_bytes (line 573) | async def test_aiter_bytes():
  function test_aiter_bytes_with_chunk_size (line 586) | async def test_aiter_bytes_with_chunk_size():
  function test_iter_text (line 600) | def test_iter_text():
  function test_iter_text_with_chunk_size (line 612) | def test_iter_text_with_chunk_size():
  function test_aiter_text (line 635) | async def test_aiter_text():
  function test_aiter_text_with_chunk_size (line 648) | async def test_aiter_text_with_chunk_size():
  function test_iter_lines (line 662) | def test_iter_lines():
  function test_aiter_lines (line 672) | async def test_aiter_lines():
  function test_sync_streaming_response (line 684) | def test_sync_streaming_response():
  function test_async_streaming_response (line 701) | async def test_async_streaming_response():
  function test_cannot_read_after_stream_consumed (line 717) | def test_cannot_read_after_stream_consumed():
  function test_cannot_aread_after_stream_consumed (line 732) | async def test_cannot_aread_after_stream_consumed():
  function test_cannot_read_after_response_closed (line 746) | def test_cannot_read_after_response_closed():
  function test_cannot_aread_after_response_closed (line 758) | async def test_cannot_aread_after_response_closed():
  function test_elapsed_not_available_until_closed (line 770) | async def test_elapsed_not_available_until_closed():
  function test_unknown_status_code (line 780) | def test_unknown_status_code():
  function test_json_with_specified_encoding (line 789) | def test_json_with_specified_encoding():
  function test_json_with_options (line 801) | def test_json_with_options():
  function test_json_without_specified_charset (line 826) | def test_json_without_specified_charset(encoding):
  function test_json_with_specified_charset (line 851) | def test_json_with_specified_charset(encoding):
  function test_link_headers (line 879) | def test_link_headers(headers, expected):
  function test_decode_error_with_request (line 889) | def test_decode_error_with_request(header_value):
  function test_value_error_without_request (line 909) | def test_value_error_without_request(header_value):
  function test_response_with_unset_request (line 916) | def test_response_with_unset_request():
  function test_set_request_after_init (line 925) | def test_set_request_after_init():
  function test_cannot_access_unset_request (line 934) | def test_cannot_access_unset_request():
  function test_generator_with_transfer_encoding_header (line 941) | def test_generator_with_transfer_encoding_header():
  function test_generator_with_content_length_header (line 949) | def test_generator_with_content_length_header():
  function test_response_picklable (line 958) | def test_response_picklable():
  function test_response_async_streaming_picklable (line 977) | async def test_response_async_streaming_picklable():
  function test_response_decode_text_using_autodetect (line 996) | def test_response_decode_text_using_autodetect():
  function test_response_decode_text_using_explicit_encoding (line 1021) | def test_response_decode_text_using_explicit_encoding():

FILE: tests/models/test_url.py
  function test_basic_url (line 8) | def test_basic_url():
  function test_complete_url (line 24) | def test_complete_url():
  function test_url_with_empty_query (line 40) | def test_url_with_empty_query():
  function test_url_no_scheme (line 56) | def test_url_no_scheme():
  function test_url_no_authority (line 63) | def test_url_no_authority():
  function test_path_query_fragment (line 135) | def test_path_query_fragment(url, raw_path, path, query, fragment):
  function test_url_query_encoding (line 143) | def test_url_query_encoding():
  function test_url_params (line 154) | def test_url_params():
  function test_url_username_and_password (line 207) | def test_url_username_and_password(url, userinfo, username, password):
  function test_url_valid_host (line 217) | def test_url_valid_host():
  function test_url_normalized_host (line 222) | def test_url_normalized_host():
  function test_url_percent_escape_host (line 227) | def test_url_percent_escape_host():
  function test_url_ipv4_like_host (line 232) | def test_url_ipv4_like_host():
  function test_url_valid_port (line 241) | def test_url_valid_port():
  function test_url_normalized_port (line 246) | def test_url_normalized_port():
  function test_url_invalid_port (line 252) | def test_url_invalid_port():
  function test_url_normalized_path (line 261) | def test_url_normalized_path():
  function test_url_escaped_path (line 266) | def test_url_escaped_path():
  function test_url_leading_dot_prefix_on_absolute_url (line 271) | def test_url_leading_dot_prefix_on_absolute_url():
  function test_url_leading_dot_prefix_on_relative_url (line 276) | def test_url_leading_dot_prefix_on_relative_url():
  function test_param_with_space (line 286) | def test_param_with_space():
  function test_param_requires_encoding (line 293) | def test_param_requires_encoding():
  function test_param_with_percent_encoded (line 299) | def test_param_with_percent_encoded():
  function test_param_with_existing_escape_requires_encoding (line 307) | def test_param_with_existing_escape_requires_encoding():
  function test_query_with_existing_percent_encoding (line 320) | def test_query_with_existing_percent_encoding():
  function test_query_requiring_percent_encoding (line 326) | def test_query_requiring_percent_encoding():
  function test_query_with_mixed_percent_encoding (line 332) | def test_query_with_mixed_percent_encoding():
  function test_url_invalid_hostname (line 343) | def test_url_invalid_hostname():
  function test_url_excessively_long_url (line 351) | def test_url_excessively_long_url():
  function test_url_excessively_long_component (line 357) | def test_url_excessively_long_component():
  function test_url_non_printing_character_in_url (line 363) | def test_url_non_printing_character_in_url():
  function test_url_non_printing_character_in_component (line 371) | def test_url_non_printing_character_in_component():
  function test_url_with_components (line 383) | def test_url_with_components():
  function test_urlparse_with_invalid_component (line 397) | def test_urlparse_with_invalid_component():
  function test_urlparse_with_invalid_scheme (line 403) | def test_urlparse_with_invalid_scheme():
  function test_urlparse_with_invalid_path (line 409) | def test_urlparse_with_invalid_path():
  function test_url_with_relative_path (line 423) | def test_url_with_relative_path():
  function test_url_eq_str (line 432) | def test_url_eq_str():
  function test_url_set (line 441) | def test_url_set():
  function test_url_invalid_type (line 458) | def test_url_invalid_type():
  function test_url_with_invalid_component (line 470) | def test_url_with_invalid_component():
  function test_url_join (line 479) | def test_url_join():
  function test_relative_url_join (line 494) | def test_relative_url_join():
  function test_url_join_rfc3986 (line 502) | def test_url_join_rfc3986():
  function test_resolution_error_1833 (line 557) | def test_resolution_error_1833():
  function test_copy_with (line 568) | def test_copy_with():
  function test_url_copywith_authority_subcomponents (line 585) | def test_url_copywith_authority_subcomponents():
  function test_url_copywith_netloc (line 597) | def test_url_copywith_netloc():
  function test_url_copywith_userinfo_subcomponents (line 606) | def test_url_copywith_userinfo_subcomponents():
  function test_url_copywith_invalid_component (line 619) | def test_url_copywith_invalid_component():
  function test_url_copywith_urlencoded_path (line 627) | def test_url_copywith_urlencoded_path():
  function test_url_copywith_query (line 635) | def test_url_copywith_query():
  function test_url_copywith_raw_path (line 643) | def test_url_copywith_raw_path():
  function test_url_copywith_security (line 663) | def test_url_copywith_security():
  function test_url_set_param_manipulation (line 684) | def test_url_set_param_manipulation():
  function test_url_add_param_manipulation (line 692) | def test_url_add_param_manipulation():
  function test_url_remove_param_manipulation (line 700) | def test_url_remove_param_manipulation():
  function test_url_merge_params_manipulation (line 708) | def test_url_merge_params_manipulation():
  function test_idna_url (line 780) | def test_idna_url(given, idna, host, raw_host, scheme, port):
  function test_url_unescaped_idna_host (line 789) | def test_url_unescaped_idna_host():
  function test_url_escaped_idna_host (line 794) | def test_url_escaped_idna_host():
  function test_url_invalid_idna_host (line 799) | def test_url_invalid_idna_host():
  function test_url_valid_ipv4 (line 808) | def test_url_valid_ipv4():
  function test_url_invalid_ipv4 (line 813) | def test_url_invalid_ipv4():
  function test_ipv6_url (line 822) | def test_ipv6_url():
  function test_url_valid_ipv6 (line 829) | def test_url_valid_ipv6():
  function test_url_invalid_ipv6 (line 834) | def test_url_invalid_ipv6():
  function test_ipv6_url_from_raw_url (line 841) | def test_ipv6_url_from_raw_url(host):
  function test_ipv6_url_copy_with_host (line 858) | def test_ipv6_url_copy_with_host(url_str, new_host):

FILE: tests/models/test_whatwg.py
  function test_urlparse (line 23) | def test_urlparse(test_case):

FILE: tests/test_api.py
  function test_get (line 8) | def test_get(server):
  function test_post (line 16) | def test_post(server):
  function test_post_byte_iterator (line 22) | def test_post_byte_iterator(server):
  function test_post_byte_stream (line 33) | def test_post_byte_stream(server):
  function test_options (line 45) | def test_options(server):
  function test_head (line 51) | def test_head(server):
  function test_put (line 57) | def test_put(server):
  function test_patch (line 63) | def test_patch(server):
  function test_delete (line 69) | def test_delete(server):
  function test_stream (line 75) | def test_stream(server):
  function test_get_invalid_url (line 85) | def test_get_invalid_url():
  function test_httpcore_lazy_loading (line 91) | def test_httpcore_lazy_loading(server):

FILE: tests/test_asgi.py
  function hello_world (line 8) | async def hello_world(scope, receive, send):
  function echo_path (line 17) | async def echo_path(scope, receive, send):
  function echo_raw_path (line 26) | async def echo_raw_path(scope, receive, send):
  function echo_body (line 35) | async def echo_body(scope, receive, send):
  function echo_headers (line 48) | async def echo_headers(scope, receive, send):
  function raise_exc (line 59) | async def raise_exc(scope, receive, send):
  function raise_exc_after_response (line 63) | async def raise_exc_after_response(scope, receive, send):
  function test_asgi_transport (line 74) | async def test_asgi_transport():
  function test_asgi_transport_no_body (line 84) | async def test_asgi_transport_no_body():
  function test_asgi (line 94) | async def test_asgi():
  function test_asgi_urlencoded_path (line 104) | async def test_asgi_urlencoded_path():
  function test_asgi_raw_path (line 115) | async def test_asgi_raw_path():
  function test_asgi_raw_path_should_not_include_querystring_portion (line 126) | async def test_asgi_raw_path_should_not_include_querystring_portion():
  function test_asgi_upload (line 140) | async def test_asgi_upload():
  function test_asgi_headers (line 150) | async def test_asgi_headers():
  function test_asgi_exc (line 168) | async def test_asgi_exc():
  function test_asgi_exc_after_response (line 176) | async def test_asgi_exc_after_response():
  function test_asgi_disconnect_after_response_complete (line 184) | async def test_asgi_disconnect_after_response_complete():
  function test_asgi_exc_no_raise (line 219) | async def test_asgi_exc_no_raise():

FILE: tests/test_auth.py
  function test_basic_auth (line 14) | def test_basic_auth():
  function test_digest_auth_with_200 (line 29) | def test_digest_auth_with_200():
  function test_digest_auth_with_401 (line 44) | def test_digest_auth_with_401():
  function test_digest_auth_with_401_nonce_counting (line 69) | def test_digest_auth_with_401_nonce_counting():
  function set_cookies (line 107) | def set_cookies(request: httpx.Request) -> httpx.Response:
  function test_digest_auth_setting_cookie_in_request (line 120) | def test_digest_auth_setting_cookie_in_request():
  function test_digest_auth_rfc_2069 (line 146) | def test_digest_auth_rfc_2069():
  function test_digest_auth_rfc_7616_md5 (line 191) | def test_digest_auth_rfc_7616_md5(monkeypatch):
  function test_digest_auth_rfc_7616_sha_256 (line 251) | def test_digest_auth_rfc_7616_sha_256(monkeypatch):

FILE: tests/test_config.py
  function test_load_ssl_config (line 11) | def test_load_ssl_config():
  function test_load_ssl_config_verify_non_existing_file (line 17) | def test_load_ssl_config_verify_non_existing_file():
  function test_load_ssl_with_keylog (line 23) | def test_load_ssl_with_keylog(monkeypatch: typing.Any) -> None:
  function test_load_ssl_config_verify_existing_file (line 29) | def test_load_ssl_config_verify_existing_file():
  function test_load_ssl_config_verify_directory (line 36) | def test_load_ssl_config_verify_directory():
  function test_load_ssl_config_cert_and_key (line 43) | def test_load_ssl_config_cert_and_key(cert_pem_file, cert_private_key_fi...
  function test_load_ssl_config_cert_and_encrypted_key (line 51) | def test_load_ssl_config_cert_and_encrypted_key(
  function test_load_ssl_config_cert_and_key_invalid_password (line 60) | def test_load_ssl_config_cert_and_key_invalid_password(
  function test_load_ssl_config_cert_without_key_raises (line 70) | def test_load_ssl_config_cert_without_key_raises(cert_pem_file):
  function test_load_ssl_config_no_verify (line 76) | def test_load_ssl_config_no_verify():
  function test_SSLContext_with_get_request (line 82) | def test_SSLContext_with_get_request(server, cert_pem_file):
  function test_limits_repr (line 89) | def test_limits_repr():
  function test_limits_eq (line 98) | def test_limits_eq():
  function test_timeout_eq (line 103) | def test_timeout_eq():
  function test_timeout_all_parameters_set (line 108) | def test_timeout_all_parameters_set():
  function test_timeout_from_nothing (line 113) | def test_timeout_from_nothing():
  function test_timeout_from_none (line 121) | def test_timeout_from_none():
  function test_timeout_from_one_none_value (line 126) | def test_timeout_from_one_none_value():
  function test_timeout_from_one_value (line 131) | def test_timeout_from_one_value():
  function test_timeout_from_one_value_and_default (line 136) | def test_timeout_from_one_value_and_default():
  function test_timeout_missing_default (line 141) | def test_timeout_missing_default():
  function test_timeout_from_tuple (line 146) | def test_timeout_from_tuple():
  function test_timeout_from_config_instance (line 151) | def test_timeout_from_config_instance():
  function test_timeout_repr (line 156) | def test_timeout_repr():
  function test_proxy_from_url (line 164) | def test_proxy_from_url():
  function test_proxy_with_auth_from_url (line 173) | def test_proxy_with_auth_from_url():
  function test_invalid_proxy_scheme (line 182) | def test_invalid_proxy_scheme():

FILE: tests/test_content.py
  function test_empty_content (line 13) | async def test_empty_content():
  function test_bytes_content (line 27) | async def test_bytes_content():
  function test_bytesio_content (line 54) | async def test_bytesio_content():
  function test_async_bytesio_content (line 66) | async def test_async_bytesio_content():
  function test_iterator_content (line 94) | async def test_iterator_content():
  function test_aiterator_content (line 130) | async def test_aiterator_content():
  function test_json_content (line 166) | async def test_json_content():
  function test_urlencoded_content (line 184) | async def test_urlencoded_content():
  function test_urlencoded_boolean (line 202) | async def test_urlencoded_boolean():
  function test_urlencoded_none (line 220) | async def test_urlencoded_none():
  function test_urlencoded_list (line 238) | async def test_urlencoded_list():
  function test_multipart_files_content (line 256) | async def test_multipart_files_content():
  function test_multipart_data_and_files_content (line 299) | async def test_multipart_data_and_files_content():
  function test_empty_request (line 346) | async def test_empty_request():
  function test_invalid_argument (line 359) | def test_invalid_argument():
  function test_multipart_multiple_files_single_input_content (line 368) | async def test_multipart_multiple_files_single_input_content():
  function test_response_empty_content (line 419) | async def test_response_empty_content():
  function test_response_bytes_content (line 433) | async def test_response_bytes_content():
  function test_response_iterator_content (line 447) | async def test_response_iterator_content():
  function test_response_aiterator_content (line 466) | async def test_response_aiterator_content():
  function test_response_invalid_argument (line 484) | def test_response_invalid_argument():
  function test_ensure_ascii_false_with_french_characters (line 489) | def test_ensure_ascii_false_with_french_characters():
  function test_separators_for_compact_json (line 498) | def test_separators_for_compact_json():
  function test_allow_nan_false (line 507) | def test_allow_nan_false():

FILE: tests/test_decoders.py
  function test_deflate (line 14) | def test_deflate():
  function test_zlib (line 33) | def test_zlib():
  function test_gzip (line 51) | def test_gzip():
  function test_brotli (line 65) | def test_brotli():
  function test_zstd (line 78) | def test_zstd():
  function test_zstd_decoding_error (line 91) | def test_zstd_decoding_error():
  function test_zstd_empty (line 103) | def test_zstd_empty():
  function test_zstd_truncated (line 109) | def test_zstd_truncated():
  function test_zstd_multiframe (line 122) | def test_zstd_multiframe():
  function test_multi (line 144) | def test_multi():
  function test_multi_with_identity (line 164) | def test_multi_with_identity():
  function test_streaming (line 186) | async def test_streaming():
  function test_empty_content (line 205) | def test_empty_content(header_value):
  function test_decoders_empty_cases (line 216) | def test_decoders_empty_cases(header_value):
  function test_decoding_errors (line 223) | def test_decoding_errors(header_value):
  function test_text_decoder_with_autodetect (line 244) | async def test_text_decoder_with_autodetect(data, encoding):
  function test_text_decoder_known_encoding (line 267) | async def test_text_decoder_known_encoding():
  function test_text_decoder_empty_cases (line 283) | def test_text_decoder_empty_cases():
  function test_streaming_text_decoder (line 296) | def test_streaming_text_decoder(
  function test_line_decoder_nl (line 303) | def test_line_decoder_nl():
  function test_line_decoder_cr (line 317) | def test_line_decoder_cr():
  function test_line_decoder_crnl (line 331) | def test_line_decoder_crnl():
  function test_invalid_content_encoding_header (line 346) | def test_invalid_content_encoding_header():

FILE: tests/test_exceptions.py
  function test_httpcore_all_exceptions_mapped (line 14) | def test_httpcore_all_exceptions_mapped() -> None:
  function test_httpcore_exception_mapping (line 39) | def test_httpcore_exception_mapping(server: TestServer) -> None:
  function test_request_attribute (line 54) | def test_request_attribute() -> None:

FILE: tests/test_exported_members.py
  function test_all_imports_are_exported (line 4) | def test_all_imports_are_exported() -> None:

FILE: tests/test_main.py
  function splitlines (line 9) | def splitlines(output: str) -> typing.Iterable[str]:
  function remove_date_header (line 13) | def remove_date_header(lines: typing.Iterable[str]) -> typing.Iterable[s...
  function test_help (line 17) | def test_help():
  function test_get (line 24) | def test_get(server):
  function test_json (line 39) | def test_json(server):
  function test_binary (line 56) | def test_binary(server):
  function test_redirects (line 72) | def test_redirects(server):
  function test_follow_redirects (line 86) | def test_follow_redirects(server):
  function test_post (line 106) | def test_post(server):
  function test_verbose (line 121) | def test_verbose(server):
  function test_auth (line 145) | def test_auth(server):
  function test_download (line 171) | def test_download(server):
  function test_errors (line 181) | def test_errors():

FILE: tests/test_multipart.py
  function echo_request_content (line 12) | def echo_request_content(request: httpx.Request) -> httpx.Response:
  function test_multipart (line 17) | def test_multipart(value, output):
  function test_multipart_explicit_boundary (line 57) | def test_multipart_explicit_boundary(header: str) -> None:
  function test_multipart_header_without_boundary (line 86) | def test_multipart_header_without_boundary(header: str) -> None:
  function test_multipart_invalid_key (line 98) | def test_multipart_invalid_key(key):
  function test_multipart_invalid_value (line 114) | def test_multipart_invalid_value(value):
  function test_multipart_file_tuple (line 124) | def test_multipart_file_tuple():
  function test_multipart_file_tuple_headers (line 153) | def test_multipart_file_tuple_headers(file_content_type: str | None) -> ...
  function test_multipart_headers_include_content_type (line 178) | def test_multipart_headers_include_content_type() -> None:
  function test_multipart_encode (line 208) | def test_multipart_encode(tmp_path: typing.Any) -> None:
  function test_multipart_encode_unicode_file_contents (line 251) | def test_multipart_encode_unicode_file_contents() -> None:
  function test_multipart_encode_files_allows_filenames_as_none (line 272) | def test_multipart_encode_files_allows_filenames_as_none() -> None:
  function test_multipart_encode_files_guesses_correct_content_type (line 300) | def test_multipart_encode_files_guesses_correct_content_type(
  function test_multipart_encode_files_allows_bytes_content (line 323) | def test_multipart_encode_files_allows_bytes_content() -> None:
  function test_multipart_encode_files_allows_str_content (line 345) | def test_multipart_encode_files_allows_str_content() -> None:
  function test_multipart_encode_files_raises_exception_with_StringIO_content (line 367) | def test_multipart_encode_files_raises_exception_with_StringIO_content()...
  function test_multipart_encode_files_raises_exception_with_text_mode_file (line 374) | def test_multipart_encode_files_raises_exception_with_text_mode_file() -...
  function test_multipart_encode_non_seekable_filelike (line 382) | def test_multipart_encode_non_seekable_filelike() -> None:
  function test_multipart_rewinds_files (line 423) | def test_multipart_rewinds_files():
  class TestHeaderParamHTML5Formatting (line 442) | class TestHeaderParamHTML5Formatting:
    method test_unicode (line 443) | def test_unicode(self):
    method test_ascii (line 450) | def test_ascii(self):
    method test_unicode_escape (line 457) | def test_unicode_escape(self):
    method test_unicode_with_control_character (line 464) | def test_unicode_with_control_character(self):

FILE: tests/test_status_codes.py
  function test_status_code_as_int (line 4) | def test_status_code_as_int():
  function test_status_code_value_lookup (line 10) | def test_status_code_value_lookup():
  function test_status_code_phrase_lookup (line 14) | def test_status_code_phrase_lookup():
  function test_lowercase_status_code (line 18) | def test_lowercase_status_code():
  function test_reason_phrase_for_status_code (line 22) | def test_reason_phrase_for_status_code():
  function test_reason_phrase_for_unknown_status_code (line 26) | def test_reason_phrase_for_unknown_status_code():

FILE: tests/test_timeouts.py
  function test_read_timeout (line 7) | async def test_read_timeout(server):
  function test_write_timeout (line 16) | async def test_write_timeout(server):
  function test_connect_timeout (line 27) | async def test_connect_timeout(server):
  function test_pool_timeout (line 37) | async def test_pool_timeout(server):
  function test_async_client_new_request_send_timeout (line 48) | async def test_async_client_new_request_send_timeout(server):

FILE: tests/test_utils.py
  function test_encoded (line 25) | def test_encoded(encoding):
  function test_bad_utf_like_encoding (line 31) | def test_bad_utf_like_encoding():
  function test_guess_by_bom (line 47) | def test_guess_by_bom(encoding, expected):
  function test_logging_request (line 53) | def test_logging_request(server, caplog):
  function test_logging_redirect_chain (line 68) | def test_logging_redirect_chain(server, caplog):
  function test_get_environment_proxies (line 109) | def test_get_environment_proxies(environment, proxies):
  function test_url_matches (line 132) | def test_url_matches(pattern, url, expected):
  function test_pattern_priority (line 137) | def test_pattern_priority():

FILE: tests/test_wsgi.py
  function application_factory (line 17) | def application_factory(output: typing.Iterable[bytes]) -> WSGIApplication:
  function echo_body (line 33) | def echo_body(
  function echo_body_with_response_stream (line 48) | def echo_body_with_response_stream(
  function raise_exc (line 67) | def raise_exc(
  function log_to_wsgi_log_buffer (line 88) | def log_to_wsgi_log_buffer(environ, start_response):
  function test_wsgi (line 94) | def test_wsgi():
  function test_wsgi_upload (line 102) | def test_wsgi_upload():
  function test_wsgi_upload_with_response_stream (line 110) | def test_wsgi_upload_with_response_stream():
  function test_wsgi_exc (line 118) | def test_wsgi_exc():
  function test_wsgi_http_error (line 125) | def test_wsgi_http_error():
  function test_wsgi_generator (line 132) | def test_wsgi_generator():
  function test_wsgi_generator_empty (line 141) | def test_wsgi_generator_empty():
  function test_logging (line 150) | def test_logging():
  function test_wsgi_server_port (line 168) | def test_wsgi_server_port(url: str, expected_server_port: str) -> None:
  function test_wsgi_server_protocol (line 188) | def test_wsgi_server_protocol():
Condensed preview — 115 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,076K chars).
[
  {
    "path": ".github/CONTRIBUTING.md",
    "chars": 8367,
    "preview": "# Contributing\n\nThank you for being interested in contributing to HTTPX.\nThere are many ways you can contribute to the p"
  },
  {
    "path": ".github/FUNDING.yml",
    "chars": 15,
    "preview": "github: encode\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/1-issue.md",
    "chars": 585,
    "preview": "---\nname: Issue\nabout: Please only raise an issue if you've been advised to do so after discussion. Thanks! 🙏\n---\n\nThe s"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "chars": 429,
    "preview": "# Ref: https://help.github.com/en/github/building-a-strong-community/configuring-issue-templates-for-your-repository#con"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "chars": 537,
    "preview": "<!-- Thanks for contributing to HTTPX! 💚\nGiven this is a project maintained by volunteers, please read this template to "
  },
  {
    "path": ".github/dependabot.yml",
    "chars": 275,
    "preview": "version: 2\nupdates:\n  - package-ecosystem: \"pip\"\n    directory: \"/\"\n    schedule:\n      interval: \"monthly\"\n    groups:\n"
  },
  {
    "path": ".github/workflows/publish.yml",
    "chars": 606,
    "preview": "name: Publish\n\non:\n  push:\n    tags:\n      - '*'\n\njobs:\n  publish:\n    name: \"Publish release\"\n    runs-on: \"ubuntu-late"
  },
  {
    "path": ".github/workflows/test-suite.yml",
    "chars": 809,
    "preview": "---\nname: Test Suite\n\non:\n  push:\n    branches: [\"master\"]\n  pull_request:\n    branches: [\"master\", \"version-*\"]\n\njobs:\n"
  },
  {
    "path": ".gitignore",
    "chars": 120,
    "preview": "*.pyc\n.coverage\n.pytest_cache/\n.mypy_cache/\n__pycache__/\nhtmlcov/\nsite/\n*.egg-info/\nvenv*/\n.python-version\nbuild/\ndist/\n"
  },
  {
    "path": "CHANGELOG.md",
    "chars": 53273,
    "preview": "# Changelog\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Change"
  },
  {
    "path": "LICENSE.md",
    "chars": 1507,
    "preview": "Copyright © 2019, [Encode OSS Ltd](https://www.encode.io/).\nAll rights reserved.\n\nRedistribution and use in source and b"
  },
  {
    "path": "README.md",
    "chars": 4941,
    "preview": "<p align=\"center\">\n  <a href=\"https://www.python-httpx.org/\"><img width=\"350\" height=\"208\" src=\"https://raw.githubuserco"
  },
  {
    "path": "docs/CNAME",
    "chars": 21,
    "preview": "www.python-httpx.org\n"
  },
  {
    "path": "docs/advanced/authentication.md",
    "chars": 8554,
    "preview": "Authentication can either be included on a per-request basis...\n\n```pycon\n>>> auth = httpx.BasicAuth(username=\"username\""
  },
  {
    "path": "docs/advanced/clients.md",
    "chars": 11452,
    "preview": "!!! hint\n    If you are coming from Requests, `httpx.Client()` is what you can use instead of `requests.Session()`.\n\n## "
  },
  {
    "path": "docs/advanced/event-hooks.md",
    "chars": 2419,
    "preview": "HTTPX allows you to register \"event hooks\" with the client, that are called\nevery time a particular type of event takes "
  },
  {
    "path": "docs/advanced/extensions.md",
    "chars": 9446,
    "preview": "# Extensions\n\nRequest and response extensions provide a untyped space where additional information may be added.\n\nExtens"
  },
  {
    "path": "docs/advanced/proxies.md",
    "chars": 3563,
    "preview": "HTTPX supports setting up [HTTP proxies](https://en.wikipedia.org/wiki/Proxy_server#Web_proxy_servers) via the `proxy` p"
  },
  {
    "path": "docs/advanced/resource-limits.md",
    "chars": 596,
    "preview": "You can control the connection pool size using the `limits` keyword\nargument on the client. It takes instances of `httpx"
  },
  {
    "path": "docs/advanced/ssl.md",
    "chars": 3623,
    "preview": "When making a request over HTTPS, HTTPX needs to verify the identity of the requested host. To do this, it uses a bundle"
  },
  {
    "path": "docs/advanced/text-encodings.md",
    "chars": 3655,
    "preview": "When accessing `response.text`, we need to decode the response bytes into a unicode text representation.\n\nBy default `ht"
  },
  {
    "path": "docs/advanced/timeouts.md",
    "chars": 2763,
    "preview": "HTTPX is careful to enforce timeouts everywhere by default.\n\nThe default behavior is to raise a `TimeoutException` after"
  },
  {
    "path": "docs/advanced/transports.md",
    "chars": 14201,
    "preview": "HTTPX's `Client` also accepts a `transport` argument. This argument allows you\nto provide a custom Transport object that"
  },
  {
    "path": "docs/api.md",
    "chars": 4484,
    "preview": "# Developer Interface\n\n## Helper Functions\n\n!!! note\n    Only use these functions if you're testing HTTPX in a console\n "
  },
  {
    "path": "docs/async.md",
    "chars": 6416,
    "preview": "# Async Support\n\nHTTPX offers a standard synchronous API by default, but also gives you\nthe option of an async client if"
  },
  {
    "path": "docs/code_of_conduct.md",
    "chars": 4633,
    "preview": "# Code of Conduct\n\nWe expect contributors to our projects and online spaces to follow [the Python Software Foundation’s "
  },
  {
    "path": "docs/compatibility.md",
    "chars": 9891,
    "preview": "# Requests Compatibility Guide\n\nHTTPX aims to be broadly compatible with the `requests` API, although there are a\nfew de"
  },
  {
    "path": "docs/contributing.md",
    "chars": 8359,
    "preview": "# Contributing\n\nThank you for being interested in contributing to HTTPX.\nThere are many ways you can contribute to the p"
  },
  {
    "path": "docs/css/custom.css",
    "chars": 187,
    "preview": "div.autodoc-docstring {\n  padding-left: 20px;\n  margin-bottom: 30px;\n  border-left: 5px solid rgba(230, 230, 230);\n}\n\ndi"
  },
  {
    "path": "docs/environment_variables.md",
    "chars": 2844,
    "preview": "# Environment Variables\n\nThe HTTPX library can be configured via environment variables.\nEnvironment variables are used b"
  },
  {
    "path": "docs/exceptions.md",
    "chars": 2112,
    "preview": "# Exceptions\n\nThis page lists exceptions that may be raised when using HTTPX.\n\nFor an overview of how to work with HTTPX"
  },
  {
    "path": "docs/http2.md",
    "chars": 2570,
    "preview": "# HTTP/2\n\nHTTP/2 is a major new iteration of the HTTP protocol, that provides a far more\nefficient transport, with poten"
  },
  {
    "path": "docs/index.md",
    "chars": 4160,
    "preview": "<p align=\"center\" style=\"margin: 0 0 10px\">\n  <img width=\"350\" height=\"208\" src=\"https://raw.githubusercontent.com/encod"
  },
  {
    "path": "docs/logging.md",
    "chars": 3962,
    "preview": "# Logging\n\nIf you need to inspect the internal behaviour of `httpx`, you can use Python's standard logging to output inf"
  },
  {
    "path": "docs/overrides/partials/nav.html",
    "chars": 1517,
    "preview": "{% import \"partials/nav-item.html\" as item with context %}\n\n<!-- Determine class according to configuration -->\n {% set "
  },
  {
    "path": "docs/quickstart.md",
    "chars": 14700,
    "preview": "# QuickStart\n\nFirst, start by importing HTTPX:\n\n```pycon\n>>> import httpx\n```\n\nNow, let’s try to get a webpage.\n\n```pyco"
  },
  {
    "path": "docs/third_party_packages.md",
    "chars": 3332,
    "preview": "# Third Party Packages\n\nAs HTTPX usage grows, there is an expanding community of developers building tools and libraries"
  },
  {
    "path": "docs/troubleshooting.md",
    "chars": 2150,
    "preview": "# Troubleshooting\n\nThis page lists some common problems or issues you could encounter while developing with HTTPX, as we"
  },
  {
    "path": "httpx/__init__.py",
    "chars": 2191,
    "preview": "from .__version__ import __description__, __title__, __version__\nfrom ._api import *\nfrom ._auth import *\nfrom ._client "
  },
  {
    "path": "httpx/__version__.py",
    "chars": 108,
    "preview": "__title__ = \"httpx\"\n__description__ = \"A next generation HTTP client, for Python 3.\"\n__version__ = \"0.28.1\"\n"
  },
  {
    "path": "httpx/_api.py",
    "chars": 11743,
    "preview": "from __future__ import annotations\n\nimport typing\nfrom contextlib import contextmanager\n\nfrom ._client import Client\nfro"
  },
  {
    "path": "httpx/_auth.py",
    "chars": 11907,
    "preview": "from __future__ import annotations\n\nimport hashlib\nimport os\nimport re\nimport time\nimport typing\nfrom base64 import b64e"
  },
  {
    "path": "httpx/_client.py",
    "chars": 65713,
    "preview": "from __future__ import annotations\n\nimport datetime\nimport enum\nimport logging\nimport time\nimport typing\nimport warnings"
  },
  {
    "path": "httpx/_config.py",
    "chars": 8547,
    "preview": "from __future__ import annotations\n\nimport os\nimport typing\n\nfrom ._models import Headers\nfrom ._types import CertTypes,"
  },
  {
    "path": "httpx/_content.py",
    "chars": 8161,
    "preview": "from __future__ import annotations\n\nimport inspect\nimport warnings\nfrom json import dumps as json_dumps\nfrom typing impo"
  },
  {
    "path": "httpx/_decoders.py",
    "chars": 12041,
    "preview": "\"\"\"\nHandlers for Content-Encoding.\n\nSee: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Encoding\n\"\"\"\n"
  },
  {
    "path": "httpx/_exceptions.py",
    "chars": 8490,
    "preview": "\"\"\"\nOur exception hierarchy:\n\n* HTTPError\n  x RequestError\n    + TransportError\n      - TimeoutException\n        · Conne"
  },
  {
    "path": "httpx/_main.py",
    "chars": 15626,
    "preview": "from __future__ import annotations\n\nimport functools\nimport json\nimport sys\nimport typing\n\nimport click\nimport pygments."
  },
  {
    "path": "httpx/_models.py",
    "chars": 44697,
    "preview": "from __future__ import annotations\n\nimport codecs\nimport datetime\nimport email.message\nimport json as jsonlib\nimport re\n"
  },
  {
    "path": "httpx/_multipart.py",
    "chars": 9843,
    "preview": "from __future__ import annotations\n\nimport io\nimport mimetypes\nimport os\nimport re\nimport typing\nfrom pathlib import Pat"
  },
  {
    "path": "httpx/_status_codes.py",
    "chars": 5639,
    "preview": "from __future__ import annotations\n\nfrom enum import IntEnum\n\n__all__ = [\"codes\"]\n\n\nclass codes(IntEnum):\n    \"\"\"HTTP st"
  },
  {
    "path": "httpx/_transports/__init__.py",
    "chars": 275,
    "preview": "from .asgi import *\nfrom .base import *\nfrom .default import *\nfrom .mock import *\nfrom .wsgi import *\n\n__all__ = [\n    "
  },
  {
    "path": "httpx/_transports/asgi.py",
    "chars": 5501,
    "preview": "from __future__ import annotations\n\nimport typing\n\nfrom .._models import Request, Response\nfrom .._types import AsyncByt"
  },
  {
    "path": "httpx/_transports/base.py",
    "chars": 2523,
    "preview": "from __future__ import annotations\n\nimport typing\nfrom types import TracebackType\n\nfrom .._models import Request, Respon"
  },
  {
    "path": "httpx/_transports/default.py",
    "chars": 13984,
    "preview": "\"\"\"\nCustom transports, with nicely configured defaults.\n\nThe following additional keyword arguments are currently suppor"
  },
  {
    "path": "httpx/_transports/mock.py",
    "chars": 1232,
    "preview": "from __future__ import annotations\n\nimport typing\n\nfrom .._models import Request, Response\nfrom .base import AsyncBaseTr"
  },
  {
    "path": "httpx/_transports/wsgi.py",
    "chars": 4825,
    "preview": "from __future__ import annotations\n\nimport io\nimport itertools\nimport sys\nimport typing\n\nfrom .._models import Request, "
  },
  {
    "path": "httpx/_types.py",
    "chars": 2965,
    "preview": "\"\"\"\nType definitions for type checking purposes.\n\"\"\"\n\nfrom http.cookiejar import CookieJar\nfrom typing import (\n    IO,\n"
  },
  {
    "path": "httpx/_urlparse.py",
    "chars": 18546,
    "preview": "\"\"\"\nAn implementation of `urlparse` that provides URL validation and normalization\nas described by RFC3986.\n\nWe rely on "
  },
  {
    "path": "httpx/_urls.py",
    "chars": 21515,
    "preview": "from __future__ import annotations\n\nimport typing\nfrom urllib.parse import parse_qs, unquote, urlencode\n\nimport idna\n\nfr"
  },
  {
    "path": "httpx/_utils.py",
    "chars": 8285,
    "preview": "from __future__ import annotations\n\nimport ipaddress\nimport os\nimport re\nimport typing\nfrom urllib.request import getpro"
  },
  {
    "path": "httpx/py.typed",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "mkdocs.yml",
    "chars": 1857,
    "preview": "site_name: HTTPX\nsite_description: A next-generation HTTP client for Python.\nsite_url: https://www.python-httpx.org/\n\nth"
  },
  {
    "path": "pyproject.toml",
    "chars": 3640,
    "preview": "[build-system]\nrequires = [\"hatchling\", \"hatch-fancy-pypi-readme\"]\nbuild-backend = \"hatchling.build\"\n\n[project]\nname = \""
  },
  {
    "path": "requirements.txt",
    "chars": 683,
    "preview": "# We're pinning our tooling, because it's an environment we can strictly control.\n# On the other hand, we're not pinning"
  },
  {
    "path": "scripts/build",
    "chars": 167,
    "preview": "#!/bin/sh -e\n\nif [ -d 'venv' ] ; then\n    PREFIX=\"venv/bin/\"\nelse\n    PREFIX=\"\"\nfi\n\nset -x\n\n${PREFIX}python -m build\n${P"
  },
  {
    "path": "scripts/check",
    "chars": 258,
    "preview": "#!/bin/sh -e\n\nexport PREFIX=\"\"\nif [ -d 'venv' ] ; then\n    export PREFIX=\"venv/bin/\"\nfi\nexport SOURCE_FILES=\"httpx tests"
  },
  {
    "path": "scripts/clean",
    "chars": 208,
    "preview": "#!/bin/sh -e\n\nif [ -d 'dist' ] ; then\n    rm -r dist\nfi\nif [ -d 'site' ] ; then\n    rm -r site\nfi\nif [ -d 'htmlcov' ] ; "
  },
  {
    "path": "scripts/coverage",
    "chars": 203,
    "preview": "#!/bin/sh -e\n\nexport PREFIX=\"\"\nif [ -d 'venv' ] ; then\n    export PREFIX=\"venv/bin/\"\nfi\nexport SOURCE_FILES=\"httpx tests"
  },
  {
    "path": "scripts/docs",
    "chars": 119,
    "preview": "#!/bin/sh -e\n\nexport PREFIX=\"\"\nif [ -d 'venv' ] ; then\n    export PREFIX=\"venv/bin/\"\nfi\n\nset -x\n\n${PREFIX}mkdocs serve\n"
  },
  {
    "path": "scripts/install",
    "chars": 357,
    "preview": "#!/bin/sh -e\n\n# Use the Python executable provided from the `-p` option, or a default.\n[ \"$1\" = \"-p\" ] && PYTHON=$2 || P"
  },
  {
    "path": "scripts/lint",
    "chars": 205,
    "preview": "#!/bin/sh -e\n\nexport PREFIX=\"\"\nif [ -d 'venv' ]; then\n    export PREFIX=\"venv/bin/\"\nfi\nexport SOURCE_FILES=\"httpx tests\""
  },
  {
    "path": "scripts/publish",
    "chars": 592,
    "preview": "#!/bin/sh -e\n\nVERSION_FILE=\"httpx/__version__.py\"\n\nif [ -d 'venv' ] ; then\n    PREFIX=\"venv/bin/\"\nelse\n    PREFIX=\"\"\nfi\n"
  },
  {
    "path": "scripts/sync-version",
    "chars": 469,
    "preview": "#!/bin/sh -e\n\nSEMVER_REGEX=\"([0-9]+)\\.([0-9]+)\\.([0-9]+)(?:-([0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*))?(?:\\+[0-9A-Za-z-]+)?\"\nC"
  },
  {
    "path": "scripts/test",
    "chars": 243,
    "preview": "#!/bin/sh\n\nexport PREFIX=\"\"\nif [ -d 'venv' ] ; then\n    export PREFIX=\"venv/bin/\"\nfi\n\nset -ex\n\nif [ -z $GITHUB_ACTIONS ]"
  },
  {
    "path": "tests/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "tests/client/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "tests/client/test_async_client.py",
    "chars": 11670,
    "preview": "from __future__ import annotations\n\nimport typing\nfrom datetime import timedelta\n\nimport pytest\n\nimport httpx\n\n\n@pytest."
  },
  {
    "path": "tests/client/test_auth.py",
    "chars": 25250,
    "preview": "\"\"\"\nIntegration tests for authentication.\n\nUnit tests for auth classes also exist in tests/test_auth.py\n\"\"\"\n\nimport hash"
  },
  {
    "path": "tests/client/test_client.py",
    "chars": 14976,
    "preview": "from __future__ import annotations\n\nimport typing\nfrom datetime import timedelta\n\nimport chardet\nimport pytest\n\nimport h"
  },
  {
    "path": "tests/client/test_cookies.py",
    "chars": 4943,
    "preview": "from http.cookiejar import Cookie, CookieJar\n\nimport pytest\n\nimport httpx\n\n\ndef get_and_set_cookies(request: httpx.Reque"
  },
  {
    "path": "tests/client/test_event_hooks.py",
    "chars": 7105,
    "preview": "import pytest\n\nimport httpx\n\n\ndef app(request: httpx.Request) -> httpx.Response:\n    if request.url.path == \"/redirect\":"
  },
  {
    "path": "tests/client/test_headers.py",
    "chars": 8918,
    "preview": "#!/usr/bin/env python3\n\nimport pytest\n\nimport httpx\n\n\ndef echo_headers(request: httpx.Request) -> httpx.Response:\n    da"
  },
  {
    "path": "tests/client/test_properties.py",
    "chars": 1976,
    "preview": "import httpx\n\n\ndef test_client_base_url():\n    client = httpx.Client()\n    client.base_url = \"https://www.example.org/\"\n"
  },
  {
    "path": "tests/client/test_proxies.py",
    "chars": 9649,
    "preview": "import httpcore\nimport pytest\n\nimport httpx\n\n\ndef url_to_origin(url: str) -> httpcore.URL:\n    \"\"\"\n    Given a URL strin"
  },
  {
    "path": "tests/client/test_queryparams.py",
    "chars": 1083,
    "preview": "import httpx\n\n\ndef hello_world(request: httpx.Request) -> httpx.Response:\n    return httpx.Response(200, text=\"Hello, wo"
  },
  {
    "path": "tests/client/test_redirects.py",
    "chars": 17479,
    "preview": "import typing\n\nimport pytest\n\nimport httpx\n\n\ndef redirects(request: httpx.Request) -> httpx.Response:\n    if request.url"
  },
  {
    "path": "tests/common.py",
    "chars": 96,
    "preview": "import pathlib\n\nTESTS_DIR = pathlib.Path(__file__).parent\nFIXTURES_DIR = TESTS_DIR / \"fixtures\"\n"
  },
  {
    "path": "tests/concurrency.py",
    "chars": 324,
    "preview": "\"\"\"\nAsync environment-agnostic concurrency utilities that are only used in tests.\n\"\"\"\n\nimport asyncio\n\nimport sniffio\nim"
  },
  {
    "path": "tests/conftest.py",
    "chars": 8630,
    "preview": "import asyncio\nimport json\nimport os\nimport threading\nimport time\nimport typing\n\nimport pytest\nimport trustme\nfrom crypt"
  },
  {
    "path": "tests/fixtures/.netrc",
    "chars": 73,
    "preview": "machine netrcexample.org\nlogin example-username\npassword example-password"
  },
  {
    "path": "tests/fixtures/.netrc-nopassword",
    "chars": 48,
    "preview": "machine netrcexample.org\nlogin example-username\n"
  },
  {
    "path": "tests/models/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "tests/models/test_cookies.py",
    "chars": 2917,
    "preview": "import http\n\nimport pytest\n\nimport httpx\n\n\ndef test_cookies():\n    cookies = httpx.Cookies({\"name\": \"value\"})\n    assert"
  },
  {
    "path": "tests/models/test_headers.py",
    "chars": 6982,
    "preview": "import pytest\n\nimport httpx\n\n\ndef test_headers():\n    h = httpx.Headers([(\"a\", \"123\"), (\"a\", \"456\"), (\"b\", \"789\")])\n    "
  },
  {
    "path": "tests/models/test_queryparams.py",
    "chars": 3567,
    "preview": "import pytest\n\nimport httpx\n\n\n@pytest.mark.parametrize(\n    \"source\",\n    [\n        \"a=123&a=456&b=789\",\n        {\"a\": ["
  },
  {
    "path": "tests/models/test_requests.py",
    "chars": 8183,
    "preview": "import pickle\nimport typing\n\nimport pytest\n\nimport httpx\n\n\ndef test_request_repr():\n    request = httpx.Request(\"GET\", \""
  },
  {
    "path": "tests/models/test_responses.py",
    "chars": 30218,
    "preview": "import json\nimport pickle\nimport typing\n\nimport chardet\nimport pytest\n\nimport httpx\n\n\nclass StreamingBody:\n    def __ite"
  },
  {
    "path": "tests/models/test_url.py",
    "chars": 26490,
    "preview": "import pytest\n\nimport httpx\n\n# Tests for `httpx.URL` instantiation and property accessors.\n\n\ndef test_basic_url():\n    u"
  },
  {
    "path": "tests/models/test_whatwg.py",
    "chars": 1939,
    "preview": "# The WHATWG have various tests that can be used to validate the URL parsing.\n#\n# https://url.spec.whatwg.org/\n\nimport j"
  },
  {
    "path": "tests/models/whatwg.json",
    "chars": 208737,
    "preview": "[\n  \"See ../README.md for a description of the format.\",\n  {\n    \"input\": \"http://example\\t.\\norg\",\n    \"base\": \"http://"
  },
  {
    "path": "tests/test_api.py",
    "chars": 2631,
    "preview": "import typing\n\nimport pytest\n\nimport httpx\n\n\ndef test_get(server):\n    response = httpx.get(server.url)\n    assert respo"
  },
  {
    "path": "tests/test_asgi.py",
    "chars": 7789,
    "preview": "import json\n\nimport pytest\n\nimport httpx\n\n\nasync def hello_world(scope, receive, send):\n    status = 200\n    output = b\""
  },
  {
    "path": "tests/test_auth.py",
    "chars": 11524,
    "preview": "\"\"\"\nUnit tests for auth classes.\n\nIntegration tests also exist in tests/client/test_auth.py\n\"\"\"\n\nfrom urllib.request imp"
  },
  {
    "path": "tests/test_config.py",
    "chars": 5561,
    "preview": "import ssl\nimport typing\nfrom pathlib import Path\n\nimport certifi\nimport pytest\n\nimport httpx\n\n\ndef test_load_ssl_config"
  },
  {
    "path": "tests/test_content.py",
    "chars": 17311,
    "preview": "import io\nimport typing\n\nimport pytest\n\nimport httpx\n\nmethod = \"POST\"\nurl = \"https://www.example.com\"\n\n\n@pytest.mark.any"
  },
  {
    "path": "tests/test_decoders.py",
    "chars": 10299,
    "preview": "from __future__ import annotations\n\nimport io\nimport typing\nimport zlib\n\nimport chardet\nimport pytest\nimport zstandard a"
  },
  {
    "path": "tests/test_exceptions.py",
    "chars": 1847,
    "preview": "from __future__ import annotations\n\nimport typing\n\nimport httpcore\nimport pytest\n\nimport httpx\n\nif typing.TYPE_CHECKING:"
  },
  {
    "path": "tests/test_exported_members.py",
    "chars": 372,
    "preview": "import httpx\n\n\ndef test_all_imports_are_exported() -> None:\n    included_private_members = [\"__description__\", \"__title_"
  },
  {
    "path": "tests/test_main.py",
    "chars": 5544,
    "preview": "import os\nimport typing\n\nfrom click.testing import CliRunner\n\nimport httpx\n\n\ndef splitlines(output: str) -> typing.Itera"
  },
  {
    "path": "tests/test_multipart.py",
    "chars": 17076,
    "preview": "from __future__ import annotations\n\nimport io\nimport tempfile\nimport typing\n\nimport pytest\n\nimport httpx\n\n\ndef echo_requ"
  },
  {
    "path": "tests/test_status_codes.py",
    "chars": 711,
    "preview": "import httpx\n\n\ndef test_status_code_as_int():\n    # mypy doesn't (yet) recognize that IntEnum members are ints, so ignor"
  },
  {
    "path": "tests/test_timeouts.py",
    "chars": 1750,
    "preview": "import pytest\n\nimport httpx\n\n\n@pytest.mark.anyio\nasync def test_read_timeout(server):\n    timeout = httpx.Timeout(None, "
  },
  {
    "path": "tests/test_utils.py",
    "chars": 4565,
    "preview": "import json\nimport logging\nimport os\nimport random\n\nimport pytest\n\nimport httpx\nfrom httpx._utils import URLPattern, get"
  },
  {
    "path": "tests/test_wsgi.py",
    "chars": 6009,
    "preview": "from __future__ import annotations\n\nimport sys\nimport typing\nimport wsgiref.validate\nfrom functools import partial\nfrom "
  }
]

About this extraction

This page contains the full source code of the encode/httpx GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 115 files (967.5 KB), approximately 256.7k tokens, and a symbol index with 1151 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!