2010-12-07 16 views
6

estoy recibiendo el siguiente error en el lado del cliente al pasar caracteres no válidos XML a un SimpleXMLRPCServer Python:Unicode/XML no válido con Python SimpleXMLRPCServer?

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

¿Por qué? ¿Debo cambiar el código de la biblioteca SimpleXMLRPCServer para solucionarlo?

Aquí está mi código de servidor 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() 

Aquí es mi XML-RPC código de cliente:

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

En el lado del servidor, no consigo algún error o de rastreo :

liXXXXXX.members.linode.com - - [06/Dec/2010 23:19:40] "POST /RPC2 HTTP/1.0" 200 - 

¿Por qué no hay errores en el lado del servidor? ¿Cómo diagnostico lo que está pasando?

Y me sale el siguiente rastreo del lado del cliente:

/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"> 

¿Cómo consigo el procesamiento del lado del servidor en su sano juicio si la entrada contiene XML no válido? ¿Puedo limpiar este lado del servidor de datos? ¿Cómo?

Respuesta

3

Primero, su ejemplo tampoco funciona para mí. No sé qué preguntas sobre "un buen procesamiento del lado del servidor si la entrada contiene XML no válido": le envías al servidor un XML no válido y te devuelve un error ... ¿qué más quieres?

En segundo lugar, se pega un print 'hi there' en tt, se verá que tt no está siendo llamada cuando envía unichr(0x8). La respuesta exacta (a 200) por parte del servidor es:

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> 

Entonces, aparece el mensaje de error.

Ahora, de acuerdo con the XML-RPC spec,

  • Qué caracteres se permiten en las cadenas? ¿Caracteres no imprimibles? ¿Caracteres nulos? ¿Se puede usar una "cadena" para contener un fragmento arbitrario de datos binarios?

Los caracteres están permitidos en una cadena excepto < y &, que son codificados como & lt; y & amp ;. Se puede usar una cadena para codificar datos binarios.

Está bien, pero esto es XML, y de acuerdo con la XML spec:

Los caracteres legales son pestaña, retorno de carro, avance de línea, y los personajes legales de Unicode e ISO/IEC 10646.

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

¡Lo que no incluye 0x08, y parece contradecir completamente las especificaciones XML-RPC! Por lo tanto, vería que la especificación XML está siendo implementada de manera bastante rigurosa por su analizador XML (que, a juzgar por el error, parece ser expat). Como XML no permite 0x08, no puede enviar 0x08 y, de hecho, le devuelve un error.

Si hacemos:

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) 

... que recibamos su error. De nuevo, está pasando basura XML al servidor, el servidor le está devolviendo un mensaje de error, y Python en el medio le está presentando ese error como una excepción. ¿Qué comportamiento esperabas?

+0

Así que sí, gracias por investigar. Como indiqué, entiendo que este no es un XML válido. Me gustaría poder atrapar el error del lado del servidor (en lugar de fallar silenciosamente), y luego quitar cualquier carácter no válido en la entrada. No escribo los clientes, y me gustaría ofrecer los mejores resultados parciales posibles a los clientes si me pasan XML que tiene uno o dos caracteres no válidos. –

0

Indicó en su comentario que le gustaría manejar la mayor cantidad posible de XML para el cliente. Si bien esto puede sonar bien a primera vista, hay desventajas a tener en cuenta (?):

  • ¿Cómo saber qué se puede quitar? Tal vez le quite algo que hubiera sido importante, pero el cliente lo envió codificado incorrectamente, etc.

  • Imagine que inicialmente necesita la ayuda de una determinada malformación. Pero luego los usuarios comienzan a enviarle un segundo tipo de malformación, y usted agrega una excepción para eso también (una vez que haya agregado para el primero, ¿por qué no?). Esto es un largo camino en el camino ...

  • Es mejor dejar que las cosas fallen tan pronto como sea posible y dejar que se solucionen donde debería estar. Esta vez, la implementación del cliente es incorrecta, por lo que permite que el cliente lo solucione. Mejor para ambos a la larga.

Si administra el código de cliente, lo que puede durar recurso a empujar algunos XML ordenada en el mismo (ver BeautifulSoup por ejemplo). Pero en lugar de tratar el problema al deshabilitar la entrada no válida en primer lugar.

0

Thanatos explicó perfectamente el motivo de su problema en su post.

En cuanto a una solución a la solución de este problema: Puede usar xmlrpclib.Binary para codificar en base64 los datos que se enviarán. (Para PY3K: xmlrpc.client.Binary)