2010-01-20 2 views
3

Pour notre service web, j'ai écrit une logique pour empêcher multipart/form-data POST plus grand que, disons, 4mb.La réinitialisation de la connexion TCP se produit lorsque l'application WSGI répond avant de consommer environ ['wsgi.input']

Il se résume à ce qui suit (je l'ai débarrassais tout usage de WebOb et juste réduit à code WSGI plain vanilla):

import paste.httpserver 

form = """\ 
<html> 
<body> 
    <form method="post" enctype="multipart/form-data" action="/"> 
    <input type="file" name="photopicker" /> 
    <input type="submit" /> 
    </form> 
</body> 
</html> 
""" 

limit = 4 * 1024 * 1024 

def upload_app(environ, start_response): 
    if environ['REQUEST_METHOD'] == 'POST': 
     if int(environ.get('CONTENT_LENGTH', '0')) > limit: 
      start_response('400 Ouch', [('content-type', 'text/plain')]) 
      return ["Upload is too big!"] 
    # elided: consume the file appropriately 
    start_response('200 OK', [('content-type', 'text/html')]) 
    return [form] 

paste.httpserver.serve(upload_app, port=7007) 

Les œuvres présentées logique droite lorsque l'appareil testé. Mais dès que j'essayé d'envoyer des fichiers réels de plus de 4 Mo à ce point final, je suis arrivé des erreurs comme celles-ci sur le côté client:

  • Error 101 (net::ERR_CONNECTION_RESET): Unknown error. de Google Chrome
  • The connection to the server was reset while the page was loading. de Firefox

même erreur se produit lors de l'utilisation de Python wsgiref serveur HTTP intégré.

Fait: une fois que j'ai ajouté environ['wsgi.input'].read() juste avant de répondre avec HTTP 400, le problème de réinitialisation de la connexion a disparu. Bien sûr, ce n'est pas une bonne solution. Il montre juste ce qui se passe lorsque vous consommez complètement l'entrée.

J'ai lu HTTP: The Definitive Guide et j'ai trouvé quelques lignes directrices intéressantes sur la façon dont il était important de gérer soigneusement les connexions TCP lors de l'implémentation de serveurs et de clients HTTP. Il a continué sur comment, au lieu de close-socket, il était préférable de faire shutdown, de sorte que le client ait eu la chance de réagir et d'arrêter d'envoyer plus de données au serveur. Peut-être qu'il me manque des détails d'implémentation cruciaux qui empêchent de telles réinitialisations de connexion. Insights quelqu'un?

Voir the gist.

+0

J'ai fait d'autres recherches en ligne. Il s'avère que limiter la taille du corps gracieusement est mal adressée. auteur Nginx Igor Sysoev a une écriture à ce sujet: http://translate.google.com/translate?hl=en&sl=ru&tl=en&u=http://sysoev.ru/web/upload.html Pour résumer: vous devez effectuer une danse "close close", où le serveur envoie la réponse d'erreur, arrête le socket pour l'écriture, et attend le client pendant un certain temps afin d'éviter juste de tuer la connexion à droite. –

+0

Un autre lien pertinent: http://tools.ietf.org/html/draft-ietf-http-connection-00#section-8 Il conseille que le serveur HTTP doit fermer "la moitié de la connexion". –

Répondre

0

Cela se produit parce que vous terminez d'utiliser le flux d'entrée sans le lire, ce qui est forcement fermé. Le navigateur a mis en file d'attente une bonne partie du fichier à envoyer, puis il reçoit une erreur d'écriture car le serveur ferme la connexion avec force.

Il n'y a aucun moyen de contourner cela que je connais sans lire toutes les entrées.

Je recommanderais quelques Javascript pour examiner la taille du dossier avant qu'il soit envoyé. Ensuite, les seules personnes qui reçoivent l'erreur sont celles qui ignorent la vérification côté client parce qu'elles n'ont pas de Javascript ou parce qu'elles tentent délibérément d'être malveillantes.

+1

Malheureusement, sans ActiveX, Java ou Flash sur le côté client, vous ne pouvez pas interroger l'élément d'entrée sur le formulaire même lorsque vous recherchez des choses innocentes comme la taille du fichier sélectionné. –

+0

@Pavel Repin, je ne m'en suis pas rendu compte à propos de Javascript. C'est malheureux.Je signalerai cependant que ma réponse est toujours fondamentalement correcte, même si ce n'est pas une réponse heureuse. :-) – Omnifarious

Questions connexes