Nginx удаляет заголовок Content-Length для фрагментированного контента

10

Я использую nginx 1.2.3 для прокси к скрипту:

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

Скрипты отправляют как Transfer-encoding: chunkedи Content-Length: 251:

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

Мне нужны оба, но nginx автоматически удаляет 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: /...

В результате клиенты не ждут отправки чанков. Раньше это работало с более ранней версией nginx.

Julien
источник
Как выглядят заголовки из прокси nginx?
13
с какой версией он раньше работал?
CNST
Раньше он работал с nginx 0.9.8
Julien
Вы нарушаете протокол HTTP. Он работает с nginx 0.9.8, потому что до версии 1.1.4 он вообще не поддерживает чанкованное кодирование.
VBart

Ответы:

11

К сожалению, я не могу комментировать сообщение cnst - поэтому я собираюсь ответить здесь.

nginx_http_proxyМодуль по умолчанию переговоров с верховьями в HTTP / 1.0. Это можно изменить с помощью директивы proxy_http_version 1.1.

Это также может быть причиной того, что ваш сценарий возвращает ответ HTTP / 1.0, хотя чанкованное кодирование и код состояния 307не существуют в этой версии.

Вы не должны использовать чанкованное кодирование с перенаправлением , так как это не имеет смысла.

Кроме того , кажется, что nginx не передает порции от вышестоящего к клиенту один за другим, но буферизует ответ вышестоящего . Поле Content-Lengthзаголовка игнорируется, поскольку оно противоречит определению. Мне пришлось посмотреть на исходный код модуля, потому что все это выглядит недокументированным.

Возможно, вы захотите попробовать nginx_tcp_proxy_moduleпроксировать содержимое в виде сырых данных TCP: Модуль на Github


ОБНОВЛЕНИЕ (10.04.14) модуль имеет поддержку для заголовков , один из которых ( ) определяет , будет ли ответ должен быть буферизованными или нет.
nginx_http_proxyX-Accel-* X-Accel-Buffering: yes|no

Добавление этого заголовка ( X-Accel-Buffering: no) к ответу бэкэнда заставит nginx напрямую передавать чанки клиенту.

Этот заголовок позволяет контролировать буферизацию для каждого запроса .

Модуль также имеет директиву конфигурации proxy_buffering для включения или отключения буферизации ответов (не буферизация означает, что отправка чанков будет работать).

Прокси - буферизация (как заголовок и директива основе) документирован здесь .

Lukas
источник
Он не должен делать это даже с nginx_tcp_proxy_module. Он работает с некоторыми браузерами только потому, что они очень устойчивы к ошибкам.
VBart
потому что все это кажется недокументированным неправильно. Это задокументировано в RFC 2616. См. 13.5.1 .
VBart
@VBart Конечно, есть стандарты - но очень мало информации о том, как далеко их конкретно реализует nginx . Прокси-модуль TCP должен предложить обходной путь .
Лукас
9

Как намекает Лукас, HTTP 1.1 запрещает Content-Lengthналичие Transfer-Encodingнабора.

Цитирование 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.
Джо Лисс
источник
Кроме того, правильное поведение Nginx в соответствии с HTTP 1.1 имеет большое значение для предотвращения атак контрабанды HTTP-запросов .
атп
3

Вы специально не выяснили, почему вашему сценарию нужно в первую очередь частичное кодирование, особенно с ответом на перенаправление.

Я вижу множество проблем здесь.

  • Transfer-Encoding: chunkedэто HTTP/1.1особенность (и ваш скрипт, кажется, отвечает с HTTP/1.0заголовком)

  • нет 307вHTTP/1.0

  • вся цель в chunkedтом, что вы не знаете, чем Content-Lengthбы вы были, поэтому chunkedиспользуется вместо предоставления длины внутри Content-Length, где вместо этого длины предоставляются в теле ответа, смешанного с фактическим содержанием; для сценария было бы бессмысленно генерировать оба заголовка заранее

Я лично не знаком с chunked, но согласно основной информации на http://en.wikipedia.org/wiki/Chunked_transfer_encoding, а также http://tools.ietf.org/html/rfc2616#section-3.6.1 , Я бы предположил, что вся обработка вашего скрипта кусковой кодировки может быть полностью неверной.

Если вышеупомянутое все еще не покрывает это, и во всей действительности иначе, также неясно, почему ответ с кодом состояния 307или 302http должен быть снабжен "странной" кодировкой. Недавно в списке рассылки nginx было похожее обсуждение, 410 Goneи другие страницы с ошибками всегда исключались из gzipсжатия, и я думаю, что мнение будет в равной степени применимо и здесь. ( http://mailman.nginx.org/pipermail/nginx/2013-March/037890.html )

CNST
источник
Я использую его, чтобы заставить пользователя ждать: я отправляю куски каждую секунду, так что пользователь будет ждать перенаправления в течение X секунд без истечения времени ожидания
Julien
Я бы посоветовал Вам сначала исправить HTTP / 1.0 к HTTP / 1.1 (эти вещи делают сделать разницу), и убедитесь , что Chunked кодирование не неприличным. Более новая версия nginx, вероятно, отбрасывает некоторые заголовки, от которых вы зависите, потому что они не правы.
CNST
1

У меня была та же самая проблема потокового файла mp4 через тег видео html5.

Safari и Firefox вели себя нормально, в то время как Chrome в какой-то момент запускал ERR_CONTENT_LENGTH_MISMATCH (но это позволило мне просмотреть несколько минут видео перед тем, как его потерпеть неудачу).

Проблема не воспроизводилась после того, как я отключил управление кэшем для файлов mp4.

Buzut
источник
0

Делясь этим ответом, я отправил сообщение SO на случай, если это будет полезно: /programming/50499637/mp4-video-safari-cloudflare-nginx-rails-no-play/59348509#59348509

У меня была похожая проблема с воспроизведением mp4 из-за того, что куски не обслуживались, и я подтвердил проблему в соответствии с руководством Apple, перечисленным ниже. Я подтвердил, что загружаю весь файл, и после исправления ниже только первый чанк.

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

Я разрешил воспроизведение Safari .mp4, изменив настройки сжатия gzip в файле nginx.conf, чтобы удалить сжатие gzip файлов .mp4 .

Вот блок в nginx для справки. (Примечание: в зависимости от того, как настроено ваше приложение, может потребоваться изменить строку местоположения наlocation ~ \.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;
}

Ссылка на справочную документацию Apple: https://developer.apple.com/library/archive/documentation/AppleApplications/Reference/SafariWebContent/CreatingVideoforSafarioniPhone/CreatingVideoforSafarioniPhone.html#//apple_ref/doc/uid-TP665

Kelton.Temby
источник