11"""Helpers for downloading files from the network."""
22
3+ import functools
4+ import time
5+ from typing import Callable , ParamSpec , TypeVar
6+
37import httpx
48
5- from ..config import environment
9+ from reflex .utils .decorator import once
10+
611from . import console
712
813
@@ -12,30 +17,114 @@ def _httpx_verify_kwarg() -> bool:
1217 Returns:
1318 True if SSL verification is enabled, False otherwise
1419 """
20+ from ..config import environment
21+
1522 return not environment .SSL_NO_VERIFY .get ()
1623
1724
18- def get (url : str , ** kwargs ) -> httpx .Response :
19- """Make an HTTP GET request.
25+ _P = ParamSpec ("_P" )
26+ _T = TypeVar ("_T" )
27+
28+
29+ def _wrap_https_func (
30+ func : Callable [_P , _T ],
31+ ) -> Callable [_P , _T ]:
32+ """Wrap an HTTPS function with logging.
2033
2134 Args:
22- url: The URL to request.
23- **kwargs: Additional keyword arguments to pass to httpx.get.
35+ func: The function to wrap.
2436
2537 Returns:
26- The response object.
38+ The wrapped function.
39+ """
40+
41+ @functools .wraps (func )
42+ def wrapper (* args : _P .args , ** kwargs : _P .kwargs ) -> _T :
43+ url = args [0 ]
44+ console .debug (f"Sending HTTPS request to { args [0 ]} " )
45+ initial_time = time .time ()
46+ try :
47+ response = func (* args , ** kwargs )
48+ except httpx .ConnectError as err :
49+ if "CERTIFICATE_VERIFY_FAILED" in str (err ):
50+ # If the error is a certificate verification error, recommend mitigating steps.
51+ console .error (
52+ f"Certificate verification failed for { url } . Set environment variable SSL_CERT_FILE to the "
53+ "path of the certificate file or SSL_NO_VERIFY=1 to disable verification."
54+ )
55+ raise
56+ else :
57+ console .debug (
58+ f"Received response from { url } in { time .time () - initial_time :.3f} seconds"
59+ )
60+ return response
61+
62+ return wrapper
63+
2764
28- Raises:
29- httpx.ConnectError: If the connection cannot be established.
65+ def _is_ipv4_supported () -> bool :
66+ """Determine if the system supports IPv4.
67+
68+ Returns:
69+ True if the system supports IPv4, False otherwise.
3070 """
31- kwargs .setdefault ("verify" , _httpx_verify_kwarg ())
3271 try :
33- return httpx .get (url , ** kwargs )
34- except httpx .ConnectError as err :
35- if "CERTIFICATE_VERIFY_FAILED" in str (err ):
36- # If the error is a certificate verification error, recommend mitigating steps.
37- console .error (
38- f"Certificate verification failed for { url } . Set environment variable SSL_CERT_FILE to the "
39- "path of the certificate file or SSL_NO_VERIFY=1 to disable verification."
40- )
41- raise
72+ httpx .head ("http://1.1.1.1" , timeout = 3 )
73+ except httpx .RequestError :
74+ return False
75+ else :
76+ return True
77+
78+
79+ def _is_ipv6_supported () -> bool :
80+ """Determine if the system supports IPv6.
81+
82+ Returns:
83+ True if the system supports IPv6, False otherwise.
84+ """
85+ try :
86+ httpx .head ("http://[2606:4700:4700::1111]" , timeout = 3 )
87+ except httpx .RequestError :
88+ return False
89+ else :
90+ return True
91+
92+
93+ def _should_use_ipv6 () -> bool :
94+ """Determine if the system supports IPv6.
95+
96+ Returns:
97+ True if the system supports IPv6, False otherwise.
98+ """
99+ return not _is_ipv4_supported () and _is_ipv6_supported ()
100+
101+
102+ def _httpx_local_address_kwarg () -> str :
103+ """Get the value of the HTTPX local_address keyword argument.
104+
105+ Returns:
106+ The local address to bind to
107+ """
108+ from ..config import environment
109+
110+ return environment .REFLEX_HTTP_CLIENT_BIND_ADDRESS .get () or (
111+ "::" if _should_use_ipv6 () else "0.0.0.0"
112+ )
113+
114+
115+ @once
116+ def _httpx_client () -> httpx .Client :
117+ """Get an HTTPX client.
118+
119+ Returns:
120+ An HTTPX client.
121+ """
122+ return httpx .Client (
123+ transport = httpx .HTTPTransport (
124+ local_address = _httpx_local_address_kwarg (),
125+ verify = _httpx_verify_kwarg (),
126+ )
127+ )
128+
129+
130+ get = _wrap_https_func (_httpx_client ().get )
0 commit comments