@@ -75,97 +75,96 @@ def jsonrpc_error(message, retid=None, c
class JSONRPCController(WSGIController):
"""
A WSGI-speaking JSON-RPC controller class
See the specification:
<http://json-rpc.org/wiki/specification>`.
Valid controller return values should be json-serializable objects.
Sub-classes should catch their exceptions and raise JSONRPCError
if they want to pass meaningful errors to the client.
def _get_ip_addr(self, environ):
return _get_ip(environ)
def _get_method_args(self):
Return `self._rpc_args` to dispatched controller method
chosen by __call__
return self._rpc_args
def __call__(self, environ, start_response):
Parse the request body as JSON, look up the method on the
controller and if it exists, dispatch to it.
try:
return self._handle_request(environ, start_response)
finally:
meta.Session.remove()
def _handle_request(self, environ, start_response):
start = time.time()
ip_addr = self.ip_addr = self._get_ip_addr(environ)
self._req_id = None
if 'CONTENT_LENGTH' not in environ:
log.debug("No Content-Length")
return jsonrpc_error(retid=self._req_id,
message="No Content-Length in request")
else:
length = environ['CONTENT_LENGTH'] or 0
length = int(environ['CONTENT_LENGTH'])
log.debug('Content-Length: %s', length)
if length == 0:
log.debug("Content-Length is 0")
message="Content-Length is 0")
raw_body = environ['wsgi.input'].read(length)
json_body = json.loads(raw_body)
except ValueError as e:
# catch JSON errors Here
message="JSON parse error ERR:%s RAW:%r"
% (e, raw_body))
# check AUTH based on API key
self._req_api_key = json_body['api_key']
self._req_id = json_body['id']
self._req_method = json_body['method']
self._request_params = json_body['args']
if not isinstance(self._request_params, dict):
self._request_params = {}
log.debug(
'method: %s, params: %s', self._req_method,
self._request_params
)
except KeyError as e:
message='Incorrect JSON query missing %s' % e)
# check if we can find this session using api_key
u = User.get_by_api_key(self._req_api_key)
if u is None:
message='Invalid API key')
auth_u = AuthUser(dbuser=u)
if not AuthUser.check_ip_allowed(auth_u, ip_addr):
message='request from IP:%s not allowed' % (ip_addr,))
log.info('Access for IP:%s allowed', ip_addr)
except Exception as e:
@@ -194,98 +193,97 @@ class JSONRPCController(WSGIController):
# This attribute will need to be first param of a method that uses
# api_key, which is translated to instance of user at that name
USER_SESSION_ATTR = 'apiuser'
if USER_SESSION_ATTR not in arglist:
return jsonrpc_error(
retid=self._req_id,
message='This method [%s] does not support '
'authentication (missing %s param)' % (
self._func.__name__, USER_SESSION_ATTR)
# get our arglist and check if we provided them as args
for arg, default in func_kwargs.iteritems():
if arg == USER_SESSION_ATTR:
# USER_SESSION_ATTR is something translated from API key and
# this is checked before so we don't need validate it
continue
# skip the required param check if it's default value is
# NotImplementedType (default_empty)
if default == default_empty and arg not in self._request_params:
message=(
'Missing non optional `%s` arg in JSON DATA' % arg
self._rpc_args = {USER_SESSION_ATTR: u}
self._rpc_args.update(self._request_params)
self._rpc_args['action'] = self._req_method
self._rpc_args['environ'] = environ
self._rpc_args['start_response'] = start_response
status = []
headers = []
exc_info = []
def change_content(new_status, new_headers, new_exc_info=None):
status.append(new_status)
headers.extend(new_headers)
exc_info.append(new_exc_info)
output = WSGIController.__call__(self, environ, change_content)
output = list(output)
headers.append(('Content-Length', str(len(output[0]))))
output = list(output) # expand iterator - just to ensure exact timing
replace_header(headers, 'Content-Type', 'application/json')
start_response(status[0], headers, exc_info[0])
log.info('IP: %s Request to %s time: %.3fs' % (
self._get_ip_addr(environ),
safe_unicode(_get_access_path(environ)), time.time() - start)
return output
def _dispatch_call(self):
Implement dispatch interface specified by WSGIController
raw_response = ''
raw_response = self._inspect_call(self._func)
if isinstance(raw_response, HTTPError):
self._error = str(raw_response)
except JSONRPCError as e:
self._error = safe_str(e)
log.error('Encountered unhandled exception: %s',
traceback.format_exc(),)
json_exc = JSONRPCError('Internal server error')
self._error = safe_str(json_exc)
if self._error is not None:
raw_response = None
response = dict(id=self._req_id, result=raw_response, error=self._error)
return json.dumps(response)
except TypeError as e:
log.error('API FAILED. Error encoding response: %s', e)
return json.dumps(
dict(
id=self._req_id,
result=None,
error="Error encoding response"
def _find_method(self):
Return method named by `self._req_method` in controller if able
log.debug('Trying to find JSON-RPC method: %s', self._req_method)
if self._req_method.startswith('_'):
raise AttributeError("Method not allowed")
Status change: