httpx 1.0.2 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/lib/httpx/session.rb CHANGED
@@ -1,6 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module HTTPX
4
+ # Class implementing the APIs being used publicly.
5
+ #
6
+ # HTTPX.get(..) #=> delegating to an internal HTTPX::Session object.
7
+ # HTTPX.plugin(..).get(..) #=> creating an intermediate HTTPX::Session with plugin, then sending the GET request
4
8
  class Session
5
9
  include Loggable
6
10
  include Chainable
@@ -8,6 +12,10 @@ module HTTPX
8
12
 
9
13
  EMPTY_HASH = {}.freeze
10
14
 
15
+ # initializes the session with a set of +options+, which will be shared by all
16
+ # requests sent from it.
17
+ #
18
+ # When pass a block, it'll yield itself to it, then closes after the block is evaluated.
11
19
  def initialize(options = EMPTY_HASH, &blk)
12
20
  @options = self.class.default_options.merge(options)
13
21
  @responses = {}
@@ -15,6 +23,11 @@ module HTTPX
15
23
  wrap(&blk) if blk
16
24
  end
17
25
 
26
+ # Yields itself the block, then closes it after the block is evaluated.
27
+ #
28
+ # session.wrap do |http|
29
+ # http.get("https://wikipedia.com")
30
+ # end # wikipedia connection closes here
18
31
  def wrap
19
32
  begin
20
33
  prev_persistent = @persistent
@@ -26,10 +39,31 @@ module HTTPX
26
39
  end
27
40
  end
28
41
 
42
+ # closes all the active connections from the session
29
43
  def close(*args)
30
44
  pool.close(*args)
31
45
  end
32
46
 
47
+ # performs one, or multple requests; it accepts:
48
+ #
49
+ # 1. one or multiple HTTPX::Request objects;
50
+ # 2. an HTTP verb, then a sequence of URIs or URI/options tuples;
51
+ # 3. one or multiple HTTP verb / uri / (optional) options tuples;
52
+ #
53
+ # when present, the set of +options+ kwargs is applied to all of the
54
+ # sent requests.
55
+ #
56
+ # respectively returns a single HTTPX::Response response, or all of them in an Array, in the same order.
57
+ #
58
+ # resp1 = session.request(req1)
59
+ # resp1, resp2 = session.request(req1, req2)
60
+ # resp1 = session.request("GET", "https://server.org/a")
61
+ # resp1, resp2 = session.request("GET", ["https://server.org/a", "https://server.org/b"])
62
+ # resp1, resp2 = session.request(["GET", "https://server.org/a"], ["GET", "https://server.org/b"])
63
+ # resp1 = session.request("POST", "https://server.org/a", form: { "foo" => "bar" })
64
+ # resp1, resp2 = session.request(["POST", "https://server.org/a", form: { "foo" => "bar" }], ["GET", "https://server.org/b"])
65
+ # resp1, resp2 = session.request("GET", ["https://server.org/a", "https://server.org/b"], headers: { "x-api-token" => "TOKEN" })
66
+ #
33
67
  def request(*args, **options)
34
68
  raise ArgumentError, "must perform at least one request" if args.empty?
35
69
 
@@ -40,10 +74,17 @@ module HTTPX
40
74
  responses
41
75
  end
42
76
 
77
+ # returns a HTTP::Request instance built from the HTTP +verb+, the request +uri+, and
78
+ # the optional set of request-specific +options+. This request **must** be sent through
79
+ # the same session it was built from.
80
+ #
81
+ # req = session.build_request("GET", "https://server.com")
82
+ # resp = session.request(req)
43
83
  def build_request(verb, uri, options = EMPTY_HASH)
44
84
  rklass = @options.request_class
45
85
  options = @options.merge(options) unless options.is_a?(Options)
46
- request = rklass.new(verb, uri, options.merge(persistent: @persistent))
86
+ request = rklass.new(verb, uri, options)
87
+ request.persistent = @persistent
47
88
  request.on(:response, &method(:on_response).curry(2)[request])
48
89
  request.on(:promise, &method(:on_promise))
49
90
 
@@ -76,23 +117,29 @@ module HTTPX
76
117
 
77
118
  private
78
119
 
120
+ # returns the HTTPX::Pool object which manages the networking required to
121
+ # perform requests.
79
122
  def pool
80
123
  Thread.current[:httpx_connection_pool] ||= Pool.new
81
124
  end
82
125
 
126
+ # callback executed when a response for a given request has been received.
83
127
  def on_response(request, response)
84
128
  @responses[request] = response
85
129
  end
86
130
 
