nginx upload problème client_max_body_size

117

J'utilise nginx / ruby-on-rails et j'ai un simple formulaire en plusieurs parties pour télécharger des fichiers. Tout fonctionne bien jusqu'à ce que je décide de limiter la taille maximale des fichiers que je souhaite télécharger. Pour ce faire, j'ai défini le nginx client_max_body_sizesur 1 m (1 Mo) et j'attends un statut HTTP 413 (Request Entity Too Large) en réponse lorsque cette règle se rompt.

Le problème est que lorsque je télécharge un fichier de 1,2 Mo, au lieu d'afficher la page d'erreur HTTP 413, le navigateur se bloque un peu puis meurt avec un message «La connexion a été réinitialisée pendant le chargement de la page».

J'ai essayé à peu près toutes les options proposées par nginx, rien ne semble fonctionner. Quelqu'un a-t-il des idées à ce sujet?

Voici mon nginx.conf:

worker_processes  1;
timer_resolution  1000ms;
events {
    worker_connections  1024;
}

http {
    passenger_root /the_passenger_root;
    passenger_ruby /the_ruby;

    include       mime.types;
    default_type  application/octet-stream;

    sendfile           on;
    keepalive_timeout  65;

    server {
      listen 80;
      server_name www.x.com;
      client_max_body_size 1M;
      passenger_use_global_queue on;
      root /the_root;
      passenger_enabled on;

      error_page 404 /404.html;
      error_page 413 /413.html;    
    }    
}

Merci.


**Edit**

Environnement / UA: Windows XP / Firefox 3.6.13

Krukid
la source

Réponses:

128

nginx "échoue rapidement" lorsque le client l'informe qu'il va envoyer un corps plus grand que le client_max_body_sizeen envoyant une réponse 413 et en fermant la connexion.

La plupart des clients ne lisent pas les réponses tant que le corps de la requête n'est pas entièrement envoyé. Étant donné que nginx ferme la connexion, le client envoie des données au socket fermé, provoquant un TCP RST.

Si votre client HTTP le prend en charge, la meilleure façon de gérer cela est d'envoyer un en- Expect: 100-Continuetête. Nginx prend en charge cela correctement à partir de 1.2.7 et répondra avec une 413 Request Entity Too Largeréponse plutôt que 100 Continuesi Content-Lengthdépasse la taille maximale du corps.

