1*9c5db199SXin Li""" 2*9c5db199SXin Li Copyright (c) 2007 Jan-Klaas Kollhof 3*9c5db199SXin Li 4*9c5db199SXin Li This file is part of jsonrpc. 5*9c5db199SXin Li 6*9c5db199SXin Li jsonrpc is free software; you can redistribute it and/or modify 7*9c5db199SXin Li it under the terms of the GNU Lesser General Public License as published by 8*9c5db199SXin Li the Free Software Foundation; either version 2.1 of the License, or 9*9c5db199SXin Li (at your option) any later version. 10*9c5db199SXin Li 11*9c5db199SXin Li This software is distributed in the hope that it will be useful, 12*9c5db199SXin Li but WITHOUT ANY WARRANTY; without even the implied warranty of 13*9c5db199SXin Li MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14*9c5db199SXin Li GNU Lesser General Public License for more details. 15*9c5db199SXin Li 16*9c5db199SXin Li You should have received a copy of the GNU Lesser General Public License 17*9c5db199SXin Li along with this software; if not, write to the Free Software 18*9c5db199SXin Li Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19*9c5db199SXin Li""" 20*9c5db199SXin Li 21*9c5db199SXin Lifrom __future__ import absolute_import 22*9c5db199SXin Lifrom __future__ import division 23*9c5db199SXin Lifrom __future__ import print_function 24*9c5db199SXin Li 25*9c5db199SXin Liimport socket 26*9c5db199SXin Liimport traceback 27*9c5db199SXin Li 28*9c5db199SXin Lifrom json import decoder 29*9c5db199SXin Li 30*9c5db199SXin Liimport six 31*9c5db199SXin Li 32*9c5db199SXin Litry: 33*9c5db199SXin Li from django.core import exceptions as django_exceptions 34*9c5db199SXin Li # Django JSON encoder uses the standard json encoder but can handle DateTime 35*9c5db199SXin Li from django.core.serializers import json as django_encoder 36*9c5db199SXin Li json_encoder = django_encoder.DjangoJSONEncoder() 37*9c5db199SXin Liexcept django_exceptions.ImproperlyConfigured: 38*9c5db199SXin Li from json import encoder 39*9c5db199SXin Li json_encoder = encoder.JSONEncoder() 40*9c5db199SXin Li 41*9c5db199SXin Li 42*9c5db199SXin Lijson_decoder = decoder.JSONDecoder() 43*9c5db199SXin Li 44*9c5db199SXin Li 45*9c5db199SXin Lidef customConvertJson(value): 46*9c5db199SXin Li """\ 47*9c5db199SXin Li Recursively process JSON values and do type conversions. 48*9c5db199SXin Li -change floats to ints 49*9c5db199SXin Li -change unicodes to strs 50*9c5db199SXin Li """ 51*9c5db199SXin Li if isinstance(value, float): 52*9c5db199SXin Li return int(value) 53*9c5db199SXin Li elif isinstance(value, six.text_type): 54*9c5db199SXin Li return str(value) 55*9c5db199SXin Li elif isinstance(value, list): 56*9c5db199SXin Li return [customConvertJson(item) for item in value] 57*9c5db199SXin Li elif isinstance(value, dict): 58*9c5db199SXin Li new_dict = {} 59*9c5db199SXin Li for key, val in six.iteritems(value): 60*9c5db199SXin Li new_key = customConvertJson(key) 61*9c5db199SXin Li new_val = customConvertJson(val) 62*9c5db199SXin Li new_dict[new_key] = new_val 63*9c5db199SXin Li return new_dict 64*9c5db199SXin Li else: 65*9c5db199SXin Li return value 66*9c5db199SXin Li 67*9c5db199SXin Li 68*9c5db199SXin Lidef ServiceMethod(fn): 69*9c5db199SXin Li fn.IsServiceMethod = True 70*9c5db199SXin Li return fn 71*9c5db199SXin Li 72*9c5db199SXin Liclass ServiceException(Exception): 73*9c5db199SXin Li pass 74*9c5db199SXin Li 75*9c5db199SXin Liclass ServiceRequestNotTranslatable(ServiceException): 76*9c5db199SXin Li pass 77*9c5db199SXin Li 78*9c5db199SXin Liclass BadServiceRequest(ServiceException): 79*9c5db199SXin Li pass 80*9c5db199SXin Li 81*9c5db199SXin Liclass ServiceMethodNotFound(ServiceException): 82*9c5db199SXin Li pass 83*9c5db199SXin Li 84*9c5db199SXin Li 85*9c5db199SXin Liclass ServiceHandler(object): 86*9c5db199SXin Li 87*9c5db199SXin Li def __init__(self, service): 88*9c5db199SXin Li self.service=service 89*9c5db199SXin Li 90*9c5db199SXin Li 91*9c5db199SXin Li @classmethod 92*9c5db199SXin Li def blank_result_dict(cls): 93*9c5db199SXin Li return {'id': None, 'result': None, 'err': None, 'err_traceback': None} 94*9c5db199SXin Li 95*9c5db199SXin Li def dispatchRequest(self, request): 96*9c5db199SXin Li """ 97*9c5db199SXin Li Invoke a json RPC call from a decoded json request. 98*9c5db199SXin Li @param request: a decoded json_request 99*9c5db199SXin Li @returns a dictionary with keys id, result, err and err_traceback 100*9c5db199SXin Li """ 101*9c5db199SXin Li results = self.blank_result_dict() 102*9c5db199SXin Li 103*9c5db199SXin Li try: 104*9c5db199SXin Li results['id'] = self._getRequestId(request) 105*9c5db199SXin Li methName = request['method'] 106*9c5db199SXin Li args = request['params'] 107*9c5db199SXin Li except KeyError: 108*9c5db199SXin Li raise BadServiceRequest(request) 109*9c5db199SXin Li 110*9c5db199SXin Li metadata = request.copy() 111*9c5db199SXin Li metadata['_type'] = 'rpc' 112*9c5db199SXin Li metadata['rpc_server'] = socket.gethostname() 113*9c5db199SXin Li try: 114*9c5db199SXin Li meth = self.findServiceEndpoint(methName) 115*9c5db199SXin Li results['result'] = self.invokeServiceEndpoint(meth, args) 116*9c5db199SXin Li except Exception as err: 117*9c5db199SXin Li results['err_traceback'] = traceback.format_exc() 118*9c5db199SXin Li results['err'] = err 119*9c5db199SXin Li 120*9c5db199SXin Li return results 121*9c5db199SXin Li 122*9c5db199SXin Li 123*9c5db199SXin Li def _getRequestId(self, request): 124*9c5db199SXin Li try: 125*9c5db199SXin Li return request['id'] 126*9c5db199SXin Li except KeyError: 127*9c5db199SXin Li raise BadServiceRequest(request) 128*9c5db199SXin Li 129*9c5db199SXin Li 130*9c5db199SXin Li def handleRequest(self, jsonRequest): 131*9c5db199SXin Li request = self.translateRequest(jsonRequest) 132*9c5db199SXin Li results = self.dispatchRequest(request) 133*9c5db199SXin Li return self.translateResult(results) 134*9c5db199SXin Li 135*9c5db199SXin Li 136*9c5db199SXin Li @staticmethod 137*9c5db199SXin Li def translateRequest(data): 138*9c5db199SXin Li try: 139*9c5db199SXin Li req = json_decoder.decode(data) 140*9c5db199SXin Li except: 141*9c5db199SXin Li raise ServiceRequestNotTranslatable(data) 142*9c5db199SXin Li req = customConvertJson(req) 143*9c5db199SXin Li return req 144*9c5db199SXin Li 145*9c5db199SXin Li def findServiceEndpoint(self, name): 146*9c5db199SXin Li try: 147*9c5db199SXin Li meth = getattr(self.service, name) 148*9c5db199SXin Li return meth 149*9c5db199SXin Li except AttributeError: 150*9c5db199SXin Li raise ServiceMethodNotFound(name) 151*9c5db199SXin Li 152*9c5db199SXin Li def invokeServiceEndpoint(self, meth, args): 153*9c5db199SXin Li return meth(*args) 154*9c5db199SXin Li 155*9c5db199SXin Li @staticmethod 156*9c5db199SXin Li def translateResult(result_dict): 157*9c5db199SXin Li """ 158*9c5db199SXin Li @param result_dict: a dictionary containing the result, error, traceback 159*9c5db199SXin Li and id. 160*9c5db199SXin Li @returns translated json result 161*9c5db199SXin Li """ 162*9c5db199SXin Li if result_dict['err'] is not None: 163*9c5db199SXin Li error_name = result_dict['err'].__class__.__name__ 164*9c5db199SXin Li result_dict['err'] = {'name': error_name, 165*9c5db199SXin Li 'message': str(result_dict['err']), 166*9c5db199SXin Li 'traceback': result_dict['err_traceback']} 167*9c5db199SXin Li result_dict['result'] = None 168*9c5db199SXin Li 169*9c5db199SXin Li try: 170*9c5db199SXin Li json_dict = {'result': result_dict['result'], 171*9c5db199SXin Li 'id': result_dict['id'], 172*9c5db199SXin Li 'error': result_dict['err'] } 173*9c5db199SXin Li data = json_encoder.encode(json_dict) 174*9c5db199SXin Li except TypeError as e: 175*9c5db199SXin Li err_traceback = traceback.format_exc() 176*9c5db199SXin Li print(err_traceback) 177*9c5db199SXin Li err = {"name" : "JSONEncodeException", 178*9c5db199SXin Li "message" : "Result Object Not Serializable", 179*9c5db199SXin Li "traceback" : err_traceback} 180*9c5db199SXin Li data = json_encoder.encode({"result":None, "id":result_dict['id'], 181*9c5db199SXin Li "error":err}) 182*9c5db199SXin Li 183*9c5db199SXin Li return data 184