Example #1
0
 def test_batch_invalid(self):
     req = json.dumps([{
         'jsonrpc': '2.0',
         'method': 'jsonrpc.echoNotFound',
         'params': [],
         'id': text_type(uuid.uuid4())
     }, {
         'jsonrpc': '2.0',
         'method': 'jsonrpc.echo',
         'params': [],
         'id': text_type(uuid.uuid4())
     }, {
         'jsonrpc': '2.0',
         'method': 'jsonrpc.echoNotFound',
         'params': [],
         'id': text_type(uuid.uuid4())
     }])
     resp = self._call(req)
     self.assertTrue(len(resp) == 3)
     self.assertNotIn('result', resp[0])
     self.assertIn('error', resp[0])
     self.assertEqual('Hello Flask JSON-RPC', resp[1]['result'])
     self.assertNotIn('error', resp[1])
     self.assertNotIn('result', resp[2])
     self.assertIn('error', resp[2])
Example #2
0
 def test_batch_call(self):
     req = json.dumps(
         [
             {"jsonrpc": "2.0", "method": "jsonrpc.echo", "id": text_type(uuid.uuid4())},
             {"jsonrpc": "2.0", "method": "jsonrpc.echo", "params": ["Flask"], "id": text_type(uuid.uuid4())},
             {"jsonrpc": "2.0", "method": "jsonrpc.echo", "params": None, "id": text_type(uuid.uuid4())},
             {"jsonrpc": "2.0", "method": "jsonrpc.echo", "params": [], "id": text_type(uuid.uuid4())},
             {"jsonrpc": "2.0", "method": "jsonrpc.echo", "params": {}, "id": text_type(uuid.uuid4())},
             {"jsonrpc": "2.0", "method": "jsonrpc.echo", "params": ["フラスコ"], "id": text_type(uuid.uuid4())},
         ]
     )
     resp = self._call(req)
     self.assertTrue(len(resp) == 6)
     self.assertEqual("Hello Flask JSON-RPC", resp[0]["result"])
     self.assertNotIn("error", resp[0])
     self.assertEqual("Hello Flask", resp[1]["result"])
     self.assertNotIn("error", resp[1])
     self.assertEqual("Hello Flask JSON-RPC", resp[2]["result"])
     self.assertNotIn("error", resp[2])
     self.assertEqual("Hello Flask JSON-RPC", resp[3]["result"])
     self.assertNotIn("error", resp[3])
     self.assertEqual("Hello Flask JSON-RPC", resp[4]["result"])
     self.assertNotIn("error", resp[4])
     self.assertEqual("Hello フラスコ", resp[5]["result"])
     self.assertNotIn("error", resp[5])
Example #3
0
def extract_raw_data_request(request):
    def _extract_raw_data_request(request):
        if request.method == 'GET':
            return request.query_string
        elif request.method == 'POST':
            if request.data:
                return request.data
            elif request.form.to_dict():
                return list(request.form.to_dict().keys())[0]
        return b('')
    raw_data = _extract_raw_data_request(request)

    tried_encodings = []

    # Try charset from content-type
    encoding = request.charset if request.charset else 'utf-8'

    if encoding:
        try:
            return text_type(raw_data, encoding)
        except UnicodeError:
            tried_encodings.append(encoding)

    # Fall back:
    try:
        return text_type(raw_data, encoding, errors='replace')
    except TypeError:
        return raw_data
Example #4
0
def extract_raw_data_request(request):
    def _extract_raw_data_request(request):
        if request.method == 'GET':
            return request.query_string
        elif request.method == 'POST':
            if request.data:
                return request.data
            elif request.form.to_dict():
                #return list(request.form.to_dict().keys())[0]
                return list(request.form.to_dict().keys())[0] if isinstance(list(request.form.to_dict().keys())[0], bytes) else list(request.form.to_dict().keys())[0].encode()
        return b('')
    raw_data = _extract_raw_data_request(request)

    tried_encodings = []

    # Try charset from content-type
    encoding = request.charset if request.charset else 'utf-8'

    if encoding:
        try:
            return text_type(raw_data, encoding)
        except UnicodeError:
            tried_encodings.append(encoding)

    # Fall back:
    try:
        return text_type(raw_data, encoding, errors='replace')
    except TypeError:
        return raw_data
Example #5
0
 def procedure_desc(self, key):
     M = self.urls[key]
     return {
         'name': M.json_method,
         'summary': M.__doc__,
         'idempotent': M.json_safe,
         'params': [{'type': text_type(Any.kind(t)), 'name': k}
             for k, t in iteritems(M.json_arg_types)],
         'return': {'type': text_type(Any.kind(M.json_return_type))}}
