gsk_cache 0.1.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 +7 -0
- data/lib/gsk_cache.rb +175 -0
- metadata +72 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 4e4eaba75a4c51cec1231c1e554101679c450c1736e9fd1d6849ff3d3d92b0ec
|
|
4
|
+
data.tar.gz: 48019eda48fb59c0efbef7f71b6e06e74c1ee7312e9e129386249222ba5d8ac9
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 9d15feb37ef8bdfc36f4f4bd2c157af70214bfdc33c195726c7804f00c935a7bfc2b1354d973ec4ed745ee02611d486107f9999e5116f34363d51162647a23ef
|
|
7
|
+
data.tar.gz: 117c8510dd5978cce0ba8c00559a5d8b97348d761e6736875a0baf2c9e0f7bdf36649bc21edac574128ee4ce8941ac285ff959a619988a5eb3c4b4a4d959383e
|
data/lib/gsk_cache.rb
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
require 'excon'
|
|
2
|
+
require 'json'
|
|
3
|
+
|
|
4
|
+
module GSKCache
|
|
5
|
+
@@key_updater_semaphore = Mutex.new
|
|
6
|
+
@@key_updater_thread = nil
|
|
7
|
+
|
|
8
|
+
##
|
|
9
|
+
# Keys used for signing in production
|
|
10
|
+
SIGNING_KEY_URL = 'https://payments.developers.google.com/paymentmethodtoken/keys.json'.freeze
|
|
11
|
+
|
|
12
|
+
##
|
|
13
|
+
# Keys used for signing in a testing environment
|
|
14
|
+
TEST_SIGNING_KEY_URL = 'https://payments.developers.google.com/paymentmethodtoken/test/keys.json'.freeze
|
|
15
|
+
|
|
16
|
+
MAX_CACHE_TIMEOUT = 31 * 24 * 3600 # One month
|
|
17
|
+
MIN_CACHE_TIMEOUT = 600 # 10 minutes
|
|
18
|
+
|
|
19
|
+
BOOT_WAIT_TIME_MAX = 8
|
|
20
|
+
|
|
21
|
+
HEADER_KEY = 'Cache-Control'.freeze
|
|
22
|
+
|
|
23
|
+
READ_TIMEOUT = 10
|
|
24
|
+
CONNECT_TIMEOUT = 10
|
|
25
|
+
|
|
26
|
+
UPDATER_BLOCK = proc do
|
|
27
|
+
loop do
|
|
28
|
+
begin
|
|
29
|
+
timeout = 0
|
|
30
|
+
|
|
31
|
+
conn = Excon.new(@@source, connect_timeout: @@connect_timeout, read_timeout: @@read_timeout)
|
|
32
|
+
resp = conn.get
|
|
33
|
+
|
|
34
|
+
raise 'Unable to update keys: ' + resp.data[:status_line] unless resp.status == 200
|
|
35
|
+
|
|
36
|
+
if resp.headers.key?(HEADER_KEY) && resp.headers[HEADER_KEY].is_a?(String)
|
|
37
|
+
cache_control = resp.headers[HEADER_KEY].split(/,\s*/)
|
|
38
|
+
h = cache_control.map { |x| /\Amax-age=(?<timeout>\d+)\z/ =~ x; timeout }.compact
|
|
39
|
+
timeout = h.first.to_i if h.length == 1
|
|
40
|
+
else
|
|
41
|
+
log(:warning, "Missing/malformed #{HEADER_KEY} header")
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
if timeout.nil? || !timeout.positive?
|
|
45
|
+
log(:warning, 'Cache timeout was not parsed, falling back to 1 day')
|
|
46
|
+
timeout = 86400
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Fallback to longer cache if less than 10 minutes
|
|
50
|
+
if timeout < MIN_CACHE_TIMEOUT
|
|
51
|
+
log(:warning, "Cache timeout less than 10 minutes (#{timeout}s), defaulting to #{MIN_CACHE_TIMEOUT / 60} minutes")
|
|
52
|
+
timeout = MIN_CACHE_TIMEOUT
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Fallback to shorter cache if longer than 30 days
|
|
56
|
+
if timeout > MAX_CACHE_TIMEOUT
|
|
57
|
+
log(:warning, "Cache timeout more than a month (#{timeout}s), defaulting to #{MAX_CACHE_TIMEOUT / (24 * 3600)} days")
|
|
58
|
+
timeout = MAX_CACHE_TIMEOUT
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
Thread.current.thread_variable_set('keys', resp.body)
|
|
62
|
+
|
|
63
|
+
# Supposedly recommended by Tink library
|
|
64
|
+
sleep_time = timeout / 2
|
|
65
|
+
|
|
66
|
+
log(:info, "Updated Google signing keys. Sleeping for #{seconds_to_time(sleep_time)}")
|
|
67
|
+
|
|
68
|
+
sleep sleep_time
|
|
69
|
+
rescue Interrupt => e
|
|
70
|
+
# When interrupted
|
|
71
|
+
log(:fatal, 'Quitting: ' + e.message)
|
|
72
|
+
return
|
|
73
|
+
rescue => e
|
|
74
|
+
log(:error, "Exception updating Google signing keys: '#{e.message}' at #{e.backtrace}")
|
|
75
|
+
|
|
76
|
+
# Don't retry excessively.
|
|
77
|
+
sleep 1
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
##
|
|
83
|
+
# Start a thread that keeps the Google signing keys updated.
|
|
84
|
+
def self.start(logger: nil, source: nil, waittime: BOOT_WAIT_TIME_MAX)
|
|
85
|
+
@@key_updater_semaphore.synchronize do
|
|
86
|
+
# Another thread might have been waiting for on the mutex
|
|
87
|
+
break unless @@key_updater_thread.nil?
|
|
88
|
+
|
|
89
|
+
@@logger = logger
|
|
90
|
+
@@source = if source
|
|
91
|
+
source
|
|
92
|
+
elsif ENV['ENVIRONMENT'] == 'production'
|
|
93
|
+
SIGNING_KEY_URL
|
|
94
|
+
else
|
|
95
|
+
TEST_SIGNING_KEY_URL
|
|
96
|
+
end
|
|
97
|
+
@@read_timeout = READ_TIMEOUT
|
|
98
|
+
@@connect_timeout = CONNECT_TIMEOUT
|
|
99
|
+
|
|
100
|
+
new_thread = Thread.new(&UPDATER_BLOCK)
|
|
101
|
+
|
|
102
|
+
start_time = Time.now.to_i
|
|
103
|
+
|
|
104
|
+
while new_thread.thread_variable_get('keys').nil? && Time.now.to_i - start_time < waittime
|
|
105
|
+
sleep 0.2
|
|
106
|
+
end
|
|
107
|
+
# Body has now been set.
|
|
108
|
+
# Let other clients through.
|
|
109
|
+
@@key_updater_thread = new_thread
|
|
110
|
+
|
|
111
|
+
nil
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def self.set_timeout(read_timeout: nil, connect_timeout: nil)
|
|
116
|
+
@@read_timeout = read_timeout if read_timeout
|
|
117
|
+
@@connect_timeout = connect_timeout if connect_timeout
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def self.signing_keys
|
|
121
|
+
start if @@key_updater_thread.nil?
|
|
122
|
+
|
|
123
|
+
@@key_updater_thread.thread_variable_get('keys')
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# Required for specs
|
|
127
|
+
def self.terminate
|
|
128
|
+
@@key_updater_thread&.terminate
|
|
129
|
+
@@key_updater_thread = nil
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
class << self
|
|
133
|
+
private
|
|
134
|
+
|
|
135
|
+
def log(level, message)
|
|
136
|
+
return if @@logger.nil?
|
|
137
|
+
|
|
138
|
+
case level
|
|
139
|
+
when :info
|
|
140
|
+
@@logger.info(message)
|
|
141
|
+
when :warning
|
|
142
|
+
if @@logger.respond_to?(:warning)
|
|
143
|
+
@@logger.warning(message)
|
|
144
|
+
elsif @@logger.respond_to?(:warn)
|
|
145
|
+
@@logger.warn(message)
|
|
146
|
+
end
|
|
147
|
+
when :error
|
|
148
|
+
@@logger.error(message)
|
|
149
|
+
when :fatal
|
|
150
|
+
@@logger.fatal(message)
|
|
151
|
+
else
|
|
152
|
+
throw RuntimeError.new('Invalid log level')
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def seconds_to_time(s)
|
|
157
|
+
days = (s / 86400).floor
|
|
158
|
+
s %= 86400
|
|
159
|
+
|
|
160
|
+
hours = (s / 3600).floor
|
|
161
|
+
s %= 3600
|
|
162
|
+
|
|
163
|
+
minutes = (s / 60).floor
|
|
164
|
+
s %= 60
|
|
165
|
+
|
|
166
|
+
out = ''
|
|
167
|
+
out += "#{days} days " if days.positive?
|
|
168
|
+
out += "#{hours} hours " if hours.positive?
|
|
169
|
+
out += "#{minutes} minutes " if minutes.positive?
|
|
170
|
+
out += "#{s} seconds" if s.positive?
|
|
171
|
+
|
|
172
|
+
out.rstrip
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: gsk_cache
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Clearhaus
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2019-01-04 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: excon
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '0'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '0'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: rspec
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - "~>"
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '3'
|
|
34
|
+
type: :development
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - "~>"
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '3'
|
|
41
|
+
description:
|
|
42
|
+
email: hello@clearhaus.com
|
|
43
|
+
executables: []
|
|
44
|
+
extensions: []
|
|
45
|
+
extra_rdoc_files: []
|
|
46
|
+
files:
|
|
47
|
+
- lib/gsk_cache.rb
|
|
48
|
+
homepage: https://github.com/clearhaus/gsk_cache
|
|
49
|
+
licenses:
|
|
50
|
+
- MIT
|
|
51
|
+
metadata: {}
|
|
52
|
+
post_install_message:
|
|
53
|
+
rdoc_options: []
|
|
54
|
+
require_paths:
|
|
55
|
+
- lib
|
|
56
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
57
|
+
requirements:
|
|
58
|
+
- - ">="
|
|
59
|
+
- !ruby/object:Gem::Version
|
|
60
|
+
version: '0'
|
|
61
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
62
|
+
requirements:
|
|
63
|
+
- - ">="
|
|
64
|
+
- !ruby/object:Gem::Version
|
|
65
|
+
version: '0'
|
|
66
|
+
requirements: []
|
|
67
|
+
rubyforge_project:
|
|
68
|
+
rubygems_version: 2.7.7
|
|
69
|
+
signing_key:
|
|
70
|
+
specification_version: 4
|
|
71
|
+
summary: Fetches and caches Google Pay signing keys
|
|
72
|
+
test_files: []
|