patient_http 1.1.2 → 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/CHANGELOG.md +31 -0
- data/README.md +116 -3
- data/VERSION +1 -1
- data/lib/patient_http/client.rb +7 -10
- data/lib/patient_http/configuration.rb +56 -1
- data/lib/patient_http/http_headers.rb +7 -0
- data/lib/patient_http/inline_task_handler.rb +30 -0
- data/lib/patient_http/lifecycle_manager.rb +20 -8
- data/lib/patient_http/outgoing_request.rb +72 -0
- data/lib/patient_http/payload.rb +1 -1
- data/lib/patient_http/payload_store/redis_store.rb +4 -1
- data/lib/patient_http/processor.rb +299 -113
- data/lib/patient_http/redirect_error.rb +5 -0
- data/lib/patient_http/request.rb +26 -3
- data/lib/patient_http/request_error.rb +1 -1
- data/lib/patient_http/request_helper.rb +16 -6
- data/lib/patient_http/request_preparer.rb +52 -0
- data/lib/patient_http/request_task.rb +16 -7
- data/lib/patient_http/request_template.rb +9 -3
- data/lib/patient_http/response.rb +3 -0
- data/lib/patient_http/response_reader.rb +16 -4
- data/lib/patient_http/synchronous_executor.rb +53 -45
- data/lib/patient_http.rb +182 -2
- metadata +5 -2
data/lib/patient_http.rb
CHANGED
|
@@ -51,7 +51,9 @@ module PatientHttp
|
|
|
51
51
|
autoload :ExternalStorage, File.join(__dir__, "patient_http/external_storage")
|
|
52
52
|
autoload :HttpError, File.join(__dir__, "patient_http/http_error")
|
|
53
53
|
autoload :HttpHeaders, File.join(__dir__, "patient_http/http_headers")
|
|
54
|
+
autoload :InlineTaskHandler, File.join(__dir__, "patient_http/inline_task_handler")
|
|
54
55
|
autoload :LifecycleManager, File.join(__dir__, "patient_http/lifecycle_manager")
|
|
56
|
+
autoload :OutgoingRequest, File.join(__dir__, "patient_http/outgoing_request")
|
|
55
57
|
autoload :Payload, File.join(__dir__, "patient_http/payload")
|
|
56
58
|
autoload :PayloadStore, File.join(__dir__, "patient_http/payload_store")
|
|
57
59
|
autoload :Processor, File.join(__dir__, "patient_http/processor")
|
|
@@ -62,6 +64,7 @@ module PatientHttp
|
|
|
62
64
|
autoload :Request, File.join(__dir__, "patient_http/request")
|
|
63
65
|
autoload :RequestError, File.join(__dir__, "patient_http/request_error")
|
|
64
66
|
autoload :RequestHelper, File.join(__dir__, "patient_http/request_helper")
|
|
67
|
+
autoload :RequestPreparer, File.join(__dir__, "patient_http/request_preparer")
|
|
65
68
|
autoload :RequestTask, File.join(__dir__, "patient_http/request_task")
|
|
66
69
|
autoload :RequestTemplate, File.join(__dir__, "patient_http/request_template")
|
|
67
70
|
autoload :Response, File.join(__dir__, "patient_http/response")
|
|
@@ -76,6 +79,11 @@ module PatientHttp
|
|
|
76
79
|
@testing = %w[RAILS_ENV RACK_ENV APP_ENV].any? { |var| ENV[var] == "test" }
|
|
77
80
|
@handler = nil
|
|
78
81
|
@handler_mutex = Monitor.new
|
|
82
|
+
@inline_handler = nil
|
|
83
|
+
@default_configuration = nil
|
|
84
|
+
@inline_configuration = nil
|
|
85
|
+
@module_secrets = {}
|
|
86
|
+
@config_mutex = Monitor.new
|
|
79
87
|
|
|
80
88
|
class << self
|
|
81
89
|
# Check if running in testing mode.
|
|
@@ -151,6 +159,81 @@ module PatientHttp
|
|
|
151
159
|
end
|
|
152
160
|
end
|
|
153
161
|
|
|
162
|
+
# Registers a request handler that executes requests inline (synchronously,
|
|
163
|
+
# in-process) instead of dispatching them to a job system.
|
|
164
|
+
#
|
|
165
|
+
# This is intended for consoles, tests, and development environments where no
|
|
166
|
+
# job-system integration gem is configured. Each request runs through
|
|
167
|
+
# {SynchronousExecutor} and the callback is invoked on the calling thread
|
|
168
|
+
# before the handler returns.
|
|
169
|
+
#
|
|
170
|
+
# @param config [Configuration, nil] configuration to execute requests against.
|
|
171
|
+
# Defaults to {.default_configuration}, or a lazily created configuration that
|
|
172
|
+
# includes any secrets registered with {.register_secret}.
|
|
173
|
+
# @return [void]
|
|
174
|
+
def inline!(config: nil)
|
|
175
|
+
handler = lambda do |request:, callback:, callback_args: nil, raise_error_responses: nil|
|
|
176
|
+
execute_inline(
|
|
177
|
+
request: request,
|
|
178
|
+
callback: callback,
|
|
179
|
+
callback_args: callback_args,
|
|
180
|
+
raise_error_responses: raise_error_responses,
|
|
181
|
+
config: config
|
|
182
|
+
)
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
@handler_mutex.synchronize do
|
|
186
|
+
register_handler(handler)
|
|
187
|
+
@inline_handler = handler
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
# Check if the currently registered handler is the inline handler registered
|
|
192
|
+
# by {.inline!}.
|
|
193
|
+
#
|
|
194
|
+
# @return [Boolean]
|
|
195
|
+
def inline?
|
|
196
|
+
@handler_mutex.synchronize { !@handler.nil? && @handler.equal?(@inline_handler) }
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
# Check if a request handler is registered.
|
|
200
|
+
#
|
|
201
|
+
# @return [Boolean]
|
|
202
|
+
def handler_registered?
|
|
203
|
+
@handler_mutex.synchronize { !@handler.nil? }
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
# Executes a request inline (synchronously, in-process) through
|
|
207
|
+
# {SynchronousExecutor}, invoking the callback with the response or error
|
|
208
|
+
# before returning.
|
|
209
|
+
#
|
|
210
|
+
# @param request [Request] the HTTP request to execute
|
|
211
|
+
# @param callback [Class, String] the callback class or name
|
|
212
|
+
# @param callback_args [Hash, nil] JSON-compatible callback arguments
|
|
213
|
+
# @param raise_error_responses [Boolean, nil] when true, non-success responses are
|
|
214
|
+
# reported as errors; defaults to the configuration's setting
|
|
215
|
+
# @param config [Configuration, nil] configuration to execute the request against.
|
|
216
|
+
# Defaults to {.default_configuration}, or a lazily created configuration that
|
|
217
|
+
# includes any secrets registered with {.register_secret}.
|
|
218
|
+
# @return [String] the request id
|
|
219
|
+
def execute_inline(request:, callback:, callback_args: nil, raise_error_responses: nil, config: nil)
|
|
220
|
+
config ||= default_configuration || inline_configuration
|
|
221
|
+
raise_error_responses = config.raise_error_responses if raise_error_responses.nil?
|
|
222
|
+
|
|
223
|
+
task = RequestTask.new(
|
|
224
|
+
request: request,
|
|
225
|
+
task_handler: InlineTaskHandler.new,
|
|
226
|
+
callback: callback,
|
|
227
|
+
callback_args: callback_args,
|
|
228
|
+
raise_error_responses: raise_error_responses,
|
|
229
|
+
default_max_redirects: config.max_redirects
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
SynchronousExecutor.new(task, config: config).call
|
|
233
|
+
|
|
234
|
+
task.id
|
|
235
|
+
end
|
|
236
|
+
|
|
154
237
|
# Executes the registered request handler with the given request parameters.
|
|
155
238
|
#
|
|
156
239
|
# @param request [Request] the HTTP request to handle
|
|
@@ -238,6 +321,8 @@ module PatientHttp
|
|
|
238
321
|
# @param raise_error_responses [Boolean, nil] when true, non-success responses are
|
|
239
322
|
# reported as errors
|
|
240
323
|
# @param callback_args [Hash, nil] JSON-compatible callback arguments
|
|
324
|
+
# @param preprocessors [String, Symbol, Array<String, Symbol>, nil] names of preprocessors
|
|
325
|
+
# registered on the configuration to apply to the request when it is sent
|
|
241
326
|
# @return [Object] return value from the registered request handler
|
|
242
327
|
def request(
|
|
243
328
|
method,
|
|
@@ -249,9 +334,19 @@ module PatientHttp
|
|
|
249
334
|
params: nil,
|
|
250
335
|
timeout: nil,
|
|
251
336
|
raise_error_responses: nil,
|
|
252
|
-
callback_args: nil
|
|
337
|
+
callback_args: nil,
|
|
338
|
+
preprocessors: nil
|
|
253
339
|
)
|
|
254
|
-
request = Request.new(
|
|
340
|
+
request = Request.new(
|
|
341
|
+
method,
|
|
342
|
+
url,
|
|
343
|
+
body: body,
|
|
344
|
+
json: json,
|
|
345
|
+
headers: headers,
|
|
346
|
+
params: params,
|
|
347
|
+
timeout: timeout,
|
|
348
|
+
preprocessors: preprocessors
|
|
349
|
+
)
|
|
255
350
|
execute(
|
|
256
351
|
request: request,
|
|
257
352
|
callback: callback,
|
|
@@ -273,8 +368,93 @@ module PatientHttp
|
|
|
273
368
|
SecretReference.new(name)
|
|
274
369
|
end
|
|
275
370
|
|
|
371
|
+
# Register a named secret at the module level, independent of any configuration.
|
|
372
|
+
#
|
|
373
|
+
# Secrets registered here are applied to the {.default_configuration} (immediately
|
|
374
|
+
# if one is already set, or when one is set later) and to the configuration used
|
|
375
|
+
# for inline execution. This makes boot order irrelevant: application code can
|
|
376
|
+
# register secrets before or after the job-system integration gem configures the
|
|
377
|
+
# processor.
|
|
378
|
+
#
|
|
379
|
+
# @param name [String, Symbol] the secret name
|
|
380
|
+
# @param value [Object, nil] the secret value (omit when providing a block)
|
|
381
|
+
# @yield [name] a block that returns the secret value (omit when providing a value)
|
|
382
|
+
# @raise [ArgumentError] if neither or both of value and block are provided
|
|
383
|
+
# @return [void]
|
|
384
|
+
# @see Configuration#register_secret
|
|
385
|
+
def register_secret(name, value = nil, &block)
|
|
386
|
+
if value.nil? && block.nil?
|
|
387
|
+
raise ArgumentError.new("register_secret requires a value or a block")
|
|
388
|
+
end
|
|
389
|
+
|
|
390
|
+
if !value.nil? && block
|
|
391
|
+
raise ArgumentError.new("register_secret accepts either a value or a block, not both")
|
|
392
|
+
end
|
|
393
|
+
|
|
394
|
+
@config_mutex.synchronize do
|
|
395
|
+
secret_value = block || value
|
|
396
|
+
@module_secrets[name.to_s] = secret_value
|
|
397
|
+
@default_configuration&.register_secret(name, secret_value)
|
|
398
|
+
@inline_configuration&.register_secret(name, secret_value)
|
|
399
|
+
end
|
|
400
|
+
end
|
|
401
|
+
|
|
402
|
+
# Check if a secret name is registered, either at the module level via
|
|
403
|
+
# {.register_secret} or on the {.default_configuration}.
|
|
404
|
+
#
|
|
405
|
+
# @param name [String, Symbol] the secret name
|
|
406
|
+
# @return [Boolean]
|
|
407
|
+
def secret_registered?(name)
|
|
408
|
+
@config_mutex.synchronize do
|
|
409
|
+
return true if @module_secrets.include?(name.to_s)
|
|
410
|
+
|
|
411
|
+
!@default_configuration.nil? && @default_configuration.secret_manager.include?(name)
|
|
412
|
+
end
|
|
413
|
+
end
|
|
414
|
+
|
|
415
|
+
# The default configuration used for inline execution when none is provided.
|
|
416
|
+
# Job-system integration gems should set this at the end of their configure
|
|
417
|
+
# step so that module-level secrets registered with {.register_secret} are
|
|
418
|
+
# applied to the configuration the processor runs with.
|
|
419
|
+
#
|
|
420
|
+
# @return [Configuration, nil] the default configuration
|
|
421
|
+
def default_configuration
|
|
422
|
+
@config_mutex.synchronize { @default_configuration }
|
|
423
|
+
end
|
|
424
|
+
|
|
425
|
+
# Set the default configuration. Any secrets registered with {.register_secret}
|
|
426
|
+
# are applied to it; the module-level registry is retained, so re-assigning a
|
|
427
|
+
# new configuration re-applies the same secrets.
|
|
428
|
+
#
|
|
429
|
+
# @param config [Configuration, nil] the configuration to use as the default
|
|
430
|
+
# @return [void]
|
|
431
|
+
def default_configuration=(config)
|
|
432
|
+
@config_mutex.synchronize do
|
|
433
|
+
@default_configuration = config
|
|
434
|
+
apply_module_secrets(config) if config
|
|
435
|
+
end
|
|
436
|
+
end
|
|
437
|
+
|
|
276
438
|
private
|
|
277
439
|
|
|
440
|
+
# The lazily created configuration used for inline execution when no explicit
|
|
441
|
+
# or default configuration is available. Module-level secrets are applied to it.
|
|
442
|
+
#
|
|
443
|
+
# @return [Configuration]
|
|
444
|
+
def inline_configuration
|
|
445
|
+
@config_mutex.synchronize do
|
|
446
|
+
@inline_configuration ||= Configuration.new.tap { |config| apply_module_secrets(config) }
|
|
447
|
+
end
|
|
448
|
+
end
|
|
449
|
+
|
|
450
|
+
# Apply all module-level secrets to the given configuration.
|
|
451
|
+
#
|
|
452
|
+
# @param config [Configuration] the configuration to apply secrets to
|
|
453
|
+
# @return [void]
|
|
454
|
+
def apply_module_secrets(config)
|
|
455
|
+
@module_secrets.each { |name, value| config.register_secret(name, value) }
|
|
456
|
+
end
|
|
457
|
+
|
|
278
458
|
# Validates that the handler accepts the required keyword arguments.
|
|
279
459
|
#
|
|
280
460
|
# @param handler [#call] the handler to validate
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: patient_http
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Brian Durand
|
|
@@ -109,7 +109,9 @@ files:
|
|
|
109
109
|
- lib/patient_http/external_storage.rb
|
|
110
110
|
- lib/patient_http/http_error.rb
|
|
111
111
|
- lib/patient_http/http_headers.rb
|
|
112
|
+
- lib/patient_http/inline_task_handler.rb
|
|
112
113
|
- lib/patient_http/lifecycle_manager.rb
|
|
114
|
+
- lib/patient_http/outgoing_request.rb
|
|
113
115
|
- lib/patient_http/payload.rb
|
|
114
116
|
- lib/patient_http/payload_store.rb
|
|
115
117
|
- lib/patient_http/payload_store/active_record_store.rb
|
|
@@ -125,6 +127,7 @@ files:
|
|
|
125
127
|
- lib/patient_http/request.rb
|
|
126
128
|
- lib/patient_http/request_error.rb
|
|
127
129
|
- lib/patient_http/request_helper.rb
|
|
130
|
+
- lib/patient_http/request_preparer.rb
|
|
128
131
|
- lib/patient_http/request_task.rb
|
|
129
132
|
- lib/patient_http/request_template.rb
|
|
130
133
|
- lib/patient_http/response.rb
|
|
@@ -156,7 +159,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
156
159
|
- !ruby/object:Gem::Version
|
|
157
160
|
version: '0'
|
|
158
161
|
requirements: []
|
|
159
|
-
rubygems_version:
|
|
162
|
+
rubygems_version: 4.0.3
|
|
160
163
|
specification_version: 4
|
|
161
164
|
summary: Generic async HTTP connection pool for Ruby applications using Fiber-based
|
|
162
165
|
concurrency
|