Example #6
0
def _parse_sig(sig, arg_names, validate=False):
    """Parses signatures into a ``OrderedDict`` of paramName => type.
    Numerically-indexed arguments that do not correspond to an argument
    name in python (ie: it takes a variable number of arguments) will be
    keyed as the stringified version of it's index.

        sig           the signature to be parsed
        arg_names     a list of argument names extracted from python source

    Returns a tuple of (method name, types dict, return type)
    """
    d = SIG_RE.match(sig)
    if not d:
        raise ValueError('Invalid method signature {0}'.format(sig))
    d = d.groupdict()
    ret = [(n, Any) for n in arg_names]
    if text_type('args_sig') in d and type(
            d['args_sig']) in string_types and d['args_sig'].strip():
        for i, arg in enumerate(d['args_sig'].strip().split(',')):
            _type_checking_available(sig, validate)
            if text_type('=') in arg:
                if not type(ret) is OrderedDict:
                    ret = OrderedDict(ret)
                dk = KWARG_RE.match(arg)
                if not dk:
                    raise ValueError(
                        'Could not parse arg type {0} in {1}'.format(arg, sig))
                dk = dk.groupdict()
                if not sum([(k in dk and type(dk[k]) in string_types
                             and bool(dk[k].strip()))
                            for k in ('arg_name', 'arg_type')]):
                    raise ValueError('Invalid kwarg value {0} in {1}'.format(
                        arg, sig))
                ret[dk['arg_name']] = _eval_arg_type(dk['arg_type'], None, arg,
                                                     sig)
            else:
                if type(ret) is OrderedDict:
                    raise ValueError(
                        'Positional arguments must occur '
                        'before keyword arguments in {0}'.format(sig))
                if len(ret) < i + 1:
                    ret.append(
                        (text_type(i), _eval_arg_type(arg, None, arg, sig)))
                else:
                    ret[i] = (ret[i][0], _eval_arg_type(arg, None, arg, sig))
    if not type(ret) is OrderedDict:
        ret = OrderedDict(ret)
    return (d['method_name'], ret, (_eval_arg_type(
        d['return_sig'], Any, 'return', sig) if d['return_sig'] else Any))
 def _make_payload(self, method, params=None, version='1.0', is_notify=False):
     return json.dumps({
         'jsonrpc': version,
         'method': method,
         'params': params if params else [],
         'id': None if is_notify else text_type(uuid.uuid1())
     })
Example #8
0
    def json_rpc_format(self):
        """Return the Exception data in a format for JSON-RPC
        """

        error = {
            'name': text_type(self.__class__.__name__),
            'code': self.code,
            'message': '{0}: {1}'.format(text_type(self.__class__.__name__), text_type(self.message)),
            'data': self.data
        }

        if current_app.config['DEBUG']:
            import sys, traceback
            error['stack'] = traceback.format_exc()
            error['executable'] = sys.executable

        return error
Example #9
0
 def test_index(self):
     with self.app:
         r = self.app.get('/api/browse/')
         data = text_type(r.data)
         self.assertEqual(200, r.status_code)
         self.assertIn('Flask JSON-RPC', data)
         self.assertIn('Web browsable API', data)
         self.assertIn('https://github.com/cenobites/flask-jsonrpc', data)
    def json_rpc_format(self):
        """Return the Exception data in a format for JSON-RPC
        """

        error = {
            'name': text_type(self.__class__.__name__),
            'code': self.code,
            'message': '{0}'.format(text_type(self.message)),
            'data': self.data
        }

        if current_app.config['DEBUG']:
            import sys, traceback
            error['stack'] = str(traceback.format_exc()).replace("\"","'")
            error['executable'] = sys.executable

        return error
Example #11
0
 def __init__(self,
              service_url,
              service_name=None,
              version=JSONRPC_VERSION_DEFAULT,
              headers=None):
     self.version = text_type(version)
     self.service_url = service_url
     self.service_name = service_name
     self.headers = headers or self.DEFAULT_HEADERS
Example #12
0
 def _make_payload(self, method, params=None, version="1.0", is_notify=False):
     return json.dumps(
         {
             "jsonrpc": version,
             "method": method,
             "params": params if params else [],
             "id": None if is_notify else text_type(uuid.uuid4()),
         }
     )
