Class: HTTPX::Options

Inherits:
Object
  • Object
show all
Defined in:
lib/httpx/options.rb

Overview

Contains a set of options which are passed and shared across from session to its requests or responses.

Constant Summary collapse

BUFFER_SIZE =
1 << 14
WINDOW_SIZE =

16K

1 << 14
MAX_BODY_THRESHOLD_SIZE =

112K

(1 << 10) * 112
KEEP_ALIVE_TIMEOUT =
20
SETTINGS_TIMEOUT =
10
CLOSE_HANDSHAKE_TIMEOUT =
10
CONNECT_TIMEOUT =
READ_TIMEOUT = WRITE_TIMEOUT = 60
REQUEST_TIMEOUT =
OPERATION_TIMEOUT = nil
SET_TEMPORARY_NAME =
->(mod, pl = nil) do
  if mod.respond_to?(:set_temporary_name) # ruby 3.4 only
    name = mod.name || "#{mod.superclass.name}(plugin)"
    name = "#{name}/#{pl}" if pl
    mod.set_temporary_name(name)
  end
end
DEFAULT_OPTIONS =
{
  :max_requests => Float::INFINITY,
  :debug => nil,
  :debug_level => (ENV["HTTPX_DEBUG"] || 1).to_i,
  :debug_redact => ENV.key?("HTTPX_DEBUG_REDACT"),
  :ssl => EMPTY_HASH,
  :http2_settings => { settings_enable_push: 0 }.freeze,
  :fallback_protocol => "http/1.1",
  :supported_compression_formats => %w[gzip deflate],
  :decompress_response_body => true,
  :compress_request_body => true,
  :timeout => {
    connect_timeout: CONNECT_TIMEOUT,
    settings_timeout: SETTINGS_TIMEOUT,
    close_handshake_timeout: CLOSE_HANDSHAKE_TIMEOUT,
    operation_timeout: OPERATION_TIMEOUT,
    keep_alive_timeout: KEEP_ALIVE_TIMEOUT,
    read_timeout: READ_TIMEOUT,
    write_timeout: WRITE_TIMEOUT,
    request_timeout: REQUEST_TIMEOUT,
  },
  :headers_class => Class.new(Headers, &SET_TEMPORARY_NAME),
  :headers => {},
  :window_size => WINDOW_SIZE,
  :buffer_size => BUFFER_SIZE,
  :body_threshold_size => MAX_BODY_THRESHOLD_SIZE,
  :request_class => Class.new(Request, &SET_TEMPORARY_NAME),
  :response_class => Class.new(Response, &SET_TEMPORARY_NAME),
  :request_body_class => Class.new(Request::Body, &SET_TEMPORARY_NAME),
  :response_body_class => Class.new(Response::Body, &SET_TEMPORARY_NAME),
  :pool_class => Class.new(Pool, &SET_TEMPORARY_NAME),
  :connection_class => Class.new(Connection, &SET_TEMPORARY_NAME),
  :options_class => Class.new(self, &SET_TEMPORARY_NAME),
  :transport => nil,
  :addresses => nil,
  :persistent => false,
  :resolver_class => (ENV["HTTPX_RESOLVER"] || :native).to_sym,
  :resolver_options => { cache: true }.freeze,
  :pool_options => EMPTY_HASH,
  :ip_families => ip_address_families,
  :close_on_fork => false,
}.freeze
REQUEST_BODY_IVARS =
%i[@headers].freeze

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options = {}) ⇒ Options

creates a new options instance from a given hash, which optionally define the following:

:debug

an object which log messages are written to (must respond to <<)

:debug_level

the log level of messages (can be 1, 2, or 3).

:debug_redact

whether header/body payload should be redacted (defaults to false).

:ssl

a hash of options which can be set as params of OpenSSL::SSL::SSLContext (see HTTPX::IO::SSL)

:http2_settings

a hash of options to be passed to a HTTP2::Connection (ex: { max_concurrent_streams: 2 })

:fallback_protocol

version of HTTP protocol to use by default in the absence of protocol negotiation like ALPN (defaults to "http/1.1")

:supported_compression_formats

list of compressions supported by the transcoder layer (defaults to %w[gzip deflate]).

:decompress_response_body

whether to auto-decompress response body (defaults to true).

:compress_request_body

whether to auto-decompress response body (defaults to true)

:timeout

