Apacheでgzip圧縮が有効になっている場合、Content-Lengthは送信されませんか?


13

このApacheの振る舞いを理解する手助けをしてくれれば本当にありがたいです。

application / jsonのiPhone Objective-CアプリからPHPと通信しています。Gzip圧縮はサーバーで有効になっており、クライアントによって要求されます。

私の.htaccessから:

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

小さなリクエストの場合、Apacheは 'Content-Length'ヘッダーを設定しています。例(これらの値は、Objective-Cのヘッダーから出力されます):

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は、非圧縮JSON文字列のサイズにセットを追加するヘッダーです。

ご覧のとおり、この要求は非常に小さい(217バイト)。

大きなリクエスト(282888バイト)のヘッダーは次のとおりです。

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;

Content-Lengthが指定されていないことに注意してください。

私の質問:

  1. なぜApacheはより大きなリクエストに対してContent-Lengthを送信しないのですか?
  2. 'Contend-Encoding = gzip'が設定されているという事実は、サイズの違いを確認できない場合でも、gzip圧縮がより大きなリクエストでまだ機能していることを意味しますか?
  3. ユーザーにデータ使用量をより正確に報告するために、Apacheにこれらの大きなリクエストの実際のContent-Lengthを含める方法はありますか?

このアプリは高価なデータプランで使用できます。したがって、ユーザーに実際の使用状況を報告します。30〜70%の使用量の増加ではありません(数百KBの追加はあまり聞こえないかもしれません。 MBあたり10ドル!)。

前もって感謝します。

回答:


14

Martin Fjordvaldsの回答に追加:

Apacheは、圧縮ファイルのサイズがDeflateBufferSizeよりも大きい場合にのみ、チャンクエンコーディングを使用します。したがって、このバッファサイズを大きくすると、サーバーが大きなファイルに対してもチャンクエンコーディングを使用できなくなり、圧縮されたデータであってもContent-Lengthが送信されます。

詳細については、http//httpd.apache.org/docs/2.2/mod/mod_deflate.html#deflatebuffersizeを参照してください。


良いですね。これはおそらく、この問題を解決する最速の方法です。より高いレベルのカスタマイズが必要な場合(たとえば、一部のリクエストをチャンクし、他のリクエストをチャンクしない)、手動の解決策については、私の答えserverfault.com/a/183856/54957を参照してください。
ウィリアムデニス

7

Apacheがチャンクエンコーディングを行っているように聞こえます。これは、完全な応答がgzipされるのを待つのではなく、gzipされているときにデータを送信できることを意味します。それはかなり標準的な慣行であり、Apacheを無効にできるかどうかを言うほど十分には詳しくありません。


情報をありがとう、あなたは正しい方向に私を指摘し、私はそれを解決しました。
ウィリアムデニス

受け入れられました。ただし、この質問を読んでいる人は、詳細な解決策については私の答えを読んでください。基本的に、手動で応答をバッファリングして圧縮することにより、チャンク化(したがってコンテンツ長がゼロになる)を回避できます。
ウィリアムデニス

受け入れられた答えが元の質問への答えではなく、むしろあなたがそれを得るのを助けた何かであることは少し混乱しています。物事をもう少し明確にするために、以下に投稿した回答を受け入れる必要があるかもしれません。
-redbmk

@redbmkフェアポイント、恩知らずになりたくなかった。フィリップは実際にこれに対する完璧な単純な修正を持っているので、私は彼の私のものを受け入れました。
ウィリアムデニス

5

OK、私はこれをなんとか解決しました。Martin Fが正しく指摘しているように、Apacheは応答をチャンクしているため、コンテンツサイズは不明です。多くの人にとって、これは望ましいことです(ページの読み込みが速くなります)。これには、ダウンロードの進行状況を報告できないという代償が伴います。

ダウンロードの進行状況を本当に報告したい私のように、ApacheまたはPHPの自動gzipサポートを使用している場合、できることはほとんどありません。解決策は、手動で行うことです。思ったより簡単です:

ファイル全体を送信する場合、これは単一のチャンク(Content-Lengthを使用)を強制するPHPの優れた例です。http//www.php.net/manual/en/function.ob-start.php #94741

生成されたデータを送信する場合は、上記のサンプルのように、gzencodeを使用してデータをエンコードします。前提条件は、すべての出力データが変数に格納されていることです(バッファする必要がある場合はob_startを使用してこれを支援し、バッファの内容を取得できます)。

        // $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;

そして出来上がり!

自分で行うことのもう1つの大きな利点は、圧縮レベルを設定できることです。これは、モバイルアプリケーションに最適です。最高の圧縮レベルに設定できるため(ユーザーのデータへの支払いが少なくなります!)–サーバーは、CPUとサイズのトレードオフを改善するために、おそらく中程度の圧縮レベルのみを使用します。圧縮レベルは、httpd.conf(共有ホスティングでは編集できない)を編集できる場合にのみ変更できると信じています。

そのため、アプリケーション/ jsonの応答以外のすべてについて、DEFLATE .htaccessディレクティブを保持しました。これは、上記の方法でエンコードします。

再びマーティン・Fに感謝します、あなたは私にこれを解決するために必要な火花をくれました:)


1
ちなみに、JSONデータ(キーが頻繁に繰り返される)による節約は、1つのケースで77%の大幅削減です。それはだ大した ... MBあたり$ 1での
ウィリアムDenniss

1
おそらくのstrlen($replyBody)代わりに使用する必要がありmb_strlen($replyBody, 'latin1')ます。content-lengthは単なる文字数ではなくバイト数であり、strlen()が提供するものです。latin1文字は常に8ビットであるため、mb_strlen()を 'latin1'並べ替えで使用できますが、有効なlatin1文字ではないバイトを生成するエンコーディングに問題がある可能性があります。
orrd
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.