def test_named_parameters(self): def subtract(**kwargs): return kwargs['minuend'] - kwargs['subtrahend'] res = dispatch( [subtract], { "jsonrpc": "2.0", "method": "subtract", "params": { "subtrahend": 23, "minuend": 42 }, "id": 3 }) self.assertIsInstance(res, RequestResponse) self.assertEqual({"jsonrpc": "2.0", "result": 19, "id": 3}, res) # Second example res = dispatch( [subtract], { "jsonrpc": "2.0", "method": "subtract", "params": { "minuend": 42, "subtrahend": 23 }, "id": 4 }) self.assertIsInstance(res, RequestResponse) self.assertEqual({"jsonrpc": "2.0", "result": 19, "id": 4}, res)
def test_positional_parameters(self): def subtract(minuend, subtrahend): return minuend - subtrahend res = dispatch( [subtract], { "jsonrpc": "2.0", "method": "subtract", "params": [42, 23], "id": 1 }, ) self.assertIsInstance(res, RequestResponse) self.assertEqual({"jsonrpc": "2.0", "result": 19, "id": 1}, res) # Second example res = dispatch( [subtract], { "jsonrpc": "2.0", "method": "subtract", "params": [23, 42], "id": 2 }, ) self.assertIsInstance(res, RequestResponse) self.assertEqual({"jsonrpc": "2.0", "result": -19, "id": 2}, res)
def notification(self): methods = {'update': lambda: None, 'foobar': lambda: None} res = dispatch( methods, {"jsonrpc": "2.0", "method": "update", "params": [1, 2, 3, 4, 5]}) self.assertIsInstance(res, NotificationResponse) res = dispatch(methods, {"jsonrpc": "2.0", "method": "foobar"}) self.assertIsInstance(res, NotificationResponse)
def notification(self): methods = {'update': lambda: None, 'foobar': lambda: None} req = dispatch( methods, {"jsonrpc": "2.0", "method": "update", "params": [1, 2, 3, 4, 5]}) self.assertIsInstance(req, NotificationResponse) req = dispatch(methods, {"jsonrpc": "2.0", "method": "foobar"}) self.assertIsInstance(req, NotificationResponse)
def test_positional_parameters(self): def subtract(minuend, subtrahend): return minuend - subtrahend req = dispatch( [subtract], {"jsonrpc": "2.0", "method": "subtract", "params": [42, 23], "id": 1}) self.assertIsInstance(req, RequestResponse) self.assertEqual({'jsonrpc': '2.0', 'result': 19, 'id': 1}, req) # Second example req = dispatch( [subtract], {"jsonrpc": "2.0", "method": "subtract", "params": [23, 42], "id": 2}) self.assertIsInstance(req, RequestResponse) self.assertEqual({'jsonrpc': '2.0', 'result': -19, 'id': 2}, req)
def test_named_parameters(self): def subtract(**kwargs): return kwargs['minuend'] - kwargs['subtrahend'] req = dispatch( [subtract], {"jsonrpc": "2.0", "method": "subtract", "params": {"subtrahend": 23, "minuend": 42}, "id": 3}) self.assertIsInstance(req, RequestResponse) self.assertEqual({"jsonrpc": "2.0", "result": 19, "id": 3}, req) # Second example req = dispatch( [subtract], {"jsonrpc": "2.0", "method": "subtract", "params": {"minuend": 42, "subtrahend": 23}, "id": 4}) self.assertIsInstance(req, RequestResponse) self.assertEqual({"jsonrpc": "2.0", "result": 19, "id": 4}, req)
def test_multiple_invalid_requests(self): res = dispatch([foo], [1, 2, 3]) self.assertIsInstance(res, BatchResponse) self.assertEqual([{ 'jsonrpc': '2.0', 'error': { 'code': -32600, 'message': 'Invalid Request', 'data': '1 is not valid under any of the given schemas' }, 'id': None }, { 'jsonrpc': '2.0', 'error': { 'code': -32600, 'message': 'Invalid Request', 'data': '2 is not valid under any of the given schemas' }, 'id': None }, { 'jsonrpc': '2.0', 'error': { 'code': -32600, 'message': 'Invalid Request', 'data': '3 is not valid under any of the given schemas' }, 'id': None }], res)
def test_multiple_invalid_requests(self): req = dispatch([foo], [1, 2, 3]) self.assertIsInstance(req, BatchResponse) self.assertEqual( [{'jsonrpc': '2.0', 'error': {'code': -32600, 'message': 'Invalid Request', 'data': '1 is not valid under any of the given schemas'}, 'id': None}, {'jsonrpc': '2.0', 'error': {'code': -32600, 'message': 'Invalid Request', 'data': '2 is not valid under any of the given schemas'}, 'id': None}, {'jsonrpc': '2.0', 'error': {'code': -32600, 'message': 'Invalid Request', 'data': '3 is not valid under any of the given schemas'}, 'id': None}], req)
def test_invalid_json(self): req = dispatch( [foo], '[{"jsonrpc": "2.0", "method": "sum", "params": [1,2,4], "id": "1"}, {"jsonrpc": "2.0", "method"]') self.assertIsInstance(req, ErrorResponse) self.assertEqual( {'jsonrpc': '2.0', 'error': {'code': -32700, 'message': 'Parse error'}, 'id': None}, req)
def test_batch_with_context(self): def foo_with_context(context=None): self.assertEqual(FOO, context) return 'bar' batch_requests = [ {'jsonrpc': '2.0', 'method': 'foo_with_context'}, {'jsonrpc': '2.0', 'method': 'foo_with_context'}] res = dispatch([foo_with_context], batch_requests, context=FOO)
def test_invalid_json(self): res = dispatch( [foo], '[{"jsonrpc": "2.0", "method": "sum", "params": [1,2,4], "id": "1"}, {"jsonrpc": "2.0", "method"]') self.assertIsInstance(res, ErrorResponse) self.assertEqual( {'jsonrpc': '2.0', 'error': {'code': -32700, 'message': 'Parse error'}, 'id': None}, res)
def handle(self): logger = self.server.logger poller = select.poll() poller.register(self.request.fileno(), select.POLLIN | select.POLLPRI | select.POLLERR) while True: if poller.poll(500): self.data = self.request.recv(8192).decode() if not self.data: break while self.data[-1] != '\n': self.data += self.request.recv(8192).decode() self.data = self.data.strip() else: if self.server.STOP_EVENT.is_set(): break else: continue lock_acquired = False try: if self.server._request_cb is not None: self.server._request_cb(self.data) if self.server._client_lock.acquire(True, 10): lock_acquired = True logger.debug("Dispatching %s" % (self.data)) response = dispatcher.dispatch(self.server._methods, self.data) logger.debug("Responding with: %s" % response.json_debug) else: # Send a time out response r = Request(self.data) logger.debug("Timed out waiting for lock with request = %s" % (self.data)) request_id = r.request_id if hasattr(r, 'request_id') else None response = ErrorResponse(http_status=HTTP_STATUS_CODES[408], request_id=request_id, code=-32000, # Server error message="Timed out waiting for lock") except Exception as e: if logger is not None: logger.exception(e) finally: if lock_acquired: self.server._client_lock.release() try: json_str = json.dumps(response.json_debug) + "\n" msg = json_str.encode() logger.debug("Message length = %d" % len(msg)) self.request.sendall(msg) except BrokenPipeError: break except Exception as e: if logger is not None: logger.exception(e)
def test_all_notifications(self): res = dispatch([foo], [{ 'jsonrpc': '2.0', 'method': 'notify_sum', 'params': [1, 2, 4] }, { 'jsonrpc': '2.0', 'method': 'notify_hello', 'params': [7] }]) self.assertIsInstance(res, NotificationResponse)
def test_empty_array(self): res = dispatch([foo], []) self.assertIsInstance(res, ErrorResponse) self.assertEqual( { 'jsonrpc': '2.0', 'error': { 'code': -32600, 'message': 'Invalid Request' }, 'id': None }, res)
def test_with_context(self): def foo_with_context(context=None): self.assertEqual(FOO, context) return "bar" res = dispatch( [foo_with_context], { "jsonrpc": "2.0", "method": "foo_with_context" }, context=FOO, )
def test_empty_array(self): res = dispatch([foo], []) self.assertIsInstance(res, ErrorResponse) self.assertEqual( { "jsonrpc": "2.0", "error": { "code": -32600, "message": "Invalid Request" }, "id": None, }, res, )
def test_invalid_request(self): res = dispatch([foo], [1]) self.assertIsInstance(res, BatchResponse) self.assertEqual( [{ "jsonrpc": "2.0", "error": { "code": -32600, "message": "Invalid Request", "data": "1 is not valid under any of the given schemas", }, "id": None, }], res, )
def test_batch_with_context(self): def foo_with_context(context=None): self.assertEqual(FOO, context) return "bar" batch_requests = [ { "jsonrpc": "2.0", "method": "foo_with_context" }, { "jsonrpc": "2.0", "method": "foo_with_context" }, ] res = dispatch([foo_with_context], batch_requests, context=FOO)
def test_invalid_json(self): res = dispatch( [foo], '[{"jsonrpc": "2.0", "method": "sum", "params": [1,2,4], "id": "1"}, {"jsonrpc": "2.0", "method"]', ) self.assertIsInstance(res, ErrorResponse) self.assertEqual( { "jsonrpc": "2.0", "error": { "code": -32700, "message": "Parse error" }, "id": None, }, res, )
def test_all_notifications(self): res = dispatch( [foo], [ { "jsonrpc": "2.0", "method": "notify_sum", "params": [1, 2, 4] }, { "jsonrpc": "2.0", "method": "notify_hello", "params": [7] }, ], ) self.assertIsInstance(res, NotificationResponse)
def test_mixed_requests_and_notifications(self): res = dispatch( {'sum': lambda *args: sum(args), 'notify_hello': lambda *args: 19, 'subtract': lambda *args: args[0] - sum(args[1:]), 'get_data': lambda: ['hello', 5]}, [{'jsonrpc': '2.0', 'method': 'sum', 'params': [1, 2, 4], 'id': '1'}, {'jsonrpc': '2.0', 'method': 'notify_hello', 'params': [7]}, {'jsonrpc': '2.0', 'method': 'subtract', 'params': [42, 23], 'id': '2'}, {'foo': 'boo'}, {'jsonrpc': '2.0', 'method': 'foo.get', 'params': {'name': 'myself'}, 'id': '5'}, {'jsonrpc': '2.0', 'method': 'get_data', 'id': '9'}]) self.assertIsInstance(res, BatchResponse) self.assertEqual( [{'jsonrpc': '2.0', 'result': 7, 'id': '1'}, {'jsonrpc': '2.0', 'result': 19, 'id': '2'}, {'jsonrpc': '2.0', 'error': {'code': -32600, 'message': 'Invalid Request', 'data': "{'foo': 'boo'} is not valid under any of the given schemas"}, 'id': None}, {'jsonrpc': '2.0', 'error': {'code': -32601, 'message': 'Method not found', 'data': 'foo.get'}, 'id': '5'}, {'jsonrpc': '2.0', 'result': ['hello', 5], 'id': '9'}], res) # Response should not the notifications self.assertEqual(5, len(res))
def test_mixed_requests_and_notifications(self): res = dispatch( { "sum": lambda *args: sum(args), "notify_hello": lambda *args: 19, "subtract": lambda *args: args[0] - sum(args[1:]), "get_data": lambda: ["hello", 5], }, [ { "jsonrpc": "2.0", "method": "sum", "params": [1, 2, 4], "id": "1" }, { "jsonrpc": "2.0", "method": "notify_hello", "params": [7] }, { "jsonrpc": "2.0", "method": "subtract", "params": [42, 23], "id": "2" }, { "foo": "boo" }, { "jsonrpc": "2.0", "method": "foo.get", "params": { "name": "myself" }, "id": "5", }, { "jsonrpc": "2.0", "method": "get_data", "id": "9" }, ], ) self.assertIsInstance(res, BatchResponse) self.assertEqual( [ { "jsonrpc": "2.0", "result": 7, "id": "1" }, { "jsonrpc": "2.0", "result": 19, "id": "2" }, { "jsonrpc": "2.0", "error": { "code": -32600, "message": "Invalid Request", "data": "{'foo': 'boo'} is not valid under any of the given schemas", }, "id": None, }, { "jsonrpc": "2.0", "error": { "code": -32601, "message": "Method not found", "data": "foo.get", }, "id": "5", }, { "jsonrpc": "2.0", "result": ["hello", 5], "id": "9" }, ], res, ) # Response should not the notifications self.assertEqual(5, len(res))
def handle(self): logger = self.server.logger poller = select.poll() poller.register(self.request.fileno(), select.POLLIN | select.POLLPRI | select.POLLERR) while True: if poller.poll(500): self.data = self.request.recv(8192).decode() if not self.data: break while self.data[-1] != '\n': self.data += self.request.recv(8192).decode() self.data = self.data.strip() else: if self.server.STOP_EVENT.is_set(): break else: continue lock_acquired = False try: if self.server._request_cb is not None: self.server._request_cb(self.data) if self.server._client_lock.acquire(True, 10): lock_acquired = True logger.debug("Dispatching %s" % (self.data)) response = dispatcher.dispatch(self.server._methods, self.data) logger.debug("Responding with: %s" % response.json_debug) else: # Send a time out response r = Request(self.data) logger.debug( "Timed out waiting for lock with request = %s" % (self.data)) request_id = r.request_id if hasattr( r, 'request_id') else None response = ErrorResponse( http_status=HTTP_STATUS_CODES[408], request_id=request_id, code=-32000, # Server error message="Timed out waiting for lock") except Exception as e: if logger is not None: logger.exception(e) finally: if lock_acquired: self.server._client_lock.release() try: json_str = json.dumps(response.json_debug) + "\n" msg = json_str.encode() logger.debug("Message length = %d" % len(msg)) self.request.sendall(msg) except BrokenPipeError: break except Exception as e: if logger is not None: logger.exception(e)
def test_dispatch_with_global_methods(): global_methods.items = {} global_methods.add(ping) response = dispatch('{"jsonrpc": "2.0", "method": "ping", "id": 1}') assert response.result == "pong"
def test_dispatch_basic_logging(): response = dispatch( '{"jsonrpc": "2.0", "method": "ping", "id": 1}', Methods(ping), basic_logging=True, )
def test_errors_enabled(self): config.notification_errors = True res = dispatch([foo], {'jsonrpc': '2.0', 'method': 'non_existant'}) self.assertIsInstance(res, ErrorResponse)
def test_dispatch(): response = dispatch('{"jsonrpc": "2.0", "method": "ping", "id": 1}', Methods(ping)) assert response.result == "pong"
def test_parse_error(self): res = dispatch([foo], '{"jsonrpc') self.assertIsInstance(res, ErrorResponse) self.assertEqual('Parse error', res['error']['message'])
def test_errors_disabled(self): res = dispatch([foo], {'jsonrpc': '2.0', 'method': 'non_existant'}) self.assertIsInstance(res, NotificationResponse)
def test_invalid_str(self): # Single quotes around identifiers are invalid! res = dispatch([foo], "{'jsonrpc': '2.0', 'method': 'foo'}") self.assertIsInstance(res, ErrorResponse)
def test_object(self): res = dispatch([foo], {'jsonrpc': '2.0', 'method': 'foo'}) self.assertIsInstance(res, NotificationResponse)
def test(self): res = dispatch([foo], '{"jsonrpc": "2.0", "method": "foo"}') self.assertIsInstance(res, NotificationResponse)
def test_parse_error(self): res = dispatch([foo], '{"jsonrpc') self.assertIsInstance(res, ErrorResponse) self.assertEqual("Parse error", res["error"]["message"])
def test_errors_disabled(self): res = dispatch([foo], {"jsonrpc": "2.0", "method": "non_existant"}) self.assertIsInstance(res, NotificationResponse)
def test_errors_enabled(self): config.notification_errors = True res = dispatch([foo], {"jsonrpc": "2.0", "method": "non_existant"}) self.assertIsInstance(res, ErrorResponse)
def test(self): res = dispatch([foo], {'jsonrpc': '2.0', 'method': 'foo', 'id': 1}) self.assertIsInstance(res, RequestResponse) self.assertEqual('bar', res['result']) self.assertEqual(1, res['id'])