gzcompress () произвольно вставляет дополнительные данные? - PullRequest
4 голосов
/ 21 октября 2011

Я изучал это все утро и решил, что в качестве последней попытки, возможно, кто-то из Stack Overflow нашел для меня ответ типа "был там, готов, тот".

Справочная информация

Недавно я реализовал сжатие на нашем (ориентированном на интрасеть) сервере Apache (2.2) с использованием фильтров, чтобы все текстовые файлы сжимались (css, js, txt, html и т. Д.) через mod_deflate, не говоря уже о php-скриптах. После долгих исследований о том, как лучше всего сжать вывод PHP, я решил использовать разновидность gzcompress (), потому что документация PHP предполагает, что использование библиотеки zlib и gzip (с использованием алгоритма deflate, бла-бла-бла) предпочтительнее, чем ob_gzipwhwhat ().

Итак, я скопировал чужой метод так:

<?php # start each page by enabling output buffering and disabling automatic flushes
ob_start();ob_implicit_flush(0);

(program logic)

print_gzipped_page();

function print_gzipped_page() {
 if (headers_sent())
    $encoding = false;
 elseif(strpos($_SERVER['HTTP_ACCEPT_ENCODING'],'x-gzip') !== false )
    $encoding = 'x-gzip';
 elseif(strpos($_SERVER['HTTP_ACCEPT_ENCODING'],'gzip') !== false )
    $encoding = 'gzip';
 else
    $encoding = false;

 if($encoding){
    $contents = ob_get_contents(); # get contents of buffer
    ob_end_clean(); # turn off OB and flush buffer
    $size = strlen($contents);
    if ($size < 512) { # too small to be worth a compression
        echo $contents;
        exit();
    } else {
        header("Content-Encoding: $encoding");
        header('Vary: Accept-Encoding');
        # 8-byte file header: g-zip file (1f 8b) compression type deflate (08), next 5 bytes are padding
        echo "\x1f\x8b\x08\x00\x00\x00\x00\x00"; 
        $contents = gzcompress($contents, 9);
        $contents = substr($contents, 0,$size); # faster than not using a substr, oddly
        echo $contents;
        exit();
    }
} else {
    ob_end_flush();
    exit();
 }
}

Довольно стандартные вещи, верно?

Проблема

От 10% до 33% всех наших запросов страниц PHP, отправляемых через Firefox, выполняются нормально и возвращаются в режиме g-zip, только Firefox отображает сжатый ASCII вместо его распаковки. И, самое странное, это то, что размер отправляемого контента всегда на 30 или 31 байт больше, чем размер страницы, правильно отображаемой. Например, когда скрипт отображается правильно, Firebug показывает размер контента 1044; когда Firefox показывает огромный экран двоичного бреда, Firebug показывает размер контента 1074.

Это произошло с некоторыми из наших пользователей на старых 32-битных Fedora 12 с Firefox 3.3s ... Затем это случилось с пользователем с FF5, одним с FF6, а с новым 7.1! В любом случае, я собирался обновить их до FF7.1, поэтому я обновлял их, поскольку у них есть проблемы, но FF7.1 все еще демонстрирует то же поведение, но реже.

Диагностика

Я устанавливал Firebug на различные компьютеры, чтобы посмотреть заголовки, и вот тут я запутался: Обычные, работающие заголовки ответа страницы:
  • HTTP / 1.1 200 OK
  • Дата: пт, 21 октября 2011 18:40:15 GMT
  • Сервер: Apache / 2.2.15 (Fedora)
  • X-Powered-By: PHP / 5.3.2
  • Истекает: четверг, 19 ноября 1981 г. 08:52:00 по Гринвичу
  • Cache-Control: без сохранения, без кэширования, обязательная повторная проверка, пост-проверка = 0, предварительная проверка = 0
  • Прагма: без кэша
  • Кодировка содержимого: gzip
  • Варьируется: Accept-Encoding
  • Длина содержимого: 1045
  • Keep-Alive: тайм-аут = 10, максимум = 75
  • Соединение: Keep-Alive
  • Content-Type: text / html; кодировка = UTF-8

(обратите внимание, что длина содержимого генерируется автоматически)

Та же страница, когда сломан:

  • HTTP / 1.1 200 OK
  • (все остальное идентично)
  • Длина содержимого: 1075

Отправленные заголовки всегда включают Accept-Encoding: gzip, deflate

