Довжина вмісту не надсилається, коли в Apache увімкнено стиснення gzip?


13

Я дуже вдячний, щоб допомогти зрозуміти цю поведінку Apache.

Я спілкуюся з PHP через додаток iPhone Objective-C у програмі / json. Стиснення Gzip увімкнено на сервері та вимагає клієнт.

З мого .htaccess:

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

Для невеликих запитів Apache встановлює заголовок "Довжина вмісту". Наприклад (ці значення виводяться в 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-Uncompression-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;

Зауважте, що довжина вмісту не вказана.

Мої запитання:

  1. Чому Apache не надсилає довжину вмісту для більшого запиту?
  2. Чи означає, що встановлено "Contend-Encoding = gzip", що компресія gzip все ще працює на більш великий запит, хоча я не можу перевірити різницю розмірів?
  3. Чи можна змусити Apache включити фактичну довжину вмісту для цих великих запитів, щоб більш точно повідомити про використання даних користувачам?

Цей додаток можна використовувати в дорогих планах даних, отже, моє бажання повідомити користувача про фактичне використання, а не на 30-70% завищене використання (кілька сотень зайвих КБ можуть здатися не дуже сильними - але ці плани можуть коштувати від 1 долара до $ і 10 доларів за МБ!).

Заздалегідь спасибі.

Відповіді:


14

Доповнення до відповіді Мартіна Фьордвальдса:

Apache використовує чітке кодування лише у тому випадку, якщо розмір стисненого файлу більший за розмір DeflateBufferSize. Збільшення цього розміру буфера, таким чином, не дозволить серверу використовувати кодоване кодування також для великих файлів, внаслідок чого довжина вмісту надсилатиметься навіть для заархівованих даних.

Більше інформації можна отримати тут: http://httpd.apache.org/docs/2.2/mod/mod_deflate.html#deflatebuffersize


Хороший. Це, мабуть, найшвидший спосіб вирішити цю проблему. Якщо комусь потрібен більш високий рівень налаштування (наприклад, фрагменти деяких запитів, а не інших), дивіться мою відповідь на сервер defaultfault.com/a/183856/54957 щодо рішення вручну.
Вільям Денніс

7

Звучить, що Apache робить кодоване кодування, це означає, що він може надсилати дані під час отримання gzipped, а не чекати повного відгуку. Це досить стандартна практика, я недостатньо знайомий з Apache, щоб сказати, чи можна його відключити.


Дякую за інформацію, ти вказав мені в правильному напрямку, і я це вирішив.
Вільям Денніс

Прийнято. Для всіх, хто читає це питання, будь ласка, прочитайте мою відповідь для детального рішення. В основному, ви можете уникнути розміщення (і, таким чином, нульової довжини вмісту), буферуючи і стискаючи відповідь вручну.
Вільям Денніс

Це трохи заплутано, що прийнята відповідь - це не відповідь на початкове запитання, а скоріше те, що допомогло вам її отримати. Можливо, ви повинні прийняти відповідь, яку ви опублікували нижче, щоб зробити щось більш зрозумілим.
redbmk

@redbmk справедливий момент, я просто не хотів здаватися невдячним. Філіпп насправді має ідеальне просте виправлення для цього, тому я прийняв його над своїм.
Вільям Денніс

5

Гаразд, мені вдалося це вирішити. Як правильно вказує Мартін Ф, Apache чує відповідь, тому розмір вмісту не відомий. Для багатьох людей це бажано (сторінка завантажується швидше). Це відбувається за рахунок неможливості повідомити про хід завантаження.

Для таких, як я, хто дійсно хоче повідомити про хід завантаження, якщо ви використовуєте автоматичну підтримку gzip Apache або PHP, ви можете зробити мало. Рішення - зробити це вручну. Це простіше, ніж це звучить:

Якщо ви надсилаєте цілі файли, то це чудовий приклад у 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;

І вуаля!

Ще одна велика перевага зробити це самостійно - ви можете встановити рівень стиснення. Це чудово підходить для мого мобільного додатку, оскільки я можу встановити найвищий рівень стиснення (тому мої користувачі платять менше за дані!) - тоді як сервер, ймовірно, використовує лише середній рівень стиснення для кращої компромісії процесора / розміру. Рівні стиснення - це те, що я вважаю, що ви можете змінитись лише в тому випадку, якщо ви зможете редагувати httpd.conf (що на спільному хостингу я не можу).

Тому я зберігав свою директиву DEFLATE .htaccess для всього, окрім моєї програми / відповідей json, які я тепер кодую вищевказаним способом.

Ще раз спасибі, Мартіне Ф, ти дав мені іскру, яку мені потрібно було вирішити :)


1
До речі, заощадження за допомогою даних JSON (із сильно повторюваними клавішами) величезні , зменшення на 77% в одному випадку. Це велика справа в $ 1 за МБ ...
Вільям Денніс

1
Напевно, ви повинні просто використовувати strlen($replyBody)замість цього mb_strlen($replyBody, 'latin1'). Довжина вмісту - це лише кількість байтів (а не символів), що дає вам strlen (). Використання mb_strlen () з роботою 'latin1', оскільки символи latin1 завжди є 8 бітами, але можуть виникнути проблеми з кодуванням, які створюють байти, які не є дійсними символами latin1.
orrd
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.