hearth 1.0.0.pre1 → 1.0.0.pre2
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/CHANGELOG.md +4 -4
- data/VERSION +1 -1
- data/lib/hearth/api_error.rb +15 -1
- data/lib/hearth/auth_option.rb +21 -0
- data/lib/hearth/auth_schemes/anonymous.rb +21 -0
- data/lib/hearth/auth_schemes/http_api_key.rb +16 -0
- data/lib/hearth/auth_schemes/http_basic.rb +16 -0
- data/lib/hearth/auth_schemes/http_bearer.rb +16 -0
- data/lib/hearth/auth_schemes/http_digest.rb +16 -0
- data/lib/hearth/auth_schemes.rb +32 -0
- data/lib/hearth/checksums.rb +31 -0
- data/lib/hearth/client_stubs.rb +130 -0
- data/lib/hearth/config/env_provider.rb +53 -0
- data/lib/hearth/config/resolver.rb +52 -0
- data/lib/hearth/configuration.rb +15 -0
- data/lib/hearth/connection_pool.rb +77 -0
- data/lib/hearth/context.rb +28 -4
- data/lib/hearth/dns/host_address.rb +23 -0
- data/lib/hearth/dns/host_resolver.rb +92 -0
- data/lib/hearth/dns.rb +48 -0
- data/lib/hearth/http/api_error.rb +4 -8
- data/lib/hearth/http/client.rb +208 -59
- data/lib/hearth/http/error_inspector.rb +85 -0
- data/lib/hearth/http/error_parser.rb +18 -20
- data/lib/hearth/http/field.rb +64 -0
- data/lib/hearth/http/fields.rb +117 -0
- data/lib/hearth/http/middleware/content_length.rb +5 -2
- data/lib/hearth/http/middleware/content_md5.rb +31 -0
- data/lib/hearth/http/middleware/request_compression.rb +157 -0
- data/lib/hearth/http/middleware.rb +12 -0
- data/lib/hearth/http/networking_error.rb +1 -14
- data/lib/hearth/http/request.rb +83 -56
- data/lib/hearth/http/response.rb +42 -13
- data/lib/hearth/http.rb +14 -5
- data/lib/hearth/identities/anonymous.rb +8 -0
- data/lib/hearth/identities/http_api_key.rb +16 -0
- data/lib/hearth/identities/http_bearer.rb +16 -0
- data/lib/hearth/identities/http_login.rb +20 -0
- data/lib/hearth/identities.rb +21 -0
- data/lib/hearth/identity_resolver.rb +17 -0
- data/lib/hearth/interceptor.rb +506 -0
- data/lib/hearth/interceptor_context.rb +36 -0
- data/lib/hearth/interceptor_list.rb +48 -0
- data/lib/hearth/interceptors.rb +75 -0
- data/lib/hearth/middleware/auth.rb +100 -0
- data/lib/hearth/middleware/build.rb +32 -0
- data/lib/hearth/middleware/host_prefix.rb +10 -6
- data/lib/hearth/middleware/initialize.rb +58 -0
- data/lib/hearth/middleware/parse.rb +45 -6
- data/lib/hearth/middleware/retry.rb +97 -23
- data/lib/hearth/middleware/send.rb +137 -25
- data/lib/hearth/middleware/sign.rb +65 -0
- data/lib/hearth/middleware/validate.rb +11 -1
- data/lib/hearth/middleware.rb +19 -8
- data/lib/hearth/middleware_stack.rb +1 -43
- data/lib/hearth/networking_error.rb +18 -0
- data/lib/hearth/number_helper.rb +2 -2
- data/lib/hearth/output.rb +8 -4
- data/lib/hearth/plugin_list.rb +53 -0
- data/lib/hearth/query/param.rb +52 -0
- data/lib/hearth/query/param_list.rb +54 -0
- data/lib/hearth/query/param_matcher.rb +32 -0
- data/lib/hearth/refreshing_identity_resolver.rb +63 -0
- data/lib/hearth/request.rb +22 -0
- data/lib/hearth/response.rb +33 -0
- data/lib/hearth/retry/adaptive.rb +60 -0
- data/lib/hearth/retry/capacity_not_available_error.rb +9 -0
- data/lib/hearth/retry/client_rate_limiter.rb +143 -0
- data/lib/hearth/retry/exponential_backoff.rb +15 -0
- data/lib/hearth/retry/retry_quota.rb +56 -0
- data/lib/hearth/retry/standard.rb +46 -0
- data/lib/hearth/retry/strategy.rb +20 -0
- data/lib/hearth/retry.rb +16 -0
- data/lib/hearth/signers/anonymous.rb +16 -0
- data/lib/hearth/signers/http_api_key.rb +29 -0
- data/lib/hearth/signers/http_basic.rb +23 -0
- data/lib/hearth/signers/http_bearer.rb +19 -0
- data/lib/hearth/signers/http_digest.rb +19 -0
- data/lib/hearth/signers.rb +23 -0
- data/lib/hearth/stubs.rb +30 -0
- data/lib/hearth/time_helper.rb +5 -3
- data/lib/hearth/validator.rb +44 -5
- data/lib/hearth/waiters/poller.rb +6 -7
- data/lib/hearth/waiters/waiter.rb +17 -4
- data/lib/hearth/xml/formatter.rb +11 -2
- data/lib/hearth/xml/node.rb +2 -2
- data/lib/hearth.rb +32 -5
- data/sig/lib/hearth/aliases.rbs +4 -0
- data/sig/lib/hearth/api_error.rbs +13 -0
- data/sig/lib/hearth/auth_option.rbs +11 -0
- data/sig/lib/hearth/auth_schemes/anonymous.rbs +7 -0
- data/sig/lib/hearth/auth_schemes/http_api_key.rbs +7 -0
- data/sig/lib/hearth/auth_schemes/http_basic.rbs +7 -0
- data/sig/lib/hearth/auth_schemes/http_bearer.rbs +7 -0
- data/sig/lib/hearth/auth_schemes/http_digest.rbs +7 -0
- data/sig/lib/hearth/auth_schemes.rbs +13 -0
- data/sig/lib/hearth/block_io.rbs +9 -0
- data/sig/lib/hearth/client_stubs.rbs +5 -0
- data/sig/lib/hearth/configuration.rbs +7 -0
- data/sig/lib/hearth/dns/host_address.rbs +13 -0
- data/sig/lib/hearth/dns/host_resolver.rbs +19 -0
- data/sig/lib/hearth/http/api_error.rbs +13 -0
- data/sig/lib/hearth/http/client.rbs +9 -0
- data/sig/lib/hearth/http/field.rbs +19 -0
- data/sig/lib/hearth/http/fields.rbs +43 -0
- data/sig/lib/hearth/http/request.rbs +25 -0
- data/sig/lib/hearth/http/response.rbs +21 -0
- data/sig/lib/hearth/identities/anonymous.rbs +6 -0
- data/sig/lib/hearth/identities/http_api_key.rbs +9 -0
- data/sig/lib/hearth/identities/http_bearer.rbs +9 -0
- data/sig/lib/hearth/identities/http_login.rbs +11 -0
- data/sig/lib/hearth/identities.rbs +9 -0
- data/sig/lib/hearth/identity_resolver.rbs +7 -0
- data/sig/lib/hearth/interceptor.rbs +9 -0
- data/sig/lib/hearth/interceptor_context.rbs +15 -0
- data/sig/lib/hearth/interceptor_list.rbs +16 -0
- data/sig/lib/hearth/interfaces.rbs +65 -0
- data/sig/lib/hearth/output.rbs +11 -0
- data/sig/lib/hearth/plugin_list.rbs +15 -0
- data/sig/lib/hearth/query/param.rbs +17 -0
- data/sig/lib/hearth/query/param_list.rbs +25 -0
- data/sig/lib/hearth/request.rbs +9 -0
- data/sig/lib/hearth/response.rbs +11 -0
- data/sig/lib/hearth/retry/adaptive.rbs +13 -0
- data/sig/lib/hearth/retry/exponential_backoff.rbs +7 -0
- data/sig/lib/hearth/retry/standard.rbs +13 -0
- data/sig/lib/hearth/retry/strategy.rbs +11 -0
- data/sig/lib/hearth/retry.rbs +9 -0
- data/sig/lib/hearth/signers/anonymous.rbs +9 -0
- data/sig/lib/hearth/signers/http_api_key.rbs +9 -0
- data/sig/lib/hearth/signers/http_basic.rbs +9 -0
- data/sig/lib/hearth/signers/http_bearer.rbs +9 -0
- data/sig/lib/hearth/signers/http_digest.rbs +9 -0
- data/sig/lib/hearth/signers.rbs +9 -0
- data/sig/lib/hearth/structure.rbs +7 -0
- data/sig/lib/hearth/union.rbs +5 -0
- data/sig/lib/hearth/waiters/waiter.rbs +17 -0
- metadata +132 -22
- data/lib/hearth/http/headers.rb +0 -70
- data/lib/hearth/middleware/around_handler.rb +0 -24
- data/lib/hearth/middleware/request_handler.rb +0 -24
- data/lib/hearth/middleware/response_handler.rb +0 -25
- data/lib/hearth/middleware_builder.rb +0 -246
- data/lib/hearth/stubbing/client_stubs.rb +0 -115
- data/lib/hearth/stubbing/stubs.rb +0 -32
- data/lib/hearth/waiters/errors.rb +0 -15
- data/sig/lib/seahorse/api_error.rbs +0 -10
- data/sig/lib/seahorse/document.rbs +0 -2
- data/sig/lib/seahorse/http/api_error.rbs +0 -21
- data/sig/lib/seahorse/http/headers.rbs +0 -47
- data/sig/lib/seahorse/http/response.rbs +0 -21
- data/sig/lib/seahorse/simple_delegator.rbs +0 -3
- data/sig/lib/seahorse/structure.rbs +0 -18
- data/sig/lib/seahorse/stubbing/client_stubs.rbs +0 -103
- data/sig/lib/seahorse/stubbing/stubs.rbs +0 -14
- data/sig/lib/seahorse/union.rbs +0 -6
|
@@ -0,0 +1,506 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Hearth
|
|
4
|
+
# # Interceptors
|
|
5
|
+
# Interceptors are a generic extension point that allows injecting
|
|
6
|
+
# logic at specific stages of execution within the SDK. Logic injection
|
|
7
|
+
# is done with hooks that the interceptor implements.
|
|
8
|
+
#
|
|
9
|
+
# Hooks are either read-only or read/write.
|
|
10
|
+
# Read-only hooks allow an interceptor to read the input,
|
|
11
|
+
# transport request, transport response or output messages.
|
|
12
|
+
# Read/write hooks allow an interceptor to modify one of these messages.
|
|
13
|
+
#
|
|
14
|
+
# # Creating an Interceptor
|
|
15
|
+
# To create an Interceptor, you must provide a hash with each key being
|
|
16
|
+
# a hook name and each value being a callable with arity 1 (context).
|
|
17
|
+
# For example:
|
|
18
|
+
#
|
|
19
|
+
# callbacks = {
|
|
20
|
+
# read_before_execution: proc { |context| puts "Before execution" },
|
|
21
|
+
# read_after_execution: proc { |context| puts "After execution" }
|
|
22
|
+
# }
|
|
23
|
+
# interceptor = Interceptor.new(callbacks)
|
|
24
|
+
#
|
|
25
|
+
# More advanced interceptors can be created by implementing the hooks
|
|
26
|
+
# directly in a class. For example:
|
|
27
|
+
#
|
|
28
|
+
# class MyInterceptor
|
|
29
|
+
# def initialize
|
|
30
|
+
# # keep state or do other initialization
|
|
31
|
+
# end
|
|
32
|
+
#
|
|
33
|
+
# def read_before_execution(context)
|
|
34
|
+
# puts "Before execution"
|
|
35
|
+
# end
|
|
36
|
+
#
|
|
37
|
+
# def read_after_execution(context)
|
|
38
|
+
# puts "After execution"
|
|
39
|
+
# end
|
|
40
|
+
# end
|
|
41
|
+
#
|
|
42
|
+
# # Registering an Interceptor
|
|
43
|
+
# Interceptors are registered by appending them to an {InterceptorList}
|
|
44
|
+
# and passing it to the the Client's Config. For example:
|
|
45
|
+
#
|
|
46
|
+
# interceptors = InterceptorList.new([MyInterceptor.new])
|
|
47
|
+
# config = Organization::Config.new(interceptors: interceptors)
|
|
48
|
+
# client = Organization::Client.new(config: config)
|
|
49
|
+
# client.operation
|
|
50
|
+
# #=> "Before execution"
|
|
51
|
+
# #=> "After execution"
|
|
52
|
+
#
|
|
53
|
+
# # Available Hooks
|
|
54
|
+
# The available hooks can be retrieved by calling {Interceptor.hooks}.
|
|
55
|
+
#
|
|
56
|
+
# The following hooks are available:
|
|
57
|
+
# * :read_before_execution
|
|
58
|
+
# * :modify_before_serialization
|
|
59
|
+
# * :read_before_serialization
|
|
60
|
+
# * :read_after_serialization
|
|
61
|
+
# * :modify_before_retry_loop
|
|
62
|
+
# * :read_before_attempt
|
|
63
|
+
# * :modify_before_signing
|
|
64
|
+
# * :read_before_signing
|
|
65
|
+
# * :read_after_signing
|
|
66
|
+
# * :modify_before_transmit
|
|
67
|
+
# * :read_before_transmit
|
|
68
|
+
# * :read_after_transmit
|
|
69
|
+
# * :modify_before_deserialization
|
|
70
|
+
# * :read_before_deserialization
|
|
71
|
+
# * :read_after_deserialization
|
|
72
|
+
# * :modify_before_attempt_completion
|
|
73
|
+
# * :read_after_attempt
|
|
74
|
+
# * :modify_before_completion
|
|
75
|
+
# * :read_after_execution
|
|
76
|
+
#
|
|
77
|
+
# # Hook Details
|
|
78
|
+
# The following sections describe each hook in detail.
|
|
79
|
+
#
|
|
80
|
+
# ## :read_before_execution
|
|
81
|
+
# A hook called at the start of an execution, before the SDK
|
|
82
|
+
# does anything else.
|
|
83
|
+
#
|
|
84
|
+
# When: This will ALWAYS be called once per execution. The duration
|
|
85
|
+
# between invocation of this hook and :read_after_execution is very
|
|
86
|
+
# close to full duration of the execution.
|
|
87
|
+
#
|
|
88
|
+
# Available Information: The {InterceptorContext#input} is ALWAYS
|
|
89
|
+
# available. Other information WILL NOT be available.
|
|
90
|
+
#
|
|
91
|
+
# Error Behavior: Errors raised by this hook will be stored
|
|
92
|
+
# until all interceptors have had their :read_before_execution
|
|
93
|
+
# invoked. Other hooks will then be skipped and execution will jump to
|
|
94
|
+
# :modify_before_completion with the raised error as the
|
|
95
|
+
# {Output#error} in {InterceptorContext#output}. If multiple
|
|
96
|
+
# :read_before_execution methods raise errors, the latest
|
|
97
|
+
# will be used and earlier ones will be logged and dropped.
|
|
98
|
+
#
|
|
99
|
+
# ## :modify_before_serialization
|
|
100
|
+
# A hook called before the input message is marshalled into a
|
|
101
|
+
# transport message. This method has the ability to modify the
|
|
102
|
+
# input message.
|
|
103
|
+
#
|
|
104
|
+
# When: This will ALWAYS be called once per execution, except when a
|
|
105
|
+
# failure occurs earlier in the request pipeline.
|
|
106
|
+
#
|
|
107
|
+
# Available Information: The {InterceptorContext#input} is ALWAYS
|
|
108
|
+
# available. This input may have been modified by earlier
|
|
109
|
+
# :modify_before_serialization hooks, and may be modified further by
|
|
110
|
+
# later hooks. Other information WILL NOT be available.
|
|
111
|
+
#
|
|
112
|
+
# Error Behavior: If errors are raised by this hook, execution will
|
|
113
|
+
# jump to :modify_before_completion with the raised error as the
|
|
114
|
+
# {Output#error} in {InterceptorContext#output}.
|
|
115
|
+
#
|
|
116
|
+
# ## :read_before_serialization
|
|
117
|
+
# A hook called before the input message is marshalled into a transport
|
|
118
|
+
# message.
|
|
119
|
+
#
|
|
120
|
+
# When: This will ALWAYS be called once per execution, except when a
|
|
121
|
+
# failure occurs earlier in the request pipeline. The duration
|
|
122
|
+
# between invocation of this hook and :read_after_serialization is
|
|
123
|
+
# very close to the amount of time spent marshalling the request.
|
|
124
|
+
#
|
|
125
|
+
# Available Information: The {InterceptorContext#input} is
|
|
126
|
+
# ALWAYS available. Other information WILL NOT be available.
|
|
127
|
+
#
|
|
128
|
+
# Error Behavior: If errors are raised by this hook, execution will
|
|
129
|
+
# jump to :modify_before_completion with the raised error as the
|
|
130
|
+
# {Output#error} in {InterceptorContext#output}.
|
|
131
|
+
#
|
|
132
|
+
# ## :read_after_serialization
|
|
133
|
+
# A hook called after the input message is marshalled into a transport
|
|
134
|
+
# message.
|
|
135
|
+
#
|
|
136
|
+
# When: This will ALWAYS be called once per execution, except when a
|
|
137
|
+
# failure occurs earlier in the request pipeline. The duration
|
|
138
|
+
# between invocation of this hook and :read_before_serialization is
|
|
139
|
+
# very close to the amount of time spent marshalling the request.
|
|
140
|
+
#
|
|
141
|
+
# Available Information: The {InterceptorContext#input},
|
|
142
|
+
# {InterceptorContext#request} are ALWAYS available. Other
|
|
143
|
+
# information WILL NOT be available.
|
|
144
|
+
#
|
|
145
|
+
# Error Behavior: If errors are raised by this hook, execution will
|
|
146
|
+
# jump to :modify_before_completion with the raised error as the
|
|
147
|
+
# {Output#error} in {InterceptorContext#output}.
|
|
148
|
+
#
|
|
149
|
+
# ## :modify_before_retry_loop
|
|
150
|
+
# A hook called before the retry loop is entered. This method
|
|
151
|
+
# has the ability to modify the transport request message.
|
|
152
|
+
#
|
|
153
|
+
# When: This will ALWAYS be called once per execution, except when a
|
|
154
|
+
# failure occurs earlier in the request pipeline.
|
|
155
|
+
#
|
|
156
|
+
# Available Information: The {InterceptorContext#input} and
|
|
157
|
+
# {InterceptorContext#request} are ALWAYS available. Other
|
|
158
|
+
# information WILL NOT be available.
|
|
159
|
+
#
|
|
160
|
+
# Error Behavior: If errors are raised by this hook, execution will
|
|
161
|
+
# jump to :modify_before_completion with the raised error as the
|
|
162
|
+
# {Output#error} in {InterceptorContext#output}.
|
|
163
|
+
#
|
|
164
|
+
# ## :read_before_attempt
|
|
165
|
+
# A hook called before each attempt at sending the transmission
|
|
166
|
+
# request message to the service.
|
|
167
|
+
#
|
|
168
|
+
# When: This will ALWAYS be called once per attempt, except when a
|
|
169
|
+
# failure occurs earlier in the request pipeline. This method will be
|
|
170
|
+
# called multiple times in the event of retries.
|
|
171
|
+
#
|
|
172
|
+
# Available Information: The {InterceptorContext#input} and
|
|
173
|
+
# {InterceptorContext#request} are ALWAYS available. Other
|
|
174
|
+
# information WILL NOT be available. In the event of retries, the
|
|
175
|
+
# {InterceptorContext} WILL include changes made in previous
|
|
176
|
+
# attempts (e.g. by other interceptors).
|
|
177
|
+
#
|
|
178
|
+
# Error Behavior: Errors raised by this hook will be stored
|
|
179
|
+
# until all interceptors have had their :read_before_attempt
|
|
180
|
+
# invoked. Other hooks will then be skipped and execution will jump to
|
|
181
|
+
# :modify_before_attempt_completion with the raised error as the
|
|
182
|
+
# {Output#error} in {InterceptorContext#output}. If multiple
|
|
183
|
+
# :read_before_attempt methods raise errors, the latest will be used
|
|
184
|
+
# and earlier ones will be logged and dropped.
|
|
185
|
+
#
|
|
186
|
+
# ## :modify_before_signing
|
|
187
|
+
# A hook called before the transport request message is signed. This
|
|
188
|
+
# method has the ability to modify the transport request message.
|
|
189
|
+
#
|
|
190
|
+
# When: This will ALWAYS be called once per attempt, except when a
|
|
191
|
+
# failure occurs earlier in the request pipeline. This method may be
|
|
192
|
+
# called multiple times in the event of retries.
|
|
193
|
+
#
|
|
194
|
+
# Available Information: The {InterceptorContext#input} and
|
|
195
|
+
# {InterceptorContext#request} are ALWAYS available. The request may
|
|
196
|
+
# have been modified by earlier :modify_before_signing hooks, and may
|
|
197
|
+
# be modified further by later hooks. Other information WILL NOT be
|
|
198
|
+
# available. In the event of retries, the {InterceptorContext} WILL
|
|
199
|
+
# include changes made in previous attempts (e.g. by other middleware
|
|
200
|
+
# or interceptors).
|
|
201
|
+
#
|
|
202
|
+
# Error Behavior: If errors are raised by this hook, execution will
|
|
203
|
+
# jump to :modify_before_attempt_completion with the raised error as
|
|
204
|
+
# the {Output#error} in {InterceptorContext#output}.
|
|
205
|
+
#
|
|
206
|
+
# ## :read_before_signing
|
|
207
|
+
# A hook called before the transport request message is signed.
|
|
208
|
+
#
|
|
209
|
+
# When: This will ALWAYS be called once per attempt, except when a
|
|
210
|
+
# failure occurs earlier in the request pipeline. This method may be
|
|
211
|
+
# called multiple times in the event of retries. The duration between
|
|
212
|
+
# invocation of this hook and :read_after_signing is very close to
|
|
213
|
+
# the amount of time spent signing the request.
|
|
214
|
+
#
|
|
215
|
+
# Available Information: The {InterceptorContext#input} and
|
|
216
|
+
# {InterceptorContext#request} are ALWAYS available. Other
|
|
217
|
+
# information WILL NOT be available. In the event of retries, the
|
|
218
|
+
# {InterceptorContext} WILL include changes made in previous
|
|
219
|
+
# attempts (e.g. by other interceptors).
|
|
220
|
+
#
|
|
221
|
+
# Error Behavior: If errors are raised by this hook, execution will
|
|
222
|
+
# jump to :modify_before_attempt_completion with the raised error as
|
|
223
|
+
# the {Output#error} in {InterceptorContext#output}.
|
|
224
|
+
#
|
|
225
|
+
# ## :read_after_signing
|
|
226
|
+
# A hook called after the transport request message is signed.
|
|
227
|
+
#
|
|
228
|
+
# When: This will ALWAYS be called once per attempt, except when a
|
|
229
|
+
# failure occurs earlier in the request pipeline. This method may be
|
|
230
|
+
# called multiple times in the event of retries. The duration between
|
|
231
|
+
# invocation of this hook and :read_before_signing is very close to
|
|
232
|
+
# the amount of time spent signing the request.
|
|
233
|
+
#
|
|
234
|
+
# Available Information: The {InterceptorContext#input} and
|
|
235
|
+
# {InterceptorContext#request} are ALWAYS available. Other
|
|
236
|
+
# information WILL NOT be available. In the event of retries, the
|
|
237
|
+
# {InterceptorContext} WILL include changes made in previous
|
|
238
|
+
# attempts (e.g. by other interceptors).
|
|
239
|
+
#
|
|
240
|
+
# Error Behavior: If errors are raised by this hook, execution will
|
|
241
|
+
# jump to :modify_before_attempt_completion with the raised error as
|
|
242
|
+
# the {Output#error} in {InterceptorContext#output}.
|
|
243
|
+
#
|
|
244
|
+
# ## :modify_before_transmit
|
|
245
|
+
# A hook called before the transport request message is sent to the
|
|
246
|
+
# service. This method has the ability to modify the transport request
|
|
247
|
+
# message.
|
|
248
|
+
#
|
|
249
|
+
# When: This will ALWAYS be called once per attempt, except when a
|
|
250
|
+
# failure occurs earlier in the request pipeline. This method may be
|
|
251
|
+
# called multiple times in the event of retries.
|
|
252
|
+
#
|
|
253
|
+
# Available Information: The {InterceptorContext#input} and
|
|
254
|
+
# {InterceptorContext#request} are ALWAYS available. The request may
|
|
255
|
+
# have been modified by earlier :modify_before_transmit hooks, and
|
|
256
|
+
# may be modified further by later hooks. Other information WILL NOT be
|
|
257
|
+
# available. In the event of retries, the {InterceptorContext} WILL
|
|
258
|
+
# include changes made in previous attempts (e.g. by other interceptors).
|
|
259
|
+
#
|
|
260
|
+
# Error Behavior: If errors are raised by this hook, execution will
|
|
261
|
+
# jump to :modify_before_attempt_completion with the raised error as
|
|
262
|
+
# the {Output#error} in {InterceptorContext#output}.
|
|
263
|
+
#
|
|
264
|
+
# ## :read_before_transmit
|
|
265
|
+
# A hook called before the transport request message is sent to the
|
|
266
|
+
# service.
|
|
267
|
+
#
|
|
268
|
+
# When: This will ALWAYS be called once per attempt, except when a
|
|
269
|
+
# failure occurs earlier in the request pipeline. This method may be
|
|
270
|
+
# called multiple times in the event of retries. The duration between
|
|
271
|
+
# invocation of this hook and :read_after_transmit is very close to
|
|
272
|
+
# the amount of time spent communicating with the service. Depending on
|
|
273
|
+
# the protocol, the duration may not include the time spent reading the
|
|
274
|
+
# response data.
|
|
275
|
+
#
|
|
276
|
+
# Available Information: The {InterceptorContext#input} and
|
|
277
|
+
# {InterceptorContext#request} are ALWAYS available. Other
|
|
278
|
+
# information WILL NOT be available. In the event of retries, the
|
|
279
|
+
# {InterceptorContext} WILL include changes made in previous
|
|
280
|
+
# attempts (e.g. by other interceptors).
|
|
281
|
+
#
|
|
282
|
+
# Error Behavior: If errors are raised by this hook, execution will
|
|
283
|
+
# jump to :modify_before_attempt_completion with the raised error as
|
|
284
|
+
# the {Output#error} in {InterceptorContext#output}.
|
|
285
|
+
#
|
|
286
|
+
# ## :read_after_transmit
|
|
287
|
+
# A hook called after the transport response message is sent to the
|
|
288
|
+
# service and a transport response message is received.
|
|
289
|
+
#
|
|
290
|
+
# When: This will ALWAYS be called once per attempt, except when a
|
|
291
|
+
# failure occurs earlier in the request pipeline. This method may be
|
|
292
|
+
# called multiple times in the event of retries. The duration between
|
|
293
|
+
# invocation of this hook and :read_before_transmit is very close to
|
|
294
|
+
# the amount of time spent communicating with the service. Depending on
|
|
295
|
+
# the protocol, the duration may not include the time spent reading the
|
|
296
|
+
# response data.
|
|
297
|
+
#
|
|
298
|
+
# Available Information: The {InterceptorContext#input},
|
|
299
|
+
# {InterceptorContext#request}, and {InterceptorContext#response}
|
|
300
|
+
# are ALWAYS available. Other information WILL NOT be available. In the
|
|
301
|
+
# event of retries, the {InterceptorContext} WILL include changes
|
|
302
|
+
# made in previous attempts (e.g. by other interceptors).
|
|
303
|
+
#
|
|
304
|
+
# Error Behavior: If errors are raised by this hook, execution will
|
|
305
|
+
# jump to :modify_before_attempt_completion with the raised error as
|
|
306
|
+
# the {Output#error} in {InterceptorContext#output}.
|
|
307
|
+
#
|
|
308
|
+
# ## :modify_before_deserialization
|
|
309
|
+
# A hook called before the transport response message is unmarshalled.
|
|
310
|
+
# This method has the ability to modify the transport response message.
|
|
311
|
+
#
|
|
312
|
+
# When: This will ALWAYS be called once per attempt, except when a
|
|
313
|
+
# failure occurs earlier in the request pipeline. This method may be
|
|
314
|
+
# called multiple times in the event of retries.
|
|
315
|
+
#
|
|
316
|
+
# Available Information: The {InterceptorContext#input},
|
|
317
|
+
# {InterceptorContext#request}, and {InterceptorContext#response}
|
|
318
|
+
# are ALWAYS available. The response may have been modified by earlier
|
|
319
|
+
# :modify_before_deserialization hooks, and may be modified further by
|
|
320
|
+
# later hooks. Other information WILL NOT be available. In the event of
|
|
321
|
+
# retries, the {InterceptorContext} WILL include changes made in
|
|
322
|
+
# previous attempts (e.g. by other interceptors).
|
|
323
|
+
#
|
|
324
|
+
# Error Behavior: If errors are raised by this hook, execution will
|
|
325
|
+
# jump to :modify_before_attempt_completion with the raised error as
|
|
326
|
+
# the {Output#error} in {InterceptorContext#output}.
|
|
327
|
+
#
|
|
328
|
+
# ## :read_before_deserialization
|
|
329
|
+
# A hook called before the transport response message is unmarshalled.
|
|
330
|
+
#
|
|
331
|
+
# When: This will ALWAYS be called once per attempt, except when a
|
|
332
|
+
# failure occurs earlier in the request pipeline. This method may be
|
|
333
|
+
# called multiple times in the event of retries. The duration between
|
|
334
|
+
# invocation of this hook and :read_after_deserialization is very
|
|
335
|
+
# close to the amount of time spent unmarshalling the service response.
|
|
336
|
+
# Depending on the protocol and operation, the duration may include the
|
|
337
|
+
# time spent downloading the response data.
|
|
338
|
+
#
|
|
339
|
+
# Available Information: The {InterceptorContext#input},
|
|
340
|
+
# {InterceptorContext#request}, and {InterceptorContext#response}
|
|
341
|
+
# are ALWAYS available. Other information WILL NOT be available. In the
|
|
342
|
+
# event of retries, the {InterceptorContext} WILL include changes
|
|
343
|
+
# made in previous attempts (e.g. by other interceptors).
|
|
344
|
+
#
|
|
345
|
+
# Error Behavior: If errors are raised by this hook, execution will
|
|
346
|
+
# jump to :modify_before_attempt_completion with the raised error as
|
|
347
|
+
# the {Output#error} in {InterceptorContext#output}.
|
|
348
|
+
#
|
|
349
|
+
# ## :read_after_deserialization
|
|
350
|
+
# A hook called after the transport response message is unmarshalled.
|
|
351
|
+
#
|
|
352
|
+
# When: This will ALWAYS be called once per attempt, except when a
|
|
353
|
+
# failure occurs earlier in the request pipeline. The duration between
|
|
354
|
+
# invocation of this hook and :read_before_deserialization is very
|
|
355
|
+
# close to the amount of time spent unmarshalling the service response.
|
|
356
|
+
# Depending on the protocol and operation, the duration may include the
|
|
357
|
+
# time spent downloading the response data.
|
|
358
|
+
#
|
|
359
|
+
# Available Information: The {InterceptorContext#input},
|
|
360
|
+
# {InterceptorContext#request}, {InterceptorContext#response}
|
|
361
|
+
# and {InterceptorContext#output} are ALWAYS available. In the event
|
|
362
|
+
# of retries, the {InterceptorContext} WILL include changes made
|
|
363
|
+
# in previous attempts (e.g. by other interceptors).
|
|
364
|
+
#
|
|
365
|
+
# Error Behavior: If errors are raised by this hook, execution will
|
|
366
|
+
# jump to :modify_before_attempt_completion with the raised error as
|
|
367
|
+
# the {Output#error} in {InterceptorContext#output}.
|
|
368
|
+
#
|
|
369
|
+
# ## :modify_before_attempt_completion
|
|
370
|
+
# A hook called when an attempt is completed. This method has the
|
|
371
|
+
# ability to modify the output message or error matching
|
|
372
|
+
# the currently-executing operation.
|
|
373
|
+
#
|
|
374
|
+
# When: This will ALWAYS be called once per attempt, except when a
|
|
375
|
+
# failure occurs before :read_before_attempt. This method may
|
|
376
|
+
# be called multiple times in the event of retries.
|
|
377
|
+
#
|
|
378
|
+
# Available Information: The {InterceptorContext#input},
|
|
379
|
+
# {InterceptorContext#request}, {InterceptorContext#response} and
|
|
380
|
+
# {InterceptorContext#output} are ALWAYS available. In the event
|
|
381
|
+
# of retries, the {InterceptorContext} WILL include changes made
|
|
382
|
+
# in previous attempts (e.g. by other interceptors).
|
|
383
|
+
#
|
|
384
|
+
# Error Behavior: If errors are raised by this hook, execution will
|
|
385
|
+
# jump to :read_after_attempt with the raised error as the
|
|
386
|
+
# {Output#error} in {InterceptorContext#output}.
|
|
387
|
+
#
|
|
388
|
+
# ## :read_after_attempt
|
|
389
|
+
# A hook called when an attempt is completed.
|
|
390
|
+
#
|
|
391
|
+
# When: This will ALWAYS be called once per attempt, as long as
|
|
392
|
+
# :read_before_attempt has been executed.
|
|
393
|
+
#
|
|
394
|
+
# Available Information: The {InterceptorContext#input},
|
|
395
|
+
# {InterceptorContext#request}, {InterceptorContext#response} and
|
|
396
|
+
# {InterceptorContext#output} are ALWAYS available. The
|
|
397
|
+
# {InterceptorContext#response} is available if a response
|
|
398
|
+
# was received by the service for this attempt. In the event of retries,
|
|
399
|
+
# the {InterceptorContext} WILL include changes made in previous
|
|
400
|
+
# attempts (e.g. by other interceptors).
|
|
401
|
+
#
|
|
402
|
+
# Error Behavior: Errors raised by this hook will be stored
|
|
403
|
+
# until all interceptors have had their :read_after_attempt invoked.
|
|
404
|
+
# If multiple :read_after_attempt methods raise errors, the latest
|
|
405
|
+
# will be used and earlier ones will be logged and dropped. If the retry
|
|
406
|
+
# strategy determines that {Output#error} is a retryable error, execution
|
|
407
|
+
# will then jump to :read_before_attempt. Otherwise, execution will jump
|
|
408
|
+
# to :modify_before_completion.
|
|
409
|
+
#
|
|
410
|
+
# ## :modify_before_completion
|
|
411
|
+
# A hook called when an execution is completed. This method has the
|
|
412
|
+
# ability to modify the output message or error matching
|
|
413
|
+
# the currently-executing operation.
|
|
414
|
+
#
|
|
415
|
+
# When: This will ALWAYS be called once per execution.
|
|
416
|
+
#
|
|
417
|
+
# Available Information: The {InterceptorContext#input} and
|
|
418
|
+
# {InterceptorContext#output} are ALWAYS available. The
|
|
419
|
+
# {InterceptorContext#request} and {InterceptorContext#response}
|
|
420
|
+
# are available if the execution proceeded far enough for them to be
|
|
421
|
+
# generated.
|
|
422
|
+
#
|
|
423
|
+
# Error Behavior: If errors are raised by this hook, execution will
|
|
424
|
+
# jump to :read_after_execution with the raised error as the
|
|
425
|
+
# {Output#error} in {InterceptorContext#output}.
|
|
426
|
+
#
|
|
427
|
+
# ## :read_after_execution
|
|
428
|
+
# A hook called when an execution is completed.
|
|
429
|
+
#
|
|
430
|
+
# When: This will ALWAYS be called once per execution. The duration
|
|
431
|
+
# between invocation of this hook and :read_before_execution is very
|
|
432
|
+
# close to the full duration of the execution.
|
|
433
|
+
#
|
|
434
|
+
# Available Information: The {InterceptorContext#input} and
|
|
435
|
+
# {InterceptorContext#output} are ALWAYS available. The
|
|
436
|
+
# {InterceptorContext#request} and {InterceptorContext#response}
|
|
437
|
+
# are available if the execution proceeded far enough for them to be
|
|
438
|
+
# generated.
|
|
439
|
+
#
|
|
440
|
+
# Error Behavior: Errors raised by this hook will be stored until all
|
|
441
|
+
# interceptors have had their :read_after_execution invoked. The
|
|
442
|
+
# error will then be treated as the {Output#error} raised by the
|
|
443
|
+
# operation. If multiple :read_after_execution methods raise
|
|
444
|
+
# errors, the latest will be used and earlier ones will be logged and
|
|
445
|
+
# dropped.
|
|
446
|
+
#
|
|
447
|
+
class Interceptor
|
|
448
|
+
# @api private
|
|
449
|
+
@hooks = [
|
|
450
|
+
READ_BEFORE_EXECUTION = :read_before_execution,
|
|
451
|
+
MODIFY_BEFORE_SERIALIZATION = :modify_before_serialization,
|
|
452
|
+
READ_BEFORE_SERIALIZATION = :read_before_serialization,
|
|
453
|
+
READ_AFTER_SERIALIZATION = :read_after_serialization,
|
|
454
|
+
MODIFY_BEFORE_RETRY_LOOP = :modify_before_retry_loop,
|
|
455
|
+
READ_BEFORE_ATTEMPT = :read_before_attempt,
|
|
456
|
+
MODIFY_BEFORE_SIGNING = :modify_before_signing,
|
|
457
|
+
READ_BEFORE_SIGNING = :read_before_signing,
|
|
458
|
+
READ_AFTER_SIGNING = :read_after_signing,
|
|
459
|
+
MODIFY_BEFORE_TRANSMIT = :modify_before_transmit,
|
|
460
|
+
READ_BEFORE_TRANSMIT = :read_before_transmit,
|
|
461
|
+
READ_AFTER_TRANSMIT = :read_after_transmit,
|
|
462
|
+
MODIFY_BEFORE_DESERIALIZATION = :modify_before_deserialization,
|
|
463
|
+
READ_BEFORE_DESERIALIZATION = :read_before_deserialization,
|
|
464
|
+
READ_AFTER_DESERIALIZATION = :read_after_deserialization,
|
|
465
|
+
MODIFY_BEFORE_ATTEMPT_COMPLETION = :modify_before_attempt_completion,
|
|
466
|
+
READ_AFTER_ATTEMPT = :read_after_attempt,
|
|
467
|
+
MODIFY_BEFORE_COMPLETION = :modify_before_completion,
|
|
468
|
+
READ_AFTER_EXECUTION = :read_after_execution
|
|
469
|
+
]
|
|
470
|
+
|
|
471
|
+
# Creates a new Interceptor.
|
|
472
|
+
#
|
|
473
|
+
# @param [Hash<Symbol, Proc>] callbacks
|
|
474
|
+
# A hash of hook names to callbacks. The callbacks will be invoked
|
|
475
|
+
# when the hook is called. The callback will be passed the
|
|
476
|
+
# {InterceptorContext} for the current execution.
|
|
477
|
+
def initialize(callbacks = {})
|
|
478
|
+
@callbacks = {}
|
|
479
|
+
|
|
480
|
+
Interceptor.hooks.each do |hook|
|
|
481
|
+
next unless callbacks[hook]
|
|
482
|
+
|
|
483
|
+
unless valid_callback?(callbacks[hook])
|
|
484
|
+
raise ArgumentError,
|
|
485
|
+
"#{hook} must be a callable with arity 1 (context)"
|
|
486
|
+
end
|
|
487
|
+
|
|
488
|
+
@callbacks[hook] = callbacks[hook]
|
|
489
|
+
define_singleton_method(hook) do |context|
|
|
490
|
+
@callbacks[hook].call(context)
|
|
491
|
+
end
|
|
492
|
+
end
|
|
493
|
+
end
|
|
494
|
+
|
|
495
|
+
class << self
|
|
496
|
+
# @return [Array<Symbol>] Returns a list of all available hooks.
|
|
497
|
+
attr_reader :hooks
|
|
498
|
+
end
|
|
499
|
+
|
|
500
|
+
private
|
|
501
|
+
|
|
502
|
+
def valid_callback?(callback)
|
|
503
|
+
callback.respond_to?(:call) && callback.arity == 1
|
|
504
|
+
end
|
|
505
|
+
end
|
|
506
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Hearth
|
|
4
|
+
# Context provided to interceptor hook methods. Interceptors can use this
|
|
5
|
+
# context to read and modify the input, request, response, and output.
|
|
6
|
+
# Attributes can be used to pass additional data between interceptors.
|
|
7
|
+
class InterceptorContext
|
|
8
|
+
# @param [Struct] input
|
|
9
|
+
# @param [Hearth::Request] request
|
|
10
|
+
# @param [Hearth::Response] response
|
|
11
|
+
# @param [Hearth::Output] output
|
|
12
|
+
# @param [Hash] attributes ({}) Additional interceptor data
|
|
13
|
+
def initialize(input:, request:, response:, output:, attributes: {})
|
|
14
|
+
@input = input
|
|
15
|
+
@request = request
|
|
16
|
+
@response = response
|
|
17
|
+
@output = output
|
|
18
|
+
@attributes = attributes
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# @return [Struct] Modeled input, i.e. Types::<Operation>Input
|
|
22
|
+
attr_reader :input
|
|
23
|
+
|
|
24
|
+
# @return [Hearth::Request]
|
|
25
|
+
attr_reader :request
|
|
26
|
+
|
|
27
|
+
# @return [Hearth::Response]
|
|
28
|
+
attr_reader :response
|
|
29
|
+
|
|
30
|
+
# @return [Hearth::Output] Operation output
|
|
31
|
+
attr_reader :output
|
|
32
|
+
|
|
33
|
+
# @return [Hash] attributes Additional interceptor data
|
|
34
|
+
attr_reader :attributes
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Hearth
|
|
4
|
+
# A list of {Hearth::Interceptor}s or classes that respond to Interceptor
|
|
5
|
+
# hook methods.
|
|
6
|
+
class InterceptorList
|
|
7
|
+
include Enumerable
|
|
8
|
+
|
|
9
|
+
# @param [Array<Interceptor>] interceptors ([])
|
|
10
|
+
def initialize(interceptors = [])
|
|
11
|
+
unless interceptors.respond_to?(:each)
|
|
12
|
+
raise ArgumentError, 'Interceptors must be an enumerable'
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
@interceptors = []
|
|
16
|
+
interceptors.each { |i| append(i) }
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# @param [Interceptor] interceptor
|
|
20
|
+
def append(interceptor)
|
|
21
|
+
unless valid_interceptor?(interceptor)
|
|
22
|
+
raise ArgumentError,
|
|
23
|
+
'Invalid interceptor - must respond to any of: ' \
|
|
24
|
+
"#{Interceptor.hooks.join(', ')}"
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
@interceptors << interceptor
|
|
28
|
+
end
|
|
29
|
+
alias << append
|
|
30
|
+
|
|
31
|
+
# @param [InterceptorList] other
|
|
32
|
+
# @return [InterceptorList] self
|
|
33
|
+
def concat(other)
|
|
34
|
+
other.each { |i| append(i) }
|
|
35
|
+
self
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def each(&block)
|
|
39
|
+
@interceptors.each(&block)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
def valid_interceptor?(interceptor)
|
|
45
|
+
!(interceptor.methods & Interceptor.hooks).empty?
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Hearth
|
|
4
|
+
# @api private
|
|
5
|
+
module Interceptors
|
|
6
|
+
# Invoke all interceptors.
|
|
7
|
+
#
|
|
8
|
+
# @param [Symbol] hook The specific hook to invoke.
|
|
9
|
+
# @param input [Hearth::Structure] input
|
|
10
|
+
# @param [Hearth::Context] context
|
|
11
|
+
# @param [Hearth::Output] output
|
|
12
|
+
# @param [Boolean] aggregate_errors (false) When true, all interceptors are
|
|
13
|
+
# run and only the last error is returned. When false, returns immediately
|
|
14
|
+
# if an error is encountered.
|
|
15
|
+
# @return [nil, StandardError] nil if successful, a standard error otherwise
|
|
16
|
+
def self.invoke(hook:, input:, context:, output:, aggregate_errors: false)
|
|
17
|
+
i_ctx = interceptor_context(input, context, output)
|
|
18
|
+
last_error = nil
|
|
19
|
+
|
|
20
|
+
context.interceptors.each do |i|
|
|
21
|
+
next unless i.respond_to?(hook)
|
|
22
|
+
|
|
23
|
+
log_debug(context, i, "Invoking #{hook}")
|
|
24
|
+
i.send(hook, i_ctx)
|
|
25
|
+
log_debug(context, i, "Finished #{hook}")
|
|
26
|
+
rescue StandardError => e
|
|
27
|
+
log_debug(context, i, "Error in #{hook}: #{e} (#{e.class})")
|
|
28
|
+
log_last_interceptor_error(last_error, i, context)
|
|
29
|
+
last_error = e
|
|
30
|
+
break unless aggregate_errors
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
log_last_output_error(last_error, context, output)
|
|
34
|
+
last_error
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
class << self
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
def interceptor_context(input, context, output)
|
|
41
|
+
Hearth::InterceptorContext.new(
|
|
42
|
+
input: input,
|
|
43
|
+
request: context.request,
|
|
44
|
+
response: context.response,
|
|
45
|
+
output: output
|
|
46
|
+
)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def log_last_interceptor_error(last_error, interceptor, context)
|
|
50
|
+
return unless last_error
|
|
51
|
+
|
|
52
|
+
message = "Dropping last error: #{last_error} (#{last_error.class})"
|
|
53
|
+
context.logger.error(
|
|
54
|
+
"[#{context.invocation_id}] [#{interceptor.class}] #{message}"
|
|
55
|
+
)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def log_last_output_error(last_error, context, output)
|
|
59
|
+
return unless last_error && output
|
|
60
|
+
return unless output.error
|
|
61
|
+
|
|
62
|
+
message = "Dropping last error: #{output.error} (#{output.error.class})"
|
|
63
|
+
context.logger.error(
|
|
64
|
+
"[#{context.invocation_id}] [Interceptors] #{message}"
|
|
65
|
+
)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def log_debug(context, interceptor, message)
|
|
69
|
+
context.logger.debug(
|
|
70
|
+
"[#{context.invocation_id}] [#{interceptor.class}] #{message}"
|
|
71
|
+
)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|