2010-09-13 24 views
5

He creado una aplicación C++ utilizando WinSck, que tiene un pequeño (maneja solo algunas características que necesito) servidor http implementado. Esto se usa para comunicarse con el mundo exterior usando solicitudes http. Funciona, pero a veces las solicitudes no se manejan correctamente, porque el análisis falla. Ahora estoy bastante seguro de que las solicitudes están formadas correctamente, ya que las envían los principales navegadores web como firefox/chrome o perl/C# (que tienen módulos http/dll).Cómo analizar correctamente las solicitudes HTTP entrantes

Después de algunas depuraciones descubrí que el problema está en la recepción del mensaje. Cuando el mensaje viene en más de una parte (no se lee en una llamada recv()), a veces el análisis falla. He pasado por numerosos intentos sobre cómo resolver esto, pero nada parece ser lo suficientemente confiable.

Lo que hago ahora es que leo en los datos hasta que encuentre la secuencia "\r\n\r\n" que indica el final del encabezado. Si WSAGetLastError() informa algo diferente a 10035 (conexión cerrada/fallida) antes de que se encuentre dicha secuencia, descarto el mensaje. Cuando sé que tengo todo el encabezado, lo analizo y busco información sobre la longitud del cuerpo. Sin embargo, no estoy seguro de si esta información es obligatoria (creo que no) y qué debo hacer si no hay tal información, ¿significa que no habrá un cuerpo? Otro problema es que no sé si debo buscar un "\r\n\r\n" después del cuerpo (si su longitud es mayor que cero).

¿Alguien sabe cómo analizar confiablemente un mensaje http?

Nota: sé que hay implementaciones de servidores http por ahí. Quiero lo mío por varias razones. Y sí, reinventar la rueda es malo, eso también lo sé.

+0

A menos que esté haciendo esto por diversión, consulte el enlace http-analizador que Jack ha proporcionado a continuación. Se ve brillante, y no se atreve a secuestrar su zócalo/lo que sea. –

+0

@Matt Joiner: lo miré y de hecho se ve muy bien. Pero realmente necesito escribir el mío, que solo admite una fracción de todas las funciones de http y, al mismo tiempo, conoce algunos comandos especiales. Si necesitaba un servidor http completo, definitivamente no escribiría el mío. – PeterK

+0

Tenga en cuenta que el código provisto es __tiny__ y no le impone requisitos. Puede detenerlo, ignorarlo y envolverlo de la manera que desee mediante la personalización de las pocas devoluciones de llamada que proporciona. Simpatizo con el deseo de hacer las cosas usted mismo, pero esto le ahorrará horas de depuración y errores debido a la entrada imprevista más adelante. –

Respuesta

3

Puede intentar ver su código para ver cómo manejan un mensaje HTTP.

O podría mirar the spec, hay message length campos que debe utilizar. Solo los navegadores defectuosos envían CRLF adicionales al final, al parecer.

+0

El HTTPbis WG ha clarificado el análisis de mensajes; ver http://greenbytes.de/tech/webdav/draft-ietf-httpbis-p1-messaging-11.html#message.body para el borrador actual. –

+0

Esto se ve bien, gracias. Si eso ayuda, aceptaré gustoso su respuesta. – PeterK

8

Si está programado para escribir su propio analizador, tomaría el enfoque Zed Shaw: use el compilador de máquina de estados Ragel y construya su analizador basado en eso. Ragel puede manejar la entrada que llega en trozos, si tiene cuidado.

Honestamente, sin embargo, solo usaría something like this.

Su recurso de acceso debe ser RFC 2616, que describe HTTP 1.1, que puede usar para construir un analizador. ¡Buena suerte!

+0

+1 para el analizador http y enlaces definitivos. Esa fuente generaría el código *** FAST ***, estoy realmente impresionado. Eso es rudo. –

+0

Hablando de Ragel, puede echar un vistazo a HttpMachine (https://github.com/bvanderveen/httpmachine/tree/master/src/HttpMachine/rl). Además, si está escrito en C#, la máquina de estados se compila con Ragel y creo que debería ser fácilmente adaptable a C++. Más de dos archivos .rl (fuentes Ragel) de tres no están vinculados a C#, pero son generales (por lo que ya se ha hecho mucho trabajo). – gsscoder

-1

HTTP GET/HEAD las solicitudes no tienen cuerpo, y la solicitud POST tampoco puede tener cuerpo. Debe verificar si es GET/HEAD, si es así, entonces no tiene contenido (cuerpo/mensaje) enviado. Si fue POST, hazlo como specs say about parsing a message of known/unknown length, como dijo @gbjbaanb.

+0

Las solicitudes GET y HEAD * pueden * tener un cuerpo. Entonces, no, no verificas el nombre del método. –

+0

@Julian, no se especifica exactamente en la especificación HTTP si puede incluir un cuerpo o no en las solicitudes GET/HEAD. Lo probé a nivel local y funciona con apache, pero nunca lo había visto antes en una implementación en el mundo real, estoy leyendo http://stackoverflow.com/questions/978061/ y http://stackoverflow.com/questions/1266596/ahora, gracias por señalar eso. – aularon

+0

si algo se usa en la práctica y si está permitido son preguntas separadas. Lo importante es que el análisis de solicitudes es el mismo para todos los métodos. (Contrario al análisis de respuesta donde HEAD es especial). Ver también http://trac.tools.ietf.org/wg/httpbis/trac/ticket/19 - por eso estaban revisando RFC 2616, después de todo. –

0

De todos modos, la solicitud HTTP tiene "\ r \ n \ r \ n" al final de los encabezados de solicitud y antes de los datos de solicitud si los hay, incluso si la solicitud es "GET/HTTP/1.0 \ r \ n \ r \ n ".

Si el método es "POST", debe leer tantos bytes después de "\ r \ n \ r \ n", como se especifica en el campo Content-Length.

Así es pseudocódigo:

read_until(buf, "\r\n\r\n"); 
if(buf.starts_with("POST") 
{ 
    contentLength = regex("^Content-Length: (\d+)$").find(buf)[1]; 
    read_all(buf, contentLength); 
} 

Habrá "\ r \ n \ r \ n" después de que el contenido sólo si el contenido incluye. El contenido puede ser datos binarios, no tiene ninguna secuencia de terminación, y el único método para obtener su tamaño es usar el campo Content-Length.

+0

No, no depende del nombre del método. Consulte http://greenbytes.de/tech/webdav/draft-ietf-httpbis-p1-messaging-11.html#message.body para obtener más información. –

+0

Además, tenga en cuenta que las solicitudes HTTP 1.1 no necesitan usar un encabezado 'Content-Length' tampoco. En su lugar, pueden usar 'Transfer-Encoding: chunked', en cuyo caso la longitud del mensaje se codifica dentro de los datos del mensaje. –

Cuestiones relacionadas