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
562 def self.after_fork
563   INSTANCES.each_value(&:close)
564   nil
565 end
inherited(klass)
[show source]
    # File lib/httpx/session.rb
475 def inherited(klass)
476   super
477   klass.instance_variable_set(:@default_options, @default_options)
478   klass.instance_variable_set(:@plugins, @plugins.dup)
479   klass.instance_variable_set(:@callbacks, @callbacks.dup)
480 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
487 def plugin(pl, options = nil, &block)
488   label = pl
489   # raise Error, "Cannot add a plugin to a frozen config" if frozen?
490   pl = Plugins.load_plugin(pl) if pl.is_a?(Symbol)
491   if !@plugins.include?(pl)
492     @plugins << pl
493     pl.load_dependencies(self, &block) if pl.respond_to?(:load_dependencies)
494 
495     @default_options = @default_options.dup
496 
497     include(pl::InstanceMethods) if defined?(pl::InstanceMethods)
498     extend(pl::ClassMethods) if defined?(pl::ClassMethods)
499 
500     opts = @default_options
501     opts.extend_with_plugin_classes(pl)
502     if defined?(pl::OptionsMethods)
503 
504       (pl::OptionsMethods.instance_methods - Object.instance_methods).each do |meth|
505         opts.options_class.method_added(meth)
506       end
507       @default_options = opts.options_class.new(opts)
508     end
509 
510     @default_options = pl.extra_options(@default_options) if pl.respond_to?(:extra_options)
511     @default_options = @default_options.merge(options) if options
512 
513     if pl.respond_to?(:subplugins)
514       pl.subplugins.transform_keys(&Plugins.method(:load_plugin)).each do |main_pl, sub_pl|
515         # in case the main plugin has already been loaded, then apply subplugin functionality
516         # immediately
517         next unless @plugins.include?(main_pl)
518 
519         plugin(sub_pl, options, &block)
520       end
521     end
522 
523     pl.configure(self, &block) if pl.respond_to?(:configure)
524 
525     if label.is_a?(Symbol)
526       # in case an already-loaded plugin complements functionality of
527       # the plugin currently being loaded, loaded it now
528       @plugins.each do |registered_pl|
529         next if registered_pl == pl
530 
531         next unless registered_pl.respond_to?(:subplugins)
532 
533         sub_pl = registered_pl.subplugins[label]
534 
535         next unless sub_pl
536 
537         plugin(sub_pl, options, &block)
538       end
539     end
540 
541     @default_options.freeze
542     set_temporary_name("#{superclass}/#{pl}") if respond_to?(:set_temporary_name) # ruby 3.4 only
543   elsif options
544     # this can happen when two plugins are loaded, an one of them calls the other under the hood,
545     # albeit changing some default.
546     @default_options = pl.extra_options(@default_options) if pl.respond_to?(:extra_options)
547     @default_options = @default_options.merge(options) if options
548 
549     @default_options.freeze
550   end
551 
552   self
553 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
118 def build_request(verb, uri, params = EMPTY_HASH, options = @options)
119   rklass = options.request_class
120   request = rklass.new(verb, uri, options, params)
121   request.persistent = @persistent
122   set_request_callbacks(request)
123   request
124 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   begin
75     @closing = true
76     selector.terminate
77   ensure
78     @closing = false
79   end
80 end
deselect_connection(connection, selector, cloned = false)
[show source]
    # File lib/httpx/session.rb
138 def deselect_connection(connection, selector, cloned = false)
139   connection.log(level: 2) do
140     "deregistering connection##{connection.object_id}(#{connection.state}) from selector##{selector.object_id}"
141   end
142   selector.deregister(connection)
143 
144   # when connections coalesce
145   return if connection.state == :idle
146 
147   return if cloned
148 
149   return if @closing && connection.state == :closed
150 
151   connection.log(level: 2) { "check-in connection##{connection.object_id}(#{connection.state}) in pool##{@pool.object_id}" }
152   @pool.checkin_connection(connection)
153 end
deselect_resolver(resolver, selector)
[show source]
    # File lib/httpx/session.rb
155 def deselect_resolver(resolver, selector)
156   resolver.log(level: 2) do
157     "deregistering resolver##{resolver.object_id}(#{resolver.state}) from selector##{selector.object_id}"
158   end
159   selector.deregister(resolver)
160 
161   return if @closing && resolver.closed?
162 
163   resolver.log(level: 2) { "check-in resolver##{resolver.object_id}(#{resolver.state}) in pool##{@pool.object_id}" }
164   @pool.checkin_resolver(resolver)
165 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
183 def find_connection(request_uri, selector, options)
184   if (connection = selector.find_connection(request_uri, options))
185     connection.idling if connection.state == :closed
186     connection.log(level: 2) { "found connection##{connection.object_id}(#{connection.state}) in selector##{selector.object_id}" }
187     return connection
188   end
189 
190   connection = @pool.checkout_connection(request_uri, options)
191 
192   connection.log(level: 2) { "found connection##{connection.object_id}(#{connection.state}) in pool##{@pool.object_id}" }
193 
194   case connection.state
195   when :idle
196     do_init_connection(connection, selector)
197   when :open
198     if options.io
199       select_connection(connection, selector)
200     else
201       pin_connection(connection, selector)
202     end
203   when :closing, :closed
204     connection.idling
205     select_connection(connection, selector)
206   else
207     pin_connection(connection, selector)
208   end
209 
210   connection
211 end
pin_connection(connection, selector)
[show source]
    # File lib/httpx/session.rb
131 def pin_connection(connection, selector)
132   connection.current_session = self
133   connection.current_selector = selector
134 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
102 def request(*args, **params)
103   raise ArgumentError, "must perform at least one request" if args.empty?
104 
105   requests = args.first.is_a?(Request) ? args : build_requests(*args, params)
106   responses = send_requests(*requests)
107   return responses.first if responses.size == 1
108 
109   responses
110 end
select_connection(connection, selector)
[show source]
    # File lib/httpx/session.rb
126 def select_connection(connection, selector)
127   pin_connection(connection, selector)
128   selector.register(connection)
129 end
try_clone_connection(connection, selector, family)
[show source]
    # File lib/httpx/session.rb
167 def try_clone_connection(connection, selector, family)
168   connection.family ||= family
169 
170   return connection if connection.family == family
171 
172   new_connection = connection.class.new(connection.origin, connection.options)
173 
174   new_connection.family = family
175 
176   connection.sibling = new_connection
177 
178   do_init_connection(new_connection, selector)
179   new_connection
180 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