Example #13
0
def _parse_sig(sig, arg_names, validate=False):
    """Parses signatures into a ``OrderedDict`` of paramName => type.
    Numerically-indexed arguments that do not correspond to an argument
    name in python (ie: it takes a variable number of arguments) will be
    keyed as the stringified version of it's index.

        sig           the signature to be parsed
        arg_names     a list of argument names extracted from python source

    Returns a tuple of (method name, types dict, return type)
    """
    d = SIG_RE.match(sig)
    if not d:
        raise ValueError('Invalid method signature {0}'.format(sig))
    d = d.groupdict()
    ret = [(n, Any) for n in arg_names]
    if text_type('args_sig') in d and type(d['args_sig']) in string_types and d['args_sig'].strip():
        for i, arg in enumerate(d['args_sig'].strip().split(',')):
            _type_checking_available(sig, validate)
            if text_type('=') in arg:
                if not type(ret) is OrderedDict:
                    ret = OrderedDict(ret)
                dk = KWARG_RE.match(arg)
                if not dk:
                    raise ValueError('Could not parse arg type {0} in {1}'.format(arg, sig))
                dk = dk.groupdict()
                if not sum([(k in dk and type(dk[k]) in string_types and bool(dk[k].strip()))
                        for k in ('arg_name', 'arg_type')]):
                    raise ValueError('Invalid kwarg value {0} in {1}'.format(arg, sig))
                ret[dk['arg_name']] = _eval_arg_type(dk['arg_type'], None, arg, sig)
            else:
                if type(ret) is OrderedDict:
                    raise ValueError('Positional arguments must occur '
                                     'before keyword arguments in {0}'.format(sig))
                if len(ret) < i + 1:
                    ret.append((text_type(i), _eval_arg_type(arg, None, arg, sig)))
                else:
                    ret[i] = (ret[i][0], _eval_arg_type(arg, None, arg, sig))
    if not type(ret) is OrderedDict:
        ret = OrderedDict(ret)
    return (d['method_name'],
                    ret,
                    (_eval_arg_type(d['return_sig'], Any, 'return', sig)
                        if d['return_sig'] else Any))
Example #14
0
 def service_desc(self):
     return {
         'sdversion': '1.0',
         'name': self.name,
         'id': 'urn:uuid:{0}'.format(text_type(self.uuid)),
         'summary': self.__doc__,
         'version': self.version,
         'procs': [self.procedure_desc(k)
             for k in iterkeys(self.urls)
                 if self.urls[k] != self.describe]}
Example #15
0
 def send_payload(self, params):
     dump = json.dumps({
         'jsonrpc': self.version,
         'method': self.service_name,
         'params': params,
         'id': text_type(uuid.uuid4())
     })
     dump_payload = FakePayload(dump)
     response = current_app.post(self.service_url, **{'wsgi.input' : dump_payload, 'CONTENT_LENGTH' : len(dump)})
     return response.content
Example #16
0
 def send_payload(self, params):
     """Performs the actual sending action and returns the result
     """
     data = json.dumps({
         'jsonrpc': self.version,
         'method': self.service_name,
         'params': params,
         'id': text_type(uuid.uuid1())
     })
     data_binary = data.encode('utf-8')
     return urlopen(self.service_url, data_binary).read()
Example #17
0
 def send_payload(self, params):
     """Performs the actual sending action and returns the result
     """
     data = json.dumps({
         'jsonrpc': self.version,
         'method': self.service_name,
         'params': params,
         'id': text_type(uuid.uuid1())
     })
     data_binary = data.encode('utf-8')
     return urlopen(self.service_url, data_binary).read()
Example #18
0
 def _make_payload(self,
                   method,
                   params=None,
                   version='1.0',
                   is_notify=False):
     return json.dumps({
         'jsonrpc': version,
         'method': method,
         'params': params if params else [],
         'id': None if is_notify else text_type(uuid.uuid1())
     })
Example #19
0
 def send_payload(self, params):
     dump = json.dumps({
         'jsonrpc': self.version,
         'method': self.service_name,
         'params': params,
         'id': text_type(uuid.uuid1())
     })
     dump_payload = FakePayload(dump)
     response = current_app.post(self.service_url,
                       **{'wsgi.input' : dump_payload,
                       'CONTENT_LENGTH' : len(dump)})
     return response.content
Example #20
0
 def validate_get(self, request, method):
     encode_get_params = lambda r: dict([(k, v[0] if len(v) == 1 else v) for k, v in r])
     if request.method == 'GET':
         method = text_type(method)
         if method in self.urls and getattr(self.urls[method], 'json_safe', False):
             D = {
                 'params': request.args.to_dict(),
                 'method': method,
                 'id': 'jsonrpc',
                 'version': '1.1'
             }
             return True, D
     return False, {}
Example #21
0
 def extract_id_request(self, raw_data):
     try:
         D = json.loads(raw_data)
         return D.get('id')
     except Exception as e:
         if not raw_data is None and raw_data.find('id') != -1:
             find_id = re.findall(r'["|\']id["|\']:([0-9]+)|["|\']id["|\']:["|\'](.+?)["|\']',
                                  raw_data.replace(' ', ''), re.U)
             if find_id:
                 g1, g2 = find_id[0]
                 raw_id = g1 if g1 else g2
                 if text_type(raw_id).isnumeric():
                     return int(raw_id)
                 return raw_id
         return None
Example #22
0
def encode_arg11(p):
    if type(p) is list:
        return p
    elif not type(p) is dict:
        return []
    else:
        pos = []
        d = encode_kw(p)
        for k, v in iteritems(d):
            try:
                pos.append(int(k))
            except ValueError:
                pass
        pos = list(set(pos))
        pos.sort()
        return [d[text_type(i)] for i in pos]
