-
Notifications
You must be signed in to change notification settings - Fork 2.4k
feat: Add proxy support to all cloud-based recognizers #867
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,173 @@ | ||||||||||||||||||
| """Centralized proxy utilities for SpeechRecognition. | ||||||||||||||||||
|
|
||||||||||||||||||
| Provides helper functions that build proxy-aware clients for each HTTP | ||||||||||||||||||
| library used in the project (urllib, httpx, requests, boto3, gRPC). | ||||||||||||||||||
|
|
||||||||||||||||||
| Proxy URL semantics: | ||||||||||||||||||
| None - use system/env proxy settings (default, backward compatible) | ||||||||||||||||||
| "" - explicitly disable proxies | ||||||||||||||||||
| "http://host:port" - use that HTTP proxy | ||||||||||||||||||
| "socks5://host:port" - SOCKS proxy (requires PySocks) | ||||||||||||||||||
| """ | ||||||||||||||||||
|
|
||||||||||||||||||
| from __future__ import annotations | ||||||||||||||||||
|
|
||||||||||||||||||
| import contextlib | ||||||||||||||||||
| import os | ||||||||||||||||||
| from urllib.request import ( | ||||||||||||||||||
| OpenerDirector, | ||||||||||||||||||
| ProxyHandler, | ||||||||||||||||||
| Request, | ||||||||||||||||||
| build_opener, | ||||||||||||||||||
| urlopen, | ||||||||||||||||||
| ) | ||||||||||||||||||
|
|
||||||||||||||||||
| from speech_recognition.exceptions import SetupError | ||||||||||||||||||
|
|
||||||||||||||||||
|
|
||||||||||||||||||
| def build_urllib_opener(proxy_url: str | None) -> OpenerDirector | None: | ||||||||||||||||||
| """Return an ``OpenerDirector`` configured with *proxy_url*. | ||||||||||||||||||
|
|
||||||||||||||||||
| Returns ``None`` when *proxy_url* is ``None`` (use default behaviour). | ||||||||||||||||||
| An empty string disables proxying; a ``socks5://`` URL requires | ||||||||||||||||||
| ``PySocks`` + ``sockshandler``. | ||||||||||||||||||
| """ | ||||||||||||||||||
| if proxy_url is None: | ||||||||||||||||||
| return None | ||||||||||||||||||
|
|
||||||||||||||||||
| if proxy_url == "": | ||||||||||||||||||
| return build_opener(ProxyHandler({})) | ||||||||||||||||||
|
|
||||||||||||||||||
| if proxy_url.startswith("socks"): | ||||||||||||||||||
| try: | ||||||||||||||||||
| from sockshandler import SocksiPyHandler | ||||||||||||||||||
| except ImportError: | ||||||||||||||||||
| raise SetupError( | ||||||||||||||||||
| "SOCKS proxy support requires the PySocks package. " | ||||||||||||||||||
| "Install it with: pip install PySocks" | ||||||||||||||||||
| ) | ||||||||||||||||||
|
|
||||||||||||||||||
| from urllib.parse import urlparse | ||||||||||||||||||
|
|
||||||||||||||||||
| parsed = urlparse(proxy_url) | ||||||||||||||||||
| import socks | ||||||||||||||||||
|
|
||||||||||||||||||
| socks_type_map = { | ||||||||||||||||||
| "socks4": socks.SOCKS4, | ||||||||||||||||||
| "socks5": socks.SOCKS5, | ||||||||||||||||||
| "socks5h": socks.SOCKS5, | ||||||||||||||||||
| } | ||||||||||||||||||
| socks_type = socks_type_map.get(parsed.scheme) | ||||||||||||||||||
| if socks_type is None: | ||||||||||||||||||
| raise SetupError( | ||||||||||||||||||
| f"Unsupported SOCKS scheme: {parsed.scheme!r}. " | ||||||||||||||||||
| "Use socks4, socks5, or socks5h." | ||||||||||||||||||
| ) | ||||||||||||||||||
|
|
||||||||||||||||||
| return build_opener( | ||||||||||||||||||
| SocksiPyHandler( | ||||||||||||||||||
| socks_type, | ||||||||||||||||||
| parsed.hostname, | ||||||||||||||||||
| parsed.port or 1080, | ||||||||||||||||||
| username=parsed.username, | ||||||||||||||||||
| password=parsed.password, | ||||||||||||||||||
| ) | ||||||||||||||||||
| ) | ||||||||||||||||||
|
|
||||||||||||||||||
| # HTTP/HTTPS proxy | ||||||||||||||||||
| return build_opener( | ||||||||||||||||||
| ProxyHandler({"http": proxy_url, "https": proxy_url}) | ||||||||||||||||||
| ) | ||||||||||||||||||
|
|
||||||||||||||||||
|
|
||||||||||||||||||
| def urlopen_with_proxy(request: Request, timeout: int | None, proxy_url: str | None): | ||||||||||||||||||
| """Drop-in replacement for ``urlopen()`` that respects *proxy_url*.""" | ||||||||||||||||||
| opener = build_urllib_opener(proxy_url) | ||||||||||||||||||
| kwargs = {} | ||||||||||||||||||
| if timeout is not None: | ||||||||||||||||||
| kwargs["timeout"] = timeout | ||||||||||||||||||
|
|
||||||||||||||||||
| if opener is not None: | ||||||||||||||||||
| return opener.open(request, **kwargs) | ||||||||||||||||||
| return urlopen(request, **kwargs) | ||||||||||||||||||
|
|
||||||||||||||||||
|
|
||||||||||||||||||
| def build_httpx_client(proxy_url: str | None): | ||||||||||||||||||
| """Return an ``httpx.Client`` configured with *proxy_url*. | ||||||||||||||||||
|
|
||||||||||||||||||
| Returns ``None`` when *proxy_url* is ``None`` (caller should use | ||||||||||||||||||
| the SDK default client). | ||||||||||||||||||
| """ | ||||||||||||||||||
| if proxy_url is None: | ||||||||||||||||||
| return None | ||||||||||||||||||
|
|
||||||||||||||||||
| import httpx | ||||||||||||||||||
|
|
||||||||||||||||||
| if proxy_url == "": | ||||||||||||||||||
| return httpx.Client(proxy=None) | ||||||||||||||||||
|
|
||||||||||||||||||
| return httpx.Client(proxy=proxy_url) | ||||||||||||||||||
|
Comment on lines
+107
to
+109
|
||||||||||||||||||
| return httpx.Client(proxy=None) | |
| return httpx.Client(proxy=proxy_url) | |
| # Explicitly disable proxies: ignore environment proxies as well. | |
| return httpx.Client(proxy=None, trust_env=False) | |
| # Use the explicit proxy URL and ignore environment proxy settings. | |
| return httpx.Client(proxy=proxy_url, trust_env=False) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
urlopen_with_proxytypestimeoutasint | None, butRecognizer.operation_timeoutis documented asUnion[float, None]and is passed through in several call sites. Update the annotation here (and any related ones) to acceptfloat | Noneto match the public API and actual usage.