Skip to content

Commit e6aabc8

Browse files
committed
Enable brotli compression in the caches_page
1 parent cebddba commit e6aabc8

File tree

7 files changed

+101
-54
lines changed

7 files changed

+101
-54
lines changed

.travis.yml

+6-22
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,17 @@ cache:
55
bundler: true
66

77
before_install:
8-
- gem install bundler
8+
- "travis_retry gem update --system 2.7.9"
9+
- "travis_retry gem install bundler -v '1.17.3'"
910

1011
rvm:
11-
- 1.9.3
12-
- 2.0.0
13-
- 2.1.9
14-
- 2.2.6
1512
- 2.3.3
1613
- 2.4.0
14+
- 2.5.0
15+
- 2.6.3
1716

1817
gemfile:
1918
- Gemfile
20-
- gemfiles/Gemfile-4-0-stable
2119
- gemfiles/Gemfile-4-1-stable
2220
- gemfiles/Gemfile-4-2-stable
2321
- gemfiles/Gemfile-5-0-stable
@@ -27,23 +25,9 @@ matrix:
2725
allow_failures:
2826
- gemfile: gemfiles/Gemfile-edge
2927
exclude:
30-
- rvm: 1.9.3
31-
gemfile: Gemfile
32-
- rvm: 2.0.0
33-
gemfile: Gemfile
34-
- rvm: 2.1.9
35-
gemfile: Gemfile
36-
- rvm: 1.9.3
37-
gemfile: gemfiles/Gemfile-5-0-stable
38-
- rvm: 2.0.0
39-
gemfile: gemfiles/Gemfile-5-0-stable
40-
- rvm: 2.1.9
41-
gemfile: gemfiles/Gemfile-5-0-stable
42-
- rvm: 1.9.3
28+
- rvm: 2.3.3
4329
gemfile: gemfiles/Gemfile-edge
44-
- rvm: 2.0.0
45-
gemfile: gemfiles/Gemfile-edge
46-
- rvm: 2.1.9
30+
- rvm: 2.4.0
4731
gemfile: gemfiles/Gemfile-edge
4832
- rvm: 2.4.0
4933
gemfile: gemfiles/Gemfile-4-0-stable

actionpack-page_caching.gemspec

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ Gem::Specification.new do |gem|
1616
gem.license = 'MIT'
1717

1818
gem.add_dependency "actionpack", ">= 4.0.0"
19+
gem.add_dependency 'brotli', '>= 0.2.0'
1920

2021
gem.add_development_dependency "mocha"
2122
end

lib/action_controller/caching/pages.rb

+48-22
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
1+
# frozen_string_literal: true
2+
13
require "fileutils"
24
require "uri"
35
require "active_support/core_ext/class/attribute_accessors"
46
require "active_support/core_ext/string/strip"
7+
require 'brotli'
58

69
module ActionController
710
module Caching
11+
COMPRESSIONS_DEFAULTS = {gzip: 9, brotli: 9} # ::Zlib::BEST_COMPRESSION
12+
813
# Page caching is an approach to caching where the entire action output of is
914
# stored as a HTML file that the web server can serve without going through
1015
# Action Pack. This is the fastest way to cache your content as opposed to going
@@ -60,6 +65,9 @@ module Pages
6065
# or <tt>:best_speed</tt> or an integer configuring the compression level.
6166
class_attribute :page_cache_compression
6267
self.page_cache_compression ||= false
68+
69+
class_attribute :page_cache_compressions
70+
self.page_cache_compressions ||= COMPRESSIONS_DEFAULTS
6371
end
6472

6573
class PageCache #:nodoc:
@@ -75,9 +83,9 @@ def expire(path)
7583
end
7684
end
7785

78-
def cache(content, path, extension = nil, gzip = Zlib::BEST_COMPRESSION)
86+
def cache(content, path, extension = nil, compressions: COMPRESSIONS_DEFAULTS)
7987
instrument :write_page, path do
80-
write(content, cache_path(path, extension), gzip)
88+
write(content, cache_path(path, extension), compressions: compressions)
8189
end
8290
end
8391