Example #23
0
def encode_arg11(p):
    if type(p) is list:
        return p
    elif not type(p) is dict:
        return []
    else:
        pos = []
        d = encode_kw(p)
        for k, v in iteritems(d):
            try:
                pos.append(int(k))
            except ValueError:
                pass
        pos = list(set(pos))
        pos.sort()
        return [d[text_type(i)] for i in pos]
Example #24
0
 def test_batch_invalid(self):
     req = json.dumps(
         [
             {"jsonrpc": "2.0", "method": "jsonrpc.echoNotFound", "params": [], "id": text_type(uuid.uuid4())},
             {"jsonrpc": "2.0", "method": "jsonrpc.echo", "params": [], "id": text_type(uuid.uuid4())},
             {"jsonrpc": "2.0", "method": "jsonrpc.echoNotFound", "params": [], "id": text_type(uuid.uuid4())},
         ]
     )
     resp = self._call(req)
     self.assertTrue(len(resp) == 3)
     self.assertNotIn("result", resp[0])
     self.assertIn("error", resp[0])
     self.assertEqual("Hello Flask JSON-RPC", resp[1]["result"])
     self.assertNotIn("error", resp[1])
     self.assertNotIn("result", resp[2])
     self.assertIn("error", resp[2])
Example #25
0
def _eval_arg_type(arg_type, T=Any, arg=None, sig=None):
    """Returns a type from a snippit of python source. Should normally be
    something just like 'str' or 'Object'.

        arg_type            the source to be evaluated
        T                         the default type
        arg                     context of where this type was extracted
        sig                     context from where the arg was extracted

    Returns a type or a Type
    """
    try:
        T = eval(arg_type)
    except Exception as e:
        raise ValueError('The type of {0} could not be evaluated in {1} for {2}: {3}' \
            .format(arg_type, arg, sig, text_type(e)))
    else:
        if type(T) not in (type, Type):
            raise TypeError('{0} is not a valid type in {1} for {2}' \
                .format(repr(T), arg, sig))
        return T
Example #26
0
def _eval_arg_type(arg_type, T=Any, arg=None, sig=None):
    """Returns a type from a snippit of python source. Should normally be
    something just like 'str' or 'Object'.

        arg_type            the source to be evaluated
        T                         the default type
        arg                     context of where this type was extracted
        sig                     context from where the arg was extracted

    Returns a type or a Type
    """
    try:
        T = eval(arg_type)
    except Exception as e:
        raise ValueError('The type of {0} could not be evaluated in {1} for {2}: {3}' \
            .format(arg_type, arg, sig, text_type(e)))
    else:
        if type(T) not in (type, Type):
            raise TypeError('{0} is not a valid type in {1} for {2}' \
                .format(repr(T), arg, sig))
        return T
Example #27
0
 def test_batch_call(self):
     req = json.dumps([{
         'jsonrpc': '2.0',
         'method': 'jsonrpc.echo',
         'id': text_type(uuid.uuid4())
     }, {
         'jsonrpc': '2.0',
         'method': 'jsonrpc.echo',
         'params': ['Flask'],
         'id': text_type(uuid.uuid4())
     }, {
         'jsonrpc': '2.0',
         'method': 'jsonrpc.echo',
         'params': None,
         'id': text_type(uuid.uuid4())
     }, {
         'jsonrpc': '2.0',
         'method': 'jsonrpc.echo',
         'params': [],
         'id': text_type(uuid.uuid4())
     }, {
         'jsonrpc': '2.0',
         'method': 'jsonrpc.echo',
         'params': {},
         'id': text_type(uuid.uuid4())
     }, {
         'jsonrpc': '2.0',
         'method': 'jsonrpc.echo',
         'params': ['フラスコ'],
         'id': text_type(uuid.uuid4())
     }])
     resp = self._call(req)
     self.assertTrue(len(resp) == 6)
     self.assertEqual('Hello Flask JSON-RPC', resp[0]['result'])
     self.assertNotIn('error', resp[0])
     self.assertEqual('Hello Flask', resp[1]['result'])
     self.assertNotIn('error', resp[1])
     self.assertEqual('Hello Flask JSON-RPC', resp[2]['result'])
     self.assertNotIn('error', resp[2])
     self.assertEqual('Hello Flask JSON-RPC', resp[3]['result'])
     self.assertNotIn('error', resp[3])
     self.assertEqual('Hello Flask JSON-RPC', resp[4]['result'])
     self.assertNotIn('error', resp[4])
     self.assertEqual('Hello フラスコ', resp[5]['result'])
     self.assertNotIn('error', resp[5])
Example #28
0
from flask_jsonrpc.exceptions import (Error, ParseError, InvalidRequestError,
                                      MethodNotFoundError, InvalidParamsError,
                                      ServerError, RequestPostError,
                                      InvalidCredentialsError, OtherError)