hash of timeout configurations (supports :connect_timeout, :settings_timeout, :operation_timeout, :keep_alive_timeout, :read_timeout, :write_timeout and :request_timeout

:headers

hash of HTTP headers (ex: { "x-custom-foo" => "bar" })

:window_size

number of bytes to read from a socket

:buffer_size

internal read and write buffer size in bytes

:body_threshold_size

maximum size in bytes of response payload that is buffered in memory.

:request_class

class used to instantiate a request

:response_class

class used to instantiate a response

:headers_class

class used to instantiate headers

:request_body_class

class used to instantiate a request body

:response_body_class

class used to instantiate a response body

:connection_class

class used to instantiate connections

:pool_class

class used to instantiate the session connection pool

:options_class

class used to instantiate options

:transport

type of transport to use (set to “unix” for UNIX sockets)

:addresses

bucket of peer addresses (can be a list of IP addresses, a hash of domain to list of adddresses; paths should be used for UNIX sockets instead)

:io

open socket, or domain/ip-to-socket hash, which requests should be sent to

:persistent

whether to persist connections in between requests (defaults to true)

:resolver_class

which resolver to use (defaults to :native, can also be :system<tt> for using getaddrinfo or <tt>:https for DoH resolver, or a custom class)

:resolver_options

hash of options passed to the resolver. Accepted keys depend on the resolver type.

:pool_options

hash of options passed to the connection pool (See Pool#initialize).

:ip_families

which socket families are supported (system-dependent)

:origin

HTTP origin to set on requests with relative path (ex: “api.serv.com”)

:base_path

path to prefix given relative paths with (ex: “/v2”)

:max_concurrent_requests

max number of requests which can be set concurrently

:max_requests

max number of requests which can be made on socket before it reconnects.

:close_on_fork

whether the session automatically closes when the process is fork (defaults to false). it only works if the session is persistent (and ruby 3.1 or higher is used).

This list of options are enhanced with each loaded plugin, see the plugin docs for details.



146
147
148
149
# File 'lib/httpx/options.rb', line 146

def initialize(options = {})
  do_initialize(options)
  freeze
end

Class Method Details

.method_added(meth) ⇒ Object



90
91
92
93
94
95
96
97
98
# File 'lib/httpx/options.rb', line 90

def method_added(meth)
  super

  return unless meth =~ /^option_(.+)$/

  optname = Regexp.last_match(1).to_sym

  attr_reader(optname)
end

.new(options = {}) ⇒ Object



82
83
84
85
86
87
88
# File 'lib/httpx/options.rb', line 82

def new(options = {})
  # let enhanced options go through
  return options if self == Options && options.class < self
  return options if options.is_a?(self)

  super
end

Instance Method Details

#==(other) ⇒ Object



243
244
245
# File 'lib/httpx/options.rb', line 243

def ==(other)
  super || options_equals?(other)
end

#extend_with_plugin_classes(pl) ⇒ Object



304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
# File 'lib/httpx/options.rb', line 304

def extend_with_plugin_classes(pl)
  if defined?(pl::RequestMethods) || defined?(pl::RequestClassMethods)
    @request_class = @request_class.dup
    SET_TEMPORARY_NAME[@request_class, pl]
    @request_class.__send__(:include, pl::RequestMethods) if defined?(pl::RequestMethods)
    @request_class.extend(pl::RequestClassMethods) if defined?(pl::RequestClassMethods)
  end
  if defined?(pl::ResponseMethods) || defined?(pl::ResponseClassMethods)
    @response_class = @response_class.dup
    SET_TEMPORARY_NAME[@response_class, pl]
    @response_class.__send__(:include, pl::ResponseMethods) if defined?(pl::ResponseMethods)
    @response_class.extend(pl::ResponseClassMethods) if defined?(pl::ResponseClassMethods)
  end
  if defined?(pl::HeadersMethods) || defined?(pl::HeadersClassMethods)
    @headers_class = @headers_class.dup
    SET_TEMPORARY_NAME[@headers_class, pl]
    @headers_class.__send__(:include, pl::HeadersMethods) if defined?(pl::HeadersMethods)
    @headers_class.extend(pl::HeadersClassMethods) if defined?(pl::HeadersClassMethods)
  end
  if defined?(pl::RequestBodyMethods) || defined?(pl::RequestBodyClassMethods)
    @request_body_class = @request_body_class.dup
    SET_TEMPORARY_NAME[@request_body_class, pl]
    @request_body_class.__send__(:include, pl::RequestBodyMethods) if defined?(pl::RequestBodyMethods)
    @request_body_class.extend(pl::RequestBodyClassMethods) if defined?(pl::RequestBodyClassMethods)
  end
  if defined?(pl::ResponseBodyMethods) || defined?(pl::ResponseBodyClassMethods)
    @response_body_class = @response_body_class.dup
    SET_TEMPORARY_NAME[@response_body_class, pl]
    @response_body_class.__send__(:include, pl::ResponseBodyMethods) if defined?(pl::ResponseBodyMethods)
    @response_body_class.extend(pl::ResponseBodyClassMethods) if defined?(pl::ResponseBodyClassMethods)
  end
  if defined?(pl::PoolMethods)
    @pool_class = @pool_class.dup
    SET_TEMPORARY_NAME[@pool_class, pl]
    @pool_class.__send__(:include, pl::PoolMethods)
  end
  if defined?(pl::ConnectionMethods)
    @connection_class = @connection_class.dup
    SET_TEMPORARY_NAME[@connection_class, pl]
    @connection_class.__send__(:include, pl::ConnectionMethods)
  end
  return unless defined?(pl::OptionsMethods)

  @options_class = @options_class.dup
  @options_class.__send__(:include, pl::OptionsMethods)
end

#freezeObject



151
152
153
154
155
156
157
158
159
160
161
162
163
164
# File 'lib/httpx/options.rb', line 151

def freeze
  @origin.freeze
  @base_path.freeze
  @timeout.freeze
  @headers.freeze
  @addresses.freeze
  @supported_compression_formats.freeze
  @ssl.freeze
  @http2_settings.freeze
  @pool_options.freeze
  @resolver_options.freeze
  @ip_families.freeze
  super
end

#merge(other) ⇒ Object



262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
# File 'lib/httpx/options.rb', line 262

def merge(other)
  ivar_map = nil
  other_ivars = case other
                when Hash
                  ivar_map = other.keys.to_h { |k| [:"@#{k}", k] }
                  ivar_map.keys
                else
                  other.instance_variables
  end

  return self if other_ivars.empty?

  return self if other_ivars.all? { |ivar| instance_variable_get(ivar) == access_option(other, ivar, ivar_map) }

  opts = dup

  other_ivars.each do |ivar|
    v = access_option(other, ivar, ivar_map)

    unless v
      opts.instance_variable_set(ivar, v)
      next
    end

    v = opts.__send__(:"option_#{ivar[1..-1]}", v)

    orig_v = instance_variable_get(ivar)

    v = orig_v.merge(v) if orig_v.respond_to?(:merge) && v.respond_to?(:merge)

    opts.instance_variable_set(ivar, v)
  end

  opts
end

#option_addresses(value) ⇒ Object



193
194
195
# File 'lib/httpx/options.rb', line 193

def option_addresses(value)
  Array(value)
end

#option_base_path(value) ⇒ Object



170
171
172
# File 'lib/httpx/options.rb', line 170

def option_base_path(value)
  String(value)
end

#option_headers(value) ⇒ Object



174
175
176
# File 'lib/httpx/options.rb', line 174

def option_headers(value)
  headers_class.new(value)
end

#option_ip_families(value) ⇒ Object



197
198
199
# File 'lib/httpx/options.rb', line 197

def option_ip_families(value)
  Array(value)
end

#option_origin(value) ⇒ Object



166
167
168
# File 'lib/httpx/options.rb', line 166

def option_origin(value)
  URI(value)
end

#option_supported_compression_formats(value) ⇒ Object



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

def option_supported_compression_formats(value)
  Array(value).map(&:to_s)
end

#option_timeout(value) ⇒ Object



178
179
180
# File 'lib/httpx/options.rb', line 178

def option_timeout(value)
  Hash[value]
end

#option_transport(value) ⇒ Object

Raises:

  • (TypeError)


186
187
188
189
190
191
# File 'lib/httpx/options.rb', line 186

def option_transport(value)
  transport = value.to_s
  raise TypeError, "#{transport} is an unsupported transport type" unless %w[unix].include?(transport)

  transport
end

#options_equals?(other, ignore_ivars = REQUEST_BODY_IVARS) ⇒ Boolean

Returns:

  • (Boolean)


247
248
249
250
251
252
253
254
255
256
257
258
259
260
# File 'lib/httpx/options.rb', line 247

def options_equals?(other, ignore_ivars = REQUEST_BODY_IVARS)
  # headers and other request options do not play a role, as they are
  # relevant only for the request.
  ivars = instance_variables - ignore_ivars
  other_ivars = other.instance_variables - ignore_ivars

  return false if ivars.size != other_ivars.size

  return false if ivars.sort != other_ivars.sort

  ivars.all? do |ivar|
    instance_variable_get(ivar) == other.instance_variable_get(ivar)
  end
end

#to_hashObject



298
299
300
301
302
# File 'lib/httpx/options.rb', line 298

def to_hash
  instance_variables.each_with_object({}) do |ivar, hs|
    hs[ivar[1..-1].to_sym] = instance_variable_get(ivar)
  end
end