class HTTPX::Session

  1. lib/httpx/session.rb
Superclass: Object

Class implementing the APIs being used publicly.

HTTPX.get(..) #=> delegating to an internal HTTPX::Session object.
HTTPX.plugin(..).get(..) #=> creating an intermediate HTTPX::Session with plugin, then sending the GET request

Included modules

  1. Loggable
  2. Chainable

Constants

INSTANCES = ObjectSpace::WeakMap.new  

Public Instance Aliases

select_resolver -> select_connection

Attributes

Public Class methods

after_fork()
[show source]
    # File lib/httpx/session.rb
579 def self.after_fork
580   INSTANCES.each_value(&:close)
581   nil
582 end
inherited(klass)
[show source]
    # File lib/httpx/session.rb
486 def inherited(klass)
487   super
488   klass.instance_variable_set(:@default_options, @default_options)
489   klass.instance_variable_set(:@plugins, @plugins.dup)
490   klass.instance_variable_set(:@callbacks, @callbacks.dup)
491 end
new(options = EMPTY_HASH, &blk)

initializes the session with a set of options, which will be shared by all requests sent from it.

When pass a block, it’ll yield itself to it, then closes after the block is evaluated.

[show source]
   # File lib/httpx/session.rb
16 def initialize(options = EMPTY_HASH, &blk)
17   @options = self.class.default_options.merge(options)
18   @persistent = @options.persistent
19   @pool = @options.pool_class.new(@options.pool_options)
20   @wrapped = false
21   @closing = false
22   INSTANCES[self] = self if @persistent && @options.close_on_fork && INSTANCES
23   wrap(&blk) if blk
24 end
plugin(pl, options = nil, &block)

returns a new HTTPX::Session instance, with the plugin pointed by pl loaded.

session_with_retries = session.plugin(:retries)
session_with_custom = session.plugin(CustomPlugin)
[show source]
    # File lib/httpx/session.rb
498 def plugin(pl, options = nil, &block)
499   label = pl
500   # raise Error, "Cannot add a plugin to a frozen config" if frozen?
501   pl = Plugins.load_plugin(pl) if pl.is_a?(Symbol)
502   raise ArgumentError, "Invalid plugin type: #{pl.class.inspect}" unless pl.is_a?(Module)
503 
504   if !@plugins.include?(pl)
505     @plugins << pl
506     pl.load_dependencies(self, &block) if pl.respond_to?(:load_dependencies)
507 
508     @default_options = @default_options.dup
509 
510     include(pl::InstanceMethods) if defined?(pl::InstanceMethods)
511     extend(pl::ClassMethods) if defined?(pl::ClassMethods)
512 
513     opts = @default_options
514     opts.extend_with_plugin_classes(pl)
515 
516     if defined?(pl::OptionsMethods)
517       # when a class gets dup'ed, the #initialize_dup callbacks isn't triggered.
518       # moreover, and because #method_added does not get triggered on mixin include,
519       # the callback is also forcefully manually called here.
520       opts.options_class.instance_variable_set(:@options_names, opts.options_class.options_names.dup)
521       (pl::OptionsMethods.instance_methods + pl::OptionsMethods.private_instance_methods - Object.instance_methods).each do |meth|
522         opts.options_class.method_added(meth)
523       end
524       @default_options = opts.options_class.new(opts)
525     end
526 
527     @default_options = pl.extra_options(@default_options) if pl.respond_to?(:extra_options)
528     @default_options = @default_options.merge(options) if options
529 
530     if pl.respond_to?(:subplugins)
531       pl.subplugins.transform_keys(&Plugins.method(:load_plugin)).each do |main_pl, sub_pl|
532         # in case the main plugin has already been loaded, then apply subplugin functionality
533         # immediately
534         next unless @plugins.include?(main_pl)
535 
536         plugin(sub_pl, options, &block)
537       end
538     end
539 
540     pl.configure(self, &block) if pl.respond_to?(:configure)
541 
542     if label.is_a?(Symbol)
543       # in case an already-loaded plugin complements functionality of
544       # the plugin currently being loaded, loaded it now
545       @plugins.each do |registered_pl|
546         next if registered_pl == pl
547 
548         next unless registered_pl.respond_to?(:subplugins)
549 
550         sub_pl = registered_pl.subplugins[label]
551 
552         next unless sub_pl
553 
554         plugin(sub_pl, options, &block)
555       end
556     end
557 
558     @default_options.freeze
559     set_temporary_name("#{superclass}/#{pl}") if respond_to?(:set_temporary_name) # ruby 3.4 only
560   elsif options
561     # this can happen when two plugins are loaded, an one of them calls the other under the hood,
562     # albeit changing some default.
563     @default_options = pl.extra_options(@default_options) if pl.respond_to?(:extra_options)
564     @default_options = @default_options.merge(options) if options
565 
566     @default_options.freeze
567   end
568 
569   self
570 end