JSONRPC_VERSION_DEFAULT = '2.0'

empty_dec = lambda f: f
try:
    # TODO: Add CSRF check
    csrf_exempt = empty_dec
except (NameError, ImportError):
    csrf_exempt = empty_dec

NoneType = type(None)
encode_kw = lambda p: dict([(text_type(k), v) for k, v in iteritems(p)])

def encode_kw11(p):
    if not type(p) is dict:
        return {}
    ret = p.copy()
    removes = []
    for k, v in iteritems(ret):
        try:
            int(k)
        except ValueError:
            pass
        else:
            removes.append(k)
    for k in removes:
        ret.pop(k)
Example #29
0
 def decode(self, n):
     return reduce(
         lambda L, R: R if (text_type(R) == n) else L,
         _types_gen(self))
Example #30
0
 def test_response_object(self):
     with self.app:
         r = self.app.get('/api/browse/partials/response_object.html')
         data = text_type(r.data)
         self.assertEqual(200, r.status_code)
         self.assertIn('HTTP', data)
Example #31
0
 def __init__(self, service_url, service_name=None, version="2.0"):
     self.version = text_type(version)
     self.service_url = service_url
     self.service_name = service_name
Example #32
0
 def __init__(self, service_url, service_name=None, version=JSONRPC_VERSION_DEFAULT, headers=None):
     self.version = text_type(version)
     self.service_url = service_url
     self.service_name = service_name
     self.headers = headers or self.DEFAULT_HEADERS
Example #33
0
 def test_partials_dashboard(self):
     with self.app:
         r = self.app.get('/api/browse/partials/dashboard.html')
         data = text_type(r.data)
         self.assertEqual(200, r.status_code)
         self.assertIn('Welcome to web browsable API', data)
Example #34
0
    def response_obj(self, request, D, version_hint=JSONRPC_VERSION_DEFAULT):
        version = version_hint
        response = self.empty_response(version=version)
        apply_version = {
            '2.0': self.apply_version_2_0,
            '1.1': self.apply_version_1_1,
            '1.0': self.apply_version_1_0
        }

        try:
            try:
                # determine if an object is iterable?
                iter(D)
            except TypeError as e:
                raise InvalidRequestError(
                    getattr(e, 'message',
                            e.args[0] if len(e.args) > 0 else None))

            # version: validate
            if 'jsonrpc' in D:
                if text_type(D['jsonrpc']) not in apply_version:
                    raise InvalidRequestError(
                        'JSON-RPC version {0} not supported.'.format(
                            D['jsonrpc']))
                version = request.jsonrpc_version = response[
                    'jsonrpc'] = text_type(D['jsonrpc'])
            elif 'version' in D:
                if text_type(D['version']) not in apply_version:
                    raise InvalidRequestError(
                        'JSON-RPC version {0} not supported.'.format(
                            D['version']))
                version = request.jsonrpc_version = response[
                    'version'] = text_type(D['version'])
            else:
                version = request.jsonrpc_version = JSONRPC_VERSION_DEFAULT

            # params: An Array or Object, that holds the actual parameter values
            # for the invocation of the procedure. Can be omitted if empty.
            if 'params' not in D or not D['params']:
                D['params'] = []
            if 'method' not in D or 'params' not in D:
                raise InvalidParamsError(
                    'Request requires str:"method" and list:"params"')
            if D['method'] not in self.urls:
                raise MethodNotFoundError('Method not found. Available methods: {0}' \
                    .format('\n'.join(list(self.urls.keys()))))

            method = self.urls[text_type(D['method'])]
            if getattr(method, 'json_validate', False):
                validate_params(method, D)

            if 'id' in D and D['id'] is not None:  # regular request
                response['id'] = D['id']
                if version in ('1.1', '2.0'):
                    response.pop('error', None)
            else:  # notification
                return None, 204

            R = apply_version[version](method, D['params'])

            if 'id' not in D or ('id' in D
                                 and D['id'] is None):  # notification
                return None, 204

            if isinstance(R, Response):
                if R.status_code == 200:
                    return R, R.status_code
                if R.status_code == 401:
                    raise InvalidCredentialsError(R.status)
                raise OtherError(R.status, R.status_code)

            try:
                # New in Flask version 0.10.
                encoder = current_app.json_encoder()
            except AttributeError:
                encoder = json.JSONEncoder()

            # type of `R` should be one of these or...
            if not sum([isinstance(R, e) for e in \
                    string_types + integer_types + \
                    (float, complex, dict, list, tuple, set, frozenset, NoneType, bool)]):
                try:
                    rs = encoder.default(
                        R)  # ...or something this thing supports
                except TypeError as exc:
                    raise TypeError(
                        'Return type not supported, for {0!r}'.format(R))

            response['result'] = R
            status = 200
        except Error as e:
            response['error'] = e.json_rpc_format
            if version in ('1.1', '2.0'):
                response.pop('result', None)
            status = e.status
        except HTTPException as e:
            other_error = OtherError(e)
            response['error'] = other_error.json_rpc_format
            response['error']['code'] = e.code
            if version in ('1.1', '2.0'):
                response.pop('result', None)
            status = e.code
        except Exception as e:
            other_error = OtherError(e)
            response['error'] = other_error.json_rpc_format
            status = other_error.status
            if version in ('1.1', '2.0'):
                response.pop('result', None)

        # Exactly one of result or error MUST be specified. It's not
        # allowed to specify both or none.
        if version in ('1.1', '2.0') and 'result' in response:
            response.pop('error', None)

        return response, status
