12 votes

Gestion de l'encodage chunked HTTP avec Django

J'ai un problème avec la gestion de l'encodage de transfert en morceaux (chunked) HTTP.

Je suis en train d'utiliser :

  • apache.
  • le plugin mod_wsgi.
  • django.

Django est seulement capable de gérer des requêtes HTTP régulières avec le champ d'en-tête Content-Length, mais lorsqu'il s'agit de gérer TE (Transfer-Encoding), chunked ou gzip, il renvoie un résultat vide.

Je pense à 2 approches :

  1. Faire quelques modifications au fichier python django.wsgi
  2. Ajouter un fichier middleware python à django pour intercepter toute demande HTTP en morceaux, la convertir en une demande HTTP régulière avec le champ d'en-tête Content-Length, puis la transmettre à django où elle pourra être traitée correctement.

Quelqu'un peut aider avec l'une des 2 options ci-dessus (d'autres options sont bien sûr les bienvenues)

Merci!


Ceci est une extension à ma question après la première réponse de Graham:

Tout d'abord, merci pour votre réponse rapide. Le client utilisé est Axis, qui fait partie du système d'une autre entreprise communiquant avec le nôtre. J'avais défini WSGIChunkedRequest On, j'ai également apporté quelques modifications à mon wrapper wsgi comme ceci :

def application(environ, start_response):

    if environ.get("mod_wsgi.input_chunked") == "1":
        stream = environ["wsgi.input"]
        print stream
        print 'type: ', type(stream)
        length = 0
        for byte in stream:
            length+=1
        environ["CONTENT_LENGTH"] = len(stream.read(length))

    django_application = get_wsgi_application()
    return django_application(environ, start_response)

mais cela me donne ces erreurs (extraites du fichier error.log d'apache):

[Sat Aug 25 17:26:07 2012] [error] 
[Sat Aug 25 17:26:07 2012] [error] type:  
[Sat Aug 25 17:26:08 2012] [error] [client xxxxxxxxxxxxx] mod_wsgi (pid=27210): Une exception s'est produite lors du traitement du script WSGI '/..../wsgi.py'.
[Sat Aug 25 17:26:08 2012] [error] [client xxxxxxxxxxxxx] Traceback (dernier appel le plus récent) :
[Sat Aug 25 17:26:08 2012] [error] [client xxxxxxxxxxxxx]   Fichier "/..../wsgi.py", ligne 57, dans l'application
[Sat Aug 25 17:26:08 2012] [error] [client xxxxxxxxxxxxx]     pour octet dans le flux:
[Sat Aug 25 17:26:08 2012] [error] [client xxxxxxxxxxxxx] IOError: erreur de lecture des données de la demande

Qu'est-ce que je fais de mal ?!

15voto

Graham Dumpleton Points 23711

Ce n'est pas un problème de Django. Il s'agit d'une limitation de la spécification WSGI elle-même dans la mesure où la spécification WSGI interdit l'utilisation de contenu de requête chunked en exigeant une valeur CONTENT_LENGTH pour la requête.

Lorsque vous utilisez mod_wsgi, il y a un interrupteur pour activer le support non standard pour le contenu de requête chunked, mais cela signifie que votre application n'est pas conforme à WSGI, et cela nécessiterait une application web personnalisée ou un wrapper WSGI car cela ne fonctionnera toujours pas avec Django.

L'option dans mod_wsgi pour autoriser le contenu de requête chunked est :

WSGIChunkedRequest On

Votre wrapper WSGI devrait appeler wsgi.input.read() pour obtenir tout le contenu, créer une instance StringIO avec celui-ci et utiliser cela pour remplacer wsgi.input, puis ajouter également une nouvelle valeur CONTENT_LENGTH à environ avec la longueur réelle avant d'appeler l'application enveloppée.

Remarque que c'est dangereux car vous ne saurez pas combien de données sont envoyées.

Quel client utilisez-vous de toute façon qui ne prend en charge que le contenu de requête chunked?


MISE À JOUR 1

Votre code est cassé pour de nombreuses raisons. Vous devriez utiliser quelque chose comme :

import StringIO

django_application = get_wsgi_application()

def application(environ, start_response):

    if environ.get("mod_wsgi.input_chunked") == "1":
        stream = environ["wsgi.input"]
        data = stream.read()   
        environ["CONTENT_LENGTH"] = str(len(data))
        environ["wsgi.input"] = StringIO.StringIO(data)

    return django_application(environ, start_response)

Notez que cela n'aidera pas avec le contenu de requête gzip'd. Vous auriez besoin d'une vérification supplémentaire pour voir quand le codage du contenu était des données compressées, puis faire la même chose que ci-dessus. Cela est dû au fait que lorsque les données sont décompressées par Apache, la longueur du contenu change et vous devez la recalculer.

3voto

securecurve Points 967

Tout fonctionne maintenant correctement, le problème était dans le mode démon, car il ne fonctionne pas avec le trafic http segmenté, peut-être dans mod_wsgi 4 -- comme l'a indiqué Graham Dumpleton. Donc, si vous avez ce problème, passez mod_wsgi en mode intégré.

Comme modification au code de Graham dans l'enveloppe wsgi, il y a 2 options où vous pouvez lire le flux mis en mémoire tampon dans une variable d'environnement :

Première option :

try:
    while True:
        data+= stream.next()
except:
    print 'Terminé avec la lecture du flux...'

Deuxième option :

try:
   data+= stream.read()
except:
   print 'Terminé avec la lecture du flux...'

Le premier extrait de code était capable de lire la mémoire tampon en mode démon mais s'arrêtait quelque part, et le programme ne continuait pas à fonctionner (ce qui m'a un peu surpris, car je m'attendais à le voir fonctionner correctement), tandis que l'autre extrait de code plantait avec une IOError, et ne fonctionnait que en mode intégré.

Une dernière chose à ajouter, passer de 3.3 à 3.4 n'a pas résolu le problème, donc vous devez passer en mode intégré.

Ce sont mes résultats et mes observations. Si vous avez des commentaires, des ajouts ou des corrections, n'hésitez pas.

Merci !

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X