Nginx supprime l'en-tête Content-Length pour le contenu fragmenté

10

J'utilise nginx 1.2.3 pour proxy vers un script:

proxy_set_header Host $host;
proxy_pass http://127.0.0.1:8880;
proxy_buffering off;
proxy_read_timeout 300s;
gzip off;

Le script envoie les deux Transfer-encoding: chunkedet Content-Length: 251:

HTTP/1.0 307 Temporary Redirect
Content-length: 251
Pragma: no-cache
Location: /...
Cache-control: no-cache
Transfer-encoding: chunked

J'ai besoin des deux, mais nginx supprime automatiquement Content-Length:

HTTP/1.1 302 Found
Server: nginx/1.2.3
Content-Type: application/json; charset=utf-8
Content-Length: 58
Connection: keep-alive
Location: /...

Par conséquent, les clients n'attendent pas l'envoi des morceaux. Cela fonctionnait avec une version antérieure de nginx.

Julien
la source
À quoi ressemblent les en-têtes du proxy nginx?
chasse
avec quelle version fonctionnait-il?
2013
Il fonctionnait avec nginx 0.9.8
Julien
Vous violez le protocole HTTP. Il fonctionne avec nginx 0.9.8, car jusqu'à la version 1.1.4, il ne prend pas du tout en charge l'encodage en morceaux.
VBart

Réponses:

11

Malheureusement, je ne peux pas commenter le post du cnst - je vais donc répondre ici.

Le nginx_http_proxymodule parle par défaut avec l'amont en HTTP / 1.0. Cela peut être changé avec la directive proxy_http_version 1.1.

Cela peut également être la raison pour laquelle votre script retourne une réponse HTTP / 1.0, bien que le codage en morceaux et le code d'état 307n'existent pas dans cette version.

Vous ne devriez pas non plus utiliser de codage fragmenté avec une redirection , car cela n'a pas vraiment de sens.

De plus , il semble que nginx ne transmette pas les morceaux de l'amont au client un par un, mais il tamponne la réponse de l'amont . Le Content-Lengthchamp d'en-tête est ignoré car il est contraire à la définition. J'ai dû regarder le code source du module car tout cela ne semble pas documenté.

Vous voudrez peut-être essayer de mettre nginx_tcp_proxy_moduleen proxy le contenu fragmenté en tant que données TCP brutes: Module sur Github


MISE À JOUR (10.04.14)
Le nginx_http_proxymodule prend en charge les en- X-Accel-* têtes , dont un ( X-Accel-Buffering: yes|no) contrôle si la réponse doit être mise en mémoire tampon ou non.

L'ajout de cet en-tête ( X-Accel-Buffering: no) à la réponse du backend fera que nginx passera directement des morceaux au client.

Cet en-tête permet de contrôler la mise en mémoire tampon à la demande .

Le module dispose également d'une directive de configuration proxy_buffering pour activer ou désactiver la mise en mémoire tampon des réponses (la mise en mémoire tampon ne signifie pas que l'envoi de blocs fonctionnera).

