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
RESOLVER_TYPES =
%i[memory file].freeze
USER_AGENT =

default value used for "user-agent" header, when not overridden.

"httpx.rb/#{VERSION}".freeze
REQUEST_BODY_IVARS =
%i[@headers].freeze
RESOLVER_IVARS =
%i[
  @resolver_class @resolver_cache @resolver_options
  @resolver_native_class @resolver_system_class @resolver_https_class
].freeze

Class Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options = EMPTY_HASH) ⇒ 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::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 :http1_class :: class used to manage HTTP1 sessions :http2_class :: class used to imanage HTTP2 sessions :resolver_native_class :: class used to resolve names using pure ruby DNS implementation :resolver_system_class :: class used to resolve names using system-based (getaddrinfo) name resolution :resolver_https_class :: class used to resolve names using DoH :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 for using getaddrinfo or :https for DoH resolver, or a custom class inheriting from HTTPX::Resolver::Resolver) :resolver_cache :: strategy to cache DNS results, ignored by the :system resolver, can be set to :memory or an instance of a custom class inheriting from HTTPX::Resolver::Cache::Base :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: "https://api.serv.com&quot;) :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.



122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
# File 'lib/httpx/options.rb', line 122

def initialize(options = EMPTY_HASH)
  options_names = self.class.options_names

  defaults =
    case options
    when Options
      unknown_options = options.class.options_names - options_names

      raise Error, "unknown option: #{unknown_options.first}" unless unknown_options.empty?

      DEFAULT_OPTIONS.merge(options)
    else
      options.each_key do |k|
        raise Error, "unknown option: #{k}" unless options_names.include?(k)
      end

      options.empty? ? DEFAULT_OPTIONS : DEFAULT_OPTIONS.merge(options)
    end

  options_names.each do |k|
    v = defaults[k]

    if v.nil?
      instance_variable_set(:"@#{k}", v)

      next
    end

    value = __send__(:"option_#{k}", v)
    instance_variable_set(:"@#{k}", value)
  end

  do_initialize
  freeze
end

Class Attribute Details

.options_namesObject (readonly)

Returns the value of attribute options_names.



23
24
25
# File 'lib/httpx/options.rb', line 23

def options_names
  @options_names
end

Class Method Details

.freezeObject



38
39
40
41
# File 'lib/httpx/options.rb', line 38

def freeze
  @options_names.freeze
  super
end

.inherited(klass) ⇒ Object



25
26
27
28
# File 'lib/httpx/options.rb', line 25

def inherited(klass)
  super
  klass.instance_variable_set(:@options_names, @options_names.dup)
end

.method_added(meth) ⇒ Object



43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/httpx/options.rb', line 43

def method_added(meth)
  super

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

  optname = Regexp.last_match(1) #: String

  if optname =~ /^(.+[^_])_+with/
    # ignore alias method chain generated methods.
    # this is the case with RBS runtime tests.
    # it relies on the "_with/_without" separator, which is the most used convention,
    # however it shouldn't be used in practice in httpx given the plugin architecture
    # as the main extension API.
    orig_name = Regexp.last_match(1) #: String

    return if @options_names.include?(orig_name.to_sym)
  end

  optname = optname.to_sym

  attr_reader(optname) unless method_defined?(optname)

  @options_names << optname unless @options_names.include?(optname)
end

.new(options = {}) ⇒ Object



30
31
32
33
34
35
36
# File 'lib/httpx/options.rb', line 30

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

#connection_options_match?(other, ignore_ivars = nil) ⇒ Boolean

checks whether other matches the same connection-level options

Returns:

  • (Boolean)


206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
# File 'lib/httpx/options.rb', line 206

def connection_options_match?(other, ignore_ivars = nil)
  return true if self == other

  # headers and other request options do not play a role, as they are
  # relevant only for the request.
  ivars = instance_variables
  ivars.reject! { |iv| REQUEST_BODY_IVARS.include?(iv) }
  ivars.reject! { |iv| ignore_ivars.include?(iv) } if ignore_ivars

  other_ivars = other.instance_variables
  other_ivars.reject! { |iv| REQUEST_BODY_IVARS.include?(iv) }
  other_ivars.reject! { |iv| ignore_ivars.include?(iv) } if 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

#extend_with_plugin_classes(pl) ⇒ Object



297
298
299
300
301
302
303
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
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
# File 'lib/httpx/options.rb', line 297