Public Instance methods

build_request(verb, uri, params = EMPTY_HASH, options = @options)

returns a HTTP::Request instance built from the HTTP verb, the request uri, and the optional set of request-specific options. This request must be sent through the same session it was built from.

req = session.build_request("GET", "https://server.com")
resp = session.request(req)
[show source]
    # File lib/httpx/session.rb
114 def build_request(verb, uri, params = EMPTY_HASH, options = @options)
115   rklass = options.request_class
116   request = rklass.new(verb, uri, options, params)
117   request.persistent = @persistent
118   set_request_callbacks(request)
119   request
120 end
close(selector = Selector.new)

closes all the active connections from the session.

when called directly without specifying selector, all available connections will be picked up from the connection pool and closed. Connections in use by other sessions, or same session in a different thread, will not be reaped.

[show source]
   # File lib/httpx/session.rb
64 def close(selector = Selector.new)
65   # throw resolvers away from the pool
66   @pool.reset_resolvers
67 
68   # preparing to throw away connections
69   while (connection = @pool.pop_connection)
70     next if connection.state == :closed
71 
72     select_connection(connection, selector)
73   end
74 
75   selector_close(selector)
76 end
deselect_connection(connection, selector, cloned = false)
[show source]
    # File lib/httpx/session.rb
134 def deselect_connection(connection, selector, cloned = false)
135   connection.log(level: 2) do
136     "deregistering connection##{connection.object_id}(#{connection.state}) from selector##{selector.object_id}"
137   end
138   selector.deregister(connection)
139 
140   # when connections coalesce
141   return if connection.state == :idle
142 
143   return if cloned
144 
145   return if @closing && connection.state == :closed
146 
147   connection.log(level: 2) { "check-in connection##{connection.object_id}(#{connection.state}) in pool##{@pool.object_id}" }
148   @pool.checkin_connection(connection)
149 end
deselect_resolver(resolver, selector)
[show source]
    # File lib/httpx/session.rb
151 def deselect_resolver(resolver, selector)
152   resolver.log(level: 2) do
153     "deregistering resolver##{resolver.object_id}(#{resolver.state}) from selector##{selector.object_id}"
154   end
155   selector.deregister(resolver)
156 
157   return if @closing && resolver.closed?
158 
159   resolver.log(level: 2) { "check-in resolver##{resolver.object_id}(#{resolver.state}) in pool##{@pool.object_id}" }
160   @pool.checkin_resolver(resolver)
161 end
find_connection(request_uri, selector, options)

returns the HTTPX::Connection through which the request should be sent through.

[show source]
    # File lib/httpx/session.rb