@@ -161,14 +169,20 @@ def cache_path(path, extension = nil)
161169
def delete(path)
162170
File.delete(path) if File.exist?(path)
163171
File.delete(path + ".gz") if File.exist?(path + ".gz")
172+
File.delete(path + ".br") if File.exist?(path + ".br")
164173
end
165174

166-
def write(content, path, gzip)
175+
def write(content, path, compressions:)
167176
FileUtils.makedirs(File.dirname(path))
168177
File.open(path, "wb+") { |f| f.write(content) }
169178

170-
if gzip
171-
Zlib::GzipWriter.open(path + ".gz", gzip) { |f| f.write(content) }
179+
if compressions[:gzip]
180+
Zlib::GzipWriter.open(path + ".gz", compressions[:gzip]) { |f| f.write(content) }
181+
end
182+
183+
if compressions[:brotli]
184+
brotli = ::Brotli.deflate(content, mode: :text, quality: compressions[:brotli])
185+
File.atomic_write(path + ".br") { |f| f.write(brotli) }
172186
end
173187
end
174188

@@ -190,9 +204,9 @@ def expire_page(path)
190204
# Manually cache the +content+ in the key determined by +path+.
191205
#
192206
# cache_page "I'm the cached content", "/lists/show"
193-
def cache_page(content, path, extension = nil, gzip = Zlib::BEST_COMPRESSION)
207+
def cache_page(content, path, extension = nil, compressions: COMPRESSIONS_DEFAULTS)
194208
if perform_caching
195-
page_cache.cache(content, path, extension, gzip)
209+
page_cache.cache(content, path, extension, compressions: compressions)
196210
end
197211
end
198212

@@ -214,21 +228,33 @@ def caches_page(*actions)
214228
if perform_caching
215229
options = actions.extract_options!
216230

217-
gzip_level = options.fetch(:gzip, page_cache_compression)
218-
gzip_level = \
219-
case gzip_level
220-
when Symbol
221-
Zlib.const_get(gzip_level.upcase)
222-
when Integer
223-
gzip_level
224-
when false
225-
nil
226-
else
227-
Zlib::BEST_COMPRESSION
228-
end
231+
compressions = options.fetch(:compressions, page_cache_compressions)
232+
233+
if options.key?(:gzip)
234+
ActiveSupport::Deprecation.warn(
235+
"actionpack-page-caching now support brotli compression.\n
236+
Using gzip directly is deprecated. instead of\n caches_page :index, gzip: Zlib::BEST_COMPRESSION \n
237+
please use\n caches_page :index, compressions: {gzip: Zlib::BEST_COMPRESSION, brotli: 9}"
238+
)
239+
240+
gzip_level = options.fetch(:gzip, page_cache_compression)
241+
gzip_level = \
242+
case gzip_level
243+
when Symbol
244+
Zlib.const_get(gzip_level.upcase)
245+
when Integer
246+
gzip_level
247+
when false
248+
nil
249+
else
250+
Zlib::BEST_COMPRESSION
251+
end
252+
253+
compressions[:gzip] = gzip_level
254+
end
229255

230256
after_action({ only: actions }.merge(options)) do |c|
231-
c.cache_page(nil, nil, gzip_level)
257+
c.cache_page(nil, nil, compressions: compressions)
232258
end
233259
end
234260
end
@@ -263,7 +289,7 @@ def expire_page(options = {})
263289
# request being handled is used.
264290
#
265291
# cache_page "I'm the cached content", controller: "lists", action: "show"
266-
def cache_page(content = nil, options = nil, gzip = Zlib::BEST_COMPRESSION)
292+
def cache_page(content = nil, options = nil, compressions: COMPRESSIONS_DEFAULTS)
267293
if perform_caching? && caching_allowed?
268294
path = \
269295
case options
@@ -279,7 +305,7 @@ def cache_page(content = nil, options = nil, gzip = Zlib::BEST_COMPRESSION)
279305
extension = ".#{type_symbol}"
280306
end
281307

282-
page_cache.cache(content || response.body, path, extension, gzip)
308+
page_cache.cache(content || response.body, path, extension, compressions: compressions)
283309
end
284310
end
285311

lib/action_controller/page_caching.rb

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
# frozen_string_literal: true
2+
13
require "action_controller/caching/pages"
24

35
module ActionController

lib/actionpack/page_caching.rb