def extend_with_plugin_classes(pl)
  # extend request class
  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
  # extend response class
  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
  # extend headers class
  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
  # extend request body class
  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
  # extend response body class
  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
  # extend connection pool class
  if defined?(pl::PoolMethods)
    @pool_class = @pool_class.dup
    SET_TEMPORARY_NAME[@pool_class, pl]
    @pool_class.__send__(:include, pl::PoolMethods)
  end
  # extend connection class
  if defined?(pl::ConnectionMethods)
    @connection_class = @connection_class.dup
    SET_TEMPORARY_NAME[@connection_class, pl]
    @connection_class.__send__(:include, pl::ConnectionMethods)
  end
  # extend http1 class
  if defined?(pl::HTTP1Methods)
    @http1_class = @http1_class.dup
    SET_TEMPORARY_NAME[@http1_class, pl]
    @http1_class.__send__(:include, pl::HTTP1Methods)
  end
  # extend http2 class
  if defined?(pl::HTTP2Methods)
    @http2_class = @http2_class.dup
    SET_TEMPORARY_NAME[@http2_class, pl]
    @http2_class.__send__(:include, pl::HTTP2Methods)
  end
  # extend native resolver class
  if defined?(pl::ResolverNativeMethods)
    @resolver_native_class = @resolver_native_class.dup
    SET_TEMPORARY_NAME[@resolver_native_class, pl]
    @resolver_native_class.__send__(:include, pl::ResolverNativeMethods)
  end
  # extend system resolver class
  if defined?(pl::ResolverSystemMethods)
    @resolver_system_class = @resolver_system_class.dup
    SET_TEMPORARY_NAME[@resolver_system_class, pl]
    @resolver_system_class.__send__(:include, pl::ResolverSystemMethods)
  end
  # extend https resolver class
  if defined?(pl::ResolverHTTPSMethods)
    @resolver_https_class = @resolver_https_class.dup
    SET_TEMPORARY_NAME[@resolver_https_class, pl]
    @resolver_https_class.__send__(:include, pl::ResolverHTTPSMethods)
  end

  return unless defined?(pl::OptionsMethods)

  # extend option class
  # works around lack of initialize_dup callback
  @options_class = @options_class.dup
  # (self.class.options_names)
  @options_class.__send__(:include, pl::OptionsMethods)
end

#freezeObject



188
189
190
191
192
193
194
195
196
197
198
199
200
201
# File 'lib/httpx/options.rb', line 188

def freeze
  self.class.options_names.each do |ivar|
    # avoid freezing debug option, as when it's set, it's usually an
    # object which cannot be frozen, like stderr or stdout. It's a
    # documented exception then, and still does not defeat the purpose
    # here, which is to make option objects shareable across ractors,
    # and in most cases debug should be nil, or one of the objects
    # which will eventually be shareable, like STDOUT or STDERR.
    next if ivar == :debug

    instance_variable_get(:"@#{ivar}").freeze
  end
  super
end

#merge(other) ⇒ Object

returns a HTTPX::Options instance resulting of the merging of other with self. it may return self if other is self or equal to self.



243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
# File 'lib/httpx/options.rb', line 243

def merge(other)
  if (is_options = other.is_a?(Options))

    return self if eql?(other)

    opts_names = other.class.options_names

    return self if opts_names.all? { |opt| public_send(opt) == other.public_send(opt) }

    other_opts = opts_names
  else
    other_opts = other # : Hash[Symbol, untyped]
    other_opts = Hash[other] unless other.is_a?(Hash)

    return self if other_opts.empty?

    return self if other_opts.all? { |opt, v| !respond_to?(opt) || public_send(opt) == v }
  end

  opts = dup

  other_opts.each do |opt, v|
    next unless respond_to?(opt)

    v = other.public_send(opt) if is_options
    ivar = :"@#{opt}"

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

    v = opts.__send__(:"option_#{opt}", v)

    orig_v = public_send(opt)

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

    opts.instance_variable_set(ivar, v)
  end

  opts
end

#resolver_cacheObject



168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
# File 'lib/httpx/options.rb', line 168

def resolver_cache
  cache_type = @resolver_cache

  case cache_type
  when :memory
    Resolver::Cache::Memory.cache(cache_type)
  when :file
    Resolver::Cache::File.cache(cache_type)
  else
    unless cache_type.respond_to?(:resolve) &&
           cache_type.respond_to?(:get) &&
           cache_type.respond_to?(:set) &&
           cache_type.respond_to?(:evict)
      raise TypeError, ":resolver_cache must be a compatible resolver cache and implement #get, #set and #evict"
    end

    cache_type #: Object & Resolver::_Cache
  end
end

#resolver_classObject

returns the class with which to instantiate the DNS resolver.



159
160
161
162
163
164
165
166
# File 'lib/httpx/options.rb', line 159

def resolver_class
  case @resolver_class
  when Symbol
    public_send(:"resolver_#{@resolver_class}_class")
  else
    @resolver_class
  end
end

#resolver_options_match?(other) ⇒ Boolean

checks whether other matches the same resolver-level options

Returns:

  • (Boolean)


234
235
236
237
238
239
# File 'lib/httpx/options.rb', line 234

def resolver_options_match?(other)
  self == other ||
    RESOLVER_IVARS.all? do |ivar|
      instance_variable_get(ivar) == other.instance_variable_get(ivar)
    end
end

#to_hashObject



287
288
289
290
291
292
293
294
295
# File 'lib/httpx/options.rb', line 287

def to_hash
  instance_variables.each_with_object({}) do |ivar, hs|
    val = instance_variable_get(ivar)

    next if val.nil?

    hs[ivar[1..-1].to_sym] = val
  end
end