Changeset View
Changeset View
Standalone View
Standalone View
test/functional/test_framework/authproxy.py
Show All 33 Lines | |||||
- uses standard Python json lib | - uses standard Python json lib | ||||
""" | """ | ||||
import base64 | import base64 | ||||
import decimal | import decimal | ||||
import http.client | import http.client | ||||
import json | import json | ||||
import logging | import logging | ||||
import os | |||||
import socket | import socket | ||||
import time | import time | ||||
import urllib.parse | import urllib.parse | ||||
HTTP_TIMEOUT = 30 | HTTP_TIMEOUT = 30 | ||||
USER_AGENT = "AuthServiceProxy/0.1" | USER_AGENT = "AuthServiceProxy/0.1" | ||||
log = logging.getLogger("BitcoinRPC") | log = logging.getLogger("BitcoinRPC") | ||||
Show All 20 Lines | class AuthServiceProxy(): | ||||
# ensure_ascii: escape unicode as \uXXXX, passed to json.dumps | # ensure_ascii: escape unicode as \uXXXX, passed to json.dumps | ||||
def __init__(self, service_url, service_name=None, | def __init__(self, service_url, service_name=None, | ||||
timeout=HTTP_TIMEOUT, connection=None, ensure_ascii=True): | timeout=HTTP_TIMEOUT, connection=None, ensure_ascii=True): | ||||
self.__service_url = service_url | self.__service_url = service_url | ||||
self._service_name = service_name | self._service_name = service_name | ||||
self.ensure_ascii = ensure_ascii # can be toggled on the fly by tests | self.ensure_ascii = ensure_ascii # can be toggled on the fly by tests | ||||
self.__url = urllib.parse.urlparse(service_url) | self.__url = urllib.parse.urlparse(service_url) | ||||
port = 80 if self.__url.port is None else self.__url.port | |||||
user = None if self.__url.username is None else self.__url.username.encode( | user = None if self.__url.username is None else self.__url.username.encode( | ||||
'utf8') | 'utf8') | ||||
passwd = None if self.__url.password is None else self.__url.password.encode( | passwd = None if self.__url.password is None else self.__url.password.encode( | ||||
'utf8') | 'utf8') | ||||
authpair = user + b':' + passwd | authpair = user + b':' + passwd | ||||
self.__auth_header = b'Basic ' + base64.b64encode(authpair) | self.__auth_header = b'Basic ' + base64.b64encode(authpair) | ||||
self.timeout = timeout | |||||
if connection: | self._set_conn(connection) | ||||
# Callables re-use the connection of the original proxy | |||||
self.__conn = connection | |||||
elif self.__url.scheme == 'https': | |||||
self.__conn = http.client.HTTPSConnection( | |||||
self.__url.hostname, port, timeout=timeout) | |||||
else: | |||||
self.__conn = http.client.HTTPConnection( | |||||
self.__url.hostname, port, timeout=timeout) | |||||
def __getattr__(self, name): | def __getattr__(self, name): | ||||
if name.startswith('__') and name.endswith('__'): | if name.startswith('__') and name.endswith('__'): | ||||
# Python internal stuff | # Python internal stuff | ||||
raise AttributeError | raise AttributeError | ||||
if self._service_name is not None: | if self._service_name is not None: | ||||
name = "{}.{}".format(self._service_name, name) | name = "{}.{}".format(self._service_name, name) | ||||
return AuthServiceProxy( | return AuthServiceProxy( | ||||
self.__service_url, name, connection=self.__conn) | self.__service_url, name, connection=self.__conn) | ||||
def _request(self, method, path, postdata): | def _request(self, method, path, postdata): | ||||
''' | ''' | ||||
Do a HTTP request, with retry if we get disconnected (e.g. due to a timeout). | Do a HTTP request, with retry if we get disconnected (e.g. due to a timeout). | ||||
This is a workaround for https://bugs.python.org/issue3566 which is fixed in Python 3.5. | This is a workaround for https://bugs.python.org/issue3566 which is fixed in Python 3.5. | ||||
''' | ''' | ||||
headers = {'Host': self.__url.hostname, | headers = {'Host': self.__url.hostname, | ||||
'User-Agent': USER_AGENT, | 'User-Agent': USER_AGENT, | ||||
'Authorization': self.__auth_header, | 'Authorization': self.__auth_header, | ||||
'Content-type': 'application/json'} | 'Content-type': 'application/json'} | ||||
if os.name == 'nt': | |||||
# Windows somehow does not like to re-use connections | |||||
# TODO: Find out why the connection would disconnect occasionally | |||||
# and make it reusable on Windows | |||||
self._set_conn() | |||||
try: | try: | ||||
self.__conn.request(method, path, postdata, headers) | self.__conn.request(method, path, postdata, headers) | ||||
return self._get_response() | return self._get_response() | ||||
except http.client.BadStatusLine as e: | except http.client.BadStatusLine as e: | ||||
if e.line == "''": # if connection was closed, try again | if e.line == "''": # if connection was closed, try again | ||||
self.__conn.close() | self.__conn.close() | ||||
self.__conn.request(method, path, postdata, headers) | self.__conn.request(method, path, postdata, headers) | ||||
return self._get_response() | return self._get_response() | ||||
▲ Show 20 Lines • Show All 68 Lines • ▼ Show 20 Lines | def _get_response(self): | ||||
response["result"], default=EncodeDecimal, ensure_ascii=self.ensure_ascii))) | response["result"], default=EncodeDecimal, ensure_ascii=self.ensure_ascii))) | ||||
else: | else: | ||||
log.debug("<-- [{:.6f}] {}".format(elapsed, responsedata)) | log.debug("<-- [{:.6f}] {}".format(elapsed, responsedata)) | ||||
return response | return response | ||||
def __truediv__(self, relative_uri): | def __truediv__(self, relative_uri): | ||||
return AuthServiceProxy("{}/{}".format(self.__service_url, | return AuthServiceProxy("{}/{}".format(self.__service_url, | ||||
relative_uri), self._service_name, connection=self.__conn) | relative_uri), self._service_name, connection=self.__conn) | ||||
def _set_conn(self, connection=None): | |||||
port = 80 if self.__url.port is None else self.__url.port | |||||
if connection: | |||||
self.__conn = connection | |||||
self.timeout = connection.timeout | |||||
elif self.__url.scheme == 'https': | |||||
self.__conn = http.client.HTTPSConnection( | |||||
self.__url.hostname, port, timeout=self.timeout) | |||||
else: | |||||
self.__conn = http.client.HTTPConnection( | |||||
self.__url.hostname, port, timeout=self.timeout) |