[http-client] - récupérer une requete complète sans passer par les sockets asynchrones

Soyez le premier à donner votre avis sur cette source.

Snippet vu 5 327 fois - Téléchargée 18 fois

Contenu du snippet

Hello !
Etant tout nouveau dans ce merveilleux monde qu'est python, je suis content de mettre en ligne ma première source python ;-)
Je travail beaucoup sur le protocole HTTP et je n'arrête pas d'avoir des problèmes avec la fonction recv() du module socket.
Pour recevoir une entête et tout le contenu envoyé par le serveur (dans le cadre d'un client HTTP), j'ai du user de ruses pour arriver a récupérer l'intégralité des paquets dans un buffer et arrêter la fonction recv() sans passer par le mode asynchrone ni les threads.
Il s'agit d'une (toute) petite astuce, je m'excuse d'avance si quelqu'un d'autre l'a postée avant moi.

Source / Exemple :


def HTTPRecv(Socket):
	sResult = ''
	while 1:
		sResult += Socket.recv(1024)
		if ( ord(sResult[-1:]) == 10 ):
			break
		
	return sResult

Conclusion :


En fait, le fonctionnement est très simple : je reçois les paquets dans un buffer de 1024 bytes. Je concatène au résultat final et je teste le dernier caractère : si il s'agit du caractère <LF>, je stop l'exécution de la boucle et je renvoi le résultat final.

A voir également

Ajouter un commentaire

Commentaires

cs_jean84
Messages postés
450
Date d'inscription
jeudi 26 août 2004
Statut
Membre
Dernière intervention
5 mars 2009
-
Hello ;-)

Ok je viens de saisir pour le pseudo fichier. Je vais tester et je te tiens au courant.

Merci :-p
cs_Dobel
Messages postés
333
Date d'inscription
dimanche 25 mai 2003
Statut
Membre
Dernière intervention
23 novembre 2009
-
oups, oui, tu bosses côté client.
(c'est ça de poster aussi "tôt" un weekend :))

Enfin, c'est la même chose.

tu peux aussi créer des pseudos fichiers avec makefile,
en lecture et/ou en écriture suivant les arguments
(wfile = sock.makefile("wb", 0) par exemple pour un pseudo fichier en écriture)

pour HTTP 0.9 :
tu lis tant que tu peux, cad juste la méthode read du pseudo fichier, sans donner d'argument size, qui lira jusqu'à EOF

pour les autres,
tu lis ligne par ligne la version, code réponse, etc., puis les entêtes jusqu'à trouver une ligne blanche
et tu peux utiliser le champ content-length si il est là, ou sinon, tu lis aussi jusqu'au bout.

bonne prog.
cs_Dobel
Messages postés
333
Date d'inscription
dimanche 25 mai 2003
Statut
Membre
Dernière intervention
23 novembre 2009
-
ok.
j'espère que ce que je vais dire est juste, j'ai jamais testé et :)


autant que je sache, la méthode readline qu'ils ont fait utilise son buffer pour faire abstraction des trames TCP.
ça doit être un truc du genre :
recv, regarde si il y a un \n (LF) dedans, si oui, coupe au \n, retourne la 1ère partie, conserve la 2nde
sinon, recv à nouveau.

donc 1er readline : commande HTTP, chemin, version
readlines suivant : tous les entêtes, jusqu'à rencontrer une ligne vide

puis lecture du corps de la requête (là, ça va dépendre de la version d'HTTP)


pour le test if not data: break,
il semble que quand le client ferme sa socket, recv retourne "", un paquet vide.
si tu ne gères pas les connexions persistantes, ça doit être bon.

ça doit fonctionner quelque soit le nombre de trames utilisées par le client :)


"car on veut éviter de faire appel à cette fonction si on n'a plus rien à recevoir (et donc éviter qu'elle attende dans le vide)"
=> d'où l'intérêt d'utiliser des threads, pour ne plus avoir cette limitation :)
t'attends que ton client ferme tranquillement sa socket, et hop. ton thread gérant la requête se termine.
cs_jean84
Messages postés
450
Date d'inscription
jeudi 26 août 2004
Statut
Membre
Dernière intervention
5 mars 2009
-
Salut

