4
|
1 #!/usr/bin/env python
|
5
|
2
|
|
3 # Copyright (c) 2011 Ben Croston
|
|
4 #
|
|
5 # Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
6 # this software and associated documentation files (the "Software"), to deal in
|
|
7 # the Software without restriction, including without limitation the rights to
|
|
8 # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
|
9 # of the Software, and to permit persons to whom the Software is furnished to do
|
|
10 # so, subject to the following conditions:
|
|
11 #
|
|
12 # The above copyright notice and this permission notice shall be included in all
|
|
13 # copies or substantial portions of the Software.
|
|
14 #
|
|
15 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21 # SOFTWARE.
|
|
22
|
0
|
23 from uuid import uuid4
|
|
24 from urlparse import urlparse
|
4
|
25 import json
|
0
|
26 import httplib
|
|
27 import copy
|
|
28 import socket
|
|
29 import hashlib
|
|
30
|
|
31 class _Method(object):
|
|
32 def __init__(self, call, name, username=None, password=None):
|
|
33 self.call = call
|
|
34 self.name = name
|
|
35 self._username = username
|
|
36 self._password = password
|
|
37
|
|
38 def __call__(self, *args, **kwargs):
|
|
39 request = {}
|
|
40 request['id'] = str(uuid4())
|
|
41 request['method'] = self.name
|
|
42
|
|
43 if len(kwargs) is not 0:
|
|
44 params = copy.copy(kwargs)
|
|
45 index = 0
|
|
46 for arg in args:
|
|
47 params[str(index)] = arg
|
|
48 index = index + 1
|
|
49 elif len(args) is not 0:
|
|
50 params = copy.copy(args)
|
|
51 else:
|
|
52 params = None
|
|
53 request['params'] = params
|
|
54
|
|
55 if self._username is not None:
|
|
56 request['username'] = self._username
|
|
57 if self._password is not None:
|
|
58 request['password'] = hashlib.md5(self._password).hexdigest()
|
|
59
|
|
60 resp = self.call(json.dumps(request))
|
|
61 if resp is not None and resp['error'] is None and resp['id'] == request['id']:
|
|
62 return resp['result']
|
|
63 else:
|
|
64 raise Exception('This is not supposed to happen -- btc') ########
|
|
65
|
|
66 def __getattr__(self, name):
|
|
67 return _Method(self.call, "%s.%s" % (self.name, name), self._username, self._password)
|
|
68
|
|
69 class _JSONRPCTransport(object):
|
|
70 headers = {'Content-Type':'application/json',
|
|
71 'Accept':'application/json'}
|
|
72
|
|
73 def __init__(self, uri, proxy_uri=None, user_agent=None):
|
|
74 self.headers['User-Agent'] = user_agent if user_agent is not None else 'jsonrpclib'
|
|
75 if proxy_uri is not None:
|
|
76 self.connection_url = urlparse(proxy_uri)
|
|
77 self.request_path = uri
|
|
78 else:
|
|
79 self.connection_url = urlparse(uri)
|
|
80 self.request_path = self.connection_url.path
|
|
81
|
|
82 def request(self, request_body):
|
|
83 if self.connection_url.scheme == 'http':
|
|
84 if self.connection_url.port is None:
|
|
85 port = 80
|
|
86 else:
|
|
87 port = self.connection_url.port
|
|
88 connection = httplib.HTTPConnection(self.connection_url.hostname+':'+str(port))
|
|
89 elif self.connection_url.scheme == 'https':
|
|
90 if self.connection_url.port is None:
|
|
91 port = 443
|
|
92 else:
|
|
93 port = self.connection_url.port
|
|
94 connection = httplib.HTTPSConnection(self.connection_url.hostname+':'+str(port))
|
|
95 else:
|
|
96 raise Exception('unsupported transport')
|
|
97 connection.request('POST', self.request_path, body=request_body, headers=self.headers)
|
|
98 return connection.getresponse()
|
|
99
|
|
100 class BadRequestException(Exception):
|
|
101 """HTTP 400 - Bad Request"""
|
|
102 def __init__(self):
|
|
103 Exception.__init__(self,'HTTP 400 - Bad Request')
|
|
104
|
|
105 class UnauthorisedException(Exception):
|
|
106 """HTTP 401 - Unauthorised"""
|
|
107 def __init__(self):
|
|
108 Exception.__init__(self,'HTTP 401 - Unauthorised')
|
|
109
|
|
110 class ForbiddenException(Exception):
|
|
111 """HTTP 403 - Forbidden"""
|
|
112 def __init__(self):
|
|
113 Exception.__init__(self,'HTTP 403 - Forbidden')
|
|
114
|
|
115 class NotFoundException(Exception):
|
|
116 """HTTP 404 - Not Found"""
|
|
117 def __init__(self):
|
|
118 Exception.__init__(self,'HTTP 404 - Not Found')
|
|
119
|
|
120 class NetworkSocketException(Exception):
|
|
121 def __init__(self):
|
|
122 Exception.__init__(self,'Network socket exception')
|
|
123
|
|
124 class BadGatewayException(Exception):
|
|
125 """HTTP 502 - Bad Gateway"""
|
|
126 def __init__(self):
|
|
127 Exception.__init__(self,'HTTP 502 - Bad Gateway')
|
|
128
|
4
|
129 class ServerProxy(object):
|
0
|
130 def __init__(self, uri=None, transport=None, proxy_uri=None, user_agent=None, username=None, password=None):
|
|
131 if uri is None and transport is None:
|
|
132 raise Exception('either uri or transport needs to be specified')
|
|
133
|
|
134 if transport is None:
|
|
135 transport = _JSONRPCTransport(uri, proxy_uri=proxy_uri, user_agent=user_agent)
|
|
136 self.__transport = transport
|
|
137 self._username = username
|
|
138 self._password = password
|
|
139
|
|
140 def __request(self, request):
|
|
141 # call a method on the remote server
|
|
142 try:
|
|
143 response = self.__transport.request(request)
|
|
144 except socket.error:
|
|
145 raise NetworkSocketException
|
|
146 if response.status == 200:
|
|
147 return json.loads(response.read())
|
|
148 elif response.status == 400:
|
|
149 raise BadRequestException
|
|
150 elif response.status == 401:
|
|
151 raise UnauthorisedException
|
|
152 elif response.status == 403:
|
|
153 raise ForbiddenException
|
|
154 elif response.status == 404:
|
|
155 raise NotFoundException
|
|
156 elif response.status == 500:
|
|
157 msg = json.loads(response.read())
|
|
158 raise Exception('JSONRPCError\n%s'%msg['error']['error'])
|
|
159 elif response.status == 502:
|
|
160 raise BadGatewayException
|
|
161 else:
|
|
162 raise Exception('HTTP Status %s'%response.status)
|
|
163
|
|
164 def __repr__(self):
|
|
165 return (
|
|
166 "<ServerProxy for %s%s>" %
|
|
167 (self.__host, self.__handler)
|
|
168 )
|
|
169
|
|
170 __str__ = __repr__
|
|
171
|
|
172 def __getattr__(self, name):
|
|
173 # magic method dispatcher
|
|
174 return _Method(self.__request, name, self._username, self._password)
|
|
175
|
|
176 if __name__ == '__main__':
|
|
177 ##### btc fixme
|
|
178 jsonrpc_client = ServerProxy2('http://localhost:1337/', username='testuser', password='', user_agent='Py2NotInternetExploiter')
|
|
179 #jsonrpc_client = ServerProxy2('https://www.croston.org/test/index.py',
|
|
180 # username='testuser',
|
|
181 # password='',
|
|
182 # user_agent='Py2NotInternetExploiter')
|
|
183 assert jsonrpc_client.api.mymethod() == jsonrpc_client.mymethod()
|
|
184 try:
|
|
185 print(jsonrpc_client.wibble('this should fail'))
|
|
186 except BadRequestException:
|
|
187 pass # test passed
|
|
188 else:
|
|
189 raise Exception('Test failed (calling unknown method)')
|
|
190
|
|
191 print 'All tests passed'
|
|
192
|