Module: PatientHttp

Defined in:
lib/patient_http.rb,
lib/patient_http/error.rb,
lib/patient_http/client.rb,
lib/patient_http/payload.rb,
lib/patient_http/request.rb,
lib/patient_http/response.rb,
lib/patient_http/encryptor.rb,
lib/patient_http/processor.rb,
lib/patient_http/http_error.rb,
lib/patient_http/client_pool.rb,
lib/patient_http/time_helper.rb,
lib/patient_http/class_helper.rb,
lib/patient_http/http_headers.rb,
lib/patient_http/rails/engine.rb,
lib/patient_http/request_task.rb,
lib/patient_http/task_handler.rb,
lib/patient_http/callback_args.rb,
lib/patient_http/configuration.rb,
lib/patient_http/payload_store.rb,
lib/patient_http/request_error.rb,
lib/patient_http/redirect_error.rb,
lib/patient_http/request_helper.rb,
lib/patient_http/redirect_helper.rb,
lib/patient_http/response_reader.rb,
lib/patient_http/external_storage.rb,
lib/patient_http/request_template.rb,
lib/patient_http/lifecycle_manager.rb,
lib/patient_http/callback_validator.rb,
lib/patient_http/payload_store/base.rb,
lib/patient_http/processor_observer.rb,
lib/patient_http/synchronous_executor.rb,
lib/patient_http/payload_store/s3_store.rb,
lib/patient_http/payload_store/file_store.rb,
lib/patient_http/payload_store/redis_store.rb,
lib/patient_http/payload_store/active_record_store.rb

Overview

Generic async HTTP connection pool for Ruby applications.

This module provides:

  • Async HTTP request processing using Ruby’s Fiber scheduler

  • Connection pooling with HTTP/2 support

  • Configurable timeouts, retries, and proxy support

  • Error handling with typed errors

This module can be used standalone or integrated with job systems like Sidekiq via adapters.

Defined Under Namespace

Modules: CallbackValidator, ClassHelper, PayloadStore, Rails, RedirectHelper, RequestHelper, TimeHelper Classes: CallbackArgs, Client, ClientError, ClientPool, Configuration, Encryptor, Error, ExternalStorage, HttpError, HttpHeaders, LifecycleManager, MaxCapacityError, NotRunningError, Payload, Processor, ProcessorObserver, RecursiveRedirectError, RedirectError, Request, RequestError, RequestTask, RequestTemplate, Response, ResponseReader, ResponseTooLargeError, ServerError, SynchronousExecutor, TaskHandler, TooManyRedirectsError

Constant Summary collapse

FOLLOWABLE_REDIRECT_STATUSES =

HTTP redirect status codes that should be followed

[301, 302, 303, 307, 308].freeze
VERSION =
File.read(File.join(__dir__, "../VERSION")).strip

Class Method Summary collapse

Class Method Details

.delete(uri, callback:, **kwargs) ⇒ Object

Enqueues an HTTP DELETE request.

Parameters:

  • uri (String)

    absolute URL

  • callback (Class, String)

    callback class to handle the response

  • kwargs (Hash)

    forwarded to ‘request`

Returns:

  • (Object)

    return value from the registered request handler



222
223
224
# File 'lib/patient_http.rb', line 222

def delete(uri, callback:, **kwargs)
  request(:delete, uri, callback: callback, **kwargs)
end

.execute(request:, callback:, callback_args: nil, raise_error_responses: nil) ⇒ Object

Executes the registered request handler with the given request parameters.

Parameters:

  • request (Request)

    the HTTP request to handle

  • callback (Class, String)

    the callback class or name

  • callback_args (Hash, nil) (defaults to: nil)

    JSON-compatible callback arguments

  • raise_error_responses (Boolean, nil) (defaults to: nil)

    when true, non-success responses are reported as errors

Returns:

  • (Object)

    return value from the registered request handler

Raises:

  • (RuntimeError)

    if no handler is registered



161
162
163
164
165
166
167
168
169
170
171
172
173
174
# File 'lib/patient_http.rb', line 161

def execute(request:, callback:, callback_args: nil, raise_error_responses: nil)
  handler = @handler_mutex.synchronize { @handler }

  unless handler
    raise "No request handler registered; you must register a PatientHttp handler before executing requests"
  end

  handler.call(
    request: request,
    callback: callback,
    callback_args: callback_args,
    raise_error_responses: raise_error_responses
  )
end

.get(uri, callback:, **kwargs) ⇒ Object

Enqueues an HTTP GET request.

Parameters:

  • uri (String)

    absolute URL

  • callback (Class, String)

    callback class to handle the response

  • kwargs (Hash)

    forwarded to ‘request`

Returns:

  • (Object)

    return value from the registered request handler



182
183
184
# File 'lib/patient_http.rb', line 182

def get(uri, callback:, **kwargs)
  request(:get, uri, callback: callback, **kwargs)
end

.patch(uri, callback:, **kwargs) ⇒ Object

Enqueues an HTTP PATCH request.

Parameters:

  • uri (String)

    absolute URL

  • callback (Class, String)

    callback class to handle the response

  • kwargs (Hash)

    forwarded to ‘request`

Returns:

  • (Object)

    return value from the registered request handler



212
213
214
# File 'lib/patient_http.rb', line 212

def patch(uri, callback:, **kwargs)
  request(:patch, uri, callback: callback, **kwargs)
end

.post(uri, callback:, **kwargs) ⇒ Object

Enqueues an HTTP POST request.

Parameters:

  • uri (String)

    absolute URL

  • callback (Class, String)

    callback class to handle the response

  • kwargs (Hash)

    forwarded to ‘request`

