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 561 def self.after_fork 562 INSTANCES.each_value(&:close) 563 nil 564 end
# File lib/httpx/session.rb 469 def inherited(klass) 470 super 471 klass.instance_variable_set(:@default_options, @default_options) 472 klass.instance_variable_set(:@plugins, @plugins.dup) 473 klass.instance_variable_set(:@callbacks, @callbacks.dup) 474 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 481 def plugin(pl, options = nil, &block) 482 label = pl 483 pl = Plugins.load_plugin(pl) if pl.is_a?(Symbol) 484 raise ArgumentError, "Invalid plugin type: #{pl.class.inspect}" unless pl.is_a?(Module) 485 486 if !@plugins.include?(pl) 487 @plugins << pl 488 pl.load_dependencies(self, &block) if pl.respond_to?(:load_dependencies) 489 490 @default_options = @default_options.dup 491 492 include(pl::InstanceMethods) if defined?(pl::InstanceMethods) 493 extend(pl::ClassMethods) if defined?(pl::ClassMethods) 494 495 opts = @default_options 496 opts.extend_with_plugin_classes(pl) 497 498 if defined?(pl::OptionsMethods) 499 # when a class gets dup'ed, the #initialize_dup callbacks isn't triggered. 500 # moreover, and because #method_added does not get triggered on mixin include, 501 # the callback is also forcefully manually called here. 502 opts.options_class.instance_variable_set(:@options_names, opts.options_class.options_names.dup) 503 (pl::OptionsMethods.instance_methods + pl::OptionsMethods.private_instance_methods - Object.instance_methods).each do |meth| 504 opts.options_class.method_added(meth) 505 end 506 @default_options = opts.options_class.new(opts) 507 end 508 509 @default_options = pl.extra_options(@default_options) if pl.respond_to?(:extra_options) 510 @default_options = @default_options.merge(options) if options 511 512 if pl.respond_to?(:subplugins) 513 pl.subplugins.transform_keys(&Plugins.method(:load_plugin)).each do |main_pl, sub_pl| 514 # in case the main plugin has already been loaded, then apply subplugin functionality 515 # immediately 516 next unless @plugins.include?(main_pl) 517 518 plugin(sub_pl, options, &block) 519 end 520 end 521 522 pl.configure(self, &block) if pl.respond_to?(:configure) 523 524 if label.is_a?(Symbol) 525 # in case an already-loaded plugin complements functionality of 526 # the plugin currently being loaded, loaded it now 527 @plugins.each do |registered_pl| 528 next if registered_pl == pl 529 530 next unless registered_pl.respond_to?(:subplugins) 531 532 sub_pl = registered_pl.subplugins[label] 533 534 next unless sub_pl 535 536 plugin(sub_pl, options, &block) 537 end 538 end 539 540 @default_options.freeze 541 set_temporary_name("#{superclass}/#{pl}") if respond_to?(:set_temporary_name) # ruby 3.4 only 542 elsif options 543 # this can happen when two plugins are loaded, an one of them calls the other under the hood, 544 # albeit changing some default. 545 @default_options = pl.extra_options(@default_options) if pl.respond_to?(:extra_options) 546 @default_options = @default_options.merge(options) if options 547 548 @default_options.freeze 549 end 550 551 self 552 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 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
# 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
returns the HTTPX::Connection through which the request should be sent through.
# 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
# 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
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 connection.log(level: 2) do 125 "registering into selector##{selector.object_id}" 126 end 127 selector.register(connection) 128 end
# 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
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