class Rack::Brotli::Deflater

This middleware enables compression of http responses.

Currently supported compression algorithms:

* br

The middleware automatically detects when compression is supported and allowed. For example no transformation is made when a cache directive of 'no-transform' is present, or when the response status code is one that doesn't allow an entity body.

Public Class Methods

new(app, options = {}) click to toggle source

Creates Rack::Brotli middleware.

app

rack app instance

options

hash of deflater options, i.e. 'if' - a lambda enabling / disabling deflation based on returned boolean value

e.g use Rack::Brotli, :if => lambda { |env, status, headers, body| body.map(&:bytesize).reduce(0, :+) > 512 }

'include' - a list of content types that should be compressed 'deflater' - Brotli compression options

   # File lib/rack/brotli/deflater.rb
27 def initialize(app, options = {})
28   @app = app
29 
30   @condition = options[:if]
31   @compressible_types = options[:include]
32   @deflater_options = { quality: 5 }
33   @deflater_options.merge!(options[:deflater]) if options[:deflater]
34   @deflater_options
35 end

Public Instance Methods

call(env) click to toggle source
   # File lib/rack/brotli/deflater.rb
37 def call(env)
38   status, headers, body = @app.call(env)
39   headers = header_hash(headers)
40 
41   unless should_deflate?(env, status, headers, body)
42     return [status, headers, body]
43   end
44 
45   request = Rack::Request.new(env)
46 
47   encoding = Rack::Utils.select_best_encoding(%w(br),
48                                         request.accept_encoding)
49 
50   return [status, headers, body] unless encoding
51 
52   # Set the Vary HTTP header.
53   vary = headers["Vary"].to_s.split(",").map(&:strip)
54   unless vary.include?("*") || vary.include?("Accept-Encoding")
55     headers["Vary"] = vary.push("Accept-Encoding").join(",")
56   end
57 
58   case encoding
59   when "br"
60     headers['Content-Encoding'] = "br"
61     headers.delete('Content-Length')
62     [status, headers, BrotliStream.new(body, @deflater_options)]
63   when nil
64     message = "An acceptable encoding for the requested resource #{request.fullpath} could not be found."
65     bp = Rack::BodyProxy.new([message]) { body.close if body.respond_to?(:close) }
66     [406, {'Content-Type' => "text/plain", 'Content-Length' => message.length.to_s}, bp]
67   end
68 end

Private Instance Methods

header_hash(headers) click to toggle source
    # File lib/rack/brotli/deflater.rb
 96 def header_hash(headers)
 97   if headers.is_a?(Rack::Utils::HeaderHash)
 98     headers
 99   else
100     Rack::Utils::HeaderHash.new(headers) # rack < 2.2
101   end
102 end
should_deflate?(env, status, headers, body) click to toggle source
    # File lib/rack/brotli/deflater.rb
104 def should_deflate?(env, status, headers, body)
105   # Skip compressing empty entity body responses and responses with
106   # no-transform set.
107   if Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.key?(status.to_i) ||
108       /\bno-transform\b/.match?(headers['Cache-Control'].to_s) ||
109       headers['Content-Encoding']&.!~(/\bidentity\b/)
110     return false
111   end
112 
113   # Skip if @compressible_types are given and does not include request's content type
114   return false if @compressible_types && !(headers.has_key?('Content-Type') && @compressible_types.include?(headers['Content-Type'][/[^;]*/]))
115 
116   # Skip if @condition lambda is given and evaluates to false
117   return false if @condition && !@condition.call(env, status, headers, body)
118 
119   # No point in compressing empty body, also handles usage with
120   # Rack::Sendfile.
121   return false if headers['Content-Length'] == '0'
122 
123   true
124 end