Returns:

  • (Object)

    return value from the registered request handler



192
193
194
# File 'lib/patient_http.rb', line 192

def post(uri, callback:, **kwargs)
  request(:post, uri, callback: callback, **kwargs)
end

.put(uri, callback:, **kwargs) ⇒ Object

Enqueues an HTTP PUT request.

Parameters:

  • uri (String)

    absolute URL

  • callback (Class, String)

    callback class to handle the response

  • kwargs (Hash)

    forwarded to ‘request`

Returns:

  • (Object)

    return value from the registered request handler



202
203
204
# File 'lib/patient_http.rb', line 202

def put(uri, callback:, **kwargs)
  request(:put, uri, callback: callback, **kwargs)
end

.register_handler(callable = nil) {|request, callback, callback_args, raise_error_responses| ... } ⇒ #call

Registers a request handler that will be called to process each request. The handler must be a callable object (responds to ‘call`) or a block.

The handler will receive keyword arguments: request, callback, callback_args, and raise_error_responses. It should return the request id for the enqueued request.

Parameters:

  • callable (#call, nil) (defaults to: nil)

    A callable object that will handle requests.

Yields:

  • (request, callback, callback_args, raise_error_responses)

    If a block is given, it will be used as the request handler

Returns:

  • (#call)

    the registered handler

Raises:

  • (ArgumentError)

    if neither a callable nor a block is provided, or if both are provided

  • (ArgumentError)

    if the provided callable does not respond to ‘call`

  • (ArgumentError)

    if the handler does not support the required keyword arguments



106
107
108
109
110
111
112
113
114
115
116
# File 'lib/patient_http.rb', line 106

def register_handler(callable = nil, &block)
  raise ArgumentError.new("Must provide a callable object or a block") unless callable || block_given?
  raise ArgumentError.new("Cannot provide both a callable object and a block") if callable && block_given?

  handler = callable || block
  raise ArgumentError.new("Handler must be a callable object or a block") unless handler.respond_to?(:call)

  validate_handler_parameters!(handler)

  @handler_mutex.synchronize { @handler = handler }
end

.register_handler!(callable = nil) {|request, callback, callback_args, raise_error_responses| ... } ⇒ #call

Registers a request handler, raising an error if one is already registered.

This is a safer alternative to register_handler that prevents accidental double-registration.

Parameters:

  • callable (#call, nil) (defaults to: nil)

    A callable object that will handle requests.

Yields:

  • (request, callback, callback_args, raise_error_responses)

    If a block is given, it will be used as the request handler

Returns:

  • (#call)

    the registered handler

Raises:

  • (RuntimeError)

    if a handler is already registered

  • (ArgumentError)

    if neither a callable nor a block is provided, or if both are provided

  • (ArgumentError)

    if the provided callable does not respond to ‘call`

  • (ArgumentError)

    if the handler does not support the required keyword arguments



131
132
133
134
135
136
137
138
139
# File 'lib/patient_http.rb', line 131

def register_handler!(callable = nil, &block)
  @handler_mutex.synchronize do
    if @handler
      raise "A PatientHttp handler is already registered. Unregister the existing handler before registering a new one."
    end

    register_handler(callable, &block)
  end
end

.request(method, url, callback:, headers: nil, body: nil, json: nil, params: nil, timeout: nil, raise_error_responses: nil, callback_args: nil) ⇒ Object

Builds and dispatches an HTTP request.

Parameters:

  • method (Symbol)

    HTTP method (‘:get`, `:post`, `:put`, `:patch`, `:delete`)

  • url (String)

    absolute URL

  • callback (Class, String)

    callback class to handle the response

  • headers (Hash, nil) (defaults to: nil)

    request headers

  • body (String, nil) (defaults to: nil)

    raw request body

  • json (Hash, Array, nil) (defaults to: nil)

    JSON payload encoded by the request layer

  • params (Hash, nil) (defaults to: nil)

    query parameters

  • timeout (Numeric, nil) (defaults to: nil)

    timeout in seconds for this request

  • raise_error_responses (Boolean, nil) (defaults to: nil)

    when true, non-success responses are reported as errors

  • callback_args (Hash, nil) (defaults to: nil)

    JSON-compatible callback arguments

Returns:

  • (Object)

    return value from the registered request handler



240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
# File 'lib/patient_http.rb', line 240

def request(
  method,
  url,
  callback:,
  headers: nil,
  body: nil,
  json: nil,
  params: nil,
  timeout: nil,
  raise_error_responses: nil,
  callback_args: nil
)
  request = Request.new(method, url, body: body, json: json, headers: headers, params: params, timeout: timeout)
  execute(
    request: request,
    callback: callback,
    callback_args: callback_args,
    raise_error_responses: raise_error_responses
  )
end

.testing=(value) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Set testing mode.



89
90
91
# File 'lib/patient_http.rb', line 89

def testing=(value)
  @testing = !!value
end

.testing?Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Check if running in testing mode.

Returns:

  • (Boolean)


82
83
84
# File 'lib/patient_http.rb', line 82

def testing?
  @testing
end

.unregister_handler(handler = nil) ⇒ void

This method returns an undefined value.

Unregisters the current request handler.

Parameters:

  • handler (#call, nil) (defaults to: nil)

    If provided, only unregisters if the given handler matches the current handler



146
147
148
149
150
# File 'lib/patient_http.rb', line 146

def unregister_handler(handler = nil)
  @handler_mutex.synchronize do
    @handler = nil if @handler == handler || handler.nil?
  end
end