def __init__(self, auth=False): resource.Resource.__init__(self) self.methods = {} if auth: self.auth = Auth() self.register_object(self.auth) else: self.auth = None
class JsonRpc(resource.Resource): """ A Twisted Web resource that exposes a JSON-RPC interface for web clients \ to use. """ def __init__(self, auth=False): resource.Resource.__init__(self) self.methods = {} if auth: self.auth = Auth() self.register_object(self.auth) else: self.auth = None def get_methods(self): return self.methods.keys() def exec_method(self, method, params, request): """ Handles executing all local methods. """ if method == "system.listMethods": return self.get_methods() elif method in self.methods: # This will eventually process methods that the server adds # and any plugins. meth = self.methods[method] meth.func_globals['__request__'] = request if self.auth: self.auth.check_request(request, meth) return meth(*params) raise JSONException("Unknown method") def has_method(self, method): """ Checks to see if we can handle the specified method. :param method: The method name :type method: str :returns: True or False :rtype: bool """ return method == 'system.listMethods' or method in self.methods def handle_request(self, request): """ Takes some json data as a string and attempts to decode it, and process the rpc object that should be contained, returning a deferred for all procedure calls and the request id. """ try: request.json = json.loads(request.json) except ValueError: raise JSONException("JSON not decodable") if "method" not in request.json or "id" not in request.json or \ "params" not in request.json: raise JSONException("Invalid JSON request") method, params = request.json['method'], request.json['params'] request.request_id = request.json['id'] if self.has_method(method): try: return self.exec_method(method, params, request) except AuthError: raise JsonError(1, 'Not authenticated') except Exception as e: log.error("Error calling method `%s`", method) log.exception(e) raise JsonError(3, e.message) else: raise JsonError(2, 'Unknown method') def on_json_request(self, request): """ Handler to take the json data as a string and pass it on to the _handle_request method for further processing. """ log.debug("json-request: %s", request.json) response = { 'result': None, 'error': None, 'id': None } # Declare result incase we hit an error result = None # Pass the JSON-RPC request off to be handled try: result = self.handle_request(request) except JsonError as e: response['error'] = {'message': e.message, 'code': e.code} except JSONException as e: log.exception(e) return # Store the request id in the response dict response['id'] = request.request_id # Check to see if we have a Deferred and change our behaviour if so if isinstance(result, Deferred): result.addCallback(self.on_got_result, request, response) result.addErrback(self.on_err_result, request, response) return result else: response['result'] = result return self.send_response(request, response) def on_got_result(self, result, request, response): """ Sends the result of a RPC call that returned a Deferred. """ response['result'] = result return self.send_response(request, response) def on_err_result(self, failure, request, response): """ Handles any failures that occured while making a RPC call. """ response['error'] = failure.value.args[0] if failure.value.args else '' return self.send_response(request, response) def on_json_request_failed(self, reason, request): """ Errback handler to return a HTTP code of 500. """ log.exception(reason) request.setResponseCode(http.INTERNAL_SERVER_ERROR) return "" def send_response(self, request, response): """ Handles sending the response dictionary back to the client. :param request: The original request object :type request: Request :param response: The response dictionary :type response: dict """ request.setHeader("content-type", "application/x-json") request.write(compress(json.dumps(response), request)) request.finish() def render(self, request): """ Handles all the POST requests made to the JsonRpc resource. """ if request.method != "POST": request.setResponseCode(http.NOT_ALLOWED) return "" try: request.content.seek(0) request.json = request.content.read() d = self.on_json_request(request) return server.NOT_DONE_YET except Exception, e: return self.on_json_request_failed(e, request)