2017-06-06 21:22:03 +00:00
|
|
|
import json
|
|
|
|
import socket
|
2018-03-09 15:20:34 +00:00
|
|
|
import time
|
2019-01-25 16:03:09 +00:00
|
|
|
import os
|
2017-06-06 21:22:03 +00:00
|
|
|
|
|
|
|
|
|
|
|
def print_dict(d):
|
2018-03-20 00:40:09 +00:00
|
|
|
print(json.dumps(d, indent=2))
|
2017-06-06 21:22:03 +00:00
|
|
|
|
|
|
|
|
2018-03-29 07:36:42 +00:00
|
|
|
class JSONRPCException(Exception):
|
|
|
|
def __init__(self, message):
|
|
|
|
self.message = message
|
|
|
|
|
|
|
|
|
2017-06-06 21:22:03 +00:00
|
|
|
class JSONRPCClient(object):
|
2018-03-09 15:20:34 +00:00
|
|
|
def __init__(self, addr, port=None, verbose=False, timeout=60.0):
|
2018-11-29 12:42:32 +00:00
|
|
|
self.sock = None
|
2018-02-05 20:29:05 +00:00
|
|
|
self.verbose = verbose
|
2018-03-09 15:20:34 +00:00
|
|
|
self.timeout = timeout
|
2018-11-29 11:54:50 +00:00
|
|
|
self.request_id = 0
|
2018-04-11 12:42:58 +00:00
|
|
|
try:
|
2019-01-25 16:03:09 +00:00
|
|
|
if os.path.exists(addr):
|
2018-04-11 12:42:58 +00:00
|
|
|
self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
|
|
|
self.sock.connect(addr)
|
|
|
|
elif ':' in addr:
|
|
|
|
for res in socket.getaddrinfo(addr, port, socket.AF_INET6, socket.SOCK_STREAM, socket.SOL_TCP):
|
|
|
|
af, socktype, proto, canonname, sa = res
|
|
|
|
self.sock = socket.socket(af, socktype, proto)
|
|
|
|
self.sock.connect(sa)
|
|
|
|
else:
|
|
|
|
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
|
|
self.sock.connect((addr, port))
|
|
|
|
except socket.error as ex:
|
|
|
|
raise JSONRPCException("Error while connecting to %s\n"
|
|
|
|
"Error details: %s" % (addr, ex))
|
2017-06-06 21:22:03 +00:00
|
|
|
|
|
|
|
def __del__(self):
|
2018-11-29 12:42:32 +00:00
|
|
|
if self.sock:
|
|
|
|
self.sock.close()
|
2017-06-06 21:22:03 +00:00
|
|
|
|
2018-11-29 12:42:32 +00:00
|
|
|
def send(self, method, params=None):
|
2018-11-29 11:54:50 +00:00
|
|
|
self.request_id += 1
|
2018-11-29 12:42:32 +00:00
|
|
|
req = {
|
|
|
|
'jsonrpc': '2.0',
|
|
|
|
'method': method,
|
|
|
|
'id': self.request_id
|
|
|
|
}
|
2017-06-06 21:22:03 +00:00
|
|
|
|
2018-11-29 12:42:32 +00:00
|
|
|
if params:
|
|
|
|
req['params'] = params
|
2018-02-05 20:29:05 +00:00
|
|
|
|
2018-11-29 12:42:32 +00:00
|
|
|
reqstr = json.dumps(req)
|
|
|
|
if self.verbose:
|
2017-06-06 21:22:03 +00:00
|
|
|
print("request:")
|
|
|
|
print(json.dumps(req, indent=2))
|
|
|
|
|
2018-03-20 00:43:34 +00:00
|
|
|
self.sock.sendall(reqstr.encode("utf-8"))
|
2018-11-29 12:42:32 +00:00
|
|
|
return req
|
2018-03-09 15:20:34 +00:00
|
|
|
|
2018-11-29 12:42:32 +00:00
|
|
|
def recv(self):
|
|
|
|
start_time = time.clock()
|
|
|
|
response = None
|
|
|
|
buf = ''
|
|
|
|
while not response:
|
2017-06-06 21:22:03 +00:00
|
|
|
try:
|
2018-03-09 15:20:34 +00:00
|
|
|
timeout = self.timeout - (time.clock() - start_time)
|
|
|
|
self.sock.settimeout(timeout)
|
|
|
|
newdata = self.sock.recv(4096)
|
2018-11-29 12:42:32 +00:00
|
|
|
if not newdata:
|
|
|
|
self.sock.close()
|
|
|
|
self.sock = None
|
|
|
|
raise JSONRPCException("Connection closed with partial response:\n%s\n" % buf)
|
2018-03-20 00:43:34 +00:00
|
|
|
buf += newdata.decode("utf-8")
|
2017-06-06 21:22:03 +00:00
|
|
|
response = json.loads(buf)
|
2018-03-09 15:20:34 +00:00
|
|
|
except socket.timeout:
|
2018-11-29 12:42:32 +00:00
|
|
|
break # throw exception after loop to avoid Python freaking out about nested exceptions
|
2017-06-06 21:22:03 +00:00
|
|
|
except ValueError:
|
|
|
|
continue # incomplete response; keep buffering
|
|
|
|
|
|
|
|
if not response:
|
2018-11-29 12:42:32 +00:00
|
|
|
raise JSONRPCException("Timeout while waiting for response:\n%s\n" % buf)
|
2017-06-06 21:22:03 +00:00
|
|
|
|
2018-11-29 12:42:32 +00:00
|
|
|
if self.verbose:
|
2018-07-17 17:45:53 +00:00
|
|
|
print("response:")
|
|
|
|
print(json.dumps(response, indent=2))
|
|
|
|
|
2017-06-06 21:22:03 +00:00
|
|
|
if 'error' in response:
|
2018-03-29 07:36:42 +00:00
|
|
|
msg = "\n".join(["Got JSON-RPC error response",
|
|
|
|
"response:",
|
|
|
|
json.dumps(response['error'], indent=2)])
|
|
|
|
raise JSONRPCException(msg)
|
2018-11-29 12:42:32 +00:00
|
|
|
return response
|
2017-06-06 21:22:03 +00:00
|
|
|
|
2018-11-29 12:42:32 +00:00
|
|
|
def call(self, method, params=None):
|
|
|
|
self.send(method, params)
|
|
|
|
try:
|
|
|
|
return self.recv()['result']
|
|
|
|
except JSONRPCException as e:
|
|
|
|
""" Don't expect response to kill """
|
|
|
|
if not self.sock and method == "kill_instance":
|
|
|
|
return {}
|
|
|
|
else:
|
|
|
|
raise e
|