Dash Core  0.12.2.1
P2P Digital Currency
authproxy.py
Go to the documentation of this file.
1 
2 """
3  Copyright 2011 Jeff Garzik
4 
5  AuthServiceProxy has the following improvements over python-jsonrpc's
6  ServiceProxy class:
7 
8  - HTTP connections persist for the life of the AuthServiceProxy object
9  (if server supports HTTP/1.1)
10  - sends protocol 'version', per JSON-RPC 1.1
11  - sends proper, incrementing 'id'
12  - sends Basic HTTP authentication headers
13  - parses all JSON numbers that look like floats as Decimal
14  - uses standard Python json lib
15 
16  Previous copyright, from python-jsonrpc/jsonrpc/proxy.py:
17 
18  Copyright (c) 2007 Jan-Klaas Kollhof
19 
20  This file is part of jsonrpc.
21 
22  jsonrpc is free software; you can redistribute it and/or modify
23  it under the terms of the GNU Lesser General Public License as published by
24  the Free Software Foundation; either version 2.1 of the License, or
25  (at your option) any later version.
26 
27  This software is distributed in the hope that it will be useful,
28  but WITHOUT ANY WARRANTY; without even the implied warranty of
29  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
30  GNU Lesser General Public License for more details.
31 
32  You should have received a copy of the GNU Lesser General Public License
33  along with this software; if not, write to the Free Software
34  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
35 """
36 
37 try:
38  import http.client as httplib
39 except ImportError:
40  import httplib
41 import base64
42 import decimal
43 import json
44 import logging
45 try:
46  import urllib.parse as urlparse
47 except ImportError:
48  import urlparse
49 
50 USER_AGENT = "AuthServiceProxy/0.1"
51 
52 HTTP_TIMEOUT = 30
53 
54 log = logging.getLogger("BitcoinRPC")
55 
56 class JSONRPCException(Exception):
57  def __init__(self, rpc_error):
58  Exception.__init__(self)
59  self.error = rpc_error
60 
61 
63  if isinstance(o, decimal.Decimal):
64  return str(o)
65  raise TypeError(repr(o) + " is not JSON serializable")
66 
67 class AuthServiceProxy(object):
68  __id_count = 0
69 
70  # ensure_ascii: escape unicode as \uXXXX, passed to json.dumps
71  def __init__(self, service_url, service_name=None, timeout=HTTP_TIMEOUT, connection=None, ensure_ascii=True):
72  self.__service_url = service_url
73  self._service_name = service_name
74  self.ensure_ascii = ensure_ascii # can be toggled on the fly by tests
75  self.__url = urlparse.urlparse(service_url)
76  if self.__url.port is None:
77  port = 80
78  else:
79  port = self.__url.port
80  (user, passwd) = (self.__url.username, self.__url.password)
81  try:
82  user = user.encode('utf8')
83  except AttributeError:
84  pass
85  try:
86  passwd = passwd.encode('utf8')
87  except AttributeError:
88  pass
89  authpair = user + b':' + passwd
90  self.__auth_header = b'Basic ' + base64.b64encode(authpair)
91 
92  if connection:
93  # Callables re-use the connection of the original proxy
94  self.__conn = connection
95  elif self.__url.scheme == 'https':
96  self.__conn = httplib.HTTPSConnection(self.__url.hostname, port,
97  timeout=timeout)
98  else:
99  self.__conn = httplib.HTTPConnection(self.__url.hostname, port,
100  timeout=timeout)
101 
102  def __getattr__(self, name):
103  if name.startswith('__') and name.endswith('__'):
104  # Python internal stuff
105  raise AttributeError
106  if self._service_name is not None:
107  name = "%s.%s" % (self._service_name, name)
108  return AuthServiceProxy(self.__service_url, name, connection=self.__conn)
109 
110  def _request(self, method, path, postdata):
111  '''
112  Do a HTTP request, with retry if we get disconnected (e.g. due to a timeout).
113  This is a workaround for https://bugs.python.org/issue3566 which is fixed in Python 3.5.
114  '''
115  headers = {'Host': self.__url.hostname,
116  'User-Agent': USER_AGENT,
117  'Authorization': self.__auth_header,
118  'Content-type': 'application/json'}
119  try:
120  self.__conn.request(method, path, postdata, headers)
121  return self._get_response()
122  except httplib.BadStatusLine as e:
123  if e.line == "''": # if connection was closed, try again
124  self.__conn.close()
125  self.__conn.request(method, path, postdata, headers)
126  return self._get_response()
127  else:
128  raise
129 
130  def __call__(self, *args):
131  AuthServiceProxy.__id_count += 1
132 
133  log.debug("-%s-> %s %s"%(AuthServiceProxy.__id_count, self._service_name,
134  json.dumps(args, default=EncodeDecimal, ensure_ascii=self.ensure_ascii)))
135  postdata = json.dumps({'version': '1.1',
136  'method': self._service_name,
137  'params': args,
138  'id': AuthServiceProxy.__id_count}, default=EncodeDecimal, ensure_ascii=self.ensure_ascii)
139  response = self._request('POST', self.__url.path, postdata.encode('utf-8'))
140  if response['error'] is not None:
141  raise JSONRPCException(response['error'])
142  elif 'result' not in response:
143  raise JSONRPCException({
144  'code': -343, 'message': 'missing JSON-RPC result'})
145  else:
146  return response['result']
147 
148  def _batch(self, rpc_call_list):
149  postdata = json.dumps(list(rpc_call_list), default=EncodeDecimal, ensure_ascii=self.ensure_ascii)
150  log.debug("--> "+postdata)
151  return self._request('POST', self.__url.path, postdata.encode('utf-8'))
152 
153  def _get_response(self):
154  http_response = self.__conn.getresponse()
155  if http_response is None:
156  raise JSONRPCException({
157  'code': -342, 'message': 'missing HTTP response from server'})
158 
159  content_type = http_response.getheader('Content-Type')
160  if content_type != 'application/json':
161  raise JSONRPCException({
162  'code': -342, 'message': 'non-JSON HTTP response with \'%i %s\' from server' % (http_response.status, http_response.reason)})
163 
164  responsedata = http_response.read().decode('utf8')
165  response = json.loads(responsedata, parse_float=decimal.Decimal)
166  if "error" in response and response["error"] is None:
167  log.debug("<-%s- %s"%(response["id"], json.dumps(response["result"], default=EncodeDecimal, ensure_ascii=self.ensure_ascii)))
168  else:
169  log.debug("<-- "+responsedata)
170  return response
def _batch(self, rpc_call_list)
Definition: authproxy.py:148
def __init__(self, service_url, service_name=None, timeout=HTTP_TIMEOUT, connection=None, ensure_ascii=True)
Definition: authproxy.py:71
def _request(self, method, path, postdata)
Definition: authproxy.py:110