response_bank 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 75476fbf8271b8744fdca486f31ddfff585ab0680e29c600e25393d24b6b6fbb
4
- data.tar.gz: 6092909983069ec5689e4e6ada0d241e632331fd6c97fb94e4ce5029c4cf228a
3
+ metadata.gz: 7d25739f9bd846e1cd3364a422cc90590b32fcb5f7e56cd307af0e83f0d5ddac
4
+ data.tar.gz: 14c1a7f96abcc45025cee9f998342fb6185779774dc72986ebb1a5bbcb96c413
5
5
  SHA512:
6
- metadata.gz: ccade0feb4bcc259e6bd0d3435799960014f204031b564ca88892e5c80afce36997f266b91acb0eb2af1e64f80488af49166fe4a99ab2d31874cac9827e37eaa
7
- data.tar.gz: 5e74d777bfd30f8eed957d5ed07d11ffab8e6d4d462194277b7a38c28b70728445d0c598a26173020fc1d26c3c11410aabc6cf7f79f5a06cd806546a687cff7e
6
+ metadata.gz: b6120bc7a707fa5ddd1cd3a143e40681e375864b075fe0e74b1128cc4ee460690400a70c47599772addba8d2bee56b02ce14a820c5e4af376f0eaa154f1fe88c
7
+ data.tar.gz: 5ae1545f2c40d7abbf06b8ba43c8d353cb171fc97ce667fa280c5f991c37f7e514c7673898a99242e446f9975ef22f4cb91eaa053165664a9c309fa4d6b39197
@@ -1,11 +1,10 @@
1
1
  # frozen_string_literal: true
2
- require 'useragent'
3
2
 
4
3
  module ResponseBank
5
4
  class Middleware
6
5
  # Limit the cached headers
7
6
  # TODO: Make this lowercase/case-insentitive as per rfc2616 §4.2
8
- CACHEABLE_HEADERS = ["Location", "Content-Type", "ETag", "Content-Encoding", "Last-Modified", "Cache-Control", "Expires", "Surrogate-Keys", "Cache-Tags"].freeze
7
+ CACHEABLE_HEADERS = ["Location", "Content-Type", "ETag", "Content-Encoding", "Last-Modified", "Cache-Control", "Expires", "Link", "Surrogate-Keys", "Cache-Tags"].freeze
9
8
 
10
9
  REQUESTED_WITH = "HTTP_X_REQUESTED_WITH"
11
10
  ACCEPT = "HTTP_ACCEPT"
@@ -17,7 +16,7 @@ module ResponseBank
17
16
 
18
17
  def call(env)
19
18
  env['cacheable.cache'] = false
20
- gzip = env['gzip'] = env['HTTP_ACCEPT_ENCODING'].to_s.include?("gzip")
19
+ content_encoding = env['response_bank.server_cache_encoding'] = ResponseBank.check_encoding(env)
21
20
 
22
21
  status, headers, body = @app.call(env)
23
22
 
@@ -25,9 +24,6 @@ module ResponseBank
25
24
  if [200, 404, 301, 304].include?(status)
26
25
  headers['ETag'] = env['cacheable.key']
27
26
 
28
- if ie_ajax_request?(env)
29
- headers["Expires"] = "-1"
30
- end
31
27
  end
32
28
 
33
29
  if [200, 404, 301].include?(status) && env['cacheable.miss']
@@ -39,11 +35,15 @@ module ResponseBank
39
35
  body.each { |part| body_string << part }
40
36
  end
41
37
 
42
- body_gz = ResponseBank.compress(body_string)
38
+ body_compressed = nil
39
+ if body_string && body_string != ""
40
+ headers['Content-Encoding'] = content_encoding
41
+ body_compressed = ResponseBank.compress(body_string, content_encoding)
42
+ end
43
43
 
44
44
  cached_headers = headers.slice(*CACHEABLE_HEADERS)
45
45
  # Store result
46
- cache_data = [status, cached_headers, body_gz, timestamp]
46
+ cache_data = [status, cached_headers, body_compressed, timestamp]
47
47
 
48
48
  ResponseBank.write_to_cache(env['cacheable.key']) do
