1
+ # frozen_string_literal: true
2
+
1
3
require "fileutils"
2
4
require "uri"
3
5
require "active_support/core_ext/class/attribute_accessors"
4
6
require "active_support/core_ext/string/strip"
7
+ require 'brotli'
5
8
6
9
module ActionController
7
10
module Caching
11
+ COMPRESSIONS_DEFAULTS = { gzip : 9 , brotli : 9 } # ::Zlib::BEST_COMPRESSION
12
+
8
13
# Page caching is an approach to caching where the entire action output of is
9
14
# stored as a HTML file that the web server can serve without going through
10
15
# Action Pack. This is the fastest way to cache your content as opposed to going
@@ -60,6 +65,9 @@ module Pages
60
65
# or <tt>:best_speed</tt> or an integer configuring the compression level.
61
66
class_attribute :page_cache_compression
62
67
self . page_cache_compression ||= false
68
+
69
+ class_attribute :page_cache_compressions
70
+ self . page_cache_compressions ||= COMPRESSIONS_DEFAULTS
63
71
end
64
72
65
73
class PageCache #:nodoc:
@@ -75,9 +83,9 @@ def expire(path)
75
83
end
76
84
end
77
85
78
- def cache ( content , path , extension = nil , gzip = Zlib :: BEST_COMPRESSION )
86
+ def cache ( content , path , extension = nil , compressions : COMPRESSIONS_DEFAULTS )
79
87
instrument :write_page , path do
80
- write ( content , cache_path ( path , extension ) , gzip )
88
+ write ( content , cache_path ( path , extension ) , compressions : compressions )
81
89
end
82
90
end
83
91
@@ -161,14 +169,20 @@ def cache_path(path, extension = nil)
161
169
def delete ( path )
162
170
File . delete ( path ) if File . exist? ( path )
163
171
File . delete ( path + ".gz" ) if File . exist? ( path + ".gz" )
172
+ File . delete ( path + ".br" ) if File . exist? ( path + ".br" )
164
173
end
165
174
166
- def write ( content , path , gzip )
175
+ def write ( content , path , compressions : )
167
176
FileUtils . makedirs ( File . dirname ( path ) )
168
177
File . open ( path , "wb+" ) { |f | f . write ( content ) }
169
178
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 ) }
172
186
end
173
187
end
174
188
@@ -190,9 +204,9 @@ def expire_page(path)
190
204
# Manually cache the +content+ in the key determined by +path+.
191
205
#
192
206
# 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 )
194
208
if perform_caching
195
- page_cache . cache ( content , path , extension , gzip )
209
+ page_cache . cache ( content , path , extension , compressions : compressions )
196
210
end
197
211
end
198
212
@@ -214,21 +228,33 @@ def caches_page(*actions)
214
228
if perform_caching
215
229
options = actions . extract_options!
216
230
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
229
255
230
256
after_action ( { only : actions } . merge ( options ) ) do |c |
231
- c . cache_page ( nil , nil , gzip_level )
257
+ c . cache_page ( nil , nil , compressions : compressions )
232
258
end
233
259
end
234
260
end
@@ -263,7 +289,7 @@ def expire_page(options = {})
263
289
# request being handled is used.
264
290
#
265
291
# 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 )
267
293
if perform_caching? && caching_allowed?
268
294
path = \
269
295
case options
@@ -279,7 +305,7 @@ def cache_page(content = nil, options = nil, gzip = Zlib::BEST_COMPRESSION)
279
305
extension = ".#{ type_symbol } "
280
306
end
281
307
282
- page_cache . cache ( content || response . body , path , extension , gzip )
308
+ page_cache . cache ( content || response . body , path , extension , compressions : compressions )
283
309
end
284
310
end
285
311
0 commit comments