• Безымянный 222304

  • My Top 100

  • Хотел сегодня рассказать об одном неочевидном и не сильно известном нюансе, применяемом для ускорения работы браузера с динамически созданным HTML, да не вышло - время бежит, софт совершенствуется и надобность в некоторых гримасах каменного века отпадает сама собой.

    А именно: хотел поведать о том, как выдача правильного content-length помогает оптимизировать работу клиента с HTTP соединением.
    Немного истории: первые варианты HTTP протокола работали по принципу:
    1. Открыли соединение
    2. Послали запрос
    3. Слили ответ
    4. Закрыли соединение
    5. Надо, к примеру, докачать картинку, ссылку на которую нашли в только что полученном HTML? Отлично. Повторим пункты 1-4 заново.

    Как видно, в случае 100 некешированных картинок, браузер последовательно сто раз открывал-закрывал соединение с сервером. Естественно, эта ситуация была со временем решена введением Keep-Alive соединений, позволявших браузеру при соблюдении определенных условий не разрывать соединение с сервером, а пользоваться им для получения дополнительного контента.
    Появление стандарта HTTP 1.1 закрепило и стандартизировало эту практику под именем persistent connections. Теперь клиент и сервер договариваются друг-с-другом посредством объявления поля Connection: Keep-alive в запросе и ответе соответственно.
    Единственный нюанс - для использования persistent connection сервер обязан так или иначе заблаговременно дать клиенту информацию о длине тела ответа, что совершенно не было проблемой для статики типа картинок, но абсолютно игнорировалось подавляющим большинством динамических скриптов. Это необходимо прежде всего для того, чтобы клиент узнал, когда весь контент получен и соединение может быть разорвано или использовано для следующего запроса.
    Суть сегодняшнего поста должна была состоять в наглядной демонстрации этого принципа. Для этого я набросал наглядный скрипт, отсылающий ответ с и без Content-length, и выдающий img тэг, чтобы инициализировать скачивание однопиксельной картинки:

    <?php
         $content = '<img src="/blank.gif" />';
         if ($_GET['len'])
         {
             header('Content-Length:'.strlen($content));
         }
         echo $content;
     ?>
    

    Запустил wireshark, начал тест и пришел в совершенное недоумение: несмотря на все старания, браузер прекрасно запрашивал картинку в том же самом HTTP соединении. И ни единого разрыва, как говорится.
    Естественно, я сразу же обратился к стандарту, дабы удостовериться, что мне этот нюанс не приснился. В пункте 8.1.2.1 лишний раз убедился, что так и есть - чтобы вся эта шарманка работала, клиенту должен отдаваться self-defined message length, под который в том числе подходит и Content-Length. Там же нашел и объяснение, каким таким волшебным образом у меня работают persistent connections без оного. Оказывается, специально для динамического контента была разработана фишка под названием Transfer-Encoding, разбивающая медленный или громоздкий вывод скрипта на мелкие кусочки, подсчет размера которых не занимает больших объемов памяти, и, соответственно, репортящая размер каждого кусочка браузеру напрямую.
    Поэтому на сегодняшний день буферизация вывода скрипта с целью подсчета его размера не имеет особенной ценности, разве что при работе с совсем уж деревянными HTTP серверами.
    Кстати, для вывода мелких скриптов Apache сам автоматом выдает content-length. Я не специалист по настройке Apache, но подозреваю, что лимит размера задается где-то отдельно. В свою очередь всё, что превышает заданный размер, автоматом отдаётся с Transfer-Encoding: chunked.
    Надо иметь этот нюанс в виду, если придется когда-нибудь писать своего HTTP-спайдера - есть у меня одна мечта по написанию передачи файла по HTTP на z80 асме через GPRS подключение по готовым TCP/IP сэмплам, но это из разряда совсем уж диких фантазий :)











  • Безымянный 222304

  • My Top 100