format response and error properly

This commit is contained in:
Alex Grintsvayg 2017-03-23 15:37:28 -04:00
parent 0c42bc6382
commit 9410cd9e77
2 changed files with 29 additions and 39 deletions

View file

@ -1,6 +1,7 @@
import logging import logging
import urlparse import urlparse
import inspect import inspect
import json
from decimal import Decimal from decimal import Decimal
from zope.interface import implements from zope.interface import implements
@ -15,7 +16,7 @@ from lbrynet import conf
from lbrynet.core.Error import InvalidAuthenticationToken from lbrynet.core.Error import InvalidAuthenticationToken
from lbrynet.core import utils from lbrynet.core import utils
from lbrynet.undecorated import undecorated from lbrynet.undecorated import undecorated
from lbrynet.lbrynet_daemon.auth.util import APIKey, get_auth_message, jsonrpc_dumps_pretty from lbrynet.lbrynet_daemon.auth.util import APIKey, get_auth_message
from lbrynet.lbrynet_daemon.auth.client import LBRY_SECRET from lbrynet.lbrynet_daemon.auth.client import LBRY_SECRET
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -94,6 +95,21 @@ def trap(err, *to_trap):
err.trap(*to_trap) err.trap(*to_trap)
def jsonrpc_dumps_pretty(obj, **kwargs):
try:
id_ = kwargs.pop("id")
except KeyError:
id_ = None
if isinstance(obj, JSONRPCError):
data = {"jsonrpc": "2.0", "error": obj.to_dict(), "id": id_}
else:
data = {"jsonrpc": "2.0", "result": obj, "id": id_}
return json.dumps(data, cls=jsonrpclib.JSONRPCEncoder, sort_keys=True, indent=2,
separators=(',', ': '), **kwargs) + "\n"
class AuthorizedBase(object): class AuthorizedBase(object):
def __init__(self): def __init__(self):
self.authorized_functions = [] self.authorized_functions = []
@ -165,7 +181,7 @@ class AuthJSONRPCServer(AuthorizedBase):
request.write(message) request.write(message)
request.finish() request.finish()
def _render_error(self, failure, request, id_, version=jsonrpclib.VERSION_2): def _render_error(self, failure, request, id_):
if isinstance(failure, JSONRPCError): if isinstance(failure, JSONRPCError):
error = failure error = failure
elif isinstance(failure, Failure): elif isinstance(failure, Failure):
@ -178,9 +194,7 @@ class AuthJSONRPCServer(AuthorizedBase):
# last resort, just cast it as a string # last resort, just cast it as a string
error = JSONRPCError(str(failure)) error = JSONRPCError(str(failure))
response_content = jsonrpc_dumps_pretty( response_content = jsonrpc_dumps_pretty(error, id=id_)
error.to_dict(), id=id_, version=version, sort_keys=False
)
self._set_headers(request, response_content) self._set_headers(request, response_content)
# uncomment this after fixing lbrynet-cli to not raise exceptions on errors # uncomment this after fixing lbrynet-cli to not raise exceptions on errors
@ -239,18 +253,15 @@ class AuthJSONRPCServer(AuthorizedBase):
return server.NOT_DONE_YET return server.NOT_DONE_YET
id_ = None id_ = None
version = jsonrpclib.VERSION_2
try: try:
function_name = parsed.get('method') function_name = parsed.get('method')
args = parsed.get('params', {}) args = parsed.get('params', {})
id_ = parsed.get('id', None) id_ = parsed.get('id', None)
version = self._get_jsonrpc_version(parsed.get('jsonrpc'), id_)
token = parsed.pop('hmac', None) token = parsed.pop('hmac', None)
except AttributeError as err: except AttributeError as err:
log.warning(err) log.warning(err)
self._render_error( self._render_error(
JSONRPCError(None, code=JSONRPCError.CODE_INVALID_REQUEST), JSONRPCError(None, code=JSONRPCError.CODE_INVALID_REQUEST), request, id_
request, id_, version=version
) )
return server.NOT_DONE_YET return server.NOT_DONE_YET
@ -266,7 +277,7 @@ class AuthJSONRPCServer(AuthorizedBase):
err.message, code=JSONRPCError.CODE_AUTHENTICATION_ERROR, err.message, code=JSONRPCError.CODE_AUTHENTICATION_ERROR,
traceback=format_exc() traceback=format_exc()
), ),
request, id_, version=version request, id_
) )
return server.NOT_DONE_YET return server.NOT_DONE_YET
self._update_session_secret(session_id) self._update_session_secret(session_id)
@ -278,7 +289,7 @@ class AuthJSONRPCServer(AuthorizedBase):
log.warning('Failed to get function %s: %s', function_name, err) log.warning('Failed to get function %s: %s', function_name, err)
self._render_error( self._render_error(
JSONRPCError(None, JSONRPCError.CODE_METHOD_NOT_FOUND), JSONRPCError(None, JSONRPCError.CODE_METHOD_NOT_FOUND),
request, version request
) )
return server.NOT_DONE_YET return server.NOT_DONE_YET
except NotAllowedDuringStartupError as err: except NotAllowedDuringStartupError as err:
@ -286,7 +297,7 @@ class AuthJSONRPCServer(AuthorizedBase):
self._render_error( self._render_error(
JSONRPCError("This method is unavailable until the daemon is fully started", JSONRPCError("This method is unavailable until the daemon is fully started",
code=JSONRPCError.CODE_INVALID_REQUEST), code=JSONRPCError.CODE_INVALID_REQUEST),
request, version request
) )
return server.NOT_DONE_YET return server.NOT_DONE_YET
@ -310,7 +321,7 @@ class AuthJSONRPCServer(AuthorizedBase):
log.warning(params_error_message) log.warning(params_error_message)
self._render_error( self._render_error(
JSONRPCError(params_error_message, code=JSONRPCError.CODE_INVALID_PARAMS), JSONRPCError(params_error_message, code=JSONRPCError.CODE_INVALID_PARAMS),
request, version request, id_
) )
return server.NOT_DONE_YET return server.NOT_DONE_YET
@ -322,12 +333,12 @@ class AuthJSONRPCServer(AuthorizedBase):
# request.finish() from being called on a closed request. # request.finish() from being called on a closed request.
finished_deferred.addErrback(self._handle_dropped_request, d, function_name) finished_deferred.addErrback(self._handle_dropped_request, d, function_name)
d.addCallback(self._callback_render, request, id_, version, reply_with_next_secret) d.addCallback(self._callback_render, request, id_, reply_with_next_secret)
# TODO: don't trap RuntimeError, which is presently caught to # TODO: don't trap RuntimeError, which is presently caught to
# handle deferredLists that won't peacefully cancel, namely # handle deferredLists that won't peacefully cancel, namely
# get_lbry_files # get_lbry_files
d.addErrback(trap, ConnectionDone, ConnectionLost, defer.CancelledError, RuntimeError) d.addErrback(trap, ConnectionDone, ConnectionLost, defer.CancelledError, RuntimeError)
d.addErrback(log.fail(self._render_error, request, id_, version=version), d.addErrback(log.fail(self._render_error, request, id_),
'Failed to process %s', function_name) 'Failed to process %s', function_name)
d.addBoth(lambda _: log.debug("%s took %f", d.addBoth(lambda _: log.debug("%s took %f",
function_name, function_name,
@ -445,31 +456,15 @@ class AuthJSONRPCServer(AuthorizedBase):
def _update_session_secret(self, session_id): def _update_session_secret(self, session_id):
self.sessions.update({session_id: APIKey.new(name=session_id)}) self.sessions.update({session_id: APIKey.new(name=session_id)})
@staticmethod def _callback_render(self, result, request, id_, auth_required=False):
def _get_jsonrpc_version(version=None, id_=None):
if version:
return int(float(version))
elif id_:
return jsonrpclib.VERSION_1
else:
return jsonrpclib.VERSION_PRE1
def _callback_render(self, result, request, id_, version, auth_required=False):
result_for_return = result
if version == jsonrpclib.VERSION_PRE1:
if not isinstance(result, jsonrpclib.Fault):
result_for_return = (result_for_return,)
try: try:
encoded_message = jsonrpc_dumps_pretty( encoded_message = jsonrpc_dumps_pretty(result, id=id_, default=default_decimal)
result_for_return, id=id_, version=version, default=default_decimal)
request.setResponseCode(200) request.setResponseCode(200)
self._set_headers(request, encoded_message, auth_required) self._set_headers(request, encoded_message, auth_required)
self._render_message(request, encoded_message) self._render_message(request, encoded_message)
except Exception as err: except Exception as err:
log.exception("Failed to render API response: %s", result) log.exception("Failed to render API response: %s", result)
self._render_error(err, request, id_, version) self._render_error(err, request, id_)
@staticmethod @staticmethod
def _render_response(result): def _render_response(result):

View file

@ -24,11 +24,6 @@ def generate_key(x=None):
return sha(x) return sha(x)
def jsonrpc_dumps_pretty(obj, sort_keys=True, **kwargs):
return jsonrpclib.dumps(obj, sort_keys=sort_keys, indent=2, separators=(',', ': '), **kwargs) \
+ "\n"
class APIKey(object): class APIKey(object):
def __init__(self, secret, name, expiration=None): def __init__(self, secret, name, expiration=None):
self.secret = secret self.secret = secret