Nginx menghapus header Panjang Konten untuk konten yang dipotong

10

Saya menggunakan nginx 1.2.3 untuk proksi ke skrip:

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

Script mengirim keduanya Transfer-encoding: chunkeddan Content-Length: 251:

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

Saya membutuhkan keduanya, tetapi nginx secara otomatis menghapus 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: /...

Akibatnya, klien tidak menunggu bongkahan dikirim. Ini digunakan untuk bekerja dengan versi nginx sebelumnya.

Julien
sumber
Seperti apa tampilan header dari proksi nginx?
hrunting
versi apa yang digunakan untuk bekerja dengannya?
cnst
Dulu bekerja dengan nginx 0.9.8
Julien
Anda melanggar protokol HTTP. Ini bekerja dengan nginx 0.9.8, karena sampai versi 1.1.4 tidak mendukung penyandian chunked sama sekali.
VBart

Jawaban:

11

Sayangnya, saya tidak dapat mengomentari kiriman cnst - jadi saya akan menjawab di sini.

The nginx_http_proxymodul dengan pembicaraan default dengan hulu di HTTP / 1.0. Ini bisa diubah dengan arahan proxy_http_version 1.1.

Ini mungkin juga menjadi penyebab skrip Anda mengembalikan jawaban HTTP / 1.0, meskipun kode chunked dan kode status 307tidak ada dalam versi ini.

Anda juga tidak boleh menggunakan chunked coding dengan redirect , karena ini tidak masuk akal.

Selain itu, sepertinya nginx tidak meneruskan bongkahan dari hulu ke klien satu per satu, tetapi ia mendukung respons hulu . Bidang Content-Lengthtajuk diabaikan karena melanggar definisi. Saya harus melihat kode sumber modul karena semua ini tampaknya tidak berdokumen.

Anda mungkin ingin mencoba nginx_tcp_proxy_moduleproksi konten chunked sebagai data TCP mentah: Modul di Github


UPDATE (10.04.14)
The nginx_http_proxymodul memiliki dukungan untuk X-Accel-* header , yang satu ( X-Accel-Buffering: yes|no) mengontrol apakah respon harus buffered atau tidak.

Menambahkan header ini ( X-Accel-Buffering: no) ke respons backend akan menyebabkan nginx untuk secara langsung meneruskan potongan ke klien.

Header ini memungkinkan untuk mengontrol penyanggaan berdasarkan permintaan .

Modul ini juga memiliki arahan konfigurasi proxy_buffering untuk mengaktifkan atau menonaktifkan buffering respons (bukan buffering berarti mengirim potongan akan berfungsi).

Proxy buffering (berbasis header dan direktif) didokumentasikan di sini .

Lukas
sumber
Dia seharusnya tidak melakukan itu bahkan dengan nginx_tcp_proxy_module. Ini bekerja dengan beberapa browser hanya karena mereka sangat toleran terhadap kesalahan.
VBart
karena semua ini tampaknya tidak berdokumen Salah. Ini didokumentasikan dalam RFC 2616. Lihat 13.5.1 .
VBart
@ VBart Tentu ada standar - tetapi hanya ada sedikit informasi tentang seberapa jauh nginx mengimplementasikannya. Modul proksi TCP adalah solusi yang disarankan .
Lukas
9

Sebagai Lukas alludet to, HTTP 1.1 melarang Content-Lengthjika ada Transfer-Encodingset.

Mengutip 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
sumber
Selain itu, perilaku Nginx yang benar dalam kepatuhan terhadap HTTP 1.1 sangat membantu mencegah serangan penyelundupan permintaan HTTP .
amn
3

Anda belum menjelaskan secara terperinci mengapa skrip Anda perlu penyandian chunked, terutama dengan respons pengalihan.

Saya melihat banyak masalah di sini.

  • Transfer-Encoding: chunkedadalah HTTP/1.1fitur (dan skrip Anda tampaknya membalas dengan HTTP/1.0tajuk)

  • tidak ada 307diHTTP/1.0

  • tujuan keseluruhannya chunkedadalah bahwa Anda tidak tahu Content-Lengthakan menjadi apa Anda , jadi, chunkeddigunakan untuk menyediakan panjang di dalam Content-Length, di mana alih-alih panjang disediakan di dalam tubuh respons, dicampur dengan konten yang sebenarnya; tidak ada gunanya bagi skrip untuk membuat kedua header di muka

Saya pribadi tidak terbiasa dengan chunked, tetapi sesuai info dasar di http://en.wikipedia.org/wiki/Chunked_transfer_encoding dan juga http://tools.ietf.org/html/rfc2616#section-3.6.1 , Saya akan memperkirakan bahwa seluruh penanganan skrip chunked skrip Anda mungkin sepenuhnya salah.

Jika hal di atas masih belum mencakupnya, dan dalam kenyataannya semua, sebaliknya, juga tidak jelas mengapa balasan dengan kode status http 307atau 302harus diberikan dengan pengkodean "aneh". Baru-baru ini ada diskusi serupa di milis nginx tentang 410 Gonedan halaman kesalahan lainnya selalu dikecualikan dari gzipkompresi, dan saya pikir sentimen akan sama berlaku di sini. ( http://mailman.nginx.org/pipermail/nginx/2013-March/037890.html )

cnst
sumber
Saya menggunakannya untuk membuat pengguna menunggu: Saya mengirim potongan setiap detik, sehingga pengguna akan menunggu pengalihan selama X detik tanpa mendapat batas waktu
Julien
Saya akan menyarankan Anda untuk memperbaiki HTTP pertama / 1.0 untuk HTTP / 1.1 (hal-hal ini dilakukan membuat perbedaan), dan pastikan encoding chunked Anda tidak benar. Versi nginx yang lebih baru mungkin membuang beberapa tajuk tempat Anda bergantung karena itu salah.
cnst
1

Saya memiliki masalah yang sama saat streaming file mp4 melalui tag video html5.

Safari dan Firefox berperilaku normal sedangkan Chrome memicu ERR_CONTENT_LENGTH_MISMATCH di beberapa titik (tapi itu memungkinkan saya untuk memutar beberapa menit video sebelum gagal).

Masalahnya tidak muncul kembali setelah saya mematikan kontrol cache untuk file mp4.

Buzut
sumber
0

Membagikan jawaban ini yang saya posting ke SO jika bermanfaat: /programming/50499637/mp4-video-safari-cloudflare-nginx-rails-no-play/59348509#59348509

Saya memiliki masalah yang sama dengan pemutaran mp4 karena potongan tidak dilayani, dan mengonfirmasi masalah per panduan Apple, tercantum di bawah ini. Saya memverifikasi saya mengunduh seluruh file, dan setelah perbaikan di bawah ini, hanya potongan pertama.

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

Saya menyelesaikan pemutaran Safari .mp4 dengan mengubah pengaturan kompresi gzip di nginx.conf, untuk menghapus kompresi gzip pada file .mp4 .

Inilah blok di nginx untuk referensi. (Catatan: tergantung pada bagaimana aplikasi Anda dikonfigurasi, Anda mungkin perlu mengubah garis lokasi kelocation ~ \.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;
}

Tautan ke referensi dokumentasi Apple: https://developer.apple.com/library/archive/documentation/AppleApplications/Reference/SafariWebContent/CreatingVideoforSafarioniPhone/CreatingVideoforSafarioniPhone.html#//apple_ref/doc/uid/TP400014

Kelton. Menara
sumber