Python SimpleXMLRPCServer 如何处理无效的 Unicode/XML?

7 投票
3 回答
4460 浏览
提问于 2025-04-16 08:09

我在客户端遇到了一个错误,当我把无效的XML字符传给Python的SimpleXMLRPCServer时,出现了这个问题:

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 个回答

0

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

至于解决这个问题的方法:你可以使用xmlrpclib.Binary来将要发送的数据进行base64编码。(如果你用的是PY3K版本,可以用xmlrpc.client.Binary

0

你在评论中提到希望尽可能处理客户端的XML。这听起来不错,但其实有一些缺点需要考虑:

  • 你怎么知道哪些内容可以去掉?也许你去掉了某些本来很重要的东西,但客户端发来的数据格式不对等等。

  • 想象一下,最开始你只支持一种特定的格式错误。但后来用户开始发来第二种格式错误,你又得为这种情况添加处理代码(既然已经为第一种添加了,为什么不为第二种也加呢?)。这条路走起来可不简单……

  • 最好是让问题尽早暴露出来,然后在应该处理的地方解决。这次是客户端的实现有问题,所以让客户端来修复它。从长远来看,对你们俩都更好。

如果你也在管理客户端的代码,那么可以考虑在最后的情况下使用一些工具来整理XML(比如看看BeautifulSoup)。但更好的办法是从一开始就禁止无效输入。

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将这个错误作为异常呈现给你。你期待什么样的行为呢?

撰写回答