|
@@ -1,8 +1,13 @@
|
|
|
"""Helpers for downloading files from the network."""
|
|
|
|
|
|
+import functools
|
|
|
+import time
|
|
|
+from typing import Callable, ParamSpec, TypeVar
|
|
|
+
|
|
|
import httpx
|
|
|
|
|
|
-from ..config import environment
|
|
|
+from reflex.utils.decorator import once
|
|
|
+
|
|
|
from . import console
|
|
|
|
|
|
|
|
@@ -12,30 +17,114 @@ def _httpx_verify_kwarg() -> bool:
|
|
|
Returns:
|
|
|
True if SSL verification is enabled, False otherwise
|
|
|
"""
|
|
|
+ from ..config import environment
|
|
|
+
|
|
|
return not environment.SSL_NO_VERIFY.get()
|
|
|
|
|
|
|
|
|
-def get(url: str, **kwargs) -> httpx.Response:
|
|
|
- """Make an HTTP GET request.
|
|
|
+_P = ParamSpec("_P")
|
|
|
+_T = TypeVar("_T")
|
|
|
+
|
|
|
+
|
|
|
+def _wrap_https_func(
|
|
|
+ func: Callable[_P, _T],
|
|
|
+) -> Callable[_P, _T]:
|
|
|
+ """Wrap an HTTPS function with logging.
|
|
|
|
|
|
Args:
|
|
|
- url: The URL to request.
|
|
|
- **kwargs: Additional keyword arguments to pass to httpx.get.
|
|
|
+ func: The function to wrap.
|
|
|
|
|
|
Returns:
|
|
|
- The response object.
|
|
|
+ The wrapped function.
|
|
|
+ """
|
|
|
+
|
|
|
+ @functools.wraps(func)
|
|
|
+ def wrapper(*args: _P.args, **kwargs: _P.kwargs) -> _T:
|
|
|
+ url = args[0]
|
|
|
+ console.debug(f"Sending HTTPS request to {args[0]}")
|
|
|
+ initial_time = time.time()
|
|
|
+ try:
|
|
|
+ response = func(*args, **kwargs)
|
|
|
+ except httpx.ConnectError as err:
|
|
|
+ if "CERTIFICATE_VERIFY_FAILED" in str(err):
|
|
|
+ # If the error is a certificate verification error, recommend mitigating steps.
|
|
|
+ console.error(
|
|
|
+ f"Certificate verification failed for {url}. Set environment variable SSL_CERT_FILE to the "
|
|
|
+ "path of the certificate file or SSL_NO_VERIFY=1 to disable verification."
|
|
|
+ )
|
|
|
+ raise
|
|
|
+ else:
|
|
|
+ console.debug(
|
|
|
+ f"Received response from {url} in {time.time() - initial_time:.3f} seconds"
|
|
|
+ )
|
|
|
+ return response
|
|
|
+
|
|
|
+ return wrapper
|
|
|
+
|
|
|
|
|
|
- Raises:
|
|
|
- httpx.ConnectError: If the connection cannot be established.
|
|
|
+def _is_ipv4_supported() -> bool:
|
|
|
+ """Determine if the system supports IPv4.
|
|
|
+
|
|
|
+ Returns:
|
|
|
+ True if the system supports IPv4, False otherwise.
|
|
|
"""
|
|
|
- kwargs.setdefault("verify", _httpx_verify_kwarg())
|
|
|
try:
|
|
|
- return httpx.get(url, **kwargs)
|
|
|
- except httpx.ConnectError as err:
|
|
|
- if "CERTIFICATE_VERIFY_FAILED" in str(err):
|
|
|
- # If the error is a certificate verification error, recommend mitigating steps.
|
|
|
- console.error(
|
|
|
- f"Certificate verification failed for {url}. Set environment variable SSL_CERT_FILE to the "
|
|
|
- "path of the certificate file or SSL_NO_VERIFY=1 to disable verification."
|
|
|
- )
|
|
|
- raise
|
|
|
+ httpx.head("http://1.1.1.1", timeout=3)
|
|
|
+ except httpx.RequestError:
|
|
|
+ return False
|
|
|
+ else:
|
|
|
+ return True
|
|
|
+
|
|
|
+
|
|
|
+def _is_ipv6_supported() -> bool:
|
|
|
+ """Determine if the system supports IPv6.
|
|
|
+
|
|
|
+ Returns:
|
|
|
+ True if the system supports IPv6, False otherwise.
|
|
|
+ """
|
|
|
+ try:
|
|
|
+ httpx.head("http://[2606:4700:4700::1111]", timeout=3)
|
|
|
+ except httpx.RequestError:
|
|
|
+ return False
|
|
|
+ else:
|
|
|
+ return True
|
|
|
+
|
|
|
+
|
|
|
+def _should_use_ipv6() -> bool:
|
|
|
+ """Determine if the system supports IPv6.
|
|
|
+
|
|
|
+ Returns:
|
|
|
+ True if the system supports IPv6, False otherwise.
|
|
|
+ """
|
|
|
+ return not _is_ipv4_supported() and _is_ipv6_supported()
|
|
|
+
|
|
|
+
|
|
|
+def _httpx_local_address_kwarg() -> str:
|
|
|
+ """Get the value of the HTTPX local_address keyword argument.
|
|
|
+
|
|
|
+ Returns:
|
|
|
+ The local address to bind to
|
|
|
+ """
|
|
|
+ from ..config import environment
|
|
|
+
|
|
|
+ return environment.REFLEX_HTTP_CLIENT_BIND_ADDRESS.get() or (
|
|
|
+ "::" if _should_use_ipv6() else "0.0.0.0"
|
|
|
+ )
|
|
|
+
|
|
|
+
|
|
|
+@once
|
|
|
+def _httpx_client() -> httpx.Client:
|
|
|
+ """Get an HTTPX client.
|
|
|
+
|
|
|
+ Returns:
|
|
|
+ An HTTPX client.
|
|
|
+ """
|
|
|
+ return httpx.Client(
|
|
|
+ transport=httpx.HTTPTransport(
|
|
|
+ local_address=_httpx_local_address_kwarg(),
|
|
|
+ verify=_httpx_verify_kwarg(),
|
|
|
+ )
|
|
|
+ )
|
|
|
+
|
|
|
+
|
|
|
+get = _wrap_https_func(_httpx_client().get)
|