Искусство отладки или «Убей меня! Убей меня сейчас!»

Автор: Евгений Рыжков и Татьяна Головко Дата публикации: 04.04.2011

Хочу обратить внимание на два важных момента. Первый — это касается того, что только что было прочитано, но, возможно, не осознано до конца: даже если хотя бы один из ресурсов, перечисленных в manifest, не будет успешно загружен, весь процесс сохранения оффлайнового веб приложения терпит неудачу. Браузер сгенерирует событие error, но не будет указана причина ошибки. Это может сделать отладку оффланового веб приложения сущим кошмаром.

Второй важный момент не связан, технически говоря, с ошибкой, но будет выглядеть как серьезная ошибка браузера, пока не поймешь в чем дело. Связано это с тем, как браузер проверяет, что файл manifest изменился. Это трехэтапный процесс, хотя он скучен, но важен, так что будь внимателен.

  1. С помощью HTTP семантики браузер проверяет истек ли срок действия файла manifest. Как и для любого другого файла сервер включает в свой ответ матаинформацию о файле в HTTP заголовках. Некоторые из них (Expires и Cache-Control) сообщают браузеру, как ему закешировать файл без последующих запросов к серверу о том, изменился ли файл. Этот вид кеширования не является чем-то специфическим для оффлановых веб приложений. Он используется для кешировния HTML страниц, файлов стилей, сценариев, изображений и других ресурсов в Веб.
  2. Если срок хранения файла manifest истек (в соответствии с его HTTP заголовками), браузер сделает запрос серверу на наличие новой версии. Если таковая имеется, она будет загружена. Для этого браузер при обращении к серверу включит в HTTP запрос информацию о времени последнего изменения файла manifest, которая в свою очередь была получена в HTTP заголовках во время предыдущей загрузки этого файла. Если веб сервер определит, что файл не изменился, он просто вернет код 304 (нет изменений). Опять же — это не является чем-то специфическим именно для оффлайновых веб приложений. Это касается практически всех ресурсов в Интернет.
  3. Если веб сервер посчитает, что файл manifest изменился с момента последнего сохранения, он вернет HTTP код 200 (OK), после чего передаст файл manifest с новым заголовком Cache-Control и новой датой последнего изменения. Это обеспечит правильную работу шагов 1 и 2 при следующем посещении сайта. (HTTP крут — веб сервер всегда думает наперед. Если веб серверу нужно отправить какой-то файл, он сделает все, чтобы этот файл второй раз без веской причины повторно не отправлять). Однажды загрузив в кеш файл manifest, браузер будет проверять его на наличие изменений. Если содержимое файла не изменилось, тогда ресурсы, указанные в нем повторно загружаться не будут.

Любой из этих шагов может заставить «споткнуться» во время разработки и тестирования веб приложения. Например, мы создали первую версию файла manifest, а через 10 минут вспомнили, что нужно добавить в него еще один ресурс. Без проблем, правильно? Просто дописываем то, что нужно, и обновляемся. А вот что произойдет в таком случае: во время обновления страницы браузер встретит атрибут manifest, сработает событие checking и… все. Браузер будет тебя уверять, что файл manifest не изменился. Почему? Потому что обычно веб сервера по умолчанию настроены на кеширование статических файлов на несколько часов (HTTP семантика, заголовки Cache-Control). Это означает, что браузер не пройдет дальше 1-го шага из перечисленных трех. Конечно, веб-сервер в курсе, что файл изменен, но браузер просто его об этом не будет спрашивать. Почему? Потому что при последней загрузке файла manifest веб сервер сообщил браузеру чтобы тот закешировал данный файл на несколько часов. А мы пытаемся увидеть изменения через 10 минут.

Это не ошибка, это особенность. Все работает так, как и задумано. Если бы веб-серверы не сообщали браузерам, что нужно кешировать файлы, то Веб давно бы «задохнулся». Но нас сейчас все это мало волнует: не ждать же несколько часов пока браузер попросит новую версию файла. Да к тому же файл может обновится когда ты этого не ожидаешь, потому что время кешировния истекло, что тоже может оказаться неприятным сюрпризом.

Выход из этой тупиковой ситуации — настроить веб-сервер так, чтобы файл manifest не кэшировался. Если ты работаешь с сервером на базе Apache достаточно будет внести две строки в .htaccess:

ExpiresActive On
ExpiresDefault "access"

На самом деле это отключает кэширование всех файлов в директории и всех поддиректориях. Скорей всего это не то, что тебе нужно для производительности. Поэтому нужно команду подкорретировать директивой <Files>, чтобы эффект распространялся только на файл manifest или создавать подирректорию, в которой не будет ничего кроме .htaccess и файла manifest. Если используется не Apache, следует обратиться к документации по настройке своего сервера.

Отключение кэширования файла manifest само по себе не решит всех проблем. Ситуация повторится, если будет изменен один из ресурсов, указанных в файле manifest. Браузер получит команду от сервера, что файл manifest не изменен (ведь так оно и есть), соответственно заново не будут закачаны ресурсы, указанные в нем. Рассмотрим следующий пример:

CACHE MANIFEST
# rev 42
clock.js
clock.css

Если мы изменим clock.css и попробуем обновиться, мы не увидим никаких изменений, потому что сам файл manifest не был изменен. При каждом изменении одного из ресурсов, указанных в manifest, следует изменять и сам файл manifest. Это может быть даже просто изменение одного символа. Для себя я нашел простой способ — включай комментарий с версией файла manifest. Пр каждом обновлении я меняю в комментариях версию, браузер загрузит свежий manifest и ресурсы указанные в нем.

CACHE MANIFEST
# rev 43
clock.js
clock.css

Куда дальше