+2
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
1+
# frozen_string_literal: true
2+
13
require "actionpack/page_caching/railtie"

lib/actionpack/page_caching/railtie.rb

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
# frozen_string_literal: true
2+
13
require "rails/railtie"
24

35
module ActionPack

test/caching_test.rb

+40-10
Original file line numberDiff line numberDiff line change
@@ -118,9 +118,11 @@ class PageCachingTestController < CachingController
118118
caches_page :ok, :no_content, if: Proc.new { |c| !c.request.format.json? }
119119
caches_page :found, :not_found
120120
caches_page :about_me
121-
caches_page :default_gzip
122-
caches_page :no_gzip, gzip: false
123-
caches_page :gzip_level, gzip: :best_speed
121+
caches_page :default_compressions
122+
caches_page :no_gzip, compressions: {gzip: false}
123+
caches_page :gzip_level, compressions: {gzip: 1}
124+
caches_page :no_brotli, compressions: {brotli: false}
125+
caches_page :brotli_level, compressions: {brotli: 1}
124126

125127
def ok
126128
render html: "ok"
@@ -143,8 +145,8 @@ def custom_path
143145
cache_page(nil, "/index.html")
144146
end
145147

146-
def default_gzip
147-
render html: "default_gzip"
148+
def default_compressions
149+
render html: "default_compressions"
148150
end
149151

150152
def no_gzip
@@ -155,6 +157,14 @@ def gzip_level
155157
render html: "gzip_level"
156158
end
157159

160+
def no_brotli
161+
render html: "no_brotli"
162+
end
163+
164+
def brotli_level
165+
render html: "brotli_level"
166+
end
167+
158168
def expire_custom_path
159169
expire_page("/index.html")
160170
head :ok
@@ -241,6 +251,7 @@ def test_should_gzip_cache
241251

242252
get :expire_custom_path
243253
assert_page_not_cached :index, controller: ".", format: "html.gz"
254+
assert_page_not_cached :index, controller: ".", format: "html.br"
244255
end
245256

246257
def test_should_allow_to_disable_gzip
@@ -253,24 +264,43 @@ def test_should_allow_to_disable_gzip
253264
assert_page_not_cached :no_gzip, format: "html.gz"
254265
end
255266

256-
def test_should_use_config_gzip_by_default
267+
def test_should_allow_to_disable_brotli
268+
draw do
269+
get "/page_caching_test/no_brotli", to: "page_caching_test#no_brotli"
270+
end
271+
272+
get :no_brotli
273+
assert_page_cached :no_brotli, format: "html"
274+
assert_page_not_cached :no_brotli, format: "html.br"
275+
end
276+
277+
def test_should_use_config_compressions_by_default
257278
draw do
258-
get "/page_caching_test/default_gzip", to: "page_caching_test#default_gzip"
279+
get "/page_caching_test/default_compressions", to: "page_caching_test#default_compressions"
259280
end
260281

261-
@controller.expects(:cache_page).with(nil, nil, Zlib::BEST_COMPRESSION)
262-
get :default_gzip
282+
@controller.expects(:cache_page).with(nil, nil, compressions: {gzip: Zlib::BEST_COMPRESSION, brotli: 9})
283+
get :default_compressions
263284
end
264285

265286
def test_should_set_gzip_level
266287
draw do
267288
get "/page_caching_test/gzip_level", to: "page_caching_test#gzip_level"
268289
end
269290

270-
@controller.expects(:cache_page).with(nil, nil, Zlib::BEST_SPEED)
291+
@controller.expects(:cache_page).with(nil, nil, compressions: {gzip: Zlib::BEST_SPEED})
271292
get :gzip_level
272293
end
273294

295+
def test_should_set_brotli_level
296+
draw do
297+
get "/page_caching_test/brotli_level", to: "page_caching_test#brotli_level"
298+
end
299+
300+
@controller.expects(:cache_page).with(nil, nil, compressions: {brotli: 1})
301+
get :brotli_level
302+
end
303+
274304
def test_should_cache_without_trailing_slash_on_url
275305
@controller.class.cache_page "cached content", "/page_caching_test/trailing_slash"
276306
assert_page_cached :trailing_slash, content: "cached content"

0 commit comments

Comments
 (0)