Panjang Konten tidak dikirim saat kompresi gzip diaktifkan di Apache?

13

Saya akan sangat menghargai bantuan memahami perilaku Apache ini.

Saya berkomunikasi dengan PHP dari aplikasi iPhone Objective-C di application / json. Kompresi Gzip diaktifkan di server, dan diminta oleh klien.

Dari .htaccess saya:

AddOutputFilterByType DEFLATE text/html text/plain text/xml application/x-httpd-php application/json

Untuk permintaan kecil, Apache sedang mengatur header 'Content-Length'. Misalnya (nilai-nilai ini adalah output dalam Objective-C dari header):

Connection = "Keep-Alive";
"Content-Encoding" = gzip;
"Content-Length" = 185;     <-------------
"Content-Type" = "application/json";
Date = "Wed, 22 Sep 2010 12:20:27 GMT";
"Keep-Alive" = "timeout=3, max=149";
Server = Apache;
Vary = "Accept-Encoding";
"X-Powered-By" = "PHP/5.2.13";
"X-Uncompressed-Content-Length" = 217;

X-Uncompressed-Content-Length adalah header yang saya tambahkan set ke ukuran string JSON yang tidak terkompresi.

Seperti yang Anda lihat, permintaan ini sangat kecil (217 byte).

Inilah tajuk dari permintaan yang lebih besar (282.888 bytes):

Connection = "Keep-Alive";
"Content-Encoding" = gzip;
"Content-Type" = "application/json";
Date = "Wed, 22 Sep 2010 12:20:29 GMT";
"Keep-Alive" = "timeout=3, max=148";
Server = Apache;
"Transfer-Encoding" = Identity;
Vary = "Accept-Encoding";
"X-Powered-By" = "PHP/5.2.13";
"X-Uncompressed-Content-Length" = 282888;

Perhatikan bahwa Panjang Konten tidak diberikan.

Pertanyaan saya:

  1. Mengapa Apache tidak mengirim Panjang Konten untuk permintaan yang lebih besar?
  2. Apakah fakta bahwa 'Contend-Encoding = gzip' diatur berarti kompresi gzip masih bekerja pada permintaan yang lebih besar, walaupun saya tidak dapat memverifikasi perbedaan ukuran?
  3. Apakah ada cara saya bisa membuat Apache menyertakan Panjang Konten aktual untuk permintaan yang lebih besar ini agar lebih akurat melaporkan penggunaan data kepada pengguna?

Aplikasi ini dapat digunakan pada paket data yang mahal, karenanya keinginan saya untuk melaporkan penggunaan aktual kepada pengguna, bukan 30-70% penggunaan yang meningkat (beberapa ratus KB tambahan mungkin kedengarannya tidak banyak - tetapi paket ini dapat menelan biaya antara $ 1 dan $ 10 per MB!).

Terima kasih sebelumnya.

William Denniss
sumber

Jawaban:

14

Tambahan jawaban Martin Fjordvalds:

Apache menggunakan pengkodean chunked hanya jika ukuran file terkompresi lebih besar dari DeflateBufferSize. Meningkatkan ukuran buffer ini karena itu akan mencegah server menggunakan pengkodean chunked juga untuk file yang lebih besar, menyebabkan Content-Length dikirim bahkan untuk data zip.

Informasi lebih lanjut tersedia di sini: http://httpd.apache.org/docs/2.2/mod/mod_deflate.html#deflatebuffersize

Philippe
sumber
Bagus Ini mungkin cara tercepat untuk menyelesaikan masalah ini. Jika ada yang membutuhkan penyesuaian tingkat tinggi (mis., Memotong beberapa permintaan, bukan yang lain), lihat jawaban serverfault.com/a/183856/54957 untuk solusi manual.
William Denniss
7

Kedengarannya seperti Apache melakukan pengkodean chunked, ini berarti ia dapat mengirim data karena sedang di-gzip alih-alih menunggu respons penuh untuk di-gzip. Ini praktik yang cukup standar, saya tidak cukup akrab dengan Apache untuk mengatakan apakah itu dapat dinonaktifkan.