Example #35
0
 def register(self, name, method):
     self.urls[text_type(name)] = method
Example #36
0
 def __init__(self, service_url, service_name=None, version='2.0'):
     self.version = text_type(version)
     self.service_url = service_url
     self.service_name = service_name
Example #37
0
 def __init__(self):
     self.urls = {}
     self.uuid = text_type(uuid4())
     self.version = JSONRPC_VERSION_DEFAULT
     self.name = 'Flask-JSONRPC'
     self.register('system.describe', self.describe)
Example #38
0
    def response_obj(self, request, D, version_hint=JSONRPC_VERSION_DEFAULT):
        version = version_hint
        response = self.empty_response(version=version)
        apply_version = {
            '2.0': self.apply_version_2_0,
            '1.1': self.apply_version_1_1,
            '1.0': self.apply_version_1_0
        }

        try:
            try:
                # determine if an object is iterable?
                iter(D)
            except TypeError as e:
                raise InvalidRequestError(getattr(e, 'message', e.args[0] if len(e.args) > 0 else None))

            # version: validate
            if 'jsonrpc' in D:
                if text_type(D['jsonrpc']) not in apply_version:
                    raise InvalidRequestError('JSON-RPC version {0} not supported.'.format(D['jsonrpc']))
                version = request.jsonrpc_version = response['jsonrpc'] = text_type(D['jsonrpc'])
            elif 'version' in D:
                if text_type(D['version']) not in apply_version:
                    raise InvalidRequestError('JSON-RPC version {0} not supported.'.format(D['version']))
                version = request.jsonrpc_version = response['version'] = text_type(D['version'])
            else:
                version = request.jsonrpc_version = JSONRPC_VERSION_DEFAULT

            # params: An Array or Object, that holds the actual parameter values
            # for the invocation of the procedure. Can be omitted if empty.
            if 'params' not in D or not D['params']:
                 D['params'] = []
            if 'method' not in D or 'params' not in D:
                raise InvalidParamsError('Request requires str:"method" and list:"params"')
            if D['method'] not in self.urls:
                raise MethodNotFoundError('Method not found. Available methods: {0}' \
                    .format('\n'.join(list(self.urls.keys()))))

            method = self.urls[text_type(D['method'])]
            if getattr(method, 'json_validate', False):
                validate_params(method, D)

            if 'id' in D and D['id'] is not None: # regular request
                response['id'] = D['id']
                if version in ('1.1', '2.0'):
                    response.pop('error', None)
            else: # notification
                return None, 204

            R = apply_version[version](method, D['params'])

            if 'id' not in D or ('id' in D and D['id'] is None): # notification
                return None, 204

            if isinstance(R, Response):
                if R.status_code == 200:
                    return R, R.status_code
                if R.status_code == 401:
                    raise InvalidCredentialsError(R.status)
                raise OtherError(R.status, R.status_code)

            try:
                # New in Flask version 0.10.
                encoder = current_app.json_encoder()
            except AttributeError:
                encoder = json.JSONEncoder()

            # type of `R` should be one of these or...
            if not sum([isinstance(R, e) for e in \
                    string_types + integer_types + \
                    (float, complex, dict, list, tuple, set, frozenset, NoneType, bool)]):
                try:
                    rs = encoder.default(R) # ...or something this thing supports
                except TypeError as exc:
                    raise TypeError('Return type not supported, for {0!r}'.format(R))

            response['result'] = R
            status = 200
        except Error as e:
            response['error'] = e.json_rpc_format
            if version in ('1.1', '2.0'):
                response.pop('result', None)
            status = e.status
        except HTTPException as e:
            other_error = OtherError(e)
            response['error'] = other_error.json_rpc_format
            response['error']['code'] = e.code
            if version in ('1.1', '2.0'):
                response.pop('result', None)
            status = e.code
        except Exception as e:
            other_error = OtherError(e)
            response['error'] = other_error.json_rpc_format
            status = other_error.status
            if version in ('1.1', '2.0'):
                response.pop('result', None)

        # Exactly one of result or error MUST be specified. It's not
        # allowed to specify both or none.
        if version in ('1.1', '2.0') and 'result' in response:
            response.pop('error', None)

        return response, status
