Skip to content

syshttp.tls and syshttp.retry

syshttp.tls

The system's TLS client as an SSLContext -- what the client (syshttp.client) and a curl front end both connect with.

With neither cafile nor capath the context is sslcontext.system_context(): certificates are verified by the operating system's own verifier on Python 3.10+ (CryptoAPI on Windows, the Security framework on macOS, OpenSSL over the system's store elsewhere), so intermediate fetching, enterprise roots and policy apply as they do for the system's own clients; on 3.9 by OpenSSL over the default store. A server that asks for a client certificate gets one from the OS store (Windows Personal store and smart cards, the macOS keychains, the default PKCS#11 module), chosen as a browser chooses, or none. A cafile / capath replaces the store: a plain ssl context trusting only them. No certifi bundle is involved.

new_os_context(cafile=None, capath=None, *, client_certificates=True)

A fresh verifying client context: on cafile / capath when given (they replace the OS store), else the system's (sslcontext.system_context). client_certificates=False never offers a certificate from the OS store: pass it before loading a client certificate of your own, which a store certificate would otherwise replace.

Source code in src/syshttp/tls.py
36
37
38
39
40
41
42
43
44
45
46
def new_os_context(
    cafile: Optional[str] = None, capath: Optional[str] = None, *, client_certificates: bool = True
) -> "ssl.SSLContext":
    """A fresh verifying client context: on ``cafile`` / ``capath`` when given
    (they replace the OS store), else the system's (``sslcontext.system_context``).
    ``client_certificates=False`` never offers a certificate from the OS store:
    pass it before loading a client certificate of your own, which a store
    certificate would otherwise replace."""
    if cafile or capath:
        return ssl.create_default_context(cafile=cafile, capath=capath)
    return sslcontext.system_context(selector=None if client_certificates else False)

os_ssl_context()

The system's SSLContext (:func:new_os_context), shared for :data:OS_CONTEXT_MAX_AGE seconds. Shared, because urllib3 keys its connection pools by the context object: one per request would never reuse a connection. Never load a client certificate into it: use :func:new_os_context with client_certificates=False.

Source code in src/syshttp/tls.py
49
50
51
52
53
54
55
56
57
58
59
60
def os_ssl_context() -> "ssl.SSLContext":
    """The system's ``SSLContext`` (:func:`new_os_context`), shared for
    :data:`OS_CONTEXT_MAX_AGE` seconds. Shared, because urllib3 keys its
    connection pools by the context object: one per request would never reuse
    a connection. Never load a client certificate into it: use
    :func:`new_os_context` with ``client_certificates=False``."""
    global _os_context, _os_context_at
    now = time.monotonic()
    if _os_context is None or now - _os_context_at > OS_CONTEXT_MAX_AGE:
        _os_context = new_os_context()
        _os_context_at = now
    return _os_context

syshttp.retry

What counts as transient -- the one list the client's retries and a curl front end's --retry both use. Dependency-free.

retry_after(headers, now=None)

A response's Retry-After in seconds -- a number, or an HTTP date from now -- or None when absent or unreadable. headers are (name, value) pairs.

Source code in src/syshttp/retry.py
21
22
23
24
25
26
27
28
29
30
31
32
33
34
def retry_after(headers: Iterable[Tuple[str, str]], now: Optional[float] = None) -> Optional[float]:
    """A response's ``Retry-After`` in seconds -- a number, or an HTTP date
    from now -- or ``None`` when absent or unreadable. ``headers`` are
    ``(name, value)`` pairs."""
    value = next((v for k, v in headers if k.lower() == "retry-after"), "").strip()
    if not value:
        return None
    seconds = ascii_int(value)
    if seconds is not None:
        return float(seconds)
    when = http_date(value)
    if when is None:
        return None
    return max(0.0, when - (time.time() if now is None else now))