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
REQUEST_BODY_IVARS =
%i[@headers].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<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.



117
118
119
120
121
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
# File 'lib/httpx/options.rb', line 117

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

  freeze
end

Class Attribute Details

.options_namesObject (readonly)

Returns the value of attribute options_names.



21
22
23
# File 'lib/httpx/options.rb', line 21

def options_names
  @options_names
end

Class Method Details

.freezeObject



36
37
38
39
# File 'lib/httpx/options.rb', line 36

def freeze
  @options_names.freeze
  super
end

.inherited(klass) ⇒ Object



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

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

.method_added(meth) ⇒ Object



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

def method_added(meth)
  super

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

  optname = Regexp.last_match(1)

  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)

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

  optname = optname.to_sym

  attr_reader(optname)

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

.new(options = {}) ⇒ Object



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

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



161
162
163
# File 'lib/httpx/options.rb', line 161

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

#extend_with_plugin_classes(pl) ⇒ Object



227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
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
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
# File 'lib/httpx/options.rb', line 227

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



152
153
154
155
156
157
# File 'lib/httpx/options.rb', line 152

def freeze
  self.class.options_names.each do |ivar|
    instance_variable_get(:"@#{ivar}").freeze
  end
  super
end

#merge(other) ⇒ Object



180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
# File 'lib/httpx/options.rb', line 180

def merge(other)
  ivar_map = nil
  other_ivars = case other
                when Options
                  other.instance_variables
                else
                  other = Hash[other] unless other.is_a?(Hash)
                  ivar_map = other.keys.to_h { |k| [:"@#{k}", k] }
                  ivar_map.keys
  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

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

Returns:

  • (Boolean)


165
166
167
168
169
170
171
172
173
174
175
176
177
178
# File 'lib/httpx/options.rb', line 165

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



217
218
219
220
221
222
223
224
225
# File 'lib/httpx/options.rb', line 217

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