Преглед на файлове

Fix parsing XML-RPC values with no type specified. Closes #507

Conflicts:
	CHANGES.txt
	supervisor/tests/test_xmlrpc.py
	supervisor/xmlrpc.py
Mike Naberezny преди 10 години
родител
ревизия
10d07c15dd
променени са 3 файла, в които са добавени 59 реда и са изтрити 27 реда
  1. 9 0
      CHANGES.txt
  2. 35 18
      supervisor/tests/test_xmlrpc.py
  3. 15 9
      supervisor/xmlrpc.py

+ 9 - 0
CHANGES.txt

@@ -1,3 +1,12 @@
+3.1.3 (Next 3.x Release)
+------------------------
+
+- Fixed an XML-RPC bug where the ElementTree-based parser handled strings
+  like ``<value><string>hello</string></value>`` but not strings like
+  ``<value>hello</value>``, which are valid in the XML-RPC spec.  This
+  fixes compatibility with the Apache XML-RPC client for Java and
+  possibly other clients.
+
 3.1.2 (2014-09-07)
 ------------------
 

+ 35 - 18
supervisor/tests/test_xmlrpc.py

@@ -297,42 +297,57 @@ class IterparseLoadsTests(unittest.TestCase):
         <value><i4>41</i4></value>
         </param>
         <param>
-        <value><int>14</int></value>
+        <value><string>foo</string></value>
         </param>
         <param>
-        <value><boolean>1</boolean></value>
+        <value><string></string></value>
+        </param>
+        <param>
+        <!-- xml-rpc spec allows strings without <string> tag -->
+        <value>bar</value>
         </param>
         <param>
-        <value><string>hello world</string></value>
+        <value></value>
+        </param>
+        <param>
+        <value><boolean>1</boolean></value>
         </param>
         <param>
         <value><double>-12.214</double></value>
         </param>
         <param>
-        <value><dateTime.iso8601>19980717T14:08:55</dateTime.iso8601></value>
+        <value>
+        <dateTime.iso8601>19980717T14:08:55</dateTime.iso8601>
+        </value>
         </param>
         <param>
         <value><base64>eW91IGNhbid0IHJlYWQgdGhpcyE=</base64></value>
         </param>
         <param>
         <struct>
-          <member><name>k</name><value><i4>5</i4></value></member>
+        <member><name>j</name><value><i4>5</i4></value></member>
+        <member><name>k</name><value>abc</value></member>
         </struct>
         </param>
         <param>
         <array>
           <data>
             <value><i4>12</i4></value>
+            <value><string>abc</string></value>
+            <value>def</value>
             <value><i4>34</i4></value>
           </data>
         </array>
         </param>
         <param>
         <struct>
-        <member>
-          <name>k</name>
-          <value><array><data><value><i4>1</i4></value></data></array></value>
-        </member>
+          <member>
+            <name>k</name>
+            <value><array><data>
+              <value><i4>1</i4></value>
+              <struct></struct>
+            </data></array></value>
+          </member>
         </struct>
         </param>
         </params>
@@ -346,15 +361,17 @@ class IterparseLoadsTests(unittest.TestCase):
         import datetime
         self.assertEqual(method, 'examples.getStateName')
         self.assertEqual(params[0], 41)
-        self.assertEqual(params[1], 14)
-        self.assertEqual(params[2], True)
-        self.assertEqual(params[3], 'hello world')
-        self.assertEqual(params[4], -12.214)
-        self.assertEqual(params[5], datetime.datetime(1998, 7, 17, 14, 8, 55))
-        self.assertEqual(params[6], "you can't read this!")
-        self.assertEqual(params[7], {'k': 5})
-        self.assertEqual(params[8], [12, 34])
-        self.assertEqual(params[9], {'k': [1]})
+        self.assertEqual(params[1], 'foo')
+        self.assertEqual(params[2], '')
+        self.assertEqual(params[3], 'bar')
+        self.assertEqual(params[4], '')
+        self.assertEqual(params[5], True)
+        self.assertEqual(params[6], -12.214)
+        self.assertEqual(params[7], datetime.datetime(1998, 7, 17, 14, 8, 55))
+        self.assertEqual(params[8], "you can't read this!")
+        self.assertEqual(params[9], {'j': 5, 'k': 'abc'})
+        self.assertEqual(params[10], [12, 'abc', 'def', 34])
+        self.assertEqual(params[11], {'k': [1, {}]})
 
 class DummyResponse:
     def __init__(self, status=200, body='', reason='reason'):

+ 15 - 9
supervisor/xmlrpc.py

@@ -68,7 +68,7 @@ class DeferredXMLRPCResponse:
                     return NOT_DONE_YET
             except RPCError, err:
                 value = xmlrpclib.Fault(err.code, err.text)
-                
+
             body = xmlrpc_marshal(value)
 
             self.finished = True
@@ -203,7 +203,7 @@ class SystemNamespaceRPCInterface:
             if methodname == name:
                 return methods[methodname]
         raise RPCError(Faults.SIGNATURE_UNSUPPORTED)
-    
+
     def methodSignature(self, name):
         """ Return an array describing the method signature in the
         form [rtype, ptype, ptype...] where rtype is the return data type
@@ -321,10 +321,10 @@ class supervisor_xmlrpc_handler(xmlrpc_handler):
 
     def match(self, request):
         return request.uri.startswith(self.path)
-        
+
     def continue_request (self, data, request):
         logger = self.supervisord.options.logger
-        
+
         try:
 
             params, method = self.loads(data)
@@ -334,7 +334,7 @@ class supervisor_xmlrpc_handler(xmlrpc_handler):
                 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:
@@ -448,13 +448,13 @@ class SupervisorTransport(xmlrpclib.Transport):
                 "Content-Type" : "text/xml",
                 "Accept": "text/xml"
                 }
-            
+
             # basic auth
             if self.username is not None and self.password is not None:
                 unencoded = "%s:%s" % (self.username, self.password)
                 encoded = base64.encodestring(unencoded).replace('\n', '')
                 self.headers["Authorization"] = "Basic %s" % encoded
-                
+
         self.headers["Content-Length"] = str(len(request_body))
 
         self.connection.request('POST', handler, request_body, self.headers)
@@ -472,7 +472,7 @@ class SupervisorTransport(xmlrpclib.Transport):
         p, u = self.getparser()
         p.feed(data)
         p.close()
-        return u.close()    
+        return u.close()
 
 class UnixStreamHTTPConnection(httplib.HTTPConnection):
     def connect(self):
@@ -555,7 +555,6 @@ if iterparse is not None:
         "data": lambda x: [v.text for v in x],
         "struct": lambda x: dict([(k.text or "", v.text) for k, v in x]),
         "base64": lambda x: decodestring(x.text or ""),
-        "value": lambda x: x[0].text,
         "param": lambda x: x[0].text,
     }
 
@@ -567,6 +566,13 @@ if iterparse is not None:
                 data = unmarshal(elem)
                 elem.clear()
                 elem.text = data
+            elif elem.tag == "value":
+                try:
+                    data = elem[0].text
+                except IndexError:
+                    data = elem.text or ""
+                elem.clear()
+                elem.text = data
             elif elem.tag == "methodName":
                 method = elem.text
             elif elem.tag == "params":