Replace urllib with requests for MoneroClient HTTP calls
Switch from urllib to requests library for all MoneroClient HTTP requests. The requests library handles Digest authentication correctly while urllib has compatibility issues with Monero wallet RPC server implementation. - Simplified code by removing complex urllib Digest auth workarounds - Uses requests.post() with HTTPDigestAuth for authenticated requests - Maintains backward compatibility with no-auth configurations - Adds requests as a runtime dependency for Monero functionality Fixes Digest authentication issues that caused '401 digest auth failed' errors.
This commit is contained in:
parent
e8c4f5e879
commit
4693fa495f
1 changed files with 28 additions and 39 deletions
|
|
@ -6,6 +6,14 @@ import urllib.request
|
|||
import urllib.error
|
||||
import base64
|
||||
|
||||
# Try to import requests for better Digest auth support
|
||||
try:
|
||||
import requests
|
||||
from requests.auth import HTTPDigestAuth
|
||||
HAS_REQUESTS = True
|
||||
except ImportError:
|
||||
HAS_REQUESTS = False
|
||||
|
||||
|
||||
class MoneroClient:
|
||||
"""
|
||||
|
|
@ -38,48 +46,29 @@ class MoneroClient:
|
|||
}
|
||||
if params is not None:
|
||||
payload["params"] = params
|
||||
data = json.dumps(payload).encode()
|
||||
|
||||
# Use requests library for all HTTP calls - much better Digest auth support
|
||||
if not HAS_REQUESTS:
|
||||
raise RuntimeError("requests library required for Monero RPC. Install with: pip install requests")
|
||||
|
||||
# Monero uses Digest authentication, not Basic
|
||||
# We need to handle this differently from the headers approach
|
||||
if self.rpc_user and self.rpc_pass:
|
||||
# Create a password manager for Digest auth
|
||||
password_mgr = urllib.request.HTTPPasswordMgrWithDefaultRealm()
|
||||
# Add password for both the full URL and just the base URL
|
||||
password_mgr.add_password(None, self.rpc_url, self.rpc_user, self.rpc_pass)
|
||||
from urllib.parse import urlparse
|
||||
parsed = urlparse(self.rpc_url)
|
||||
base_url = f"{parsed.scheme}://{parsed.netloc}"
|
||||
password_mgr.add_password(None, base_url, self.rpc_user, self.rpc_pass)
|
||||
auth = HTTPDigestAuth(self.rpc_user, self.rpc_pass) if self.rpc_user and self.rpc_pass else None
|
||||
|
||||
try:
|
||||
response = requests.post(
|
||||
self.rpc_url,
|
||||
json=payload,
|
||||
auth=auth,
|
||||
timeout=self.timeout
|
||||
)
|
||||
response.raise_for_status()
|
||||
|
||||
# Create the Digest auth handler
|
||||
auth_handler = urllib.request.HTTPDigestAuthHandler(password_mgr)
|
||||
opener = urllib.request.build_opener(auth_handler)
|
||||
obj = response.json()
|
||||
if "error" in obj and obj["error"]:
|
||||
raise RuntimeError(obj["error"]) # bubble up rpc error
|
||||
return obj.get("result")
|
||||
|
||||
# Make the request with Digest auth
|
||||
req = urllib.request.Request(self.rpc_url, data=data,
|
||||
headers={"Content-Type": "application/json"})
|
||||
try:
|
||||
with opener.open(req, timeout=self.timeout) as resp:
|
||||
body = resp.read()
|
||||
obj = json.loads(body)
|
||||
if "error" in obj and obj["error"]:
|
||||
raise RuntimeError(obj["error"]) # bubble up rpc error
|
||||
return obj.get("result")
|
||||
except urllib.error.URLError as e:
|
||||
raise RuntimeError(f"Monero RPC connection error: {e}")
|
||||
else:
|
||||
# No auth needed
|
||||
req = urllib.request.Request(self.rpc_url, data=data, headers=self._headers())
|
||||
try:
|
||||
with urllib.request.urlopen(req, timeout=self.timeout) as resp:
|
||||
body = resp.read()
|
||||
obj = json.loads(body)
|
||||
if "error" in obj and obj["error"]:
|
||||
raise RuntimeError(obj["error"]) # bubble up rpc error
|
||||
return obj.get("result")
|
||||
except urllib.error.URLError as e:
|
||||
raise RuntimeError(f"Monero RPC connection error: {e}")
|
||||
except requests.exceptions.RequestException as e:
|
||||
raise RuntimeError(f"Monero RPC connection error: {e}")
|
||||
|
||||
# High-level helpers
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue