Primeramente aclaro que, si no me equivoco, JavaScript usa una
codificación interna UTF-16 (que usa 2 o 4 bytes para según que
carácter, mientras que UTF-8 emplea una longitud variable desde 1 byte
hasta 3, creo).
La web incluye al comienzo en la sección <head>:
<meta content="text/html; charset=utf-8" http-equiv="Content-Type"></meta>
De esta forma el navegador la presenta en UTF-8 y teóricamente lo que
se escriba en algún textfield de un formulario también se envía en
UTF-8.
El caso es que en efecto hay un textfield en la web, y lo relleno con
"iñaki". "iñaki" codificado en UTF-8 son 6 bytes. A continuación hay
que enviar dicho string codificado en UTF-8 así como su longitud (en
bytes codificado en UTF-8) a través de una conexión de WebSocket. Para
obtener la longitud en bytes del string codificado en UTF-8 hacemos
esto, y funciona:
function str_length(str) {
return unescape(encodeURIComponent(str)).length;
}
Si llamamos a str_length("iñaki") en un JavaScript la función devuelve
6 (ok, eso es lo que ocupa "iñaki" en bytes usando codificación
UTF-8).
NOTA: Si usamos el intérprete rhino, esa función devuelve 7, pero me
temo que rhino hace alguna guarrada interna a nivel de codificación.
Pero resulta que al enviar el string "iñaki" por la conexión
WebSocket, y loguearlo en el server destino, aparece mal:
i�aki
Mi opinión es que tal y como he leído por ahí, aunque en cada howto de
internet dicen una cosa y con bastante poca credibilidad, JavaScript
usa internamente codificación UTF-16 y por lo tanto así envía el
string "iñaki". En el server destino, se parsea el string y se crea un
string (en otro lenguaje, Ruby) con codificación UTF-8, y al hacer un
printf en pantalla sale mal (como indicaba antes). Si envío el mismo
string desde otro cliente (no una web con WebSocket) entonces el
server lo pinta bien en pantalla. Es decir, estoy seguro al 100% de
que no es un problema del server, segurísimo.
Entonces mis preguntas serían (la mayoría especulaciones):
- ¿Usa JavaScript internamente UTF-16 en todos los navegadores?
- Si es así, ¿se puede modificar eso desde el propio código JavaScript?
- O si no, ¿hay alguna forma de re-codificar a UTF-8 un string en
JavaScript en el propio código JS?
Muchas gracias. Saludos.
--
Iñaki Baz Castillo
<i...@aliax.net>
Por lo poco que he leído el estándar de Websockets trabaja con utf-8
por lo que no creo que te esté llegando utf-16 al servidor:
http://www.w3.org/TR/2011/WD-websockets-20110419/
Un saludo
>
>
> Entonces mis preguntas serían (la mayoría especulaciones):
>
> - ¿Usa JavaScript internamente UTF-16 en todos los navegadores?
> - Si es así, ¿se puede modificar eso desde el propio código JavaScript?
> - O si no, ¿hay alguna forma de re-codificar a UTF-8 un string en
> JavaScript en el propio código JS?
>
>
> Muchas gracias. Saludos.
>
> --
> Iñaki Baz Castillo
> <i...@aliax.net>
>
--
Iván Mosquera Paulo
ivanmosquera.net
twitter.com/ivmos
Aupa Iván ;)
Sí, el draft de WebSocket (que va por la revisión 78 + 9 por cierto,
un desastre en plan amateur total) especifica que websocket puede
enviar/recibir texto en UTF-8 y ahora también han añadido transmisión
de datos binarios (esto último no me interesa de momento).
> http://www.w3.org/TR/2011/WD-websockets-20110419/
>
> http://stackoverflow.com/questions/5794334/how-would-i-send-and-receive-packets-over-a-websocket-in-javascript
>
> http://stackoverflow.com/questions/5766802/send-and-receive-binary-data-over-web-sockets-in-javascript#5772093
Este último link me ha hecho darme cuenta de que, en efecto, estamos
también usando websockify [*] (un daemon que hace de gateway entre
WebSocket y TCP). Utiliza ciertas libreriás javascript y tal, así que
a saber, podría estar ahí el problema.
Por nuestra parte, en el lado cliente (la web y el JS), entiendo que
lo estamos haciendo bien:
1) La web tiene codificación UTF-8.
2) El string metido en un formulario se usa en JS para enviarlo por WebSocket.
Luego viene el resto:
3) El navegador web abre una conexión WebSocket con el servidor
websockify y le entrega el string.
4) WebSockify desencapsula el mensaje WebSocket, extrae el string y lo
envía literal vía TCP a otro server.
5) En el server TCP destino es donde el string no lelga bien
codificado (eso seguro).
El fallo podría estar precisamente en el punto 4. Lo investigaremos.
Gracias ;)
[*] https://github.com/kanaka/websockify
A modo de workaround, hemos conseguido que funcione haciendo esta ñapa:
unescape(encodeURI("iñaki"))
Esto efectivamente genera un string JS codificado en UTF-8 tal y como
dice la doc:
http://xkr.us/articles/javascript/encode-compare/
--------------
The encodeURI method returns an encoded URI. If you pass the
result to decodeURI, the original string is returned. The encodeURI
method does not encode the following characters: ":", "/", ";", and "?".
Encodes a Uniform Resource Identifier (URI) by replacing each instance
of certain characters by one, two, or three escape sequences
representing the UTF-8 encoding of the character.
---------------
Pero me parece una ñapa, o sea, NO debería hacer falta hacer eso.
WebSocket funciona con UTF-8 por lo que JavaScript le debería pasar
los datos en UTF-8.
Asumo es un error en la implementación de WebSocket en Chrome
12.0.742.100 (que por cierto usa una revisión del draft de websocket
bastante antigua).