Статьи

Обслуживание сжатых ресурсов Rails из S3 через Cloudfront

В этом посте я писал о том, как мы делаем упаковку активов с помощью Rails, как мы Jammit и переносим их на S3 . С тех пор у нас было несколько сюрпризов, один из которых был связан со сжатыми активами.

Если браузер отправляет заголовок Accept-Encoding: gzip для ресурса, который имеет как несжатую, так и сжатую копию (например, client.js и client.js.gz ), сервер может ответить сжатой версией файла вместе с исходный Content-Type (например, text / css ) и Content-Encoding: gzipзаголовки. Это называется согласованием контента. К сожалению, у S3 очень плохие навыки ведения переговоров. CloudFront CDN работает лучше, но только если сервер его поддерживает. Таким образом, размещение CloudFront перед S3 дает тот же эффект, и Amazon рекомендует использовать пользовательский сервер происхождения (причудливое слово для другого веб-сервера), отличный от S3, если вы хотите включить согласование содержимого. Мы не хотели бы обслуживать статические активы от Heroku, потому что это требует проверки их в системе контроля версий. И мы не хотели бы добавлять веб-сервер, который мы должны поддерживать, — слишком много инфраструктуры, если умножить на количество разработчиков.

Решение, как обычно, состоит в том, чтобы

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

В Rails 3.0 (ваш пробег будет варьироваться для 3.1) пути к ресурсам записываются с помощью ActionView :: Helpers :: AssetTagHelper ‘s path_to_javascript и path_to_stylesheet . Мы можем выяснить возможности браузера, изучив request.env [‘HTTP_ACCEPT_ENCODING’] и переписав эти URL по своему вкусу.

module ActionView
  module Helpers
    module AssetTagHelper
      def accept_encoding?(encoding)
        (request.env['HTTP_ACCEPT_ENCODING'] || '').split(',').include?(encoding)
      end
      def rewrite_path_to_gzip?(source)
        (! config.asset_host.blank?) and (source =~ /assets\//) and accept_encoding?('gzip')
      end
      def path_to_javascript(source)
        source = rewrite_path_to_gzip(source) if rewrite_path_to_gzip?(source)
        compute_public_path(source, 'javascripts', 'js')
      end
      def path_to_stylesheet(source)
        source = rewrite_path_to_gzip(source) if rewrite_path_to_gzip?(source)
        compute_public_path(source, 'stylesheets', 'css')
      end
      def rewrite_path_to_gzip(source)
        source + ".cgz"
      end
    end
  end

config / initializers / asset_tag_helper.rb и spec / initializers / rails / asset_tag_helper_spec.rb

Расширение .cgz заменяет расширение .gz, чтобы обойти ошибку в Safari . Rewrite_path_to_gzip? check гарантирует, что мы рендерим что-то под активы и что мы используем внешний сервер (не в разработке). Ваше состояние может быть другим.

Наконец, мы должны установить правильные заголовки кодирования содержимого при передаче ресурсов на S3. Вот мясо нашей задачи по Грабли.

    File.open(entry) do |entry_file|
      content_options = {}
      content_type = MIME::Types.type_for(entry)[0]
      content_options['x-amz-acl'] = 'public-read'
      if entry.ends_with?('.gz')
        uncompressed_entry = entry[0..-4]
        entry = "#{uncompressed_entry}.cgz"
        content_type = MIME::Types.type_for(uncompressed_entry)[0]
        content_options['content-encoding'] = 'gzip'
      end
      content_options['content-type'] = content_type.to_s
      key = 'assets/'
      key += entry.slice(from.length + 1, entry.length - from.length - 1)
      s3i.put(to, key, entry_file, content_options)
    end

Обратите внимание, что Content-Type для файла .css.gz такой же, как и для файла .css ( text / css ), и что для Content-Encoding установлено значение gzip .

Чтобы убедиться, что ваша реализация работает, проверьте исходный код страницы и убедитесь, что вы получили ссылку .css.cgz для таблиц стилей. Затем перейдите к URL-адресу .css.cgz — он должен отображать несжатый CSS .

Для пуристов эта реализация действительно плохая. Браузер может запросить веб-страницу с Accept-Encoding: gzip , но затем запросить CSS с другого сервера без него. Я собираюсь решить, что это никогда не происходит в реальной жизни.

Источник: http://code.dblock.org/serving-compressed-rails-assets-from-s3-via-cloudfront