49
49
  payload = MessagePack.dump(cache_data)
@@ -55,11 +55,15 @@ module ResponseBank
55
55
  )
56
56
  end
57
57
 
58
- # since we had to generate the gz version above already we may
58
+ # since we had to generate the compressed version already we may
59
59
  # as well serve it if the client wants it
60
- if gzip
61
- headers['Content-Encoding'] = "gzip"
62
- body = [body_gz]
60
+ if body_compressed
61
+ if env['HTTP_ACCEPT_ENCODING'].to_s.include?(content_encoding)
62
+ body = [body_compressed]
63
+ else
64
+ # Remove content-encoding header for response with compressed content
65
+ headers.delete('Content-Encoding')
66
+ end
63
67
  end
64
68
  end
65
69
 
@@ -79,14 +83,5 @@ module ResponseBank
79
83
  Time.now.to_i
80
84
  end
81
85
 
82
- def ie_ajax_request?(env)
83
- return false unless !env[USER_AGENT].nil? && !env[USER_AGENT].empty?
84
-
85
- if env[REQUESTED_WITH] == "XmlHttpRequest" || env[ACCEPT] == "application/json"
86
- UserAgent.parse(env["HTTP_USER_AGENT"]).is_a?(UserAgent::Browsers::InternetExplorer)
87
- else
88
- false
89
- end
90
- end
91
86
  end
92
87
  end
@@ -55,7 +55,7 @@ module ResponseBank
55
55
  private
56
56
 
57
57
  def hash(key)
58
- "cacheable:#{Digest::MD5.hexdigest(key)}"
58
+ "cacheable:" + Digest::MD5.hexdigest(key)
59
59
  end
60
60
 
61
61
  def entity_tag
@@ -63,7 +63,7 @@ module ResponseBank
63
63
  end
64
64
 
65
65
  def cache_key
66
- @cache_key ||= ResponseBank.cache_key_for(key: @key_data, key_schema_version: @key_schema_version)
66
+ @cache_key ||= ResponseBank.cache_key_for(key: @key_data, key_schema_version: @key_schema_version, encoding: @env['response_bank.server_cache_encoding'])
67
67
  end
68
68
 
69
69
  def cacheable_info_dump
@@ -84,7 +84,7 @@ module ResponseBank
84
84
  response = serve_from_browser_cache(entity_tag_hash, @env['HTTP_IF_NONE_MATCH'])
85
85
  return response if response
86
86
 
87
- response = serve_from_cache(cache_key_hash, entity_tag_hash, @cache_age_tolerance)
87
+ response = serve_from_cache(cache_key_hash, @serve_unversioned ? "*" : entity_tag_hash, @cache_age_tolerance)
88
88
  return response if response
89
89
 
90
90
  # No cache hit; this request cannot be handled from cache.
@@ -143,16 +143,17 @@ module ResponseBank
143
143
  # version check
144
144
  # unversioned but tolerance threshold
145
145
  # regen
146
- @headers = @headers.merge(headers)
146
+ @headers.merge!(headers)
147
147
 
148
- if @env["gzip"]
149
- @headers['Content-Encoding'] = "gzip"
150
- else
151
- # we have to uncompress because the client doesn't support gzip
152
- ResponseBank.log("uncompressing for client without gzip")
153
- body = ResponseBank.decompress(body)
148
+ # if a cache key hit and client doesn't match encoding, return the raw body
149
+ if !@env['HTTP_ACCEPT_ENCODING'].to_s.include?(@headers['Content-Encoding'])
150
+ ResponseBank.log("uncompressing payload for client as client doesn't require encoding")
151
+ body = ResponseBank.decompress(body, @headers['Content-Encoding'])
152
+ @headers.delete('Content-Encoding')
154
153
  end
154
+
155
155
  [status, @headers, [body]]
156
+
156
157
  end
157
158
  end
158
159
 
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module ResponseBank
3
- VERSION = "1.2.0"
3
+ VERSION = "1.3.0"
4
4
  end
data/lib/response_bank.rb CHANGED
@@ -3,6 +3,7 @@ require 'response_bank/middleware'
3
3
  require 'response_bank/railtie' if defined?(Rails)
