2010-09-13 3 views
5

J'ai créé une application C++ en utilisant WinSck, qui a un petit serveur (gère seulement quelques fonctionnalités dont j'ai besoin) implémenté par le serveur http. Ceci est utilisé pour communiquer avec le monde extérieur en utilisant des requêtes http. Cela fonctionne, mais parfois les requêtes ne sont pas gérées correctement, car l'analyse échoue. Maintenant, je suis tout à fait sûr que les demandes sont correctement formées, puisqu'elles sont envoyées par les principaux navigateurs web comme firefox/chrome ou perl/C# (qui ont des modules http/dll). Après un certain débogage, j'ai découvert que le problème était en fait de recevoir le message. Lorsque le message arrive dans plus d'une partie (il n'est pas lu dans un appel recv()), l'analyse échoue parfois. J'ai traversé de nombreux essais sur la façon de résoudre cela, mais rien ne semble assez fiable. Ce que je fais maintenant, c'est que je lis dans les données jusqu'à ce que je trouve "\r\n\r\n" séquence qui indique la fin de l'en-tête. Si WSAGetLastError() signale quelque chose d'autre que 10035 (connexion fermée/échec) avant qu'une telle séquence soit trouvée, je rejette le message. Quand je sais que j'ai tout l'en-tête je l'analyse et cherche des informations sur la longueur du corps. Cependant, je ne suis pas sûr si cette information est obligatoire (je ne pense pas) et que devrais-je faire s'il n'y a pas de telles informations - cela signifie-t-il qu'il n'y aura pas d'organe? Un autre problème est que je ne sais pas si je devrais chercher un "\r\n\r\n" après le corps (si sa longueur est supérieure à zéro).Comment analyser correctement les requêtes HTTP entrantes

Est-ce que quelqu'un sait comment analyser de manière fiable un message http?

Remarque: je sais qu'il existe des implémentations de serveurs http là-bas. Je veux le mien pour diverses raisons. Et oui, réinventer la roue est mauvais, je le sais aussi.

+0

À moins que vous ne le fassiez pour le plaisir, regardez le lien http-analyseur que Jack a fourni ci-dessous. Il semble brillant, et ne prétend pas détourner votre socket/quoi que ce soit. –

+0

@Matt Joiner: je l'ai regardé et il semble en effet très bien. Mais j'ai vraiment besoin d'écrire le mien qui supporte juste une fraction de toutes les fonctionnalités http et en même temps connaît quelques commandes spéciales. Si j'avais besoin d'un serveur http complet, je n'écrirais certainement pas le mien. – PeterK

+0

Gardez à l'esprit que le code fourni est __tiny__ et ne vous oblige à rien. Vous pouvez l'arrêter, l'ignorer et l'emballer comme bon vous semble en personnalisant les quelques rappels qu'il fournit. Je sympathise avec le désir de faire les choses vous-même, mais cela vous évitera des heures de débogage et des bugs en raison d'une entrée imprévue plus tard. –

Répondre

3

Vous pourriez essayer de regarder leur code pour voir comment ils gèrent un message HTTP.

Ou vous pouvez regarder the spec, il ya message length les champs que vous devez utiliser. Seuls les navigateurs buggés envoient des CRLF supplémentaires à la fin, apparemment.

+0

Le groupe de travail HTTPbis a clarifié l'analyse des messages; voir http://greenbytes.de/tech/webdav/draft-ietf-httpbis-p1-messaging-11.html#message.body pour le projet de texte actuel. –

+0

Cela semble bien, merci. Si cela aide, j'accepterai volontiers votre réponse. – PeterK

8

Si vous êtes en train d'écrire votre propre analyseur, je prendrais l'approche Zed Shaw: utilisez le compilateur de machine d'état Ragel et construisez votre analyseur basé dessus. Ragel peut gérer les entrées arrivant en morceaux, si vous faites attention.

Honnêtement, cependant, je voudrais juste utiliser something like this.

Votre ressource de référence doit être RFC 2616, qui décrit HTTP 1.1, que vous pouvez utiliser pour construire un analyseur. Bonne chance!

+0

+1 pour les liens http-parser et définitifs. Cette source génèrerait *** FAST *** code, je suis vraiment impressionné. C'est badass. –

+0

En parlant de Ragel, vous pouvez jeter un oeil à HttpMachine (https://github.com/bvanderveen/httpmachine/tree/master/src/HttpMachine/rl). Aussi, si elle est écrite en C#, la machine d'état est compilée avec Ragel et je pense qu'elle devrait être facilement adaptable en C++. Plus de deux fichiers .rl (sources Ragel) de trois ne sont pas liés à C#, mais généraux (donc beaucoup de travail est déjà fait). – gsscoder

-1

HTTP GET/HEAD Les requêtes n'ont pas de corps, et POST demande ne peut pas avoir de corps aussi. Vous devez vérifier si c'est GET/HEAD, si c'est le cas, alors vous n'avez aucun contenu (corps/message) envoyé. Si c'était un POST, faites comme le specs say about parsing a message of known/unknown length, comme @gbjbaanb a dit.

+0

Demande GET et HEAD * peut * avoir un corps. Donc non, vous ne vérifiez pas le nom de la méthode. –

+0

@Julian, il n'est pas exactement spécifié dans la spécification HTTP si vous pouvez inclure un corps ou non dans les requêtes GET/HEAD. Je l'ai testé localement et cela fonctionne avec Apache, mais je n'ai jamais vu ça auparavant dans une implémentation du monde réel, je lis http://stackoverflow.com/questions/978061/ et http://stackoverflow.com/questions/1266596/maintenant, merci de le signaler. – aularon

+0

si quelque chose est utilisé dans la pratique et si elle est autorisée sont des questions distinctes. Ce qui est important, c'est que l'analyse des requêtes est la même pour toutes les méthodes. (Contrairement à l'analyse syntaxique où HEAD est spécial). Voir aussi http://trac.tools.ietf.org/wg/httpbis/trac/ticket/19 - c'est pourquoi nous avons révisé la RFC 2616, après tout. –

0

De toute façon la requête HTTP a "\ r \ n \ r \ n" à la fin des en-têtes de requête et avant les données de requête le cas échéant, même si la requête est "GET/HTTP/1.0 \ r \ n \ r \ n ".

Si la méthode est "POST", vous devez lire autant d'octets après "\ r \ n \ r \ n", comme spécifié dans le champ Content-Length.

Alors est pseudocode:

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

Il y aura "\ r \ n \ r \ n" après le contenu que si le contenu inclut. Le contenu peut être des données binaires, il n'a pas de séquences de terminaison, et la seule méthode pour obtenir sa taille est l'utilisation du champ Content-Length.

+0

Non, cela ne dépend pas du nom de la méthode. Voir http://greenbytes.de/tech/webdav/draft-ietf-httpbis-p1-messaging-11.html#message.body pour plus de détails. –

+0

De plus, gardez à l'esprit que les requêtes HTTP 1.1 n'ont pas besoin d'utiliser un en-tête 'Content-Length'. Ils peuvent utiliser 'Transfer-Encoding: chunked' à la place, auquel cas la longueur du message est encodée dans les données du message lui-même. –

Questions connexes