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
572 def self.after_fork
573   INSTANCES.each_value(&:close)
574   nil
575 end
inherited(klass)
[show source]
    # File lib/httpx/session.rb
480 def inherited(klass)
481   super
482   klass.instance_variable_set(:@default_options, @default_options)
483   klass.instance_variable_set(:@plugins, @plugins.dup)
484   klass.instance_variable_set(:@callbacks, @callbacks.dup)
485 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
492 def plugin(pl, options = nil, &block)
493   label = pl
494   pl = Plugins.load_plugin(pl) if pl.is_a?(Symbol)
495   raise ArgumentError, "Invalid plugin type: #{pl.class.inspect}" unless pl.is_a?(Module)
496 
497   if !@plugins.include?(pl)
498     @plugins << pl
499     pl.load_dependencies(self, &block) if pl.respond_to?(:load_dependencies)
500 
501     @default_options = @default_options.dup
502 
503     include(pl::InstanceMethods) if defined?(pl::InstanceMethods)
504     extend(pl::ClassMethods) if defined?(pl::ClassMethods)
505 
506     opts = @default_options
507     opts.extend_with_plugin_classes(pl)
508 
509     if defined?(pl::OptionsMethods)
510       # when a class gets dup'ed, the #initialize_dup callbacks isn't triggered.
511       # moreover, and because #method_added does not get triggered on mixin include,
512       # the callback is also forcefully manually called here.
513       opts.options_class.instance_variable_set(:@options_names, opts.options_class.options_names.dup)
514       (pl::OptionsMethods.instance_methods + pl::OptionsMethods.private_instance_methods - Object.instance_methods).each do |meth|
515         opts.options_class.method_added(meth)
516       end
517       @default_options = opts.options_class.new(opts)
518     end
519 
520     @default_options = pl.extra_options(@default_options) if pl.respond_to?(:extra_options)
521     @default_options = @default_options.merge(options) if options
522 
523     if pl.respond_to?(:subplugins)
524       pl.subplugins.transform_keys(&Plugins.method(:load_plugin)).each do |main_pl, sub_pl|
525         # in case the main plugin has already been loaded, then apply subplugin functionality
526         # immediately
527         next unless @plugins.include?(main_pl)
528 
529         plugin(sub_pl, options, &block)
530       end
531     end
532 
533     pl.configure(self, &block) if pl.respond_to?(:configure)
534 
535     if label.is_a?(Symbol)
536       # in case an already-loaded plugin complements functionality of
537       # the plugin currently being loaded, loaded it now
538       @plugins.each do |registered_pl|
539         next if registered_pl == pl
540 
541         next unless registered_pl.respond_to?(:subplugins)
542 
543         sub_pl = registered_pl.subplugins[label]
544 
545         next unless sub_pl
546 
547         plugin(sub_pl, options, &block)
548       end
549     end
550 
551     @default_options.freeze
552     set_temporary_name("#{superclass}/#{pl}") if respond_to?(:set_temporary_name) # ruby 3.4 only
553   elsif options
554     # this can happen when two plugins are loaded, an one of them calls the other under the hood,
555     # albeit changing some default.
556     @default_options = pl.extra_options(@default_options) if pl.respond_to?(:extra_options)
557     @default_options = @default_options.merge(options) if options
558 
559     @default_options.freeze
560   end
561 
562   self
563 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
137 def deselect_connection(connection, selector, cloned = false)
138   connection.log(level: 2) do
139     "deregistering connection##{connection.object_id}(#{connection.state}) from selector##{selector.object_id}"
140   end
141   selector.deregister(connection)
142 
143   # do not check-in connections only created for Happy Eyeballs
144   return if cloned
145 
146   return if @closing && connection.state == :closed && !connection.used?
147 
148   connection.log(level: 2) { "check-in connection##{connection.object_id}(#{connection.state}) in pool##{@pool.object_id}" }
149   @pool.checkin_connection(connection)
150 end
deselect_resolver(resolver, selector)
[show source]
    # File lib/httpx/session.rb
152 def deselect_resolver(resolver, selector)
153   resolver.log(level: 2) do
154     "deregistering resolver##{resolver.object_id}(#{resolver.state}) from selector##{selector.object_id}"
155   end
156   selector.deregister(resolver)
157 
158   return if @closing && resolver.closed?
159 
160   resolver.log(level: 2) { "check-in resolver##{resolver.object_id}(#{resolver.state}) in pool##{@pool.object_id}" }
161   @pool.checkin_resolver(resolver)
162 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
180 def find_connection(request_uri, selector, options)
181   if (connection = selector.find_connection(request_uri, options))
182     connection.idling if connection.state == :closed
183     log(level: 2) { "found connection##{connection.object_id}(#{connection.state}) in selector##{selector.object_id}" }
184     return connection
185   end
186 
187   connection = @pool.checkout_connection(request_uri, options)
188 
189   log(level: 2) { "found connection##{connection.object_id}(#{connection.state}) in pool##{@pool.object_id}" }
190 
191   case connection.state
192   when :idle
193     do_init_connection(connection, selector)
194   when :open
195     if options.io
196       select_connection(connection, selector)
197     else
198       pin(connection, selector)
199     end
200   when :closing, :closed
201     connection.idling
202     if connection.addresses?
203       select_connection(connection, selector)
204     else
205       # if addresses expired, resolve again
206       resolve_connection(connection, selector)
207     end
208   else
209     pin(connection, selector)
210   end
211 
212   connection
213 end
pin(conn_or_resolver, selector)
[show source]
    # File lib/httpx/session.rb
130 def pin(conn_or_resolver, selector)
131   conn_or_resolver.current_session = self
132   conn_or_resolver.current_selector = selector
133 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   connection.log(level: 2) do
125     "registering into selector##{selector.object_id}"
126   end
127   selector.register(connection)
128 end
try_clone_connection(connection, selector, family)
[show source]
    # File lib/httpx/session.rb
164 def try_clone_connection(connection, selector, family)
165   connection.family ||= family
166 
167   return connection if connection.family == family
168 
169   new_connection = connection.class.new(connection.origin, connection.options)
170 
171   new_connection.family = family
172 
173   connection.sibling = new_connection
174 
175   do_init_connection(new_connection, selector)
176   new_connection
177 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