def _rpc_process(self, req, protocol, content_type): """Process incoming RPC request and finalize response.""" proto_id = protocol.rpc_info()[0] rpcreq = req.rpc = {'mimetype': content_type} try : self.log.debug("RPC(%s) call by '%s'", proto_id, req.authname) rpcreq = req.rpc = protocol.parse_rpc_request(req, content_type) rpcreq['mimetype'] = content_type # Important ! Check after parsing RPC request to add # protocol-specific fields in response # (e.g. JSON-RPC response `id`) req.perm.require('XML_RPC') # Need at least XML_RPC method_name = rpcreq.get('method') if method_name is None : raise ProtocolException('Missing method name') args = rpcreq.get('params') or [] self.log.debug("RPC(%s) call by '%s' %s", proto_id, req.authname, method_name) try : result = (XMLRPCSystem(self.env).get_method(method_name)(req, args))[0] if isinstance(result, GeneratorType): result = list(result) except (TracError, PermissionError, ResourceNotFound), e: raise except Exception: e, tb = sys.exc_info()[-2:] self.log.error("RPC(%s) [%s] Exception caught while calling " "%s(*%r) by %s%s", proto_id, req.remote_addr, method_name, args, req.authname, exception_to_unicode(e, traceback=True)) raise ServiceException(e), None, tb else : protocol.send_rpc_result(req, result)
def send_rpc_result(self, req, result): """Send JSON-RPC response back to the caller.""" rpcreq = req.rpc r_id = rpcreq.get('id') try: if rpcreq.get('method') == 'system.multicall': # Custom multicall args = (rpcreq.get('params') or [[]])[0] mcresults = [self._json_result( isinstance(value, Exception) and \ value or value[0], \ sig.get('id') or r_id) \ for sig, value in izip(args, result)] response = self._json_result(mcresults, r_id) else: response = self._json_result(result, r_id) try: # JSON encoding self.log.debug("RPC(json) result: %s" % repr(response)) response = json.dumps(response, cls=TracRpcJSONEncoder) except Exception, e: response = json.dumps(self._json_error(e, r_id=r_id), cls=TracRpcJSONEncoder) except Exception, e: self.log.error("RPC(json) error %s" % exception_to_unicode(e, traceback=True)) response = json.dumps(self._json_error(e, r_id=r_id), cls=TracRpcJSONEncoder)
def parse_rpc_request(self, req, content_type): """ Parse JSON-RPC requests""" if not json: self.log.debug("RPC(json) call ignored (not available).") raise JsonProtocolException("Error: JSON-RPC not available.\n") try: data = json.load(req, cls=TracRpcJSONDecoder) except Exception, e: self.log.warning("RPC(json) decode error: %s", exception_to_unicode(e)) raise JsonProtocolException(e, -32700)
def parse_rpc_request(self, req, content_type): """ Parse JSON-RPC requests""" if not json: self.log.debug("RPC(json) call ignored (not available).") raise JsonProtocolException("Error: JSON-RPC not available.\n") try: data = json.load(req, cls=TracRpcJSONDecoder) self.log.info("RPC(json) JSON-RPC request ID : %s.", data.get('id')) if data.get('method') == 'system.multicall': # Prepare for multicall self.log.debug("RPC(json) Multicall request %s", data) params = data.get('params', []) for signature in params : signature['methodName'] = signature.get('method', '') data['params'] = [params] return data except Exception, e: # Abort with exception - no data can be read self.log.error("RPC(json) decode error %s", exception_to_unicode(e, traceback=True)) raise JsonProtocolException(e, -32700)
def parse_rpc_request(self, req, content_type): """ Parse JSON-RPC requests""" if not json: self.log.debug("RPC(json) call ignored (not available).") raise JsonProtocolException("Error: JSON-RPC not available.\n") try: data = json.load(req, cls=TracRpcJSONDecoder) self.log.info("RPC(json) JSON-RPC request ID : %s.", data.get('id')) if data.get('method') == 'system.multicall': # Prepare for multicall self.log.debug("RPC(json) Multicall request %s", data) params = data.get('params', []) for signature in params: signature['methodName'] = signature.get('method', '') data['params'] = [params] return data except Exception, e: # Abort with exception - no data can be read self.log.error("RPC(json) decode error %s", exception_to_unicode(e, traceback=True)) raise JsonProtocolException(e, -32700)
except Exception: e, tb = sys.exc_info()[-2:] self.log.error( "RPC(%s) [%s] Exception caught while calling " "%s(*%r) by %s%s", proto_id, req.remote_addr, method_name, args, req.authname, exception_to_unicode(e, traceback=True)) raise ServiceException(e), None, tb else: protocol.send_rpc_result(req, result) except RequestDone: raise except (TracError, PermissionError, ResourceNotFound), e: if type(e) is not ServiceException: self.log.warning("RPC(%s) [%s] %s", proto_id, req.remote_addr, exception_to_unicode(e)) try: protocol.send_rpc_error(req, e) except RequestDone: raise except Exception, e: self.log.exception("RPC(%s) Unhandled protocol error", proto_id) self._send_unknown_error(req, e) except Exception, e: self.log.exception("RPC(%s) Unhandled protocol error", proto_id) self._send_unknown_error(req, e) def _send_unknown_error(self, req, e): """Last recourse if protocol cannot handle the RPC request | error""" method_name = req.rpc and req.rpc.get('method') or '(undefined)'
class JsonRpcProtocol(Component): r""" Example `POST` request using `curl` with `Content-Type` header and body: {{{ user: ~ > cat body.json {"params": ["WikiStart"], "method": "wiki.getPage", "id": 123} user: ~ > curl -H "Content-Type: application/json" --data @body.json ${req.abs_href.rpc()} {"id": 123, "error": null, "result": "= Welcome to.... }}} Implementation details: * JSON-RPC has no formalized type system, so a class-hint system is used for input and output of non-standard types: * `{"__jsonclass__": ["datetime", "YYYY-MM-DDTHH:MM:SS"]} => DateTime (UTC)` * `{"__jsonclass__": ["binary", "<base64-encoded>"]} => Binary` * `"id"` is optional, and any marker value received with a request is returned with the response. """ implements(IRPCProtocol) # IRPCProtocol methods def rpc_info(self): return ('JSON-RPC', prepare_docs(self.__doc__)) def rpc_match(self): yield ('rpc', 'application/json') # Legacy path - provided for backwards compatibility: yield ('jsonrpc', 'application/json') def parse_rpc_request(self, req, content_type): """ Parse JSON-RPC requests""" if not json: self.log.debug("RPC(json) call ignored (not available).") raise JsonProtocolException("Error: JSON-RPC not available.\n") try: data = json.load(req, cls=TracRpcJSONDecoder) except Exception, e: self.log.warning("RPC(json) decode error: %s", exception_to_unicode(e)) raise JsonProtocolException(e, -32700) if not isinstance(data, dict): self.log.warning("RPC(json) decode error (not a dict)") raise JsonProtocolException('JSON object is not a dict', -32700) try: self.log.info("RPC(json) JSON-RPC request ID : %s.", data.get('id')) if data.get('method') == 'system.multicall': # Prepare for multicall self.log.debug("RPC(json) Multicall request %s", data) params = data.get('params', []) for signature in params: signature['methodName'] = signature.get('method', '') data['params'] = [params] return data except Exception, e: # Abort with exception - no data can be read self.log.warning("RPC(json) decode error: %s", exception_to_unicode(e)) raise JsonProtocolException(e, -32700)