15 votes

La longueur du contenu n'est pas envoyée lorsque la compression gzip est activée dans Apache?

Je serais vraiment reconnaissant de l'aide pour comprendre ce comportement d'Apache.

Je communique avec PHP depuis une application iPhone Objective-C en application/json. La compression Gzip est activée sur le serveur et demandée par le client.

Depuis mon fichier .htaccess :

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

Pour les petites requêtes, Apache définit l'en-tête 'Content-Length'. Par exemple (ces valeurs sont affichées en Objective-C depuis l'en-tête) :

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 est un en-tête que j'ajoute correspondant à la taille de la chaîne JSON non compressée.

Comme vous pouvez le voir, cette requête est très petite (217 octets).

Voici les en-têtes d'une requête plus grande (282888 octets) :

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;

Remarquez que Content-Length n'est pas indiqué.

Mes questions :

  1. Pourquoi Apache ne transmet-il pas le Content-Length pour la requête plus grande ?
  2. Est-ce que le fait que 'Content-Encoding=gzip' soit défini signifie que la compression gzip fonctionne toujours sur la requête plus grande, même si je ne peux pas vérifier la différence de taille ?
  3. Existe-t-il un moyen pour que Apache inclue le Content-Length réel pour ces requêtes plus importantes afin de reporter de manière plus précise l'utilisation des données aux utilisateurs ?

Cette application peut être utilisée avec des forfaits de données coûteux, d'où mon désir de rapporter l'utilisation réelle à l'utilisateur, et non une utilisation gonflée de 30 à 70 % (quelques centaines de Ko supplémentaires peuvent ne pas sembler beaucoup - mais ces forfaits peuvent coûter entre 1 et 10 dollars par Mo !).

Merci d'avance.

14voto

Philippe Points 256

Ajout à la réponse de Martin Fjordvalds :

Apache n'utilise le codage chunked que si la taille du fichier compressé est plus grande que DeflateBufferSize. Augmenter la taille de ce tampon empêchera donc le serveur d'utiliser le codage chunked également pour les fichiers plus volumineux, ce qui entraînera l'envoi de la Content-Length même pour les données compressées.

Plus d'informations sont disponibles ici : http://httpd.apache.org/docs/2.2/mod/mod_deflate.html#deflatebuffersize

7voto

Schotime Points 6067

Il semble que Apache utilise un encodage chunked, cela signifie qu'il peut envoyer les données alors qu'elles sont en train d'être compressées au lieu d'attendre que la réponse complète soit compressée. C'est une pratique assez standard, je ne suis pas assez familier avec Apache pour dire s'il peut être désactivé, cependant.

5voto

Blender Points 626

D'accord, j'ai réussi à résoudre cela. Comme le souligne correctement Martin F, Apache découpe la réponse donc la taille du contenu n'est pas connue. Pour beaucoup de gens, cela est souhaitable (la page se charge plus rapidement). Cela a pour conséquence de ne pas pouvoir rapporter la progression du téléchargement.

Pour ceux comme moi qui veulent vraiment rapporter la progression du téléchargement, si vous utilisez le support automatique gzip d'Apache ou de PHP, il y a peu de choses que vous pouvez faire. La solution est de le faire manuellement. C'est plus facile que ça en a l'air :

Si vous envoyez des fichiers entiers, alors voici un excellent exemple en PHP pour forcer un seul fragment (avec le Content-Length) : http://www.php.net/manual/en/function.ob-start.php#94741

Si vous envoyez des données générées, utilisez gzencode pour encoder vos données, comme dans l'exemple ci-dessus. Une condition préalable est que toutes vos données de sortie soient stockées dans une variable (vous pouvez utiliser ob_start pour aider si vous devez mettre en mémoire tampon, puis récupérer le contenu de la mémoire tampon).

        // $replyBody est l'ensemble du contenu de votre réponse

        header("Content-Type: application/json");  // ou autre chose pour vous

        // vérifie si gzip est pris en charge par le client
        $pack = true;
        if(empty($_SERVER["HTTP_ACCEPT_ENCODING"]) || strpos($_SERVER["HTTP_ACCEPT_ENCODING"], 'gzip') === false)
        {
            $pack = false;
        }

        // si pris en charge, compresse les données
        if($pack) {
            header("Content-Encoding: gzip");
            $replyBody = gzencode($replyBody, 9, FORCE_GZIP);
        }

        // compressé ou non, défini la Content-Length           
        header("Content-Length: " . mb_strlen($replyBody, 'latin1'));

        // affiche la réponse & sort
        echo $replyBody;
        exit;

Et voilà !

Un autre grand avantage de le faire vous-même est que vous pouvez définir le niveau de compression. C'est génial pour mon application mobile, car je peux définir le niveau de compression le plus élevé (ainsi mes utilisateurs paient moins pour les données !) - alors que le serveur utilise probablement seulement un niveau de compression moyen pour un meilleur compromis CPU/taille. Je crois que vous pouvez changer les niveaux de compression uniquement si vous pouvez modifier le httpd.conf (ce que je ne peux pas faire sur un hébergement partagé).

J'ai donc conservé ma directive .htaccess DEFLATE pour tout sauf mes réponses application/json que j'encode maintenant de la manière ci-dessus.

Merci encore Martin F, tu m'as donné l'étincelle dont j'avais besoin pour résoudre cela :)

SistemesEz.com

SystemesEZ est une communauté de sysadmins où vous pouvez résoudre vos problèmes et vos doutes. Vous pouvez consulter les questions des autres sysadmins, poser vos propres questions ou résoudre celles des autres.

Powered by:

X