Repository: encode/httpx

Issue:
Hints on self-signed certificates
In https://github.com/encode/httpx/issues/503, @gvbgduh mentioned the need to make HTTP/2 requests to a local server. Because of our current implementation (require TLS for HTTP/2, as browsers do), this typically requires to setup a local self-signed certificate for use in tests.

I'm probably not the one most aware of how this can be done — @sethmlarson may have some pointers? But we could at least gather some resources on how to set things up, and then see if/how we can include those to our documentation. This might take the form of a note added [here](https://www.encode.io/httpx/advanced/#specify-the-version-of-the-http-protocol) with a link to a relevant how-to guide.


Discussion:
Related: https://github.com/encode/uvicorn/issues/47#issuecomment-550266244
If it's for local development (especially within the context of testing) I highly recommend using trustme to generate the certificates and then configure them as you would any other certificate. That gets you the most realistic scenario of trusting a CA certificate that previously signed the certificate the server is presenting. It wouldn't be hard to whip up a little tool that just hands you the certificates and tells you what to do with each one.
Thanks @sethmlarson, it actually works great.
(Btw I'm putting together a little utility that generates certificates in this configuration for you from command line)
I just noticed we currently don't have any usage hints on how to use the `cert` parameter (just a definition of what it is in the Developer Interface). Would this be a good opportunity to set it up?

Perhaps a "Custom SSL certificates" section in the Advanced docs, that shows how to use a `trustme` certificate in HTTPX…
I just tried out `trustme` locally as well:

```python
# app.py
from starlette.responses import PlainTextResponse

app = PlainTextResponse("Hello, world!")
```

```python
# create_certs.py
import trustme


ca = trustme.CA()
cert = ca.issue_cert("localhost")

# For use by clients (eg 'verify' in HTTPX)
ca.cert_pem.write_to_path("ca.pem")

# For use by servers (eg '--ssl-certfile' in Uvicorn)
cert.private_key_and_cert_chain_pem.write_to_path("server.pem")
```

```python
# example.py
import httpx

# Use CA bundle generated by trustme
r = httpx.get("https://localhost:8000", verify="ca.pem")
```

Usage:

1. `$ python create_certs.py`
1. `$ uvicorn --ssl-certfile=server.pem app:app`
1. `$ python example.py`

Have I got that right @sethmlarson @gvbgduh?
Looks right to me, I created a little script that does basically what you have in `create_certs.py`: https://github.com/sethmlarson/trustme-cli

We can mention that in the docs if people aren't using `trustme` programmatically like with `pytest` to generate certs within a test suite but instead poking around or running things manually.

Totally agree we should have a section on this as it's not trivial.
Awesome @sethmlarson!

Somewhat related — we have the `cert` and `verify` parameters. As I understand, `verify` can be used to tell HTTPX to use a custom CA bundle (i.e. not certifi's), but then in what kind of situations would we use `cert`?
`cert` is client cert. It's confusing, but that's Requests' API we're inheriting :man_shrugging:.

Basically `verify` is server verification and `cert` is for the server to do client verification.
Ah. Well that *is* confusing. 😆 So `cert` would be used if we have our own certificate, and we want the server to trust that certificate when we connect to it?
`cert` is for client authentication by the server, the certificate says "I'm @sethmlarson" and is signed by a certificate that the server trusts. So if you're interacting with a service that does authentication via TLS certificates then you'd need to provide a `cert` parameter.
We *could* choose to digress from `requests` there, and use `client_cert`.

At the very least we probably want some more documentation there, yup. I'd not picked up that it was for client certification personally, and it definately falls into the category of "you don't need this, unless you know what you're doing".

Presumably it's outside the realm of what browsers will generally do, right?

(At least not without some fiddly reasonably obscure configuration - https://support.google.com/chrome/a/answer/6080885?hl=en)

Resolving patch:
diff --git a/httpx/api.py b/httpx/api.py
--- a/httpx/api.py
+++ b/httpx/api.py
@@ -27,8 +27,8 @@ def request(
     auth: AuthTypes = None,
     timeout: TimeoutTypes = None,
     allow_redirects: bool = True,
-    cert: CertTypes = None,
     verify: VerifyTypes = True,
+    cert: CertTypes = None,
     stream: bool = False,
     trust_env: bool = None,
 ) -> Response:
@@ -57,10 +57,13 @@ def request(
     * **timeout** - *(optional)* The timeout configuration to use when sending
     the request.
     * **allow_redirects** - *(optional)* Enables or disables HTTP redirects.
-    * **cert** - *(optional)* Either a path to an SSL certificate file, or
+    * **verify** - *(optional)* SSL certificates (a.k.a CA bundle) used to
+    verify the identity of requested hosts. Either `True` (default CA bundle),
+    a path to an SSL certificate file, or `False` (disable verification).
+    * **cert** - *(optional)* An SSL certificate used by the requested host
+    to authenticate the client. Either a path to an SSL certificate file, or
     two-tuple of (certificate file, key file), or a three-tuple of (certificate
     file, key file, password).
-    * **verify** - *(optional)* Enables or disables SSL verification.
     * **trust_env** - *(optional)* Enables or disables usage of environment
     variables for configuration.
     * **proxies** - *(optional)* A dictionary mapping HTTP protocols to proxy
diff --git a/httpx/client.py b/httpx/client.py
--- a/httpx/client.py
+++ b/httpx/client.py
@@ -686,8 +686,11 @@ class Client(BaseClient):
     sending requests.
     * **cookies** - *(optional)* Dictionary of Cookie items to include when
     sending requests.
-    * **verify** - *(optional)* Enables or disables SSL verification.
-    * **cert** - *(optional)* Either a path to an SSL certificate file, or
+    * **verify** - *(optional)* SSL certificates (a.k.a CA bundle) used to
+    verify the identity of requested hosts. Either `True` (default CA bundle),
+    a path to an SSL certificate file, or `False` (disable verification).
+    * **cert** - *(optional)* An SSL certificate used by the requested host
+    to authenticate the client. Either a path to an SSL certificate file, or
     two-tuple of (certificate file, key file), or a three-tuple of (certificate
     file, key file, password).
     * **http_versions** - *(optional)* A list of strings of HTTP protocol
@@ -768,8 +771,8 @@ def request(
         stream: bool = False,
         auth: AuthTypes = None,
         allow_redirects: bool = True,
-        cert: CertTypes = None,
         verify: VerifyTypes = None,
+        cert: CertTypes = None,
         timeout: TimeoutTypes = None,
         trust_env: bool = None,
     ) -> Response:
@@ -797,10 +800,13 @@ def request(
         * **auth** - *(optional)* An authentication class to use when sending the
         request.
         * **allow_redirects** - *(optional)* Enables or disables HTTP redirects.
-        * **cert** - *(optional)* Either a path to an SSL certificate file, or
+        * **verify** - *(optional)* SSL certificates (a.k.a CA bundle) used to
+        verify the identity of requested hosts. Either `True` (default CA bundle),
+        a path to an SSL certificate file, or `False` (disable verification).
+        * **cert** - *(optional)* An SSL certificate used by the requested host
+        to authenticate the client. Either a path to an SSL certificate file, or
         two-tuple of (certificate file, key file), or a three-tuple of (certificate
         file, key file, password).
-        * **verify** - *(optional)* Enables or disables SSL verification.
         * **timeout** - *(optional)* The timeout configuration to use when sending
         the request.
         * **trust_env** - *(optional)* Enables or disables usage of environment


Test patch: