1. Odd Simon Simonsen
  2. trac-rpc-mq

Commits

osimons  committed e9f53ea

Further tweaks to the error-handling, in particular:
* Paring error, returning the correct -32700 code for JSON-RPC and XML-RPC
* Method not found error, returning -32601 for both protocols

  • Participants
  • Parent commits 7426ec4
  • Branches default

Comments (0)

Files changed (1)

File t5437/t5437-protocol_api-r7194.diff

View file
  • Ignore whitespace
 diff --git a/trunk/tracrpc/api.py b/trunk/tracrpc/api.py
 --- a/trunk/tracrpc/api.py
 +++ b/trunk/tracrpc/api.py
-@@ -6,23 +6,29 @@
+@@ -6,23 +6,33 @@
  (c) 2009      ::: www.CodeResort.com - BV Network AS (simon-code@bvnetwork.no)
  """
  
 +class RPCError(TracError):
 +    """ Error class for general RPC-related errors. """
 +    pass
++
++class MethodNotFound(RPCError):
++    """ Error to raise when requested method is not found. """
++    pass
  
  RPC_TYPES = {int: 'int', bool: 'boolean', str: 'string', float: 'double',
 -             xmlrpclib.DateTime: 'dateTime.iso8601', xmlrpclib.Binary: 'base64',
  def expose_rpc(permission, return_type, *arg_types):
      """ Decorator for exposing a method as an RPC call with the given
      signature. """
-@@ -35,7 +41,29 @@
+@@ -35,7 +45,29 @@
      return decorator
  
  
      def xmlrpc_namespace():
          """ Provide the namespace in which a set of methods lives.
              This can be overridden if the 'name' element is provided by
-@@ -56,7 +84,6 @@
+@@ -56,7 +88,6 @@
          followed by argument types.
          """
  
  class AbstractRPCHandler(Component):
      implements(IXMLRPCHandler)
      abstract = True
-@@ -180,7 +207,7 @@
+@@ -180,7 +211,7 @@
                  p = Method(provider, *candidate)
                  if p.name == method:
                      return p
 -        raise xmlrpclib.Fault(1, 'RPC method "%s" not found' % method)
-+        raise RPCError('RPC method "%s" not found' % method)
++        raise MethodNotFound('RPC method "%s" not found' % method)
          
      # Exported methods
      def all_methods(self, req):
-@@ -199,10 +226,8 @@
+@@ -199,10 +230,8 @@
          for signature in signatures:
              try:
                  yield self.get_method(signature['methodName'])(req, signature['params'])
 new file mode 100644
 --- /dev/null
 +++ b/trunk/tracrpc/json_rpc.py
-@@ -0,0 +1,203 @@
+@@ -0,0 +1,207 @@
 +# -*- coding: utf-8 -*-
 +"""
 +License: BSD
 +from trac.util.datefmt import utc
 +from trac.util.text import to_unicode
 +
-+from tracrpc.api import IRPCProtocol, XMLRPCSystem, Binary
++from tracrpc.api import IRPCProtocol, XMLRPCSystem, Binary, \
++        RPCError, MethodNotFound
 +from tracrpc.util import exception_to_unicode, empty, prepare_docs
 +
 +__all__ = ['JsonRpcProtocol']
 +
 +    def _json_error(self, e, c=None, r_id=None):
 +        """ Makes a response dictionary that is an error. """
-+        if isinstance(e, PermissionError):
++        if isinstance(e, MethodNotFound):
++            c = -32601
++        elif isinstance(e, PermissionError):
 +            c = 403
-+        if isinstance(e, ResourceNotFound):
++        elif isinstance(e, ResourceNotFound):
 +            c = 404