Вещи, которые я пытался исправить поведение:

  • Явно объявляйте длину содержимого с несжатыми и сжатыми длинами
  • Не использовать substr () содержимого $ 1074 *
  • Удалить контрольную сумму в конце содержимого $ 1077 *

Я на самом деле не хочу использовать gzencode, потому что мои тесты показали, что он значительно медленнее (9%), чем gzcompress, возможно потому, что он генерирует дополнительные контрольные суммы, а также то, что мне (как предполагается) веб-браузеры не нужны или не используются .

Я не могу продублировать поведение на моей 64-битной версии Fedora 14 под управлением Firefox 7.1. Ни разу в моем тестировании, прежде чем запускать код сжатия вживую, такого не случалось со мной, ни в Chrome, ни в Firefox . (Правка: Сразу после публикации этого сообщения одно из оставленных мною окон, которое отправляет мета-обновления каждые 30 секунд, наконец-то сломалось после ~ 60 обновлений в Firefox). Наши несколько окон Windows XP ведут себя так же, как Fedora 12. Поиск в Firefox Bugzilla вызвал один или два запроса об ошибках, которые были в некоторой степени похожи на эту ситуацию, но это было для версий, предшествующих версии 3.3 и со всем gzip-контентом, тогда как наши gsip-файлы Apache css и js загружаются и отображаются без ошибок. каждый раз.

Тот факт, что длина содержимого возвращается на 30/31 байта с каждым разом, заставляет меня думать, что внутри моего скрипта / gzcompress () что-то ломается, что искажает что-то в ответе, который задыхается Firefox. Естественно, если вы играете с изменением заголовка gzip echo'd, Firefox выдает «ошибку кодирования содержимого», поэтому я действительно склоняюсь к проблеме, являющейся внутренней по отношению к gzcompress ().

Я обречен? Должен ли я отказаться от этой реализации и использовать не предпочтительный метод ob_start ("ob_gzhandler")?

Полагаю, мой вопрос "относится к нескольким ситуациям" будет таков: есть ли известные ошибки в библиотеке сжатия zlib в PHP, которые делают что-то необычное при получении очень специфических входных данных?

Редактировать: Орехи. Я прочитал gzfile () одну из сломанных, несжатых страниц, которые скачал Firefox, и, о чудо, он полностью отозвался. = (Это значит, что это должно быть ... Нет, у меня ничего нет.

Ответы [ 2 ]

0 голосов
/ 24 октября 2011

Дин! Дин! Дин! Обдумав эту проблему все выходные, я наконец наткнулся на ответ после повторного чтения справочных страниц по PHP в сотый раз ... Из документации PHP zlib: «Сжать ли прозрачно страниц». Прозрачное! Например, больше ничего не требуется, чтобы заставить PHP сжимать свои выходные данные, если zlib.output_compression установлено в значение «On». Да, стыдно.

По неизвестным причинам код, вызываемый явно из скрипта PHP, сжимал уже сжатое содержимое, а браузер просто развертывал один слой сжатия и отображал результаты. Любопытно, что strlen () содержимого не менялся, когда output_compression было включено или выключено, поэтому прозрачное сжатие должно происходить после явного сжатия, но иногда оно решало не сжимать то, что уже было сжато?

Несмотря ни на что, все решается простым предоставлением PHP своим собственным устройствам. zlib не требует буферизации вывода или чего-либо еще для сжатия вывода.

Надеюсь, это поможет другим бороться с чудесным миром сжатия HTTP.

0 голосов
/ 21 октября 2011

ладно, во-первых, вы, похоже, не устанавливаете заголовок длины контента, что вызовет проблемы, вместо этого вы делаете контент gzip длиннее, чтобы он соответствовал размеру длины контента, который вы получили на первом месте.,Это станет уродливым.Я предлагаю заменить строки

# 8-byte file header: g-zip file (1f 8b) compression type deflate (08), next 5 bytes are padding
echo "\x1f\x8b\x08\x00\x00\x00\x00\x00"; 
$contents = gzcompress($contents, 9);
$contents = substr($contents, 0,$size); # faster than not using a substr, oddly
echo $contents;

на

$compressed = gzcompress($contents, 9);
$compressed_length = strlen($compressed); /* contains no nulls i believe */
header("Content-length: $compressed_length");
echo "\x1f\x8b\x08\x00\x00\x00\x00\x00", $compressed; 

и посмотреть, поможет ли это ситуация.

...