miércoles, 29 de julio de 2009

LinkedIn no me funciona

Hace un tiempo que LinkedIn me hacía cosas raras, sólo me mostraba algunas páginas y se me quedaba pensando eternamente en el resto de páginas al intentar cargarlas. Agustín me dió la solución, el culpable es el router D-Link DSL-G624T de ya.com, tal y como cuentan aquí.

El problema es debido a una regla que tiene definida el router con el tamaño máximo de segmento (MSS) y la solución que dan es borrarla.

Se puede conectar al router por telnet (con el mismo usuario que se accede al interfaz web), para ver todas las reglas

iptables -L -n
....
Chain FORWARD (policy ACCEPT) target prot opt source destination
TCPMSS tcp -- 0.0.0.0/0 0.0.0.0/0 tcp flags:0x06/0x02 TCPMSS set 1360 ....
.....

Y se debe borrar la primera regla de tipo FORWARD (que define el MSS)

iptables -D FORWARD 1

Borrando esa regla me funcionaba LinkedIn, pero empezó a dar problemas con otras web, p.e. las imagenes de ebay, que no me las cargaba. Así que opté por leerme el infernal man de iptables a ver que se contaba....

Parece ser que lo habitual es definir el tamaño máximo de segmento con un tamaño 40 bytes menor a la unidad máxima de transferencia (MTU) en lugar de un tamaño fijo como está asignando el router, y eso se puede hacer automáticamente con una regla de iptables

iptables -R FORWARD 1 -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu

Donde el -R FORWARD 1 indica que reemplace la primera regla de tipo FORWARD (la incorrecta) por esta.

El único inconveniente es que la regla se borra cada vez que se reinicia el router y se debe asignar de nuevo

ACTUALIZACION Nov. 2009: En los comentarios Jose Luis Domingo propone otra solución más elegante, donde la modificación se aplica sólo a los paquetes de linkedIn, no a todos los paquetes. Gracias !!!


Para quien le interese un poco más, la parte del man donde lo cuenta...

tcpmss
This matches the TCP MSS (maximum segment size) field of the TCP header. You can only use this on TCP SYN or SYN/ACK packets, since the
MSS is only negotiated during the TCP handshake at connection startup time.

[!] --mss value[:value]
Match a given TCP MSS value or range.

TCPMSS
This target allows to alter the MSS value of TCP SYN packets, to control the maximum size for that connection (usually limiting it to your
outgoing interface’s MTU minus 40 for IPv4 or 60 for IPv6, respectively). Of course, it can only be used in conjunction with -p tcp. It
is only valid in the mangle table.
This target is used to overcome criminally braindead ISPs or servers which block "ICMP Fragmentation Needed" or "ICMPv6 Packet Too Big"
packets. The symptoms of this problem are that everything works fine from your Linux firewall/router, but machines behind it can never
exchange large packets:
1) Web browsers connect, then hang with no data received.
2) Small mail works fine, but large emails hang.
3) ssh works fine, but scp hangs after initial handshaking.
Workaround: activate this option and add a rule to your firewall configuration like:
iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN \
-j TCPMSS --clamp-mss-to-pmtu

--set-mss value
Explicitly set MSS option to specified value.

--clamp-mss-to-pmtu
Automatically clamp MSS value to (path_MTU - 40 for IPv4; -60 for IPv6).

These options are mutually exclusive.


martes, 7 de julio de 2009

Mechanize y content-type unknown

En una aplicación, tengo que integrar un informe que se no puede obtener a partir de un Web Service o REST ni nada parecido, así que la única forma es haciendo login en una web y navegando hasta bajarse un CSV. La integración se hace sin problemas con Mechanize, pero me he encontrado con que recibo estas cabeceras en la página del CSV:

Content-Type: unknown; charset=UTF-16LE
Content-Disposition: attachment; filename=report.csv


El problema está en la forma en que Mechanize parsea los datos recibidos. Decide que clase va a utilizar en función del Content Type. Para texto y html utiliza la clase WWW::Mechanize::Page, y si no sabe que utilizar, por defecto utiliza WWW::Mechanize::File.

En mi caso como no reconoce el Content-Type unknown, me devuelve un objeto de tipo WWW::Mechanize::File, a partir del cual no puedo parsear correctamente el CSV, ya que me devuelve un objeto binario sin ningún método fácil para procesarlo.

La primera solución que se me ocurrió a partir de la documentación de la clase PluggableParser es añadir unknown en la lista de Content-Type soportados por Page

agent.pluggable_parser['unknown'] = WWW::Mechanize::Page

Pero eso no vale, porque dentro de WWW::Mechanize::Page hay un chequeo adicional:

response['content-type'] =~ /^(text\/html)|(application\/xhtml\+xml)/i

Así que la solución para mi caso ha sido crearme una nueva clase que extiende a Page en la que su initialize es tan simple (y tan ñapa) como llamar a super y ignorar la excepción de content type inválido


require 'mechanize'
module WWW
class Mechanize
class Unknown < WWW::Mechanize::Page
def initialize(uri=nil, response=nil, body=nil, code=nil, mech=nil)
begin
super(uri, response, body, code)
rescue Mechanize::ContentTypeError
# Do nothing
end
end
end
end
end


Y hacer que el Content-Type unkown lo procese esta clase

agent.pluggable_parser['unknown'] = WWW::Mechanize::Unknown

Y con esto arreglado. Ya me devuelve un objeto que puedo parsear sin problema con FasterCSV.