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 +4 -4
- data/lib/response_bank/middleware.rb +16 -21
- data/lib/response_bank/response_cache_handler.rb +11 -10
- data/lib/response_bank/version.rb +1 -1
- data/lib/response_bank.rb +33 -9
- metadata +19 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7d25739f9bd846e1cd3364a422cc90590b32fcb5f7e56cd307af0e83f0d5ddac
|
4
|
+
data.tar.gz: 14c1a7f96abcc45025cee9f998342fb6185779774dc72986ebb1a5bbcb96c413
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
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
|
-
|
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,
|
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
|
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
|
61
|
-
|
62
|
-
|
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
|
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
|
146
|
+
@headers.merge!(headers)
|
147
147
|
|
148
|
-
if
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
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
|
|
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
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
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
|
-
|
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.
|
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-
|
12
|
+
date: 2023-04-04 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
|
-
name:
|
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:
|
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.
|
149
|
+
rubygems_version: 3.4.10
|
136
150
|
signing_key:
|
137
151
|
specification_version: 4
|
138
152
|
summary: Simple response caching for Ruby applications
|