httparty-cache 0.0.2 → 0.0.6
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/README.md +8 -0
- data/lib/httparty/cache/request.rb +17 -9
- data/lib/httparty/cache/response.rb +52 -3
- data/lib/httparty/cache/store/jekyll.rb +8 -0
- data/lib/httparty/cache/store/memory.rb +8 -0
- data/lib/httparty/cache/store/redis.rb +18 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0fede8274e3bf761ebaa8b53d8ad10ab957e92c6bfba35391de1de907bd91800
|
4
|
+
data.tar.gz: 1b5ac5dcaf6da720d56f7b9e9b6c88ff706f4b7be2fe48383eec4ecd4359619c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a515bd37ba299cfa32ffb50cae4b073592652073e6857a505a48e80195c998a21eeb464384806f4703a8855b861f0013cf9ff2fe84e8284ddca6f2f998fedfc0
|
7
|
+
data.tar.gz: 171aac5d0d92adf07222824047462c20e88a3bf806426de0e0aaae08572ac4272767b10e6279415e2f1d7502bbbe1a7191b4a8f6edc7577bf084a8cc01878e5e
|
data/README.md
CHANGED
@@ -123,3 +123,11 @@ class Client
|
|
123
123
|
# or
|
124
124
|
cache_store HTTParty::Cache::Store::Jekyll.new(name: 'My::Jekyll::Cache')
|
125
125
|
end
|
126
|
+
```
|
127
|
+
|
128
|
+
## Caveats!
|
129
|
+
|
130
|
+
* Response marshaling has been changed to prevent stack level too deep
|
131
|
+
exceptions while marshaling the request. Bringing a response from the
|
132
|
+
cache will have a request with the original HTTP method, path and
|
133
|
+
headers sent.
|
@@ -1,12 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'digest/sha2'
|
4
|
+
|
3
5
|
module HTTParty
|
4
6
|
module Cache
|
5
7
|
# Extends HTTParty::Request with caching methods
|
6
8
|
#
|
7
9
|
# @see {https://developer.mozilla.org/en-US/docs/Web/HTTP/Caching}
|
8
10
|
module Request
|
9
|
-
SENSITIVE_HEADERS = %w[
|
11
|
+
SENSITIVE_HEADERS = %w[cookie authorization date signature].freeze
|
10
12
|
CACHE_REVALIDATION_HEADERS = {
|
11
13
|
'ETag' => 'If-None-Match',
|
12
14
|
'Last-Modified' => 'If-Modified-Since'
|
@@ -16,14 +18,18 @@ module HTTParty
|
|
16
18
|
#
|
17
19
|
# @return [String]
|
18
20
|
def cache_key
|
19
|
-
@cache_key ||=
|
21
|
+
@cache_key ||=
|
22
|
+
begin
|
23
|
+
key = "#{uri}##{URI.encode_www_form(cache_params)}"
|
24
|
+
"httpparty-cache:request:#{Digest::SHA2.hexdigest(key)}"
|
25
|
+
end
|
20
26
|
end
|
21
27
|
|
22
28
|
# @return [Hash]
|
23
29
|
def cache_params
|
24
|
-
return { '
|
30
|
+
return { 'x-cache-key' => options[:cache_key] } if options.key? :cache_key
|
25
31
|
|
26
|
-
options[:headers].to_h.except(*SENSITIVE_HEADERS)
|
32
|
+
options[:headers].to_h.transform_keys(&:downcase).except(*SENSITIVE_HEADERS)
|
27
33
|
end
|
28
34
|
|
29
35
|
def validate
|
@@ -47,7 +53,7 @@ module HTTParty
|
|
47
53
|
return super(&block) unless cacheable?
|
48
54
|
|
49
55
|
if response_cached?
|
50
|
-
handle_revalidation
|
56
|
+
handle_revalidation(&block)
|
51
57
|
else
|
52
58
|
super(&block).tap do |response|
|
53
59
|
# Point current request to cached response
|
@@ -81,24 +87,26 @@ module HTTParty
|
|
81
87
|
# Revalidates request
|
82
88
|
#
|
83
89
|
# @return [HTTParty::Response]
|
84
|
-
def handle_revalidation
|
90
|
+
def handle_revalidation(&block)
|
85
91
|
if cached_response.revalidate? || cached_response.stale?
|
86
92
|
self.http_method = Net::HTTP::Head
|
87
|
-
options[:cached_response] = cached_response
|
88
93
|
|
89
94
|
add_cache_revalidation_headers
|
90
95
|
|
96
|
+
# Don't run the block here
|
91
97
|
response = perform
|
92
98
|
|
93
99
|
if response.not_modified?
|
94
100
|
cached_response.headers['date'] = response.headers['date']
|
95
101
|
response = cached_response
|
102
|
+
response.hit!
|
96
103
|
else
|
97
104
|
self.http_method = Net::HTTP::Get
|
98
105
|
# Remove from cache so we can perform the request, otherwise
|
99
106
|
# we produce an infinite loop
|
100
107
|
cache_store.delete(cache_key)
|
101
|
-
@cached_response = response = perform
|
108
|
+
@cached_response = response = perform(&block)
|
109
|
+
response.miss!
|
102
110
|
end
|
103
111
|
|
104
112
|
cache_store.set(response.cache_key, response)
|
@@ -120,7 +128,7 @@ module HTTParty
|
|
120
128
|
options[:headers] ||= {}
|
121
129
|
|
122
130
|
CACHE_REVALIDATION_HEADERS.each_pair do |original_header, revalidation_header|
|
123
|
-
next if (value =
|
131
|
+
next if (value = cached_response.headers[original_header]).nil?
|
124
132
|
|
125
133
|
options[:headers][revalidation_header] ||= value
|
126
134
|
end
|
@@ -1,11 +1,56 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'digest/sha2'
|
4
|
+
|
3
5
|
module HTTParty
|
4
6
|
module Cache
|
5
7
|
# Extends HTTParty::Response with caching methods
|
6
8
|
#
|
7
9
|
# @see {https://developer.mozilla.org/en-US/docs/Web/HTTP/Caching}
|
8
10
|
module Response
|
11
|
+
X_HEADER = 'x-httparty-cache'
|
12
|
+
HIT = 'hit'
|
13
|
+
MISS = 'miss'
|
14
|
+
|
15
|
+
# Prevent stack too deep errors while keeping important
|
16
|
+
# information
|
17
|
+
def _dump(_level)
|
18
|
+
_request = HTTParty::Request.new(request.http_method, request.path, headers: request.options[:headers]&.to_hash)
|
19
|
+
_body = response.instance_variable_get(:@body)
|
20
|
+
_read_adapter = _body.is_a?(Net::ReadAdapter)
|
21
|
+
|
22
|
+
response.instance_variable_set(:@body, nil) if _read_adapter
|
23
|
+
|
24
|
+
Marshal.dump([_request, response, parsed_response, body]).tap do
|
25
|
+
response.instance_variable_set(:@body, _body) if _read_adapter
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def hit!
|
30
|
+
response[X_HEADER] = headers[X_HEADER] = HIT
|
31
|
+
end
|
32
|
+
|
33
|
+
def miss!
|
34
|
+
response[X_HEADER] = headers[X_HEADER] = MISS
|
35
|
+
end
|
36
|
+
|
37
|
+
def fresh!
|
38
|
+
response.delete(X_HEADER)
|
39
|
+
headers.delete(X_HEADER)
|
40
|
+
end
|
41
|
+
|
42
|
+
def hit?
|
43
|
+
headers[X_HEADER] == HIT
|
44
|
+
end
|
45
|
+
|
46
|
+
def miss?
|
47
|
+
headers[X_HEADER] == MISS
|
48
|
+
end
|
49
|
+
|
50
|
+
def reused?
|
51
|
+
headers.key?(X_HEADER)
|
52
|
+
end
|
53
|
+
|
9
54
|
# Parses Cache-Control headers
|
10
55
|
#
|
11
56
|
# @see {https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/cache-control}
|
@@ -62,18 +107,22 @@ module HTTParty
|
|
62
107
|
#
|
63
108
|
# @return [Hash]
|
64
109
|
def vary
|
65
|
-
@vary ||= request.options[:headers].to_h.slice(*headers['
|
110
|
+
@vary ||= request.options[:headers].to_h.slice(*headers['vary'].to_s.split(',').map(&:strip).sort).transform_keys(&:downcase)
|
66
111
|
end
|
67
112
|
|
68
113
|
# Generates a cache key
|
69
114
|
#
|
70
115
|
# @return [String]
|
71
116
|
def cache_key
|
72
|
-
@cache_key ||=
|
117
|
+
@cache_key ||=
|
118
|
+
begin
|
119
|
+
key = "#{uri}##{URI.encode_www_form(vary)}"
|
120
|
+
"httparty-cache:response:#{Digest::SHA2.hexdigest(key)}"
|
121
|
+
end
|
73
122
|
end
|
74
123
|
end
|
75
124
|
end
|
76
125
|
end
|
77
126
|
|
78
127
|
# Patch HTTParty::Response
|
79
|
-
HTTParty::Response.
|
128
|
+
HTTParty::Response.prepend HTTParty::Cache::Response
|
@@ -21,6 +21,14 @@ module HTTParty
|
|
21
21
|
@store = ::Jekyll::Cache.new(name)
|
22
22
|
end
|
23
23
|
|
24
|
+
def _dump(_)
|
25
|
+
Marshal.dump(options[:name])
|
26
|
+
end
|
27
|
+
|
28
|
+
def self._load(string)
|
29
|
+
new(name: Marshal.load(string))
|
30
|
+
end
|
31
|
+
|
24
32
|
def set(key, response)
|
25
33
|
@store[key] = response
|
26
34
|
response
|
@@ -21,6 +21,14 @@ module HTTParty
|
|
21
21
|
|
22
22
|
# @param :redis_url [String]
|
23
23
|
def initialize(redis_url: nil)
|
24
|
+
marshal_load(redis_url)
|
25
|
+
end
|
26
|
+
|
27
|
+
def marshal_dump
|
28
|
+
options[:redis_url]
|
29
|
+
end
|
30
|
+
|
31
|
+
def marshal_load(redis_url)
|
24
32
|
@options = {}
|
25
33
|
@options[:redis_url] = redis_url || ENV['REDIS_URL']
|
26
34
|
@config = RedisClient.config(url: @options[:redis_url])
|
@@ -28,6 +36,16 @@ module HTTParty
|
|
28
36
|
@redis.call('PING')
|
29
37
|
end
|
30
38
|
|
39
|
+
# I couldn't get this to work! It's never called even if I dump
|
40
|
+
# the store directly
|
41
|
+
def _dump(level)
|
42
|
+
options[:redis_url]
|
43
|
+
end
|
44
|
+
|
45
|
+
def self._load(string)
|
46
|
+
new(redis_url: string)
|
47
|
+
end
|
48
|
+
|
31
49
|
# Retrieves a key
|
32
50
|
#
|
33
51
|
# @param :key [String]
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: httparty-cache
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- f
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-03-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: httparty
|