4
4
  require 'response_bank/response_cache_handler'
5
5
  require 'msgpack'
6
+ require 'brotli'
6
7
 
7
8
  module ResponseBank
8
9
  class << self
@@ -29,17 +30,26 @@ module ResponseBank
29
30
  backing_cache_store.read(cache_key, raw: true)
30
31
  end
31
32
 
32
- def compress(content)
33
- io = StringIO.new
34
- gz = Zlib::GzipWriter.new(io)
35
- gz.write(content)
36
- io.string
37
- ensure
38
- gz.close
33
+ def compress(content, encoding = "gzip")
34
+ case encoding
35
+ when 'gzip'
36
+ Zlib.gzip(content, level: Zlib::BEST_COMPRESSION)
37
+ when 'br'
38
+ Brotli.deflate(content, mode: :text, quality: 7)
39
+ else
40
+ raise ArgumentError, "Unsupported encoding: #{encoding}"
41
+ end
39
42
  end
40
43
 
41
- def decompress(content)
42
- Zlib::GzipReader.new(StringIO.new(content)).read
44
+ def decompress(content, encoding = "gzip")
45
+ case encoding
46
+ when 'gzip'
47
+ Zlib.gunzip(content)
48
+ when 'br'
49
+ Brotli.inflate(content)
50
+ else
51
+ raise ArgumentError, "Unsupported encoding: #{encoding}"
52
+ end
43
53
  end
44
54
 
45
55
  def cache_key_for(data)
@@ -53,6 +63,9 @@ module ResponseBank
53
63
 
54
64
  key = %{#{key}:#{hash_value_str(data[:version])}} if data[:version]
55
65
 
66
+ # add the encoding to only the cache key but don't expose this detail in the entity_tag
67
+ key = %{#{key}:#{hash_value_str(data[:encoding])}} if data[:encoding] && data[:encoding] != "gzip"
68
+
56
69
  key
57
70
  when Array
58
71
  data.inspect
@@ -67,6 +80,17 @@ module ResponseBank
67
80
  end
68
81
  end
69
82
 
83
+ def check_encoding(env, default_encoding = 'br')
84
+ if env['HTTP_ACCEPT_ENCODING'].to_s.include?('br')
85
+ 'br'
86
+ elsif env['HTTP_ACCEPT_ENCODING'].to_s.include?('gzip')
87
+ 'gzip'
88
+ else
89
+ # No encoding requested from client, but we still need to cache the page in server cache
90
+ default_encoding
91
+ end
92
+ end
93
+
70
94
  private
71
95
 
72
96
  def hash_value_str(data)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: response_bank
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tobias Lütke
@@ -9,10 +9,10 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2023-03-28 00:00:00.000000000 Z
12
+ date: 2023-04-04 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
- name: useragent
15
+ name: msgpack
16
16
  requirement: !ruby/object:Gem::Requirement
17
17
  requirements:
18
18
  - - ">="
@@ -26,7 +26,7 @@ dependencies:
26
26
  - !ruby/object:Gem::Version
27
27
  version: '0'
28
28
  - !ruby/object:Gem::Dependency
29
- name: msgpack
29
+ name: brotli
30
30
  requirement: !ruby/object:Gem::Requirement
31
31
  requirements:
32
32
  - - ">="
@@ -95,6 +95,20 @@ dependencies:
95
95
  - - ">="
96
96
  - !ruby/object:Gem::Version
97
97
  version: '6.1'
98
+ - !ruby/object:Gem::Dependency
99
+ name: pry
100
+ requirement: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ version: '0'
105
+ type: :development
106
+ prerelease: false
107
+ version_requirements: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - ">="
110
+ - !ruby/object:Gem::Version
111
+ version: '0'
98
112
  description:
99
113
  email:
100
114
  - tobi@shopify.com
@@ -132,7 +146,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
132
146
  - !ruby/object:Gem::Version
133
147
  version: '0'
134
148
  requirements: []
135
- rubygems_version: 3.4.9
149
+ rubygems_version: 3.4.10
136
150
  signing_key:
137
151
  specification_version: 4
138
152
  summary: Simple response caching for Ruby applications