-+        c = c or hasattr(e, 'code') and e.code or -32603
++        else:
++            c = c or hasattr(e, 'code') and e.code or -32603
 +        return {'result': None, 'id': r_id, 'error': {
 +                'name': hasattr(e, 'name') and e.name or 'JSONRPCError',
 +                'code': c,
                  self.assertTrue('XML_RPC' in result['error']['message'])
              finally:
                  # Add back the default permission for further tests
+@@ -129,7 +129,7 @@
+             self.assertTrue(result['error'])
+             self.assertEquals(result['id'], 'no-method')
+             self.assertEquals(None, result['result'])
+-            self.assertEquals(-32603, result['error']['code'])
++            self.assertEquals(-32601, result['error']['code'])
+             self.assertTrue('not found' in result['error']['message'])
+ 
+         def test_wrong_argspec(self):
 @@ -142,6 +142,31 @@
              self.assertTrue('listMethods() takes exactly 2 arguments' \
                                  in result['error']['message'])
              self.assertTrue('XML_RPC' in e.faultString)
              rpc_testenv._tracadmin('permission', 'add', 'anonymous',
                                          'XML_RPC')
-@@ -46,7 +46,7 @@
+@@ -38,7 +38,7 @@
+             self.admin.system.doesNotExist()
+             self.fail("What? Method exists???")
+         except xmlrpclib.Fault, e:
+-            self.assertEquals(1, e.faultCode)
++            self.assertEquals(-32601, e.faultCode)
+             self.assertTrue("not found" in e.faultString)
+ 
+     def test_wrong_argspec(self):
+@@ -46,15 +46,20 @@
              self.admin.system.listMethods("hello")
              self.fail("Oops. Wrong argspec accepted???")
          except xmlrpclib.Fault, e:
              self.assertTrue("listMethods() takes exactly 2 arguments" \
                                      in e.faultString)
  
-@@ -67,6 +67,38 @@
+     def test_content_encoding(self):
+         test_string = "øæåØÆÅàéüoö"
+         # No encoding / encoding error
+-        self.assertRaises(xmlrpclib.ProtocolError, 
+-                self.admin.ticket.create, test_string, test_string[::-1], {})
++        try:
++            t_id = self.admin.ticket.create(test_string, test_string[::-1], {})
++            self.admin.ticket.delete(t_id)
++            self.fail("Expected ticket create to fail...")
++        except Exception, e:
++            self.assertTrue(isinstance(e, xmlrpclib.Fault))
++            self.assertEquals(-32700, e.faultCode)
+         # Unicode version (encodable)
+         from trac.util.text import to_unicode
+         test_string = to_unicode(test_string)
+@@ -67,6 +72,38 @@
          self.assertEquals(unicode, type(result[3]['summary']))
          self.admin.ticket.delete(t_id)
  
 new file mode 100644
 --- /dev/null
 +++ b/trunk/tracrpc/xml_rpc.py
-@@ -0,0 +1,182 @@
+@@ -0,0 +1,194 @@
 +# -*- coding: utf-8 -*-
 +"""
 +License: BSD
 +from trac.util.datefmt import utc
 +from trac.util.text import to_unicode
 +
-+from tracrpc.api import XMLRPCSystem, IRPCProtocol, Binary
++from tracrpc.api import XMLRPCSystem, IRPCProtocol, Binary, \
++        RPCError, MethodNotFound
 +from tracrpc.util import empty, prepare_docs
 +
 +__all__ = ['XmlRpcProtocol']
 +
 +    def rpc_process(self, req, content_type):
 +        """ Handles XML-RPC requests """
-+        args, method = xmlrpclib.loads(
++        try:
++            args, method = xmlrpclib.loads(
 +                        req.read(int(req.get_header('Content-Length'))))
++        except Exception, e:
++            self.log.debug("RPC(xml) parse error: %s", to_unicode(e))
++            self._send_response(req,
++                    xmlrpclib.dumps(
++                        xmlrpclib.Fault(-32700, to_unicode(e))), content_type)
++            return
 +        self.log.debug("RPC(xml) call by '%s', method '%s' with args: %s" \
 +                                    % (req.authname, method, repr(args)))
 +        args = self._normalize_xml_input(args)
 +            result = tuple(self._normalize_xml_output(result))
 +            self._send_response(req,
 +                    xmlrpclib.dumps(result, methodresponse=True), content_type)
++        except MethodNotFound, e:
++            self._send_response(req,
++                    xmlrpclib.dumps(
++                        xmlrpclib.Fault(-32601, to_unicode(e))), content_type)
 +        except PermissionError, e:
 +            self._send_response(req,
 +                    xmlrpclib.dumps(