Example #39
0
 def send_payload(self, params):
     dump = json.dumps(
         {"jsonrpc": self.version, "method": self.service_name, "params": params, "id": text_type(uuid.uuid1())}
     )
     dump_payload = FakePayload(dump)
     response = current_app.post(self.service_url, **{"wsgi.input": dump_payload, "CONTENT_LENGTH": len(dump)})
     return response.content
Example #40
0
    def response_dict(self, request, D, is_batch=False, version_hint='1.0'):
        version = version_hint
        response = self.empty_response(version=version)
        apply_version = {
            '2.0': lambda f, p: f(**encode_kw(p)) if type(p) is dict else f(*p),
            '1.1': lambda f, p: f(*encode_arg11(p), **encode_kw(encode_kw11(p))),
            '1.0': lambda f, p: f(*p)
        }

        try:
            # params: An Array or Object, that holds the actual parameter values
            # for the invocation of the procedure. Can be omitted if empty.
            if 'params' not in D or not D['params']:
                 D['params'] = []
            if 'method' not in D or 'params' not in D:
                raise InvalidParamsError('Request requires str:"method" and list:"params"')
            if D['method'] not in self.urls:
                raise MethodNotFoundError('Method not found. Available methods: {0}' \
                    .format('\n'.join(list(self.urls.keys()))))

            if 'jsonrpc' in D:
                if text_type(D['jsonrpc']) not in apply_version:
                    raise InvalidRequestError('JSON-RPC version {0} not supported.'.format(D['jsonrpc']))
                version = request.jsonrpc_version = response['jsonrpc'] = text_type(D['jsonrpc'])
            elif 'version' in D:
                if text_type(D['version']) not in apply_version:
                    raise InvalidRequestError('JSON-RPC version {0} not supported.'.format(D['version']))
                version = request.jsonrpc_version = response['version'] = text_type(D['version'])
            else:
                request.jsonrpc_version = '1.0'

            method = self.urls[text_type(D['method'])]
            if getattr(method, 'json_validate', False):
                validate_params(method, D)

            if 'id' in D and D['id'] is not None: # regular request
                response['id'] = D['id']
                if version in ('1.1', '2.0') and 'error' in response:
                    response.pop('error')
            elif is_batch: # notification, not ok in a batch format, but happened anyway
                raise InvalidRequestError
            else: # notification
                return None, 204

            R = apply_version[version](method, D['params'])

            if 'id' not in D or ('id' in D and D['id'] is None): # notification
                return None, 204

            if isinstance(R, Response):
                if R.status_code == 401:
                    raise InvalidCredentialsError(R.status)
                raise OtherError(R.status, R.status_code)

            encoder = current_app.json_encoder()

            # type of `R` should be one of these or...
            if not sum([isinstance(R, e) for e in \
                    string_types + integer_types + (dict, list, set, NoneType, bool)]):
                try:
                    rs = encoder.default(R) # ...or something this thing supports
                except TypeError as exc:
                    raise TypeError('Return type not supported, for {0!r}'.format(R))

            response['result'] = R
            status = 200
        except Error as e:
            response['error'] = e.json_rpc_format
            if version in ('1.1', '2.0') and 'result' in response:
                response.pop('result')
            status = e.status
        except HTTPException as e:
            other_error = OtherError(e)
            response['error'] = other_error.json_rpc_format
            response['error']['code'] = e.code
            if version in ('1.1', '2.0') and 'result' in response:
                response.pop('result')
            status = e.code
        except Exception as e:
            other_error = OtherError(e)
            response['error'] = other_error.json_rpc_format
            status = other_error.status
            if version in ('1.1', '2.0') and 'result' in response:
                response.pop('result')

        # Exactly one of result or error MUST be specified. It's not
        # allowed to specify both or none.
        if version in ('1.1', '2.0') and 'error' in response and not response['error']:
            response.pop('error')

        return response, status
Example #41
0
 def decode(self, n):
     return reduce(lambda L, R: R if (text_type(R) == n) else L,
                   _types_gen(self))