131
+ # callback executed when an HTTP/2 promise frame has been received.
87
132
  def on_promise(_, stream)
88
133
  log(level: 2) { "#{stream.id}: refusing stream!" }
89
134
  stream.refuse
90
135
  end
91
136
 
137
+ # returns the corresponding HTTP::Response to the given +request+ if it has been received.
92
138
  def fetch_response(request, _, _)
93
139
  @responses.delete(request)
94
140
  end
95
141
 
142
+ # returns the HTTPX::Connection through which the +request+ should be sent through.
96
143
  def find_connection(request, connections, options)
97
144
  uri = request.uri
98
145
 
@@ -104,6 +151,8 @@ module HTTPX
104
151
  connection
105
152
  end
106
153
 
154
+ # sets the callbacks on the +connection+ required to process certain specific
155
+ # connection lifecycle events which deal with request rerouting.
107
156
  def set_connection_callbacks(connection, connections, options)
108
157
  connection.only(:misdirected) do |misdirected_request|
109
158
  other_connection = connection.create_idle(ssl: { alpn_protocols: %w[http/1.1] })
@@ -131,6 +180,7 @@ module HTTPX
131
180
  end
132
181
  end
133
182
 
183
+ # returns an HTTPX::Connection for the negotiated Alternative Service (or none).
134
184
  def build_altsvc_connection(existing_connection, connections, alt_origin, origin, alt_params, options)
135
185
  # do not allow security downgrades on altsvc negotiation
136
186
  return if existing_connection.origin.scheme == "https" && alt_origin.scheme != "https"
@@ -166,6 +216,7 @@ module HTTPX
166
216
  nil
167
217
  end
168
218
 
219
+ # returns a set of HTTPX::Request objects built from the given +args+ and +options+.
169
220
  def build_requests(*args, options)
170
221
  request_options = @options.merge(options)
171
222
 
@@ -189,6 +240,7 @@ module HTTPX
189
240
  requests
190
241
  end
191
242
 
243
+ # returns a new HTTPX::Connection object for the given +uri+ and set of +options+.
192
244
  def build_connection(uri, options)
193
245
  type = options.transport || begin
194
246
  case uri.scheme
@@ -216,11 +268,13 @@ module HTTPX
216
268
  end
217
269
  end
218
270
 
271
+ # sends an array of HTTPX::Request +requests+, returns the respective array of HTTPX::Response objects.
219
272
  def send_requests(*requests)
220
273
  connections = _send_requests(requests)
221
274
  receive_requests(requests, connections)
222
275
  end
223
276
 
277
+ # sends an array of HTTPX::Request objects
224
278
  def _send_requests(requests)
225
279
  connections = []
226
280
 
@@ -237,6 +291,7 @@ module HTTPX
237
291
  connections
238
292
  end
239
293
 
294
+ # returns the array of HTTPX::Response objects corresponding to the array of HTTPX::Request +requests+.
240
295
  def receive_requests(requests, connections)
241
296
  # @type var responses: Array[response]
242
297
  responses = []
@@ -290,6 +345,11 @@ module HTTPX
290
345
  klass.instance_variable_set(:@callbacks, @callbacks.dup)
291
346
  end
292
347
 
348
+ # returns a new HTTPX::Session instance, with the plugin pointed by +pl+ loaded.
349
+ #
350
+ # session_with_retries = session.plugin(:retries)
351
+ # session_with_custom = session.plugin(CustomPlugin)
352
+ #
293
353
  def plugin(pl, options = nil, &block)
294
354
  # raise Error, "Cannot add a plugin to a frozen config" if frozen?
295
355
  pl = Plugins.load_plugin(pl) if pl.is_a?(Symbol)
data/lib/httpx/timers.rb CHANGED
@@ -6,20 +6,25 @@ module HTTPX
6
6
  @intervals = []
7
7
  end
8
8
 
9
- def after(interval_in_secs, &blk)
9
+ def after(interval_in_secs, cb = nil, &blk)
10
10
  return unless interval_in_secs
11
11
 
12
+ callback = cb || blk
13
+
12
14
  # I'm assuming here that most requests will have the same
13
15
  # request timeout, as in most cases they share common set of
14
16
  # options. A user setting different request timeouts for 100s of
15
17
  # requests will already have a hard time dealing with that.
16
- unless (interval = @intervals.find { |t| t == interval_in_secs })
18
+ unless (interval = @intervals.find { |t| t.interval == interval_in_secs })
17
19
  interval = Interval.new(interval_in_secs)
20
+ interval.on_empty { @intervals.delete(interval) }
18
21
  @intervals << interval
19
22
  @intervals.sort!
20
23
  end
21
24
 
