使用Python SimpleXMLRPCServer处理非有效的Unicode/XML?

7

当我向Python SimpleXMLRPCServer传递无效的XML字符时,客户端会出现以下错误:

Fault: <Fault 1: "<class 'xml.parsers.expat.ExpatError'>:not well-formed (invalid token): line 6, column 15">

为什么?我必须更改SimpleXMLRPCServer库代码来解决这个问题吗?

以下是我的XML-RPC服务器代码:

from SimpleXMLRPCServer import SimpleXMLRPCServer

import logging
logging.basicConfig(level=logging.DEBUG)

def tt(text):
    return "cool"

server = SimpleXMLRPCServer(("0.0.0.0", 9000))
server.register_introspection_functions()
server.register_function(tt)

# Run the server's main loop
server.serve_forever()

这是我的XML-RPC客户端代码:

s = xmlrpclib.ServerProxy('http://localhost:9000')
s.tt(unichr(0x8))

在服务器端,我没有收到任何错误或回溯信息:
liXXXXXX.members.linode.com - - [06/Dec/2010 23:19:40] "POST /RPC2 HTTP/1.0" 200 -

为什么服务器端没有错误?我该如何诊断问题?

客户端显示以下回溯信息:

/usr/lib/python2.6/xmlrpclib.pyc in __call__(self, *args)
   1197         return _Method(self.__send, "%s.%s" % (self.__name, name))
   1198     def __call__(self, *args):
-> 1199         return self.__send(self.__name, args)
   1200 
   1201 ##


/usr/lib/python2.6/xmlrpclib.pyc in __request(self, methodname, params)
   1487             self.__handler,
   1488             request,
-> 1489             verbose=self.__verbose
   1490             )
   1491 

/usr/lib/python2.6/xmlrpclib.pyc in request(self, host, handler, request_body, verbose)
   1251             sock = None
   1252 
-> 1253         return self._parse_response(h.getfile(), sock)
   1254 
   1255     ##


/usr/lib/python2.6/xmlrpclib.pyc in _parse_response(self, file, sock)
   1390         p.close()
   1391 
-> 1392         return u.close()
   1393 
   1394 ##


/usr/lib/python2.6/xmlrpclib.pyc in close(self)
    836             raise ResponseError()
    837         if self._type == "fault":
--> 838             raise Fault(**self._stack[0])
    839         return tuple(self._stack)
    840 

Fault: <Fault 1: "<class 'xml.parsers.expat.ExpatError'>:not well-formed (invalid token): line 6, column 15">

如果输入包含无效的XML,我该如何获得合理的服务器端处理?我能在服务器端清理这些数据吗?如何操作?

3个回答

3
首先,你的例子对我也不起作用。我不知道你在问什么是“如果输入包含无效的XML,则进行合理的服务器端处理” - 你发送了无效的XML给服务器,它返回一个错误...你还想要什么?
其次,在tt中添加print 'hi there',当你发送unichr(0x8)时,你会发现tt没有被调用。服务器的确切响应(200)是:
HTTP/1.0 200 OK
Server: BaseHTTP/0.3 Python/2.6.5
Date: Tue, 07 Dec 2010 07:33:09 GMT
Content-type: text/xml
Content-length: 350

<?xml version='1.0'?>
<methodResponse>
<fault>
<value><struct>
<member>
<name>faultCode</name>
<value><int>1</int></value>
</member>
<member>
<name>faultString</name>
<value><string>&lt;class 'xml.parsers.expat.ExpatError'&gt;:not well-formed (invalid token): line 6, column 15</string></value>
</member>
</struct></value>
</fault>
</methodResponse>

因此,您看到了错误消息。

现在,根据XML-RPC规范

  • 字符串中允许使用哪些字符?非打印字符?空字符?“字符串”可以用于保存任意二进制数据块吗?

字符串中允许使用任何字符,除了 < 和 &,这两个字符会被编码为 &lt; 和 &amp;。字符串可以用于编码二进制数据。

好的,但这是XML,根据XML规范

合法字符包括制表符、回车、换行以及Unicode和ISO/IEC 10646的合法字符。

Char ::= #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]

其中不包括0x08,并且似乎完全与XML-RPC规范相矛盾!因此,您的XML解析器(从错误来看,似乎是expat)正在相当严格地实现XML规范。由于XML不允许0x08,因此您无法发送0x08,实际上会收到错误消息。

如果我们执行以下操作:

data = "<?xml version='1.0'?>\n<methodCall>\n<methodName>tt</methodName>\n<params>\n<param>\n<value><string>\x08</string></value>\n</param>\n</params>\n</methodCall>"
p = xml.parsers.expat.ParserCreate()
p.Parse(data, True)

...我们获取到了您的错误。再次说明,您向服务器传递了垃圾XML,服务器将会返回一个错误信息,并且中间的Python代码将会把这个错误呈现给您作为一个异常。您期望得到什么样的行为呢?


所以,感谢您的调查。正如我所指出的,我知道这不是有效的XML。我希望能够在服务器端捕获错误(而不是静默失败),然后剥离输入中的任何无效字符。我不编写客户端,并且如果他们向我传递具有一两个无效字符的XML,则希望向客户提供最佳的部分结果。 - Joseph Turian

0

Thanatos在他的帖子中完美地解释了你的问题原因。

至于解决此问题的方法:您可以使用xmlrpclib.Binary对要发送的数据进行base64编码。(对于PY3K:xmlrpc.client.Binary


0

您在评论中提到希望尽可能多地处理客户端的XML。虽然这听起来很不错,但需要考虑以下缺点:

  • 您如何知道可以剥离什么?也许您会剥离一些重要的内容,但客户端发送的代码有问题等。

  • 想象一下,最初您支持一种特定的畸形请求。但是随后用户开始向您发送第二种类型的畸形请求,您也为其添加了异常(既然为第一个添加了,为什么不为第二个添加呢?)。这是一个漫长的过程...

  • 最好尽早让事情失败,并让它们在应该处理的地方进行处理。这次客户端实现有误,所以让客户端修复它。从长远来看,对双方都更好。

如果您也管理客户端代码,则可以使用一些XML整理工具(例如BeautifulSoup)作为最后手段。但最好通过禁用无效输入来解决问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接