La mise en mémoire tampon du proxy (basée sur l'en-tête et la directive) est documentée ici .

Lukas
la source
Il ne devrait pas faire ça même avec nginx_tcp_proxy_module. Il fonctionne avec certains navigateurs uniquement parce qu'ils sont très tolérants aux erreurs.
VBart
parce que tout cela ne semble pas documenté . Il est documenté dans RFC 2616. Voir 13.5.1 .
VBart
@VBart Bien sûr, il existe des normes - mais il n'y a que très peu d'informations sur la mesure dans laquelle nginx les implémente en particulier.Le module proxy TCP est une solution de contournement suggérée .
Lukas
9

Comme Lukas le pense, HTTP 1.1 interdit Content-Lengths'il y a un Transfer-Encodingensemble.

Citant http://www.ietf.org/rfc/rfc2616.txt :

   3.If a Content-Length header field (section 14.13) is present, its
     decimal value in OCTETs represents both the entity-length and the
     transfer-length. The Content-Length header field MUST NOT be sent
     if these two lengths are different (i.e., if a Transfer-Encoding
     header field is present). If a message is received with both a
     Transfer-Encoding header field and a Content-Length header field,
     the latter MUST be ignored.
Jo Liss
la source
De plus, le comportement correct de Nginx en conformité avec HTTP 1.1 contribue grandement à empêcher les attaques de contrebande de requêtes HTTP .
amn
3

Vous n'avez pas spécifiquement expliqué pourquoi votre script a besoin d'un codage fragmenté en premier lieu, en particulier avec une réponse de redirection.

Je vois ici une multitude de problèmes.

  • Transfer-Encoding: chunkedest une HTTP/1.1fonctionnalité (et votre script semble répondre avec un en- HTTP/1.0tête)

  • il n'y a pas 307dansHTTP/1.0

  • le seul but de chunkedest que vous ne savez pas ce que vous Content-Lengthauriez été, donc, chunkedest utilisé au lieu de fournir la longueur à l'intérieur Content-Length, alors que les longueurs sont fournies dans le corps de la réponse, mélangées avec le contenu réel; il serait inutile qu'un script génère à l'avance les deux en-têtes

Je ne connais pas personnellement chunked, mais selon les informations de base sur http://en.wikipedia.org/wiki/Chunked_transfer_encoding et également http://tools.ietf.org/html/rfc2616#section-3.6.1 , Je dirais que la gestion complète de votre script de l'encodage en morceaux peut être complètement fausse.

Si ce qui précède ne le couvre toujours pas, et en réalité dans le cas contraire, il est également difficile de savoir pourquoi une réponse avec un code d'état 307ou 302http doit être fournie avec un codage "bizarre". Il y a eu récemment une discussion similaire dans la liste de diffusion nginx 410 Goneet d'autres pages d'erreur toujours exclues de la gzipcompression, et je pense que le sentiment s'appliquerait également ici. ( http://mailman.nginx.org/pipermail/nginx/2013-March/037890.html )

cnst
la source
Je l'utilise pour faire patienter l'utilisateur: j'envoie des morceaux toutes les secondes, pour que l'utilisateur attende la redirection pendant X secondes sans obtenir de timeout
Julien
Je vous conseille d'abord fixer HTTP / 1.0 HTTP / 1.1 (ces choses font une différence), et assurez - vous que votre encodage Chunked est pas incorrect. La nouvelle version de nginx élimine probablement certains en-têtes dont vous dépendez car ils sont erronés.
2013
1

J'ai eu le même problème de streaming de fichier mp4 via la balise vidéo html5.

Safari et Firefox se sont comportés normalement tandis que Chrome déclenchait ERR_CONTENT_LENGTH_MISMATCH à un moment donné (mais cela m'a permis de regarder plusieurs minutes de la vidéo avant d'échouer).

Le problème ne s'est pas reproduit après avoir désactivé le contrôle du cache pour les fichiers mp4.

Buzut
la source
0

Partageant cette réponse, j'ai posté sur SO au cas où cela serait utile: /programming/50499637/mp4-video-safari-cloudflare-nginx-rails-no-play/59348509#59348509

J'ai eu un problème similaire avec la lecture mp4 en raison de la non-diffusion de morceaux, et j'ai confirmé le problème selon le guide d'Apple, répertorié ci-dessous. J'ai vérifié que je téléchargeais le fichier entier, et après la correction ci-dessous, seul le premier morceau.

curl --range 0-99 http://example.com/test.mov -o /dev/null

J'ai résolu ma lecture Safari .mp4 en modifiant mes paramètres de compression gzip dans mon nginx.conf, pour supprimer la compression gzip des fichiers .mp4 .

Voici le bloc dans nginx pour référence. (Remarque: selon la configuration de votre application, vous devrez peut-être modifier la ligne delocation ~ \.mp4$ {

location ~ ^/(assets|system|videos)/  {
   expires max;
   add_header Cache-Control public;
   add_header ETag "";
   gzip on;
   gzip_http_version 1.1;
   gzip_vary on;
   gzip_comp_level 6;
   gzip_proxied any;

   # Reference configuration
   #gzip_types text/plain text/html text/css application/json application/javascript application/x-javascript text/javascript video/mp4 application/mp4 image/jpeg image/png image/svg+xml application/x-font-ttf application/x-font-truetype application/font-woff application/font-woff2 application/vnd.ms-fontobject;

   # Kelton trying to fix cloudflare by removing the mp4 settings
   gzip_types text/plain text/html text/css application/json application/javascript application/x-javascript text/javascript image/jpeg image/png image/svg+xml application/application/x-font-ttf application/x-font-truetype application/font-woff application/font-woff2 application/vnd.ms-fontobject;
}

Lien vers la référence de la documentation Apple: https://developer.apple.com/library/archive/documentation/AppleApplications/Reference/SafariWebContent/CreatingVideoforSafarioniPhone/CreatingVideoforSafarioniPhone.html#//apple_ref/doc/uid/TP400014

Kelton.Temby
la source