22
- interval << blk
25
+ interval << callback
26
+
27
+ interval
23
28
  end
24
29
 
25
30
  def wait_interval
@@ -41,11 +46,6 @@ module HTTPX
41
46
  @next_interval_at = nil if @intervals.empty?
42
47
  end
43
48
 
44
- def cancel
45
- @next_interval_at = nil
46
- @intervals.clear
47
- end
48
-
49
49
  class Interval
50
50
  include Comparable
51
51
 
@@ -54,6 +54,11 @@ module HTTPX
54
54
  def initialize(interval)
55
55
  @interval = interval
56
56
  @callbacks = []
57
+ @on_empty = nil
58
+ end
59
+
60
+ def on_empty(&blk)
61
+ @on_empty = blk
57
62
  end
58
63
 
59
64
  def <=>(other)
@@ -74,6 +79,26 @@ module HTTPX
74
79
  @callbacks << callback
75
80
  end
76
81
 
82
+ if RUBY_ENGINE == "jruby" && JRUBY_VERSION < "9.4.5.0"
83
+ # https://github.com/jruby/jruby/issues/7976
84
+ def delete(callback)
85
+ @callbacks.delete(callback)
86
+ end
87
+ else
88
+ def delete(callback)
89
+ @callbacks.delete(callback)
90
+ @on_empty.call if @callbacks.empty?
91
+ end
92
+ end
93
+
94
+ def no_callbacks?
95
+ @callbacks.empty?
96
+ end
97
+
98
+ def elapsed?
99
+ @interval <= 0
100
+ end
101
+
77
102
  def elapse(elapsed)
78
103
  @interval -= elapsed
79
104
 
@@ -6,7 +6,7 @@ module HTTPX::Transcoder
6
6
  module JSON
7
7
  module_function
8
8
 
9
- JSON_REGEX = %r{\bapplication/(?:vnd\.api\+)?json\b}i.freeze
9
+ JSON_REGEX = %r{\bapplication/(?:vnd\.api\+|hal\+)?json\b}i.freeze
10
10
 
11
11
  class Encoder
12
12
  extend Forwardable
@@ -0,0 +1,19 @@
1
+ module HTTPX
2
+ module Transcoder
3
+ class Inflater
4
+ def initialize(bytesize)
5
+ @bytesize = bytesize
6
+ end
7
+
8
+ def call(chunk)
9
+ buffer = @inflater.inflate(chunk)
10
+ @bytesize -= chunk.bytesize
11
+ if @bytesize <= 0
12
+ buffer << @inflater.finish
13
+ @inflater.close
14
+ end
15
+ buffer
16
+ end
17
+ end
18
+ end
19
+ end
data/lib/httpx/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module HTTPX
4
- VERSION = "1.0.2"
4
+ VERSION = "1.1.0"
5
5
  end
@@ -53,7 +53,7 @@ module HTTPX
53
53
 
54
54
  def ping: () -> void
55
55
 
56
- def timeout: () -> Numeric
56
+ def timeout: () -> Numeric?
57
57
 
58
58
  private
59
59
 
@@ -38,7 +38,7 @@ module HTTPX
38
38
 
39
39
  alias reset init_connection
40
40
 
41
- def timeout: () -> Numeric
41
+ def timeout: () -> Numeric?
42
42
 
43
43
  private
44
44
 
data/sig/connection.rbs CHANGED
@@ -40,6 +40,7 @@ module HTTPX
40
40
  @parser: HTTP1 | HTTP2 | _Parser
41
41
  @connected_at: Float
42
42
  @response_received_at: Float
43
+ @intervals: Array[Timers::Interval]
43
44
 
44
45
  def addresses: () -> Array[ipaddr]?
45
46
 
@@ -89,7 +90,7 @@ module HTTPX
89
90
 
90
91
  def open?: () -> bool
91
92
 
92
- def raise_timeout_error: (Numeric interval) -> void
93
+ def handle_socket_timeout: (Numeric interval) -> void
93
94
 
94
95
  private
95
96
 
@@ -129,6 +130,8 @@ module HTTPX
129
130
 
130
131
  def read_timeout_callback: (Request request, Numeric read_timeout, ?singleton(RequestTimeoutError) error_type) -> void
131
132
 
133
+ def set_request_timeout: (Request request, Numeric timeout, Symbol start_event, Symbol | Array[Symbol] finish_events) { () -> void } -> void
134
+
132
135
  def self.parser_type: (String protocol) -> (singleton(HTTP1) | singleton(HTTP2))
133
136
  end
134
137
  end
data/sig/io/tcp.rbs CHANGED
@@ -2,7 +2,7 @@ module HTTPX
2
2
  class TCP
