Module: ResponseBank

Defined in:
lib/response_bank.rb,
lib/response_bank/railtie.rb,
lib/response_bank/version.rb,
lib/response_bank/controller.rb,
lib/response_bank/middleware.rb,
lib/response_bank/model_extensions.rb,
lib/response_bank/brotli_splice_slot.rb,
lib/response_bank/brotli_splice_injector.rb,
lib/response_bank/response_cache_handler.rb

Defined Under Namespace

Modules: BrotliSpliceInjector, BrotliSpliceSlot, Controller, ModelExtensions Classes: Middleware, Railtie, ResponseCacheHandler

Constant Summary collapse

DEFAULT_BROTLI_COMPRESSION_LEVEL =
7
DEFAULT_COMPRESSION_LEVEL =
-> (_env, headers) {
  case headers['Content-Encoding']
  when 'br'
    DEFAULT_BROTLI_COMPRESSION_LEVEL
  when 'gzip'
    Zlib::BEST_COMPRESSION
  end
}
VERSION =
"1.3.8"

Class Attribute Summary collapse

Class Method Summary collapse

Class Attribute Details

.cache_storeObject

Returns the value of attribute cache_store.



13
14
15
# File 'lib/response_bank.rb', line 13

def cache_store
  @cache_store
end

.compression_level=(value) ⇒ Object (writeonly)

Sets the attribute compression_level

Parameters:

  • value

    the value to set the attribute compression_level to.



14
15
16
# File 'lib/response_bank.rb', line 14

def compression_level=(value)
  @compression_level = value
end

.logger=(value) ⇒ Object (writeonly)

Sets the attribute logger

Parameters:

  • value

    the value to set the attribute logger to.



14
15
16
# File 'lib/response_bank.rb', line 14

def logger=(value)
  @logger = value
end

Class Method Details

.acquire_lock(_cache_key) ⇒ Object

Raises:

  • (NotImplementedError)


39
40
41
# File 'lib/response_bank.rb', line 39

def acquire_lock(_cache_key)
  raise NotImplementedError, "Override ResponseBank.acquire_lock in an initializer."
end

.cache_key_for(data) ⇒ Object



96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/response_bank.rb', line 96

def cache_key_for(data)
  case data
  when Hash
    return data.inspect unless data.key?(:key)

    key = hash_value_str(data[:key])

    key = %{#{data[:key_schema_version]}:#{key}} if data[:key_schema_version]

    key = %{#{key}:#{hash_value_str(data[:version])}} if data[:version]

    # add the encoding to only the cache key but don't expose this detail in the entity_tag
    key = %{#{key}:#{hash_value_str(data[:encoding])}} if data[:encoding]

    key
  when Array
    data.inspect
  when Time, DateTime
    data.to_i
  when Date
    data.to_s # Date#to_i does not support timezones, using iso8601 instead
  when true, false, Integer, Symbol, String
    data.inspect
  else
    data.to_s.inspect
  end
end

.check_encoding(env, default_encoding = 'br') ⇒ Object



124
125
126
127
128
129
130
131
132
133
# File 'lib/response_bank.rb', line 124

def check_encoding(env, default_encoding = 'br')
  if env['HTTP_ACCEPT_ENCODING'].to_s.include?('br')
    'br'
  elsif env['HTTP_ACCEPT_ENCODING'].to_s.include?('gzip')
    'gzip'
  else
    # No encoding requested from client, but we still need to cache the page in server cache
    default_encoding
  end
end

.compress(content, encoding = "br", compression_level: nil) ⇒ Object



61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# File 'lib/response_bank.rb', line 61

def compress(content, encoding = "br", compression_level: nil)
  case encoding
  when 'gzip'
    attempts = 0

    begin
      Zlib.gzip(content, level: compression_level || Zlib::BEST_COMPRESSION)
    rescue Zlib::BufError
      # We get sporadic Zlib::BufError, so we retry once (https://github.com/ruby/zlib/issues/49)
      attempts += 1

      if attempts <= 1
        retry
      else
        raise
      end
    end
  when 'br'
    Brotli.deflate(content, mode: :text, quality: compression_level || DEFAULT_BROTLI_COMPRESSION_LEVEL)
  else
    raise ArgumentError, "Unsupported encoding: #{encoding}"
  end
end

.compression_level_for_request(env, headers) ⇒ Object



27
28
29
30
31
32
33
# File 'lib/response_bank.rb', line 27

def compression_level_for_request(env, headers)
  if @compression_level
    return @compression_level.respond_to?(:call) ? @compression_level.call(env, headers) : @compression_level
  end

  DEFAULT_COMPRESSION_LEVEL.call(env, headers)
end

.decompress(content, encoding = "br") ⇒ Object



85
86
87
88
89
90
91
92
93
94
# File 'lib/response_bank.rb', line 85

def decompress(content, encoding = "br")
  case encoding
  when 'gzip'
    Zlib.gunzip(content)
  when 'br'
    Brotli.inflate(content)
  else
    raise ArgumentError, "Unsupported encoding: #{encoding}"
  end
end

.log(message) ⇒ Object



35
36
37
# File 'lib/response_bank.rb', line 35

def log(message)
  @logger.info("[ResponseBank] #{message}")
end

.measureObject



55
56
57
58
59
# File 'lib/response_bank.rb', line 55

def measure
  Benchmark.realtime do
    yield
  end * 1000 # milliseconds
end

.read_from_backing_cache_store(_env, cache_key, backing_cache_store: cache_store) ⇒ Object



51
52
53
# File 'lib/response_bank.rb', line 51

def read_from_backing_cache_store(_env, cache_key, backing_cache_store: cache_store)
  backing_cache_store.read(cache_key, raw: true)
end

.write_to_backing_cache_store(_env, key, payload, expires_in: nil) ⇒ Object



47
48
49
# File 'lib/response_bank.rb', line 47

def write_to_backing_cache_store(_env, key, payload, expires_in: nil)
  cache_store.write(key, payload, raw: true, expires_in: expires_in)
end

.write_to_cache(_key) ⇒ Object



43
44
45
# File 'lib/response_bank.rb', line 43

def write_to_cache(_key)
  yield
end