Martin Fjordvald
sumber
Terima kasih atas informasinya, Anda mengarahkan saya ke arah yang benar, dan saya menyelesaikannya.
William Denniss
Diterima Bagi siapa pun yang membaca pertanyaan ini - harap baca jawaban saya untuk solusi terperinci. Pada dasarnya, Anda dapat menghindari chunking (dan dengan demikian panjang konten-nol) dengan buffering dan mengompresi balasan secara manual.
William Denniss
Agak membingungkan bahwa jawaban yang diterima bukanlah jawaban untuk pertanyaan awal, tetapi sesuatu yang membantu Anda mendapatkannya. Mungkin Anda harus menerima jawaban yang Anda posting di bawah ini untuk membuat semuanya lebih jelas.
redbmk
@redbmk fair point, saya hanya tidak ingin terlihat tidak berterima kasih. Philippe sebenarnya memiliki perbaikan sederhana yang sempurna untuk ini, jadi saya menerima miliknya.
William Denniss
5

OK, saya berhasil menyelesaikan ini. Seperti yang ditunjukkan Martin F dengan benar, Apache memotong jawaban sehingga ukuran konten tidak diketahui. Bagi banyak orang ini diinginkan (halaman memuat lebih cepat). Ini harus dibayar dengan tidak dapat melaporkan kemajuan pengunduhan.

Bagi mereka seperti saya yang benar-benar ingin melaporkan kemajuan pengunduhan, jika Anda menggunakan dukungan gzip otomatis dari Apache atau PHP, ada sedikit yang bisa Anda lakukan. Solusinya adalah melakukannya secara manual. Lebih mudah daripada kedengarannya:

Jika Anda mengirim seluruh file, maka ini adalah contoh yang bagus dalam PHP untuk memaksa satu potongan (dengan Content-Length): http://www.php.net/manual/en/function.ob-start.php # 94741

Jika Anda mengirim data yang dihasilkan, gunakan gzencode untuk menyandikan data Anda, seperti dalam contoh di atas. Prasyarat adalah bahwa semua data output Anda disimpan dalam variabel (Anda dapat menggunakan ob_start untuk membantu ini jika Anda perlu buffer, lalu dapatkan konten buffer).

        // $replyBody is the entire contents of your reply

        header("Content-Type: application/json");  // or whatever yours is

        // checks if gzip is supported by client
        $pack = true;
        if(empty($_SERVER["HTTP_ACCEPT_ENCODING"]) || strpos($_SERVER["HTTP_ACCEPT_ENCODING"], 'gzip') === false)
        {
            $pack = false;
        }

        // if supported, gzips data
        if($pack) {
            header("Content-Encoding: gzip");
            $replyBody = gzencode($replyBody, 9, FORCE_GZIP);
        }

        // compressed or not, sets the Content-Length           
        header("Content-Length: " . mb_strlen($replyBody, 'latin1'));

        // outputs reply & exits
        echo $replyBody;
        exit;

Dan voila!

Manfaat besar lainnya dari melakukannya sendiri adalah Anda dapat mengatur level kompresi. Ini bagus untuk aplikasi seluler saya, karena saya dapat mengatur ke tingkat kompresi tertinggi (sehingga pengguna saya membayar lebih sedikit untuk data!) - sedangkan server mungkin hanya menggunakan tingkat kompresi sedang untuk pertukaran ukuran CPU / ukuran yang lebih baik. Level kompresi adalah sesuatu yang saya percaya Anda hanya dapat berubah jika Anda dapat mengedit httpd.conf (yang pada hosting bersama, saya tidak bisa).

Jadi saya telah menyimpan direktif DEFLATE .htaccess saya untuk semuanya kecuali aplikasi / json saya yang sekarang saya encode dengan cara di atas.

Terima kasih lagi Martin F, Anda memberi saya percikan yang saya butuhkan untuk menyelesaikan ini :)

William Denniss
sumber
1
Kebetulan, penghematan dengan data JSON (dengan kunci yang berulang-ulang) sangat besar , pengurangan 77% dalam satu kasus. Itu masalah besar dengan $ 1 per MB ...
William Denniss
1
Anda mungkin harus menggunakan strlen($replyBody)saja mb_strlen($replyBody, 'latin1'). Panjang konten hanyalah jumlah byte (bukan karakter), yang memberikan Anda strlen (). Menggunakan mb_strlen () dengan semacam 'latin1' bekerja karena karakter latin1 selalu 8 bit, tetapi mungkin memiliki masalah dengan pengkodean yang menghasilkan byte yang bukan karakter latin1 yang valid.
orrd