Example #42
0
    def response_dict(self, request, D, is_batch=False, version_hint='1.0'):
        version = version_hint
        response = self.empty_response(version=version)
        apply_version = {
            '2.0': lambda f, p: f(**encode_kw(p))
            if type(p) is dict else f(*p),
            '1.1':
            lambda f, p: f(*encode_arg11(p), **encode_kw(encode_kw11(p))),
            '1.0': lambda f, p: f(*p)
        }

        try:
            # params: An Array or Object, that holds the actual parameter values
            # for the invocation of the procedure. Can be omitted if empty.
            if 'params' not in D or not D['params']:
                D['params'] = []
            if 'method' not in D or 'params' not in D:
                raise InvalidParamsError(
                    'Request requires str:"method" and list:"params"')
            if D['method'] not in self.urls:
                raise MethodNotFoundError('Method not found. Available methods: {0}' \
                    .format('\n'.join(list(self.urls.keys()))))

            if 'jsonrpc' in D:
                if text_type(D['jsonrpc']) not in apply_version:
                    raise InvalidRequestError(
                        'JSON-RPC version {0} not supported.'.format(
                            D['jsonrpc']))
                version = request.jsonrpc_version = response[
                    'jsonrpc'] = text_type(D['jsonrpc'])
            elif 'version' in D:
                if text_type(D['version']) not in apply_version:
                    raise InvalidRequestError(
                        'JSON-RPC version {0} not supported.'.format(
                            D['version']))
                version = request.jsonrpc_version = response[
                    'version'] = text_type(D['version'])
            else:
                request.jsonrpc_version = '1.0'

            method = self.urls[text_type(D['method'])]
            if getattr(method, 'json_validate', False):
                validate_params(method, D)

            if 'id' in D and D['id'] is not None:  # regular request
                response['id'] = D['id']
                if version in ('1.1', '2.0') and 'error' in response:
                    response.pop('error')
            elif is_batch:  # notification, not ok in a batch format, but happened anyway
                raise InvalidRequestError
            else:  # notification
                return None, 204

            R = apply_version[version](method, D['params'])
            if asyncio.iscoroutine(R):
                R = (yield from R)

            if 'id' not in D or ('id' in D
                                 and D['id'] is None):  # notification
                return None, 204

            encoder = current_app.json_encoder()

            # type of `R` should be one of these or...
            if not sum([isinstance(R, e) for e in \
                    string_types + integer_types + (dict, list, set, NoneType, bool)]):
                try:
                    rs = encoder.default(
                        R)  # ...or something this thing supports
                except TypeError as exc:
                    raise TypeError(
                        "Return type not supported, for {0!r}".format(R))

            response['result'] = R

            status = 200

        except Error as e:
            # exception missed by others
            #got_request_exception.connect(log_exception, current_app._get_current_object())

            response['error'] = e.json_rpc_format
            if version in ('1.1', '2.0') and 'result' in response:
                response.pop('result')
            status = e.status
        except HTTPException as e:
            # exception missed by others
            #got_request_exception.connect(log_exception, current_app._get_current_object())

            other_error = OtherError(e)
            response['error'] = other_error.json_rpc_format
            response['error']['code'] = e.code
            if version in ('1.1', '2.0') and 'result' in response:
                response.pop('result')
            status = e.code
        except Exception as e:
            # exception missed by others
            #got_request_exception.connect(log_exception, current_app._get_current_object())

            other_error = OtherError(e)
            response['error'] = other_error.json_rpc_format
            status = other_error.status
            if version in ('1.1', '2.0') and 'result' in response:
                response.pop('result')

        # Exactly one of result or error MUST be specified. It's not
        # allowed to specify both or none.
        if version in ('1.1', '2.0'
                       ) and 'error' in response and not response['error']:
            response.pop('error')

        return response, status
Example #43
0
from flask_jsonrpc.exceptions import (Error, ParseError, InvalidRequestError,
                                      MethodNotFoundError, InvalidParamsError,
                                      ServerError, RequestPostError,
                                      InvalidCredentialsError, OtherError)

JSONRPC_VERSION_DEFAULT = '2.0'

empty_dec = lambda f: f
try:
    # TODO: Add CSRF check
    csrf_exempt = empty_dec
except (NameError, ImportError):
    csrf_exempt = empty_dec

NoneType = type(None)
encode_kw = lambda p: dict([(text_type(k), v) for k, v in iteritems(p)])


def encode_kw11(p):
    if not type(p) is dict:
        return {}
    ret = p.copy()
    removes = []
    for k, v in iteritems(ret):
        try:
            int(k)
        except ValueError:
            pass
        else:
            removes.append(k)
    for k in removes:
Example #44
0
 def __init__(self):
     self.urls = {}
     self.uuid = text_type(uuid4())
     self.version = JSONRPC_VERSION_DEFAULT
     self.name = 'Flask-JSONRPC'
     self.register('system.describe', self.describe)
Example #45
0
 def register(self, name, method):
     self.urls[text_type(name)] = method
Example #46
0
 def __init__(self):
     self.urls = {}
     self.uuid = text_type(uuid1())
     self.version = '1.0'
     self.name = 'Flask-JSONRPC'
     self.register('system.describe', self.describe)
Example #47
0
 def __init__(self):
     self.urls = {}
     self.uuid = text_type(uuid1())
     self.version = '1.0'
     self.name = 'Flask-JSONRPC'
     self.register('system.describe', self.describe)
Example #48
0
 def send_payload(self, params):
     """Performs the actual sending action and returns the result
     """
     data = json.dumps(
         {"jsonrpc": self.version, "method": self.service_name, "params": params, "id": text_type(uuid.uuid1())}
     )
     data_binary = data.encode("utf-8")
     return urlopen(self.service_url, data_binary).read()