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 562 def self.after_fork 563 INSTANCES.each_value(&:close) 564 nil 565 end
# 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
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 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
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 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
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 begin 75 @closing = true 76 selector.terminate 77 ensure 78 @closing = false 79 end 80 end
# 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
# 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
returns the HTTPX::Connection
through which the request
should be sent through.
# 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
# File lib/httpx/session.rb 131 def pin_connection(connection, selector) 132 connection.current_session = self 133 connection.current_selector = selector 134 end
performs one, or multple requests; it accepts:
-
one or multiple
HTTPX::Request
objects; -
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 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
# File lib/httpx/session.rb 126 def select_connection(connection, selector) 127 pin_connection(connection, selector) 128 selector.register(connection) 129 end
# 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
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