heroku-deflater-0.6.3/0000755000175000017500000000000013467103550014141 5ustar samyaksamyakheroku-deflater-0.6.3/lib/0000755000175000017500000000000013467103550014707 5ustar samyaksamyakheroku-deflater-0.6.3/lib/heroku-deflater.rb0000644000175000017500000000004213467103550020311 0ustar samyaksamyakrequire 'heroku-deflater/railtie' heroku-deflater-0.6.3/lib/heroku-deflater/0000755000175000017500000000000013467103550017770 5ustar samyaksamyakheroku-deflater-0.6.3/lib/heroku-deflater/serve_zipped_assets.rb0000644000175000017500000000404713467103550024403 0ustar samyaksamyakrequire 'action_controller' require 'active_support/core_ext/uri' require 'action_dispatch/middleware/static' # Adapted from https://gist.github.com/guyboltonking/2152663 # # Taken from: https://github.com/mattolson/heroku_rails_deflate # module HerokuDeflater class ServeZippedAssets def initialize(app, root, assets_path, cache_control_manager) @app = app @assets_path = assets_path.chomp('/') + '/' if rails_version_5? @file_handler = ActionDispatch::FileHandler.new(root, headers: cache_control_manager.cache_control_headers) else @file_handler = ActionDispatch::FileHandler.new(root, cache_control_manager.cache_control_headers) end end def call(env) if env['REQUEST_METHOD'] == 'GET' request = Rack::Request.new(env) encoding = Rack::Utils.select_best_encoding(%w(gzip identity), request.accept_encoding) if encoding == 'gzip' # See if gzipped version exists in assets directory compressed_path = env['PATH_INFO'] + '.gz' if compressed_path.start_with?(@assets_path) && (match = @file_handler.match?(compressed_path)) # Get the FileHandler to serve up the gzipped file, then strip the .gz suffix env['PATH_INFO'] = match status, headers, body = @file_handler.call(env) env['PATH_INFO'].chomp!('.gz') # Set the Vary HTTP header. vary = headers['Vary'].to_s.split(',').map(&:strip) unless vary.include?('*') || vary.include?('Accept-Encoding') headers['Vary'] = vary.push('Accept-Encoding').join(',') end # Add encoding and type headers['Content-Encoding'] = 'gzip' headers['Content-Type'] = Rack::Mime.mime_type(File.extname(env['PATH_INFO']), 'text/plain') body.close if body.respond_to?(:close) return [status, headers, body] end end end @app.call(env) end private def rails_version_5? Rails::VERSION::MAJOR >= 5 end end end heroku-deflater-0.6.3/lib/heroku-deflater/railtie.rb0000644000175000017500000000200113467103550021737 0ustar samyaksamyakrequire 'rack/deflater' require 'heroku-deflater/skip_binary' require 'heroku-deflater/serve_zipped_assets' require 'heroku-deflater/cache_control_manager' module HerokuDeflater class Railtie < Rails::Railtie initializer 'heroku_deflater.configure_rails_initialization' do |app| app.middleware.insert_before ActionDispatch::Static, Rack::Deflater app.middleware.insert_before ActionDispatch::Static, HerokuDeflater::SkipBinary app.middleware.insert_before Rack::Deflater, HerokuDeflater::ServeZippedAssets, app.paths['public'].first, app.config.assets.prefix, self.class.cache_control_manager(app) end # Set default Cache-Control headers to one week. # The configuration block in config/application.rb overrides this. config.before_configuration do |app| cache_control = cache_control_manager(app) cache_control.setup_max_age(86400) end def self.cache_control_manager(app) @_cache_control_manager ||= CacheControlManager.new(app) end end end heroku-deflater-0.6.3/lib/heroku-deflater/skip_binary.rb0000644000175000017500000000173713467103550022637 0ustar samyaksamyakmodule HerokuDeflater # Add a no-transform Cache-Control header to binary types, so they won't get gzipped class SkipBinary def initialize(app) @app = app end WHITELIST = [ %r{^text/}, 'application/javascript', %r{^application/json}, %r{^application/.*?xml}, 'application/x-font-ttf', 'font/opentype', 'image/svg+xml' ].freeze def call(env) status, headers, body = @app.call(env) headers = Rack::Utils::HeaderHash.new(headers) content_type = headers['Content-Type'] cache_control = headers['Cache-Control'].to_s.downcase unless cache_control.include?('no-transform') || WHITELIST.any? { |type| type === content_type } if cache_control.empty? headers['Cache-Control'] = 'no-transform' else headers['Cache-Control'] += ', no-transform' end end body.close if body.respond_to?(:close) [status, headers, body] end end end heroku-deflater-0.6.3/lib/heroku-deflater/cache_control_manager.rb0000644000175000017500000000147613467103550024622 0ustar samyaksamyakmodule HerokuDeflater class CacheControlManager DEFAULT_MAX_AGE = '86400'.freeze attr_reader :app, :max_age def initialize(app) @app = app @max_age = DEFAULT_MAX_AGE end def setup_max_age(max_age) @max_age = max_age if rails_version_5? app.config.public_file_server.headers ||= {} app.config.public_file_server.headers['Cache-Control'] = cache_control else app.config.static_cache_control = cache_control end end def cache_control_headers if rails_version_5? { 'Cache-Control' => cache_control } else cache_control end end private def rails_version_5? Rails::VERSION::MAJOR >= 5 end def cache_control @_cache_control ||= "public, max-age=#{max_age}" end end end heroku-deflater-0.6.3/VERSION0000644000175000017500000000000513467103550015204 0ustar samyaksamyak0.6.3heroku-deflater-0.6.3/spec/0000755000175000017500000000000013467103550015073 5ustar samyaksamyakheroku-deflater-0.6.3/spec/serve_zipped_assets_spec.rb0000644000175000017500000000442513467103550022520 0ustar samyaksamyakrequire 'rack/mock' require 'rack/static' require 'heroku-deflater/serve_zipped_assets' require 'heroku-deflater/cache_control_manager' describe HerokuDeflater::ServeZippedAssets do def process(path, accept_encoding = 'compress, gzip, deflate') env = Rack::MockRequest.env_for(path) env['HTTP_ACCEPT_ENCODING'] = accept_encoding app.call(env) end def app @app ||= begin root_path = File.expand_path('../fixtures', __FILE__) cache_control_manager = HerokuDeflater::CacheControlManager.new(nil) mock = lambda { |env| [404, {'X-Cascade' => 'pass'}, []] } described_class.new(mock, root_path, '/assets', cache_control_manager) end end shared_examples_for 'ServeZippedAssets' do it 'does nothing for clients which do not want gzip' do status, headers, body = process('/assets/spec.js', nil) expect(status).to eq(404) end it 'handles the pre-gzipped assets' do status, headers, body = process('/assets/spec.js') expect(status).to eq(200) end it 'has correct content type' do status, headers, body = process('/assets/spec.js') expect(headers['Content-Type']).to eq('application/javascript') end it 'has correct content encoding' do status, headers, body = process('/assets/spec.js') expect(headers['Content-Encoding']).to eq('gzip') end it 'has correct content length' do status, headers, body = process('/assets/spec.js') expect(headers['Content-Length']).to eq('86') end it 'has correct cache control' do status, headers, body = process('/assets/spec.js') expect(headers['Cache-Control']).to eq('public, max-age=86400') end it 'does not serve non-gzipped assets' do status, headers, body = process('/assets/spec2.js') expect(status).to eq(404) end it 'does not serve anything from non-asset directories' do status, headers, body = process('/non-assets/spec.js') expect(status).to eq(404) end end describe 'Rais 4.x' do before do allow(app).to receive(:rails_version_5?) { false } end it_behaves_like 'ServeZippedAssets' end describe 'Rais 5.x' do before do allow(app).to receive(:rails_version_5?) { true } end it_behaves_like 'ServeZippedAssets' end end heroku-deflater-0.6.3/spec/fixtures/0000755000175000017500000000000013467103550016744 5ustar samyaksamyakheroku-deflater-0.6.3/spec/fixtures/assets/0000755000175000017500000000000013467103550020246 5ustar samyaksamyakheroku-deflater-0.6.3/spec/fixtures/assets/spec.js.gz0000644000175000017500000000012613467103550022154 0ustar samyaksamyak‹…QÓ×Wpˬ()-JUHËÌIåÒH+ÍK.ÉÌÏÓÐT¨æRPHÎÏ+ÎÏIÕËÉO×PòHÍÉÉW(Ï/ÊIQÒ´¦H¶VSH‰â6~heroku-deflater-0.6.3/spec/fixtures/assets/spec2.js0000644000175000017500000000010213467103550021611 0ustar samyaksamyak// Fixture file (function() { console.log("Hello world"); })(); heroku-deflater-0.6.3/spec/fixtures/assets/spec.js0000644000175000017500000000017613467103550021542 0ustar samyaksamyak// Fixture file (function() { console.log("Hello world"); console.log("Hello world"); console.log("Hello world"); })(); heroku-deflater-0.6.3/spec/fixtures/non-assets/0000755000175000017500000000000013467103550021036 5ustar samyaksamyakheroku-deflater-0.6.3/spec/fixtures/non-assets/spec.js.gz0000644000175000017500000000012613467103550022744 0ustar samyaksamyak‹…QÓ×Wpˬ()-JUHËÌIåÒH+ÍK.ÉÌÏÓÐT¨æRPHÎÏ+ÎÏIÕËÉO×PòHÍÉÉW(Ï/ÊIQÒ´¦H¶VSH‰â6~heroku-deflater-0.6.3/spec/cache_control_manager_spec.rb0000644000175000017500000000311613467103550022730 0ustar samyaksamyakrequire 'heroku-deflater/cache_control_manager' require 'rails' describe HerokuDeflater::CacheControlManager do class Rails5App def config @_config ||= Config.new end class Config attr_accessor :headers def initialize @headers = {} end def public_file_server self end end end class Rails4App def config @_config ||= Config.new end class Config attr_accessor :static_cache_control end end context 'Rails 4.x and below' do let(:app) { Rails4App.new } subject { described_class.new(app) } before do allow(subject).to receive(:rails_version_5?).and_return(false) subject.setup_max_age(86400) end it 'sets max age for static_cache_control config option' do expect(app.config.static_cache_control).to eq('public, max-age=86400') end it 'cache_control_headers returns cache control option' do expect(subject.cache_control_headers).to eq('public, max-age=86400') end end context 'Rails 5' do let(:app) { Rails5App.new } subject { described_class.new(app) } before do allow(subject).to receive(:rails_version_5?).and_return(true) subject.setup_max_age(86400) end it 'sets max age for public_file_server config option' do expect(app.config.public_file_server.headers['Cache-Control']).to eq('public, max-age=86400') end it 'cache_control_headers returns hash cache control headers' do expect(subject.cache_control_headers).to eq({'Cache-Control' =>'public, max-age=86400'}) end end end heroku-deflater-0.6.3/spec/skip_binary_spec.rb0000644000175000017500000000237313467103550020751 0ustar samyaksamyakrequire 'rack/mock' require 'heroku-deflater/skip_binary' describe HerokuDeflater::SkipBinary do let(:env) { Rack::MockRequest.env_for('/') } def app_returning_type(type, headers = {}) lambda { |env| [200, {'Content-Type' => type}.merge(headers), ['']] } end def process(type, headers = {}) described_class.new(app_returning_type(type, headers)).call(env)[1] end it "forbids compressing of binary types" do %w[application/gzip application/pdf image/jpeg].each do |type| headers = process(type) expect(headers['Cache-Control'].to_s).to include('no-transform') end end it "allows compressing of text types" do %w[text/plain text/html application/json application/javascript application/rss+xml].each do |type| headers = process(type) expect(headers['Cache-Control'].to_s).not_to include('no-transform') end end it "adds to existing headers" do headers = process('image/gif', 'Cache-Control' => 'public') expect(headers['Cache-Control']).to eq('public, no-transform') end it "doesn't add 'no-transform' if it's already present" do headers = process('image/gif', 'Cache-Control' => 'public, no-transform') expect(headers['Cache-Control']).to eq('public, no-transform') end end heroku-deflater-0.6.3/.rspec0000644000175000017500000000004713467103550015257 0ustar samyaksamyak--colour --format progress --tag ~slow heroku-deflater-0.6.3/LICENSE.txt0000644000175000017500000000204413467103550015764 0ustar samyaksamyakCopyright (c) 2012 Roman Shterenzon Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. heroku-deflater-0.6.3/Gemfile0000644000175000017500000000021313467103550015430 0ustar samyaksamyaksource "http://rubygems.org" gem 'rack', '>=1.4.5' group :development do gem "bundler" gem "jeweler" gem "rspec" gem "rails" end heroku-deflater-0.6.3/heroku-deflater.gemspec0000644000175000017500000000470013467103550020570 0ustar samyaksamyak# Generated by jeweler # DO NOT EDIT THIS FILE DIRECTLY # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec' # -*- encoding: utf-8 -*- # stub: heroku-deflater 0.6.3 ruby lib Gem::Specification.new do |s| s.name = "heroku-deflater".freeze s.version = "0.6.3" s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version= s.require_paths = ["lib".freeze] s.authors = ["Roman Shterenzon".freeze] s.date = "2017-01-25" s.description = "Deflate assets on heroku".freeze s.email = "romanbsd@yahoo.com".freeze s.extra_rdoc_files = [ "LICENSE.txt", "README.md" ] s.files = [ ".rspec", "Gemfile", "Gemfile.lock", "LICENSE.txt", "README.md", "Rakefile", "VERSION", "heroku-deflater.gemspec", "lib/heroku-deflater.rb", "lib/heroku-deflater/cache_control_manager.rb", "lib/heroku-deflater/railtie.rb", "lib/heroku-deflater/serve_zipped_assets.rb", "lib/heroku-deflater/skip_binary.rb", "spec/cache_control_manager_spec.rb", "spec/fixtures/assets/spec.js", "spec/fixtures/assets/spec.js.gz", "spec/fixtures/assets/spec2.js", "spec/fixtures/non-assets/spec.js.gz", "spec/serve_zipped_assets_spec.rb", "spec/skip_binary_spec.rb" ] s.homepage = "http://github.com/romanbsd/heroku-deflater".freeze s.licenses = ["MIT".freeze] s.rubygems_version = "2.6.8".freeze s.summary = "Deflate assets on heroku".freeze if s.respond_to? :specification_version then s.specification_version = 4 if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then s.add_runtime_dependency(%q.freeze, [">= 1.4.5"]) s.add_development_dependency(%q.freeze, [">= 0"]) s.add_development_dependency(%q.freeze, [">= 0"]) s.add_development_dependency(%q.freeze, [">= 0"]) s.add_development_dependency(%q.freeze, [">= 0"]) else s.add_dependency(%q.freeze, [">= 1.4.5"]) s.add_dependency(%q.freeze, [">= 0"]) s.add_dependency(%q.freeze, [">= 0"]) s.add_dependency(%q.freeze, [">= 0"]) s.add_dependency(%q.freeze, [">= 0"]) end else s.add_dependency(%q.freeze, [">= 1.4.5"]) s.add_dependency(%q.freeze, [">= 0"]) s.add_dependency(%q.freeze, [">= 0"]) s.add_dependency(%q.freeze, [">= 0"]) s.add_dependency(%q.freeze, [">= 0"]) end end heroku-deflater-0.6.3/README.md0000644000175000017500000000261013467103550015417 0ustar samyaksamyak# heroku-deflater A simple rack middleware that enables compressing of your assets and application responses on Heroku, while not wasting CPU cycles on pointlessly compressing images and other binary responses. It also includes code from https://github.com/mattolson/heroku_rails_deflate Before serving a file from disk to a gzip-enabled client, it will look for a precompressed file in the same location that ends in ".gz". The purpose is to avoid compressing the same file each time it is requested. ## Installing Add to your Gemfile: gem 'heroku-deflater', :group => :production ## Contributing to heroku-deflater * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet. * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it. * Fork the project. * Start a feature/bugfix branch. * Commit and push until you are happy with your contribution. * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally. * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it. ## Copyright Copyright (c) 2012 Roman Shterenzon. See LICENSE.txt for further details. [1]: https://github.com/rack/rack/issues/349 heroku-deflater-0.6.3/Rakefile0000644000175000017500000000224413467103550015610 0ustar samyaksamyak# encoding: utf-8 require 'rubygems' require 'bundler' begin Bundler.setup(:default, :development) rescue Bundler::BundlerError => e $stderr.puts e.message $stderr.puts "Run `bundle install` to install missing gems" exit e.status_code end require 'rake' require 'jeweler' Jeweler::Tasks.new do |gem| # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options gem.name = "heroku-deflater" gem.homepage = "http://github.com/romanbsd/heroku-deflater" gem.license = "MIT" gem.summary = %Q{Deflate assets on heroku} gem.description = gem.summary gem.email = "romanbsd@yahoo.com" gem.authors = ["Roman Shterenzon"] # dependencies defined in Gemfile end Jeweler::RubygemsDotOrgTasks.new require 'rake/testtask' Rake::TestTask.new(:test) do |test| test.libs << 'lib' << 'test' test.pattern = 'test/**/test_*.rb' test.verbose = true end task :default => :test require 'rdoc/task' Rake::RDocTask.new do |rdoc| version = File.exist?('VERSION') ? File.read('VERSION') : "" rdoc.rdoc_dir = 'rdoc' rdoc.title = "heroku-deflater #{version}" rdoc.rdoc_files.include('README*') rdoc.rdoc_files.include('lib/**/*.rb') end heroku-deflater-0.6.3/Gemfile.lock0000644000175000017500000001003613467103550016363 0ustar samyaksamyakGEM remote: http://rubygems.org/ specs: actioncable (5.0.1) actionpack (= 5.0.1) nio4r (~> 1.2) websocket-driver (~> 0.6.1) actionmailer (5.0.1) actionpack (= 5.0.1) actionview (= 5.0.1) activejob (= 5.0.1) mail (~> 2.5, >= 2.5.4) rails-dom-testing (~> 2.0) actionpack (5.0.1) actionview (= 5.0.1) activesupport (= 5.0.1) rack (~> 2.0) rack-test (~> 0.6.3) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.0.2) actionview (5.0.1) activesupport (= 5.0.1) builder (~> 3.1) erubis (~> 2.7.0) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.0.2) activejob (5.0.1) activesupport (= 5.0.1) globalid (>= 0.3.6) activemodel (5.0.1) activesupport (= 5.0.1) activerecord (5.0.1) activemodel (= 5.0.1) activesupport (= 5.0.1) arel (~> 7.0) activesupport (5.0.1) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (~> 0.7) minitest (~> 5.1) tzinfo (~> 1.1) addressable (2.5.0) public_suffix (~> 2.0, >= 2.0.2) arel (7.1.4) builder (3.2.3) concurrent-ruby (1.0.4) descendants_tracker (0.0.4) thread_safe (~> 0.3, >= 0.3.1) diff-lcs (1.3) erubis (2.7.0) faraday (0.9.2) multipart-post (>= 1.2, < 3) git (1.3.0) github_api (0.11.3) addressable (~> 2.3) descendants_tracker (~> 0.0.1) faraday (~> 0.8, < 0.10) hashie (>= 1.2) multi_json (>= 1.7.5, < 2.0) nokogiri (~> 1.6.0) oauth2 globalid (0.3.7) activesupport (>= 4.1.0) hashie (3.4.6) highline (1.7.8) i18n (0.7.0) jeweler (2.3.3) builder bundler (>= 1.0) git (>= 1.2.5) github_api (~> 0.11.0) highline (>= 1.6.15) nokogiri (>= 1.5.10) psych (~> 2.2) rake rdoc semver2 jwt (1.5.6) loofah (2.0.3) nokogiri (>= 1.5.9) mail (2.6.4) mime-types (>= 1.16, < 4) method_source (0.8.2) mime-types (3.1) mime-types-data (~> 3.2015) mime-types-data (3.2016.0521) mini_portile2 (2.1.0) minitest (5.10.1) multi_json (1.12.1) multi_xml (0.6.0) multipart-post (2.0.0) nio4r (1.2.1) nokogiri (1.6.8.1) mini_portile2 (~> 2.1.0) oauth2 (1.3.0) faraday (>= 0.8, < 0.11) jwt (~> 1.0) multi_json (~> 1.3) multi_xml (~> 0.5) rack (>= 1.2, < 3) psych (2.2.2) public_suffix (2.0.5) rack (2.0.1) rack-test (0.6.3) rack (>= 1.0) rails (5.0.1) actioncable (= 5.0.1) actionmailer (= 5.0.1) actionpack (= 5.0.1) actionview (= 5.0.1) activejob (= 5.0.1) activemodel (= 5.0.1) activerecord (= 5.0.1) activesupport (= 5.0.1) bundler (>= 1.3.0, < 2.0) railties (= 5.0.1) sprockets-rails (>= 2.0.0) rails-dom-testing (2.0.2) activesupport (>= 4.2.0, < 6.0) nokogiri (~> 1.6) rails-html-sanitizer (1.0.3) loofah (~> 2.0) railties (5.0.1) actionpack (= 5.0.1) activesupport (= 5.0.1) method_source rake (>= 0.8.7) thor (>= 0.18.1, < 2.0) rake (12.0.0) rdoc (5.0.0) rspec (3.5.0) rspec-core (~> 3.5.0) rspec-expectations (~> 3.5.0) rspec-mocks (~> 3.5.0) rspec-core (3.5.4) rspec-support (~> 3.5.0) rspec-expectations (3.5.0) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.5.0) rspec-mocks (3.5.0) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.5.0) rspec-support (3.5.0) semver2 (3.4.2) sprockets (3.7.1) concurrent-ruby (~> 1.0) rack (> 1, < 3) sprockets-rails (3.2.0) actionpack (>= 4.0) activesupport (>= 4.0) sprockets (>= 3.0.0) thor (0.19.4) thread_safe (0.3.5) tzinfo (1.2.2) thread_safe (~> 0.1) websocket-driver (0.6.5) websocket-extensions (>= 0.1.0) websocket-extensions (0.1.2) PLATFORMS ruby DEPENDENCIES bundler jeweler rack (>= 1.4.5) rails rspec BUNDLED WITH 1.14.2