Compare commits

...

21 Commits

Author SHA1 Message Date
Tom Christie
ad5234f3ac
Update docs/advanced/ssl.md 2024-11-28 11:40:58 +00:00
Tom Christie
6c483c9e07
Merge branch 'master' into tomchristie-patch-1 2024-11-22 11:43:18 +00:00
Tom Christie
4e1c691636
Update CHANGELOG.md 2024-11-21 13:54:13 +00:00
Tom Christie
2d2eab7fa8
Update CHANGELOG.md 2024-11-21 13:50:08 +00:00
Tom Christie
92d92956a5
Update CHANGELOG.md 2024-11-21 13:49:10 +00:00
Tom Christie
5560472f7d
Update docs/advanced/ssl.md 2024-11-21 13:42:26 +00:00
Tom Christie
670a156b56
Update README.md 2024-11-21 13:40:48 +00:00
Tom Christie
218db9b6db
Update ssl.md 2024-11-21 13:36:31 +00:00
Tom Christie
e49cc07bc9
Update ssl.md 2024-11-21 13:35:10 +00:00
Tom Christie
4321f008ed
Update ssl.md 2024-11-21 13:33:30 +00:00
Tom Christie
8bff380cec
Update ssl.md 2024-11-21 13:31:24 +00:00
Tom Christie
2f220ec3f5
Update ssl.md 2024-11-21 13:18:14 +00:00
Tom Christie
3933a88c2b
Update test_config.py
Drop unneeded code.
2024-11-21 11:47:42 +00:00
Tom Christie
cc72d348f8
Update test_config.py 2024-11-21 11:44:32 +00:00
Tom Christie
15ffaf9e1a
Update test_config.py 2024-11-21 11:38:53 +00:00
Tom Christie
c1a88bc873
Update README.md 2024-11-21 11:35:17 +00:00
Tom Christie
83fbd71063
Update _config.py 2024-11-21 11:31:57 +00:00
Tom Christie
b413005684
Python versions 2024-11-21 11:30:33 +00:00
Tom Christie
d861e9c179
Update pyproject.toml 2024-11-21 11:25:02 +00:00
Tom Christie
9bcb36a634
Requirements 2024-11-21 11:18:56 +00:00
Tom Christie
3769c569ed
Truststore 2024-11-21 11:15:26 +00:00
7 changed files with 26 additions and 67 deletions

View File

@ -14,7 +14,7 @@ jobs:
strategy:
matrix:
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
python-version: ["3.10", "3.11", "3.12", "3.13"]
steps:
- uses: "actions/checkout@v4"

View File

@ -6,15 +6,17 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## 0.28.0 (...)
TODO... writeup `truststore` switch & 3.10+ requirement.
The 0.28 release includes a limited set of backwards incompatible changes.
**Backwards incompatible changes**:
SSL configuration has been significantly simplified.
* The `verify` argument no longer accepts string arguments.
* The `cert` argument has now been removed.
* The `SSL_CERT_FILE` and `SSL_CERT_DIR` environment variables are no longer automatically used.
* The `verify` argument no longer accepts string arguments. Explicitly specified certificate stores can still be enabled through the SSL configuration API.
* The `cert` argument has now been removed. Client side certificates can still be enabled through the SSL configuration API.
* The `SSL_CERT_FILE` and `SSL_CERT_DIR` environment variables are no longer used. These environment variables can be enabled manually although should be obsoleted by our switch to `truststore`.
For users of the standard `verify=True` or `verify=False` cases this should require no changes.

View File

