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
Methods
Public Class
Public Instance
Constants
| INSTANCES | = | ObjectSpace::WeakMap.new |
Public Instance Aliases
| select_resolver | -> | select_connection |
Attributes
| default_options | [R] |
Public Class methods
# File lib/httpx/session.rb 579 def self.after_fork 580 INSTANCES.each_value(&:close) 581 nil 582 end
# 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
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.
# 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
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)
# 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
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)
# 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
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.
# 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
# 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
# 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
returns the HTTPX::Connection through which the request should be sent through.
# 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
# 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
performs one, or multple requests; it accepts:
-
one or multiple
HTTPX::Requestobjects; -
an HTTP verb, then a sequence of URIs or URI/options tuples;
-
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" })
# 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
# File lib/httpx/session.rb 122 def select_connection(connection, selector) 123 pin(connection, selector) 124 selector.register(connection) 125 end
# 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
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
# 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