async def dispatch(request, channel_name=None): req_json = request.json url = request.url channel = channel_name if channel_name is not None \ else StubCollection().conf[ConfigKey.CHANNEL] context = { "url": url, "channel": channel, } try: client_ip = request.remote_addr if request.remote_addr else request.ip Logger.info(f'rest_server_v3d request with {req_json}', DISPATCH_V3D_TAG) Logger.info(f"{client_ip} requested {req_json} on {url}") validate_jsonschema_v3(request=req_json) except GenericJsonRpcServerError as e: response = ExceptionResponse(e, request_id=req_json.get('id', 0)) except Exception as e: response = ExceptionResponse(e, request_id=req_json.get('id', 0)) else: response = await methods.dispatch(req_json, context=context) Logger.info(f'rest_server_v3d with response {response}', DISPATCH_V3D_TAG) return sanic_response.json(response, status=response.http_status, dumps=json.dumps)
async def dispatch(request, channel_name=None): req_json = request.json url = request.url channel = channel_name if channel_name else StubCollection().conf[ ConfigKey.CHANNEL] req_json['method'] = convert_upper_camel_method_to_lower_camel( req_json['method']) if 'params' in req_json and 'message' in req_json[ 'params']: # this will be removed after update. req_json['params'] = req_json['params']['message'] context = {'url': url, 'channel': channel} try: client_ip = request.remote_addr if request.remote_addr else request.ip Logger.info(f'rest_server_node request with {req_json}', DISPATCH_NODE_TAG) Logger.info(f'{client_ip} requested {req_json} on {url}') validate_jsonschema_node(request=req_json) except GenericJsonRpcServerError as e: response = ExceptionResponse(e, request_id=req_json.get('id', 0)) except Exception as e: response = ExceptionResponse(e, request_id=req_json.get('id', 0)) else: response = await methods.dispatch(req_json, context=context) Logger.info(f'rest_server_node with response {response}', DISPATCH_NODE_TAG) return sanic_response.json(response, status=response.http_status, dumps=json.dumps)
async def dispatch(request: 'SanicRequest', channel_name: str = ""): req_json = request.json url = request.url channel = channel_name if channel_name else StubCollection().conf[ConfigKey.CHANNEL] context = { "url": url, "channel": channel, } response: Union[Response, DictResponse, BatchResponse] try: client_ip = request.remote_addr if request.remote_addr else request.ip Logger.info(f'rest_server_v3d request with {req_json}', DISPATCH_V3D_TAG) Logger.info(f"{client_ip} requested {req_json} on {url}") validate_jsonschema_v3(request=req_json) except GenericJsonRpcServerError as e: response = ApiErrorResponse(id=req_json.get('id', 0), code=e.code, message=str(e), http_status=e.http_status, debug=False) except Exception as e: response = ExceptionResponse(e, id=req_json.get('id', 0), debug=False) else: response = await async_dispatch(request.body, methods, context=context) Logger.info(f'rest_server_v3d with response {response}', DISPATCH_V3D_TAG) return sanic_response.json(response.deserialized(), status=response.http_status, dumps=json.dumps)
async def dispatch(request: 'SanicRequest'): req = request.json url = request.url context = {"url": url} response: Union[Response, DictResponse, BatchResponse] try: client_ip = request.remote_addr if request.remote_addr else request.ip Logger.info(f'rest_server_v2 request with {req}', DISPATCH_V2_TAG) Logger.info(f"{client_ip} requested {req} on {url}") validate_jsonschema_v2(request=req) except GenericJsonRpcServerError as e: Logger.debug(f'dispatch() validate exception = {e}') response = ExceptionResponse(e, id=req.get('id', 0), debug=False) else: response = await async_dispatch(request.body, methods, context=context) Logger.info(f'rest_server_v2 response with {response}', DISPATCH_V2_TAG) return sanic_response.json(response.deserialized(), status=response.http_status, dumps=json.dumps)
async def dispatch(request: 'SanicRequest', channel_name: str = ""): """Node dispatch FIXME : this dispatch is not considered to support batch request. If you want to support batch request, need to update code that using req_json. """ req_json = request.json url = request.url channel = channel_name if channel_name else StubCollection().conf[ ConfigKey.CHANNEL] req_json['method'] = convert_upper_camel_method_to_lower_camel( req_json['method']) if 'params' in req_json and 'message' in req_json[ 'params']: # this will be removed after update. req_json['params'] = req_json['params']['message'] context = {'url': url, 'channel': channel} response: Union[Response, DictResponse, BatchResponse] try: client_ip = request.remote_addr if request.remote_addr else request.ip Logger.info(f'rest_server_node request with {req_json}', DISPATCH_NODE_TAG) Logger.info(f'{client_ip} requested {req_json} on {url}') validate_jsonschema_node(request=req_json) except GenericJsonRpcServerError as e: response = ExceptionResponse(e, id=req_json.get('id', 0), debug=False) except Exception as e: response = ExceptionResponse(e, id=req_json.get('id', 0), debug=False) else: response = await async_dispatch(request.body, methods, context=context) Logger.info(f'rest_server_node with response {response}', DISPATCH_NODE_TAG) return sanic_response.json(response.deserialized(), status=response.http_status, dumps=json.dumps)
def test_jsonrpcservererror(self): response = ExceptionResponse(InvalidParams(), None) self.assertEqual( { 'jsonrpc': '2.0', 'error': { 'code': -32602, 'message': 'Invalid params' }, 'id': None }, response)
def test_non_jsonrpcservererror(self): response = ExceptionResponse(ValueError(), None) self.assertEqual( { 'jsonrpc': '2.0', 'error': { 'code': -32000, 'message': 'Server error' }, 'id': None }, response)
def test_with_id(self): response = ExceptionResponse(InvalidParams(), 1) self.assertEqual( { 'jsonrpc': '2.0', 'error': { 'code': -32602, 'message': 'Invalid params' }, 'id': 1 }, response)
def test_jsonrpcservererror(self): response = ExceptionResponse(InvalidParams(), None) self.assertEqual( { "jsonrpc": "2.0", "error": { "code": -32602, "message": "Invalid params" }, "id": None, }, response, )
def test_non_jsonrpcservererror(self): response = ExceptionResponse(ValueError(), None) self.assertEqual( { "jsonrpc": "2.0", "error": { "code": -32000, "message": "Server error" }, "id": None, }, response, )
def test_with_id(self): response = ExceptionResponse(InvalidParams(), 1) self.assertEqual( { "jsonrpc": "2.0", "error": { "code": -32602, "message": "Invalid params" }, "id": 1, }, response, )
def test_with_data(self): config.debug = True response = ExceptionResponse(InvalidParams('Password missing'), 1) self.assertEqual( { 'jsonrpc': '2.0', 'error': { 'code': -32602, 'message': 'Invalid params', 'data': 'Password missing' }, 'id': 1 }, response)
async def dispatch(request, channel_name=None): req_json = request.json url = request.url # channel = channel_name if channel_name is not None \ # else StubCollection().conf[ConfigKey.CHANNEL] context = { "url": url, "channel": 'icon_dex', } try: client_ip = request.remote_addr if request.remote_addr else request.ip Logger.info(f'rest_server_v3 request with {req_json}', DISPATCH_V3_TAG) Logger.info(f"{client_ip} requested {req_json} on {url}") validate_jsonschema_v3(request=req_json) except GenericJsonRpcServerError as e: response = ExceptionResponse(e, id=req_json.get('id', 0), debug=False) except Exception as e: response = ExceptionResponse(e, id=req_json.get('id', 0), debug=False) else: if "params" in req_json: req_json["params"]["context"] = context else: req_json["params"] = {"context": context} response: DictResponse = await async_dispatch( json.dumps(req_json), methods) Logger.info(f'rest_server_v3 with response {response}', DISPATCH_V3_TAG) return sanic_response.json(response.deserialized(), status=response.http_status, dumps=json.dumps)
def test_with_data(self): response = ExceptionResponse(InvalidParams("Password missing"), 1, debug=True) self.assertEqual( { "jsonrpc": "2.0", "error": { "code": -32602, "message": "Invalid params", "data": "Password missing", }, "id": 1, }, response, )
def dispatch(request_raw: str) -> JsonRpcResponse: """Dispatch a request (or requests) to methods. This is the main public method, it's the only one with optional params, and the only one that can be configured with a config file/env vars. Args: request_raw: The incoming request string. Returns: A Response. """ methods = global_methods try: request_json: JSON = json.loads(request_raw) validator.validate(request_json) except JSONDecodeError as exc: return InvalidJSONResponse(data=str(exc), debug=True) except ValidationError: return InvalidJSONRPCResponse(data=None) request = create_requests(request_json, convert_camel_case=False) assert isinstance(request, Request) try: method_name = request.method method = methods.items[method_name] with sentry_sdk.start_span(op="rpc", transaction="rpc." + method_name) as span: span.set_data("args", request.args) span.set_data("kwargs", request.kwargs) result = call(method, *request.args, **request.kwargs) return SuccessResponse(result=result, id=request.id) except Exception as exc: traceback.print_exc() sys.stdout.flush() sys.stderr.flush() return ExceptionResponse(exc, id=request.id, debug=True)
def process(self, methods): """Calls the method and returns a Response object.""" error = None try: result = _call(methods, self.method_name, self.args, self.kwargs) # Catch any JsonRpcServerError raised (Invalid Request, etc) except JsonRpcServerError as e: error = e # Catch uncaught exceptions and respond with ServerError except Exception as e: # pylint: disable=broad-except # Log the uncaught exception logger.exception(e) error = e # pylint: disable=redefined-variable-type if error: if self.is_notification and not self.notification_errors: return NotificationResponse() else: return ExceptionResponse(error, self.request_id) # Success if self.is_notification: return NotificationResponse() else: return RequestResponse(self.request_id, result)
async def dispatch(request): req = request.json url = request.url context = {"url": url} if "node_" in req["method"]: return sanic_response.text("no support method!") try: client_ip = request.remote_addr if request.remote_addr else request.ip Logger.info(f'rest_server_v2 request with {req}', DISPATCH_V2_TAG) Logger.info(f"{client_ip} requested {req} on {url}") validate_jsonschema_v2(request=req) except exception.GenericJsonRpcServerError as e: response = ExceptionResponse(e, request_id=req.get('id', 0)) else: response = await methods.dispatch(req, context=context) Logger.info(f'rest_server_v2 response with {response}', DISPATCH_V2_TAG) return sanic_response.json(response, status=response.http_status, dumps=json.dumps)
def test_exception_response_debug_enabled(): response = ExceptionResponse(ValueError("There was an error"), id=1, debug=True) assert ( str(response) == '{"jsonrpc": "2.0", "error": {"code": -32000, "message": "Server error", "data": "ValueError: There was an error"}, "id": 1}' )
def test_exception_response_with_id(): assert ( str(ExceptionResponse(ValueError("foo"), id=1, debug=True)) == '{"jsonrpc": "2.0", "error": {"code": -32000, "message": "Server error", "data": "ValueError: foo"}, "id": 1}' )
def dispatch(methods, request): """Dispatch JSON-RPC requests to a collection of methods:: r = dispatch([cat, dog], {'jsonrpc': '2.0', 'method': 'cat', 'id': 1}) The first parameter can be either: - A *list* of methods, or - A *dictionary* of name:method pairs. When using a **list**, the methods must be identifiable by a ``__name__`` attribute. Functions already have a ``__name__`` attribute:: >>> def cat(): ... return 'meow' ... >>> cat.__name__ 'cat' >>> dispatch([cat], ...) Lambdas require setting it:: >>> cat = lambda: 'meow' >>> cat.__name__ = 'cat' >>> dispatch([cat], ...) As do partials:: >>> max_ten = partial(min, 10) >>> max_ten.__name__ = 'max_ten' >>> dispatch([max_ten], ...) Alternatively, use a **dictionary**:: >>> dispatch({'cat': cat, 'max_ten': max_ten}, ...) The :mod:`methods` module also gives nice and easy ways to build the collection of methods. :param methods: Collection of methods to dispatch to. :param request: JSON-RPC request - can be a JSON-serializable object, or a string. Strings must be valid json (use double quotes!). :returns: A :mod:`response` object. """ # Process the request response = None try: # Log the request request_log.info(request) # If the request is a string, convert it to a dict first if isinstance(request, string_types): request = _string_to_dict(request) # Batch requests if isinstance(request, list): # An empty list is invalid if len(request) == 0: raise InvalidRequest() # Process each request response = BatchResponse() for r in request: try: req = Request(r) except InvalidRequest as e: resp = ExceptionResponse(e, None) else: resp = req.process(methods) response.append(resp) # Remove Notification responses response = BatchResponse([ r for r in response if not isinstance(r, NotificationResponse) ]) # "Nothing is returned for all notification batches" if not response: response = NotificationResponse() # pylint: disable=redefined-variable-type # Single request else: response = Request(request).process(methods) except JsonRpcServerError as e: response = ExceptionResponse(e, None) # Batch requests can have mixed results, just return 200 http_status = 200 if isinstance(request, list) else response.http_status # Log the response response_log.info(str(response), extra={ 'http_code': http_status, 'http_reason': HTTP_STATUS_CODES[http_status] }) return response