3
3
  include Loggable
4
4
 
5
- attr_reader ip: IPAddr?
5
+ attr_reader ip: ipaddr?
6
6
 
7
7
  attr_reader port: Integer
8
8
 
data/sig/options.rbs CHANGED
@@ -12,7 +12,7 @@ module HTTPX
12
12
  DEFAULT_OPTIONS: Hash[Symbol, untyped]
13
13
 
14
14
  type timeout_type = :connect_timeout | :settings_timeout | :operation_timeout | :keep_alive_timeout | :read_timeout | :write_timeout | :request_timeout
15
- type timeout = Hash[timeout_type, Numeric]
15
+ type timeout = Hash[timeout_type, Numeric?]
16
16
 
17
17
  def self.new: (?options) -> instance
18
18
 
@@ -32,7 +32,7 @@ module HTTPX
32
32
  attr_reader max_concurrent_requests: Integer?
33
33
 
34
34
  # max_requests
35
- attr_reader max_requests: Integer?
35
+ attr_reader max_requests: Numeric?
36
36
 
37
37
  # window_size
38
38
  attr_reader window_size: Integer
data/sig/pool.rbs CHANGED
@@ -45,7 +45,7 @@ module HTTPX
45
45
 
46
46
  def coalesce_connections: (Connection coalescable, Connection coalescing) -> void
47
47
 
48
- def next_timeout: () -> (Integer | Float | nil)
48
+ def next_timeout: () -> Numeric?
49
49
 
50
50
  def find_resolver_for: (Connection) { (Resolver::Resolver resolver) -> void } -> resolver_manager
51
51
  end
data/sig/request/body.rbs CHANGED
@@ -1,7 +1,5 @@
1
1
  module HTTPX
2
2
  class Request::Body
3
- attr_reader threshold_size: Integer?
4
-
5
3
  @headers: Headers
6
4
  @body: body_encoder?
7
5
  @unbounded_body: bool
data/sig/request.rbs CHANGED
@@ -15,6 +15,10 @@ module HTTPX
15
15
  attr_reader response: response?
16
16
  attr_reader drain_error: StandardError?
17
17
 
18
+ attr_accessor peer_address: ipaddr?
19
+
20
+ attr_writer persistent: bool
21
+
18
22
  @trailers: Headers?
19
23
  @informational_status: Integer?
20
24
  @query: String?
@@ -50,11 +54,13 @@ module HTTPX
50
54
 
51
55
  def trailers?: () -> boolish
52
56
 
53
- def read_timeout: () -> Numeric
57
+ def persistent?: () -> bool
58
+
59
+ def read_timeout: () -> Numeric?
54
60
 
55
- def write_timeout: () -> Numeric
61
+ def write_timeout: () -> Numeric?
56
62
 
57
- def request_timeout: () -> Numeric
63
+ def request_timeout: () -> Numeric?
58
64
 
59
65
  private
60
66
 
@@ -34,7 +34,7 @@ module HTTPX
34
34
 
35
35
  def timeout: () -> Numeric?
36
36
 
37
- def raise_timeout_error: (Numeric interval) -> void
37
+ def handle_socket_timeout: (Numeric interval) -> void
38
38
 
39
39
  private
40
40
 
data/sig/resolver.rbs CHANGED
@@ -2,7 +2,7 @@ module HTTPX
2
2
  type ipaddr = IPAddr | String
3
3
 
4
4
  module Resolver
5
- RESOLVE_TIMEOUT: Integer | Float
5
+ RESOLVE_TIMEOUT: Integer
6
6
 
7
7
  @lookup_mutex: Thread::Mutex
8
8
 
@@ -11,7 +11,6 @@ module HTTPX
11
11
  @headers: Headers
12
12
  @options: Options
13
13
  @state: :idle | :memory | :buffer | :closed
14
- @threshold_size: Integer
15
14
  @window_size: Integer
16
15
  @length: Integer
17
16
  @buffer: StringIO | Tempfile | nil
data/sig/response.rbs CHANGED
@@ -25,22 +25,28 @@ module HTTPX
25
25
  @content_type: ContentType
26
26
 
27
27
  def copy_to: (_ToPath | _Writer destination) -> void
28
+
28
29
  def close: () -> void
30
+
29
31
  def uri: () -> URI::Generic
30
32
 
33
+ def peer_address: () -> ipaddr?
34
+
31
35
  def merge_headers: (_Each[[String, headers_value]]) -> void
36
+
32
37
  def bodyless?: () -> bool
38
+
33
39
  def content_type: () -> ContentType
40
+
34
41
  def complete?: () -> bool