179 def find_connection(request_uri, selector, options)
180   if (connection = selector.find_connection(request_uri, options))
181     connection.idling if connection.state == :closed
182     connection.log(level: 2) { "found connection##{connection.object_id}(#{connection.state}) in selector##{selector.object_id}" }
183     return connection
184   end
185 
186   connection = @pool.checkout_connection(request_uri, options)
187 
188   connection.log(level: 2) { "found connection##{connection.object_id}(#{connection.state}) in pool##{@pool.object_id}" }
189 
190   case connection.state
191   when :idle
192     do_init_connection(connection, selector)
193   when :open
194     if options.io
195       select_connection(connection, selector)
196     else
197       pin(connection, selector)
198     end
199   when :closing, :closed
200     connection.idling
201     if connection.addresses?
202       select_connection(connection, selector)
203     else
204       # if addresses expired, resolve again
205       resolve_connection(connection, selector)
206     end
207   else
208     pin(connection, selector)
209   end
210 
211   connection
212 end
pin(conn_or_resolver, selector)
[show source]
    # File lib/httpx/session.rb
127 def pin(conn_or_resolver, selector)
128   conn_or_resolver.current_session = self
129   conn_or_resolver.current_selector = selector
130 end
request(*args, **params)

performs one, or multple requests; it accepts:

  1. one or multiple HTTPX::Request objects;

  2. an HTTP verb, then a sequence of URIs or URI/options tuples;

  3. one or multiple HTTP verb / uri / (optional) options tuples;

when present, the set of options kwargs is applied to all of the sent requests.

respectively returns a single HTTPX::Response response, or all of them in an Array, in the same order.

resp1 = session.request(req1)
resp1, resp2 = session.request(req1, req2)
resp1 = session.request("GET", "https://server.org/a")
resp1, resp2 = session.request("GET", ["https://server.org/a", "https://server.org/b"])
resp1, resp2 = session.request(["GET", "https://server.org/a"], ["GET", "https://server.org/b"])
resp1 = session.request("POST", "https://server.org/a", form: { "foo" => "bar" })
resp1, resp2 = session.request(["POST", "https://server.org/a", form: { "foo" => "bar" }], ["GET", "https://server.org/b"])
resp1, resp2 = session.request("GET", ["https://server.org/a", "https://server.org/b"], headers: { "x-api-token" => "TOKEN" })
[show source]
    # File lib/httpx/session.rb
 98 def request(*args, **params)
 99   raise ArgumentError, "must perform at least one request" if args.empty?
100 
101   requests = args.first.is_a?(Request) ? args : build_requests(*args, params)
102   responses = send_requests(*requests)
103   return responses.first if responses.size == 1
104 
105   responses
106 end
select_connection(connection, selector)
[show source]
    # File lib/httpx/session.rb
122 def select_connection(connection, selector)
123   pin(connection, selector)
124   selector.register(connection)
125 end
try_clone_connection(connection, selector, family)
[show source]
    # File lib/httpx/session.rb
163 def try_clone_connection(connection, selector, family)
164   connection.family ||= family
165 
166   return connection if connection.family == family
167 
168   new_connection = connection.class.new(connection.origin, connection.options)
169 
170   new_connection.family = family
171 
172   connection.sibling = new_connection
173 
174   do_init_connection(new_connection, selector)
175   new_connection
176 end
wrap()

Yields itself the block, then closes it after the block is evaluated.

session.wrap do |http|
  http.get("https://wikipedia.com")
end # wikipedia connection closes here
[show source]
   # File lib/httpx/session.rb
31 def wrap
32   prev_wrapped = @wrapped
33   @wrapped = true
34   was_initialized = false
35   current_selector = get_current_selector do
36     selector = Selector.new
37 
38     set_current_selector(selector)
39 
40     was_initialized = true
41 
42     selector
43   end
44   begin
45     yield self
46   ensure
47     unless prev_wrapped
48       if @persistent
49         deactivate(current_selector)
50       else
51         close(current_selector)
52       end
53     end
54     @wrapped = prev_wrapped
55     set_current_selector(nil) if was_initialized
56   end
57 end