Je pense que tu m'as mal compris. Le code que tu présentes est celui d'un serveur (fonction bind()) qui ne doit recevoir effectivement que des requêtes, d'où l'intérêt de n'arriver qu'à \r\n (HTTP 1.1).
Mon problème ce positionne côté client : dans le cas d'un forward en HTPP, il est beaucoup plus pratique de recevoir toutes les données (réponse serveur + contenu de la page demandée) pour les renvoyer en intégralité au proxy suivant (que j'ai développé). Je laisse le soin à python (et à TCP) de fragmenter les trames pour ses besoins, je n'ai besoin de travaillé pour ma part qu'avec les données complètes, fragmenter les paquets HTTP devient ensuite un casse tête à gérer(encore une fois, pour mon projet).

Des expériences que j'ai réalisé, beaucoup de serveur envoi leurs entêtes dans un paquet différent de celui du contenu des pages. On peut palier ce problème en réservant un buffer plus important mais on fini par se retrouver avec des entêtes et une partie de la page HTML dans un buffer. Suivant le site, on peut essayer de 'prévoir' la taille du buffer en réception mais je ne trouve pas cela très propre et il existera toujours des exceptions (on ne peut pas connaitre à l'avance la taille que fera un site).
Je me suis basé ensuite sur le champ content-lenght mais la encore, chaque serveur fait sa sauce (suivant sa configuration essentiellement et sa façon d'implémenter le protocole HTTP) et n'envoi pas toujours ce param (ça faciliterai beaucoup de choses pourtant :^) )
J'ai trouvé ensuite plusieurs astuces ici et là mais aucune d'entre elles n'a fonctionné (je m'y suis peut être pris comme un manche, c'est possible aussi) :
- le site du zéro indique de tester le retour de recv() avec not (un peu comme tu l'as fait). Sur de nombreux forum, j'ai pu lire que cela ne fonctionne pas et ce pour une simple raison, c'est qu'on teste le buffer de sortie après l'appel a recv(), donc inutile car on veut éviter de faire appel à cette fonction si on n'a plus rien à recevoir (et donc éviter qu'elle attende dans le vide)
- progx.org donne une solution à ce problème grâce à la fonction socket.setblocking(0). Pour mon utilisation, cela n'a pas fonctionné car apparemment il faut que les communications soient absolument synchrone (au centième de seconde près). Il faut que le paquet à recevoir arrive au moment ou python exécute la fonction sinon elle passe par le try: except: et le paquet est rejeté (ou du moins, il n'est pas pris en compte)
- une dernière solution concerne les threads. Pas utile dans mon cas car je veux garder la synchronisation du serveur et du client : l'un envoi, l'autre reçoit puis envoi à son tour etc...
Même si le protocole HTTP 1.1 gère les envois multiples d'entêtes sans attendre le retour, ce ne faisait pas parti de mon cahier des charges. Je n'ai pas besoin d'envoyer et recevoir en même temps donc j'ai préféré zappé (bien que l'implémentation des threads en python soit excessivement simple, c'est indéniable)

C'est donc après mes petites recherches que j'en suis arrivé à ce code. Je le dis je le répète, je suis nouveau en python. Il est évident que j'ai du passer à coté de quelque chose mais n'ayant pas trouvé de solution probante, j'ai du me résoudre à l'obtenir par mes propres moyens.

Merci de m'avoir lu jusque là et si j'ai dit une bêtise, merci de me le signaler ;-)

++
cs_Dobel
Messages postés
333
Date d'inscription
dimanche 25 mai 2003
Statut
Membre
Dernière intervention
23 novembre 2009
-
Pour info, les objets sockets en python contiennent déjà de quoi faire ce genre de lecture.
tu as une méthode makefile pour créer des objets type fichier, qui travaille sur des buffers.

s.bind((HOST, PORT))
s.listen(1)
conn, addr = s.accept()

# pseudo fichier en lecture, binaire, buffer taille par défaut du système
rfile = conn.makefile('rb', -1)

while 1:
data = rfile.readline()
if not data: break
print data

readline retourne bien la chaîne jusqu'au premier \n rencontré, et non pas juste à la fin de ce que te retourne recv

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.