@ -101,7 +101,7 @@ Or, to include the optional HTTP/2 support, use:
$ pip install httpx[http2]
```
HTTPX requires Python 3.8+.
HTTPX requires Python 3.10+.
## Documentation
@ -125,7 +125,7 @@ The HTTPX project relies on these excellent libraries:
* `httpcore` - The underlying transport implementation for `httpx`.
* `h11` - HTTP/1.1 support.
* `certifi` - SSL certificates.
* `truststore` - System SSL certificates.
* `idna` - Internationalized domain name support.
* `sniffio` - Async library autodetection.

View File

@ -1,26 +1,28 @@
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).
When making a request over HTTPS we need to verify the identity of the requested host. We rely on the [`truststore`](https://truststore.readthedocs.io/en/latest/) package to load the system certificates, ensuring that `httpx` has the same behaviour on SSL sites as your browser.
### Enabling and disabling verification
### SSL verification
By default httpx will verify HTTPS connections, and raise an error for invalid SSL cases...
```pycon
```python
>>> 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...
If you're confident that you want to visit a site with an invalid certificate you can disable SSL verification completely...
```pycon
```python
>>> httpx.get("https://expired.badssl.com/", verify=False)
<Response [200 OK]>
```
### Configuring client instances
### Custom SSL configurations
If you're using a `Client()` instance you should pass any `verify=<...>` configuration when instantiating the client.
If you're using a `Client()` instance you can pass the `verify=<...>` configuration when instantiating the client.
By default the [certifi CA bundle](https://certifiio.readthedocs.io/en/latest/) is used for SSL verification.
```python
>>> client = httpx.Client(verify=True)
```
For more complex configurations you can pass an [SSL Context](https://docs.python.org/3/library/ssl.html) instance...
@ -28,35 +30,13 @@ For more complex configurations you can pass an [SSL Context](https://docs.pytho
import certifi
import httpx
import ssl
import certifi
# This SSL context is equivelent to the default `verify=True`.
# Use certifi for certificate validation, rather than the system truststore.
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.
@ -71,9 +51,9 @@ client = httpx.Client(verify=ctx)
### Working with `SSL_CERT_FILE` and `SSL_CERT_DIR`
Unlike `requests`, the `httpx` package does not automatically pull in [the environment variables `SSL_CERT_FILE` or `SSL_CERT_DIR`](https://www.openssl.org/docs/manmaster/man3/SSL_CTX_set_default_verify_paths.html). If you want to use these they need to be enabled explicitly.
Unlike `requests`, the `httpx` package does not automatically pull in [the environment variables `SSL_CERT_FILE` or `SSL_CERT_DIR`](https://www.openssl.org/docs/manmaster/man3/SSL_CTX_set_default_verify_paths.html).
For example...
These environment variables shouldn't be necessary since they're obsoleted by `truststore`. They can be enabled if required like so...
```python
# Use `SSL_CERT_FILE` or `SSL_CERT_DIR` if configured.
@ -87,7 +67,7 @@ client = httpx.Client(verify=ctx)
### 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.
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...

View File

@ -22,10 +22,10 @@ UNSET = UnsetType()
def create_ssl_context(verify: ssl.SSLContext | bool = True) -> ssl.SSLContext:
import ssl
import certifi
import truststore
if verify is True:
return ssl.create_default_context(cafile=certifi.where())
return truststore.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
elif verify is False:
ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
ssl_context.check_hostname = False

View File

@ -28,7 +28,7 @@ classifiers = [
"Topic :: Internet :: WWW/HTTP",
]
dependencies = [
"certifi",
"truststore==0.10.0",
"httpcore==1.*",
"anyio",
"idna",

View File

@ -1,8 +1,5 @@
import ssl
import typing
from pathlib import Path
import certifi
import pytest
import httpx
@ -20,26 +17,6 @@ def test_load_ssl_config_verify_non_existing_file():
context.load_verify_locations(cafile="/path/to/nowhere")
def test_load_ssl_with_keylog(monkeypatch: typing.Any) -> None:
monkeypatch.setenv("SSLKEYLOGFILE", "test")
context = httpx.create_ssl_context()
assert context.keylog_filename == "test"
def test_load_ssl_config_verify_existing_file():
context = httpx.create_ssl_context()
context.load_verify_locations(capath=certifi.where())
assert context.verify_mode == ssl.VerifyMode.CERT_REQUIRED
assert context.check_hostname is True
def test_load_ssl_config_verify_directory():
context = httpx.create_ssl_context()
context.load_verify_locations(capath=Path(certifi.where()).parent)
assert context.verify_mode == ssl.VerifyMode.CERT_REQUIRED
assert context.check_hostname is True
def test_load_ssl_config_cert_and_key(cert_pem_file, cert_private_key_file):
context = httpx.create_ssl_context()
context.load_cert_chain(cert_pem_file, cert_private_key_file)