浏览代码

Improve compatibility with XML-RPC clients. Fixes #14

Mike Naberezny 14 年之前
父节点
当前提交
3d29ac03cf
共有 3 个文件被更改,包括 65 次插入0 次删除
  1. 4 0
      CHANGES.txt
  2. 50 0
      src/supervisor/tests/test_xmlrpc.py
  3. 11 0
      src/supervisor/xmlrpc.py

+ 4 - 0
CHANGES.txt

@@ -15,6 +15,10 @@ Next release
 - Renamed ``README.txt`` to ``README.rst`` so GitHub renders the file as
   ReStructuredText.
 
+- The XML-RPC server is now compatible with clients that do not send empty
+  <params> when there are no parameters for the method call.  Thanks to
+  Johannes Becker for reporting this. 
+
 3.0a10 (2011-03-30)
 -------------------
 

+ 50 - 0
src/supervisor/tests/test_xmlrpc.py

@@ -92,6 +92,56 @@ class XMLRPCHandlerTests(unittest.TestCase):
         self.assertEqual(request.headers['Content-Type'], 'text/xml')
         self.assertEqual(request.headers['Content-Length'], len(xml_response))
 
+    def test_continue_request_no_params_in_request(self):
+        supervisor = DummySupervisor()
+        subinterfaces = [('supervisor', DummySupervisorRPCNamespace())]
+        handler = self._makeOne(supervisor, subinterfaces)
+        data = '<?xml version="1.0" encoding="UTF-8"?>' \
+               '<methodCall>' \
+               '<methodName>supervisor.getAPIVersion</methodName>' \
+               '</methodCall>'
+        request = DummyRequest('/what/ever', None, None, None)
+        handler.continue_request(data, request)
+        logdata = supervisor.options.logger.data
+        from supervisor.xmlrpc import loads
+        if loads:
+            expected = 2
+        else:
+            expected = 3
+        self.assertEqual(len(logdata), expected)
+        self.assertEqual(logdata[-2],
+               u'XML-RPC method called: supervisor.getAPIVersion()')
+        self.assertEqual(logdata[-1],
+            u'XML-RPC method supervisor.getAPIVersion() returned successfully')
+        self.assertEqual(len(request.producers), 1)
+        xml_response = request.producers[0]
+        import xmlrpclib
+        response = xmlrpclib.loads(xml_response)
+        self.assertEqual(response[0][0], '3.0')
+        self.assertEqual(request._done, True)
+        self.assertEqual(request.headers['Content-Type'], 'text/xml')
+        self.assertEqual(request.headers['Content-Length'], len(xml_response))
+
+    def test_continue_request_400_if_method_name_is_empty(self):
+        supervisor = DummySupervisor()
+        subinterfaces = [('supervisor', DummySupervisorRPCNamespace())]
+        handler = self._makeOne(supervisor, subinterfaces)
+        data = '<?xml version="1.0" encoding="UTF-8"?>' \
+               '<methodCall><methodName></methodName></methodCall>'
+        request = DummyRequest('/what/ever', None, None, None)
+        handler.continue_request(data, request)
+        logdata = supervisor.options.logger.data
+        from supervisor.xmlrpc import loads
+        if loads:
+            expected = 1
+        else:
+            expected = 2
+        self.assertEqual(len(logdata), expected)
+        self.assertEqual(logdata[-1],
+               u'XML-RPC request received with no method name')
+        self.assertEqual(len(request.producers), 0)
+        self.assertEqual(request._error, 400)
+
     def test_continue_request_500(self):
         supervisor = DummySupervisor()
         subinterfaces = [('supervisor', DummySupervisorRPCNamespace())]

+ 11 - 0
src/supervisor/xmlrpc.py

@@ -343,6 +343,17 @@ class supervisor_xmlrpc_handler(xmlrpc_handler):
 
             params, method = self.loads(data)
 
+            # no <methodName> in the request or name is an empty string
+            if not method:
+                logger.trace('XML-RPC request received with no method name')
+                request.error(400)
+                return
+            
+            # we allow xml-rpc clients that do not send empty <params>
+            # when there are no parameters for the method call
+            if params is None:
+                params = ()
+
             try:
                 logger.trace('XML-RPC method called: %s()' % method)
                 value = self.call(method, params)