Nginx usuwa nagłówek Content-Length dla fragmentów

10

Używam nginx 1.2.3 do proxy do skryptu:

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

Skrypty wysyła zarówno Transfer-encoding: chunkeda Content-Length: 251:

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

Potrzebuję obu, ale nginx automatycznie usuwa 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: /...

W rezultacie klienci nie czekają na wysłanie porcji. To działało z wcześniejszą wersją nginx.

Julien
źródło
Jak wyglądają nagłówki z proxy Nginx?
polowanie
z jaką wersją współpracował?
cnst
Kiedyś działał z nginx 0.9.8
Julien
Naruszasz protokół HTTP. Działa z Nginx 0.9.8, ponieważ do wersji 1.1.4 w ogóle nie obsługuje kodowania fragmentarycznego.
VBart

Odpowiedzi:

11

Niestety nie mogę komentować postu cnst - więc odpowiem tutaj.

nginx_http_proxyModuł domyślnie rozmów z Upstream w HTTP / 1.0. Można to zmienić za pomocą dyrektywy proxy_http_version 1.1.

Może to być również przyczyną, dla której skrypt zwraca odpowiedź HTTP / 1.0, chociaż 307w tej wersji nie istnieje kodowanie dzielone i kod statusu .

Nie powinieneś także używać kodowania fragmentarycznego z przekierowaniem , ponieważ tak naprawdę nie ma to sensu.

Dodatkowo wygląda na to, że nginx nie przekazuje fragmentów od klienta do klienta jeden po drugim, ale buforuje odpowiedź użytkownika . Pole Content-Lengthnagłówka jest ignorowane, ponieważ jest niezgodne z definicją. Musiałem spojrzeć na kod źródłowy modułu, ponieważ wszystko to wydaje się nieudokumentowane.

Możesz wypróbować nginx_tcp_proxy_moduleproxy do fragmentacji jako surowych danych TCP: Moduł w Github


UPDATE (10/04/14) moduł obsługuje nagłówków , z których jeden ( ) określa, czy odpowiedź powinna być buforowane lub nie.
nginx_http_proxyX-Accel-* X-Accel-Buffering: yes|no

Dodanie tego nagłówka ( X-Accel-Buffering: no) do odpowiedzi backendu spowoduje, że nginx bezpośrednio przekaże porcje do klienta.

Ten nagłówek pozwala kontrolować buforowanie na podstawie żądania .

Moduł ma również dyrektywę konfiguracji, proxy_buffering która włącza lub wyłącza buforowanie odpowiedzi (brak buforowania oznacza, że ​​wysyłanie porcji będzie działać).

Buforowanie proxy (zarówno na podstawie nagłówka, jak i dyrektywy) jest tutaj udokumentowane .

Lukas
źródło
Nie powinien tego robić nawet z nginx_tcp_proxy_module. Działa tylko z niektórymi przeglądarkami, ponieważ są one bardzo odporne na błędy.
VBart
ponieważ wszystko to wydaje się nieudokumentowane Źle. Jest to udokumentowane w RFC 2616. Patrz 13.5.1 .
VBart
@VBart Pewnie, że istnieją standardy - ale jest bardzo niewiele informacji o tym, jak bardzo je implementuje nginx . Moduł proxy TCP jest sugerowanym obejściem .
Lukas
9

Jak wspominał Lukas, HTTP 1.1 zabrania, Content-Lengthjeśli istnieje Transfer-Encodingzestaw.

Cytując 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
źródło
Ponadto prawidłowe zachowanie Nginx zgodnie z HTTP 1.1 znacznie przyczynia się do zapobiegania atakom z przemytu żądań HTTP .
AMN
3

Nie wyjaśniłeś szczegółowo, dlaczego twój skrypt wymaga kodowania fragmentarycznego, zwłaszcza z odpowiedzią przekierowującą.

Widzę tu wiele problemów.

  • Transfer-Encoding: chunkedjest HTTP/1.1funkcją (a skrypt wydaje się odpowiadać za pomocą HTTP/1.0nagłówka)

  • nie ma 307wHTTP/1.0

  • cały ten cel chunkedpolega na tym, że nie wiesz, co Content-Lengthbyś był, więc chunkedzostał użyty zamiast podania długości w środku Content-Length, gdzie zamiast tego podano długości w treści odpowiedzi, zmieszanej z rzeczywistą treścią; nie ma sensu, aby skrypt generował oba nagłówki z góry

Nie znam się osobiście chunked, ale zgodnie z podstawowymi informacjami na stronie http://en.wikipedia.org/wiki/Chunked_transfer_encoding, a także http://tools.ietf.org/html/rfc2616#section-3.6.1 , Zgadłbym, że cała obsługa kodowania fragmentów w twoim skrypcie może być całkowicie niepoprawna.

Jeśli powyższe nadal go nie obejmuje, a w rzeczywistości jest inaczej, nie jest jasne, dlaczego odpowiedź z kodem stanu 307lub 302http powinna być wyposażona w „dziwne” kodowanie. Niedawno odbyła się podobna dyskusja na liście mailingowej nginx 410 Gonei inne strony błędów zawsze wykluczone z gzipkompresji, i myślę, że sentyment miałby zastosowanie również tutaj. ( http://mailman.nginx.org/pipermail/nginx/2013-March/037890.html )

cnst
źródło
Używam go, aby użytkownik czekał: wysyłam porcje co sekundę, dzięki czemu użytkownik będzie czekał na przekierowanie przez X sekund bez limitu czasu
Julien
Radzę najpierw naprawić HTTP / 1.0 do HTTP / 1.1 (te rzeczy robią różnicę) i upewnić się, że fragmentowanie kodowania nie jest nieprawidłowe. Nowsza wersja nginx prawdopodobnie odrzuca niektóre nagłówki, od których zależysz, ponieważ są one nieprawidłowe.
cnst
1

Miałem ten sam problem ze strumieniowaniem pliku MP4 przez tag wideo HTML5.

Safari i Firefox zachowywały się normalnie, podczas gdy Chrome uruchamiał ERR_CONTENT_LENGTH_MISMATCH w pewnym momencie (ale pozwoliło mi to na obejrzenie kilku minut filmu przed niepowodzeniem).

Problem nie został odtworzony po wyłączeniu kontroli pamięci podręcznej dla plików MP4.

Buzut
źródło
0

Udostępniając tę ​​odpowiedź wysłałem do SO, na wypadek, gdyby była pomocna: /programming/50499637/mp4-video-safari-cloudflare-nginx-rails-no-play/59348509#59348509

Miałem podobny problem z odtwarzaniem MP4 z powodu braku obsługi fragmentów i potwierdziłem problem według przewodnika Apple'a wymienionego poniżej. Potwierdziłem, że pobierałem cały plik, a po poprawce poniżej tylko pierwszy fragment.

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

Rozwiązałem odtwarzanie Safari .mp4, zmieniając ustawienia kompresji gzip w pliku nginx.conf, aby usunąć kompresję gzip plików .mp4 .

Oto blok w nginx w celach informacyjnych. (Uwaga: w zależności od konfiguracji aplikacji może być konieczna zmiana linii lokalizacji nalocation ~ \.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;
}

Odsyłacz do dokumentacji Apple: https://developer.apple.com/library/archive/documentation/AppleApplications/Reference/SafariWebContent/CreatingVideoforSafarioniPhone/CreatingVideoforSafarioniPhone.html#//apple_ref/doc/uid/TP465

Kelton.Temby
źródło