Repository: encode/httpx

Issue #1374: Lazy instantiation of ssl_context in a Client or Transport
Created: 2020-10-29T14:47:11Z
Checklist


 There are no similar issues or pull requests for this yet.
 I discussed this idea on the community chat

This topic probably touches on -

Mount API syntax - #977
Maybe the Transport API - #1274

Is your feature related to a problem? Please describe.
In some situations, we need to prompt a user for a password just prior to creating an ssl.SSLContext.  For instance, if a user has password-protected PKCS12 certificates, we use pypki2 or requests_pkcs12 to decrypt the certificate and build an ssl.SSLContext.  While I do not have an example on hand for HSM (Hardware Security Module, PKCS11 format, "Smart Cards" for example), these will probably fit into the same use case.
Normally, the flow in something like a Jupyter Notebook would not be a problem - context = pypki2config.ssl_context() -> prompts for password -> client = httpx.Client(verify=context).  The wrinkle is that we have a development environment where some domains require a PKI (PKCS12) authenticated request, while others don't.  I'll call them a list of audited domains and clear domains.
The behavior I want is for a user to create a client = InternalClient() object, a subclass of httpx.Client that knows about which domains need a PKI-sourced ssl_context and which need a bare httpx.create_ssl_context().  I would like to lazily instantiate those contexts so that there is no password prompt at client init, and may never be a password prompt if the user does not make a request to an audited domain.
Our solution right now is to use a subclassed requests.Session object, session = InternalSession().  We override .send (although .get_adapter could have worked too) to create/mount the right adapter at the time of the request.
Describe the solution you would like.
There are probably many ways to make our use-case work.  Here are some options that come to mind -

Expose a client.mount method (@tomchristie was against this in 977), which we would invoke while overriding .send or something
Have the mounts dictionary values be a Transport or a callback that returns a Transport at the time the Transport is needed
Create a .get_transport(url) method that can be overridden (ala .get_adapter in requests).  The default behavior would be to perform URL matching from the .get_transports() dictionary
Change the httpcore.Transport ssl_context init variable to accept a callback, and that callback wouldn't be executed until needed (such as when ._open_socket is invoked)

Thanks so much!

Duplicate issue #1376: Dynamically change proxy in AsyncClient
Created: 2020-10-30T17:24:17Z
Checklist


 There are no similar issues or pull requests for this yet.
 I discussed this idea on the community chat and feedback is positive.

Is your feature related to a problem? Please describe.

All my requests should be send through different proxies which I have parsed from the internet. But AsyncClient handler doesn't support changing proxies dynamically. If I don't have valid proxy, should I need to recreate a httclient?
Describe the solution you would like.

Add a api that can make AsyncClient's proxies change dynamically
Describe alternatives you considered

Additional context

Closing comment (2020-11-13T14:33:17Z): I'm going to close this issue off as I don't think there's enough clear context of motivation described here. Tho it's pretty similar to #1374
If there's an unbounded number of differing proxies then you might want to use one-shot requests, eg. use httpx.get(..., proxies=...) directly. Alternately you could keep a mapping of different client instances. Or create a custom transport to route between them.
There are good design reasons why we choose not to support adding new transport instances on the fly, eg...
with httpx.client(...) as client:
    client.proxies = ...  # It's unclear what `__enter__`/`__exit__` behaviour to expect on the proxy transport instances.