35
42
 
36
43
  def json: (?json_options opts) -> untyped
37
44
 
38
45
  def form: () -> Hash[String, untyped]
39
46
 
40
- private
47
+ def initialize: (Request request, String | Integer status, String version, headers?) -> void
41
48
 
42
- def initialize: (Request request, String | Integer status, String version, headers?) -> untyped
43
- def no_data?: () -> bool
49
+ private
44
50
 
45
51
  def decode:(Transcoder::_Decode transcoder, ?untyped options) -> untyped
46
52
  end
@@ -78,6 +84,8 @@ module HTTPX
78
84
 
79
85
  def uri: () -> URI::Generic
80
86
 
87
+ def peer_address: () -> ipaddr?
88
+
81
89
  def close: () -> void
82
90
 
83
91
  private
data/sig/timers.rbs CHANGED
@@ -3,30 +3,40 @@ module HTTPX
3
3
  @intervals: Array[Interval]
4
4
  @next_interval_at: Float
5
5
 
6
- def after: (Numeric interval_in_secs) { () -> void } -> void
6
+ def after: (Numeric interval_in_secs, ^() -> void) -> Interval
7
+ | (Numeric interval_in_secs) { () -> void } -> Interval
7
8
 
8
9
  def wait_interval: () -> Numeric?
9
10
 
10
11
  def fire: (?TimeoutError error) -> void
11
12
 
12
- def cancel: () -> void
13
-
14
- private
15
-
16
13
  def initialize: () -> void
17
14
 
18
15
  class Interval
19
16
  include Comparable
20
17
 
18
+ type callback = ^() -> void
19
+
21
20
  attr_reader interval: Numeric
22
21
 
23
- @callbacks: Array[^() -> void]
22
+ @callbacks: Array[callback]
23
+ @on_empty: callback?
24
+
25
+
26
+ def on_empty: () { () -> void } -> void
24
27
 
25
28
  def to_f: () -> Float
26
29
 
27
- def <<: (^() -> void) -> void
30
+ def <<: (callback) -> void
31
+
32
+ def delete: (callback) -> void
28
33
 
29
34
  def elapse: (Numeric elapsed) -> Numeric
35
+
36
+ def elapsed?: () -> bool
37
+
38
+ def no_callbacks?: () -> bool
39
+
30
40
  private
31
41
 
32
42
  def initialize: (Numeric interval) -> void
@@ -0,0 +1,12 @@
1
+ module HTTPX
2
+ module Transcoder
3
+ class Inflater
4
+ @inflater: Zlib::Inflate
5
+ @bytesize: Integer
6
+
7
+ def initialize: (Integer | Float bytesize) -> void
8
+
9
+ def call: (String chunk) -> String
10
+ end
11
+ end
12
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: httpx
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.2
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tiago Cardoso
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-10-13 00:00:00.000000000 Z
11
+ date: 2023-10-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: http-2-next
@@ -131,6 +131,7 @@ extra_rdoc_files:
131
131
  - doc/release_notes/1_0_0.md
132
132
  - doc/release_notes/1_0_1.md
133
133
  - doc/release_notes/1_0_2.md
134
+ - doc/release_notes/1_1_0.md
134
135
  files:
135
136
  - LICENSE.txt
136
137
  - README.md
@@ -233,6 +234,7 @@ files:
233
234
  - doc/release_notes/1_0_0.md
234
235
  - doc/release_notes/1_0_1.md
235
236
  - doc/release_notes/1_0_2.md
237
+ - doc/release_notes/1_1_0.md
236
238
  - lib/httpx.rb
237
239
  - lib/httpx/adapters/datadog.rb
238
240
  - lib/httpx/adapters/faraday.rb
@@ -334,6 +336,7 @@ files:
334
336
  - lib/httpx/transcoder/multipart/part.rb
335
337
  - lib/httpx/transcoder/utils/body_reader.rb
336
338
  - lib/httpx/transcoder/utils/deflater.rb
339
+ - lib/httpx/transcoder/utils/inflater.rb
337
340
  - lib/httpx/transcoder/xml.rb
338
341
  - lib/httpx/utils.rb
339
342
  - lib/httpx/version.rb
@@ -416,6 +419,7 @@ files:
416
419
  - sig/transcoder/multipart.rbs
417
420
  - sig/transcoder/utils/body_reader.rbs
418
421
  - sig/transcoder/utils/deflater.rbs
422
+ - sig/transcoder/utils/inflater.rbs
419
423
  - sig/transcoder/xml.rbs
420
424
  - sig/utils.rbs
421
425
  homepage: https://gitlab.com/os85/httpx