Joe Shaw
la source
1
Oh, je dois souligner que cette réponse suppose que le client envoie Content-Lengthplutôt que de faire Transfer-Encoding: chunked.
Joe Shaw
2
L'auteur de nginx a posté un correctif pour résoudre ce problème sur la liste de diffusion: nginx.2469901.n2.nabble.com / ... On ne sait pas s'il sera ajouté à la branche stable 1.2.x, cependant.
Joe Shaw
Merci, cela explique beaucoup de choses. Cela ressemble certainement à Expectla voie à suivre pour les demandes importantes.
krukid
J'ai mis à jour ma réponse pour noter que le correctif que j'ai mentionné précédemment a été validé et incorporé dans la version 1.2.7.
Joe Shaw
Juste pour gagner du temps à chercher une belle syntaxe (comme je l'ai passé): request.setHeader(HttpHeaders.EXPECT, CONTINUE);avec import org.apache.http.HttpHeaders;etimport static org.jboss.netty.handler.codec.http.HttpHeaders.Values.CONTINUE;
Erez Cohen
48

Votre téléchargement meurt-il à la toute fin? 99% avant de s'écraser? Le corps du client et les tampons sont essentiels car nginx doit tamponner les données entrantes. Les configurations du corps (données du corps de la requête) spécifient comment nginx gère le flux en vrac de données binaires à partir de clients à formulaires en plusieurs parties dans la logique de votre application.

Le cleanparamètre libère de la mémoire et des limites de consommation en demandant à nginx de stocker le tampon entrant dans un fichier, puis de nettoyer ce fichier ultérieurement du disque en le supprimant.

Set body_in_file_onlyde cleantampons pour le régler et client_max_body_size. La configuration d'origine de la question avait déjà sendfile activé, augmentez également les délais d'expiration. J'utilise les paramètres ci-dessous pour résoudre ce problème, appropriés dans vos contextes de configuration, de serveur et http locaux.

client_body_in_file_only clean;
client_body_buffer_size 32K;

client_max_body_size 300M;

sendfile on;
send_timeout 300s;
Cardan courbé
la source
Même si cela aboutit à ce que nginx renvoie un HTTP 413 approprié, UA finira toujours par envoyer l'intégralité du corps de la requête, n'est-ce pas? Dans ce cas, je pense qu'il vaut la peine d'essayer l'approche suggérée par @ joe-shaw.
krukid
@krukid quand il semble que nous avons obtenu 99% de téléchargement terminé avant que NGINX "échoue rapidement", je suis d'accord avec vous. Dans ce cas, tous les signes sont positifs autour de l'objet de requête, c'est-à-dire que le diagnostic est que la logique interne de l'application serveur est correcte - tout ce qui court derrière Nginx. Ainsi, bien qu'il soit probable que la demande ait été bien formulée, nous devons alors examiner pourquoi NGINX s'étouffe sur la réponse. client_max_body_size devrait être la première option de configuration que nous examinons, puis considérez les tampons, car avec un téléchargement suffisamment grand, la solution correcte dépend de la quantité de mémoire que notre serveur peut également gérer.
Bent Cardan
@Bent Cardan. Cette approche semblait être la meilleure et je l'ai essayée. Mais j'obtiens toujours une erreur 413 après environ 20 secondes pour un fichier de 4 Mo. Mes vitesses élevées ne peuvent pas gérer 4 Mo en 20 secondes, donc cela se produit après que les données ont circulé pendant un certain temps. Pensées?
Jerome
J'ai ajouté les modifications dans le fichier nginx.conf _client_max_body_size 300M; sendfile activé; send_timeout 300s; _ Cela fonctionne parfaitement pour moi Merci
Ramesh Chand
La solution fonctionne pour moi openshift php7 nginx.
marlo
7

De la documentation :

Il faut garder à l'esprit que les navigateurs ne savent pas comment afficher correctement cette erreur.

Je soupçonne que c'est ce qui se passe, si vous inspectez le HTTP dans les deux sens en utilisant des outils tels que Firebug ou Live HTTP Headers (les deux extensions Firefox), vous pourrez voir ce qui se passe réellement.

ZoFreX
la source
1
Je l'ai rencontré ici aussi: forum.nginx.org/read.php?2,2620 Là où l'auteur de nginx dit que les gens pourraient essayer de changer lingering_time / lingering_timeout - les deux n'ont eu aucun effet dans mon cas. En outre, je ne vois tout simplement pas comment il pourrait y avoir un problème de délai d'attente persistant lorsque je télécharge un fichier de 1,2 Mo avec une limite de 1 Mo avec une connexion stable de 5 Mbps. J'ai reniflé la réponse et elle envoie la page 413 avec l'en-tête "Connection: close", mais la connexion ne semble pas se fermer.
krukid
Je suppose que j'ai juste du mal à croire que même s'il existe un statut HTTP 413 parfaitement valide, il ne se déclenche pas dans les navigateurs. J'ai recherché sur Google de nombreux endroits où les gens ne peuvent pas se débarrasser de cette page, et je ne l'ai même jamais vu.
krukid
Si vous désactivez le passager, ferme-t-il la connexion?
Mark Rose
Eh bien, j'ai comparé les réponses avec et sans passager. Lorsque tout fonctionne normalement et que je télécharge un fichier plusieurs fois plus gros (~ 14 Mo) que ma restriction de 1 Mo, j'obtiens 413 réponses plusieurs fois (car le client continue d'envoyer des morceaux) et la "réinitialisation de la connexion" finale ressemble à un délai d'attente. Sans passager, j'obtiens une réponse instantanée 413 et toute progression s'arrête, mais je vois toujours la page "Connexion réinitialisée", pas mon 413.html statique ou tout ce qui implique "Entity Too Large"
krukid