servidor - python socket server
Excepción de BadStatusLine al devolver la respuesta del servidor en Python 3 (1)
Después de mucho wireharking, me di cuenta de la causa, y la solución, del problema es la forma en que se estaba estableciendo el encabezado de longitud del contenido. En mi puerto Python 3 del script, copié el método que establece el contenido-longitud. Cual es este:
headers[''Content-length'']=str(len(body))
¡Eso es incorrecto! La forma correcta sería esta:
headers[''Content-length'']=str(len(bytes(body, ''utf-8'')))
Porque la carga útil debe ser un objeto de bytes. Cuando los bytes lo codifican, la longitud es diferente de la versión de cadena.
return urllib.request.Request(theurl, bytes(body, ''utf-8''), headers)
Puede omitir de forma manual la configuración del encabezado de longitud de contenido al usar cualquier elemento que se derive de http.client.HTTPConnection. Tiene un método interno que busca el encabezado de longitud del contenido y, si falta, lo establece en función de la longitud del cuerpo del contenido, independientemente de la forma.
El problema era una traducción, pero la sutil diferencia entre Python 2 y 3 y cómo maneja las cadenas y las codifica. Debe haber sido algún tipo de golpe de suerte que la versión regular de ASCII funcionó cuando la versión de utf-8 no funcionaba, bueno.
Estoy intentando portar un script a python 3 que envíe feeds XML que se encuentran aquí:
https://developers.google.com/search-appliance/documentation/files/pushfeed_client.py.txt
Después de ejecutar 2to3.py y hacer algunos ajustes menores para eliminar cualquier error de sintaxis, el script falla con esto:
(py33dev) d:/dev/workspace>python pushfeed_client.py --datasource="TEST1" --feedtype="full" --url="http://gsa:19900/xmlfeed" --xmlfilename="test.xml"
Traceback (most recent call last):
File "pushfeed_client.py", line 108, in <module>
main(sys.argv)
File "pushfeed_client.py", line 56, in main
result = urllib.request.urlopen(request_url)
File "C:/Python33/Lib/urllib/request.py", line 156, in urlopen
return opener.open(url, data, timeout)
File "C:/Python33/Lib/urllib/request.py", line 469, in open
response = self._open(req, data)
File "C:/Python33/Lib/urllib/request.py", line 487, in _open
''_open'', req)
File "C:/Python33/Lib/urllib/request.py", line 447, in _call_chain
result = func(*args)
File "C:/Python33/Lib/urllib/request.py", line 1268, in http_open
return self.do_open(http.client.HTTPConnection, req)
File "C:/Python33/Lib/urllib/request.py", line 1253, in do_open
r = h.getresponse()
File "C:/Python33/Lib/http/client.py", line 1147, in getresponse
response.begin()
File "C:/Python33/Lib/http/client.py", line 358, in begin
version, status, reason = self._read_status()
File "C:/Python33/Lib/http/client.py", line 340, in _read_status
raise BadStatusLine(line)
http.client.BadStatusLine: <!DOCTYPE html>
¿Por qué está devolviendo esa excepción con la respuesta del servidor? Aquí está la respuesta completa de la GSA cuando olfateé la sesión:
<!DOCTYPE html>
<html lang=en>
<meta charset=utf-8>
<meta name=viewport content="initial-scale=1, minimum-scale=1, width=device-width">
<title>Error 400 (Bad Request)!!1</title>
<style>
*{margin:0;padding:0}html,code{font:15px/22px arial,sans-serif}html{background:#fff;color:#222;padding:15px}body{margin:7% auto 0;max-width:390px;min-height:180px;padding:30px 0 15px}* > body{background:url(//www.google.com/images/errors/robot.png) 100% 5px no-repeat;padding-right:205px}p{margin:11px 0 22px;overflow:hidden}ins{color:#777;text-decoration:none}a img{border:0}@media screen and (max-width:772px){body{background:none;margin-top:0;max-width:none;padding-right:0}}
</style>
<a href=//www.google.com/><img src=//www.google.com/images/errors/logo_sm.gif alt=Google></a>
<p><b>400.</b> <ins>That’s an error.</ins>
<p>Your client has issued a malformed or illegal request. <ins>That’s all we know.</ins>
Y devolvió un HTTP 400. Puedo causar este problema de manera confiable siempre que la carga XML tenga un carácter utf-8. Funciona impecablemente cuando es simple ascii. Esta es la versión más básica del código que puedo usar para recrear de manera confiable el problema:
import http.client
http.client.HTTPConnection.debuglevel = 1
with open("GSA_full_Feed.xml", encoding=''utf-8'') as xdata:
payload = xdata.read()
content_length = len(payload)
feed_path = "xmlfeed"
content_type = "multipart/form-data; boundary=----------boundary_of_feed_data$"
headers = {"Content-type": content_type, "Content-length": content_length}
conn = http.client.HTTPConnection("gsa", 19900)
conn.request("POST", feed_path, body=payload.encode("utf-8"), headers=headers)
res = conn.getresponse()
print(res.read())
conn.close()
Y aquí hay una carga útil XML de muestra que se usa para causar la excepción:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE gsafeed PUBLIC "-//Google//DTD GSA Feeds//EN" "gsafeed.dtd">
<gsafeed>
<header>
<datasource>TEST1</datasource>
<feedtype>full</feedtype>
</header>
<group>
<record action="add" mimetype="text/html" url="https://myschweetassurl.com">
<metadata>
<meta content="shit happens, then you die" name="description"/>
</metadata>
<content>wacky Umläut test of non utf-8 characters</content>
</record>
</group>
</gsafeed>
El único delta que puedo encontrar entre la versión 2 y 3 son los encabezados de longitud de contenido en cada solicitud. La versión Python 3 es consistentemente más corta que la versión 2, 870 vs. 873.