httpx 0.18.6 → 0.18.7

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b6784fbd393b5caed888e6c4d1a421cde9c7aca49f35baaafbf13f10a68e9d02
4
- data.tar.gz: ddd5bb8793fa72b19e462b411f5eaaaf553f30d589a68dfdde64224c49d902f6
3
+ metadata.gz: ab9b9577b0e266b140e21030307b3f2436af346a13f1e0931af05020ba42f7e6
4
+ data.tar.gz: c5d043448ef41b0532c08d1622540d80c68511ef59cffcc810a6f8bd92f348d5
5
5
  SHA512:
6
- metadata.gz: 949f18741f10eaa57463b8764569e69cdfb1ca6f4eefa30bbdcf142cbbd8b61b360d9bd78898a816a4ebd9e15d4ffd1c0d8282033a459826919ce8132d5b105f
7
- data.tar.gz: e4430eea967c9f5628d51e6150bc4f63e704eaed2bd61fda191f3539a3f102e00932cc8f63ddb735c33bbf21e2694bb23fed5eaea588159e71fa6f484016c150
6
+ metadata.gz: 8b4ec58f2ab031d23f28a86bc47b40d0f0d79eea9aea64590b0cc3d07269738513de1364608e0c0282a6956d816b78b900520dbad4daab1f73522b8b7f73d138
7
+ data.tar.gz: f2d2c297b530bfb3567ebcd1678bad1d729187dd75c9107839c925def1a708c4648fe80df614a01af02bce309594e256e274e042f3c6b8347182aebff1df9935
@@ -3,8 +3,8 @@
3
3
  ## Improvements
4
4
 
5
5
  * ruby 3.1 is now officially supported.
6
- * when user sets a `Host` header for an HTTP/2 request, this will be used in the `:authority` HTTP/2 pseudo-header, instead of silently ignored (mimicking what "curl" does).
6
+ * when a user sets a `Host` header for an HTTP/2 request, this will be used in the `:authority` HTTP/2 pseudo-header, instead of silently ignored (mimicking what "curl" does).
7
7
 
8
8
  ## Bugfixes
9
9
 
10
- * fixed "throw outside of catch block" error happening when pipelining requests on an HTTP/1 connnection and resulting in a timeout.
10
+ * fixed "throw outside of catch block" error happening when pipelining requests on an HTTP/1 connection and resulting in a timeout.
@@ -0,0 +1,5 @@
1
+ # 0.18.6
2
+
3
+ ## Bugfixes
4
+
5
+ * multipart plugin: fixed `filemagic` integration by rewinding the file after mime-type detection.
@@ -297,10 +297,6 @@ module HTTPX
297
297
  extra_headers
298
298
  end
299
299
 
300
- def headline_uri(request)
301
- request.path
302
- end
303
-
304
300
  def handle(request)
305
301
  catch(:buffer_full) do
306
302
  request.transition(:headers)
@@ -314,8 +310,12 @@ module HTTPX
314
310
  end
315
311
  end
316
312
 
313
+ def join_headline(request)
314
+ "#{request.verb.to_s.upcase} #{request.path} HTTP/#{@version.join(".")}"
315
+ end
316
+
317
317
  def join_headers(request)
318
- headline = "#{request.verb.to_s.upcase} #{headline_uri(request)} HTTP/#{@version.join(".")}"
318
+ headline = join_headline(request)
319
319
  @buffer << headline << CRLF
320
320
  log(color: :yellow) { "<- HEADLINE: #{headline.chomp.inspect}" }
321
321
  extra_headers = set_protocol_headers(request)
@@ -158,10 +158,6 @@ module HTTPX
158
158
  end
159
159
  end
160
160
 
161
- def headline_uri(request)
162
- request.path
163
- end
164
-
165
161
  def handle(request, stream)
166
162
  catch(:buffer_full) do
167
163
  request.transition(:headers)
@@ -213,7 +209,7 @@ module HTTPX
213
209
  {
214
210
  ":scheme" => request.scheme,
215
211
  ":method" => request.verb.to_s.upcase,
216
- ":path" => headline_uri(request),
212
+ ":path" => request.path,
217
213
  ":authority" => request.authority,
218
214
  }
219
215
  end
@@ -44,7 +44,7 @@ module HTTPX
44
44
 
45
45
  def_delegator :@write_buffer, :empty?
46
46
 
47
- attr_reader :origin, :state, :pending, :options
47
+ attr_reader :origin, :origins, :state, :pending, :options
48
48
 
49
49
  attr_writer :timers
50
50
 
@@ -489,6 +489,18 @@ module HTTPX
489
489
  end
490
490
 
491
491
  def transition(nextstate)
492
+ handle_transition(nextstate)
493
+ rescue Errno::ECONNREFUSED,
494
+ Errno::EADDRNOTAVAIL,
495
+ Errno::EHOSTUNREACH,
496
+ TLSError => e
497
+ # connect errors, exit gracefully
498
+ handle_error(e)
499
+ @state = :closed
500
+ emit(:close)
501
+ end
502
+
503
+ def handle_transition(nextstate)
492
504
  case nextstate
493
505
  when :idle
494
506
  @timeout = @current_timeout = @options.timeout[:connect_timeout]
@@ -525,14 +537,6 @@ module HTTPX
525
537
  emit(:activate)
526
538
  end
527
539
  @state = nextstate
528
- rescue Errno::ECONNREFUSED,
529
- Errno::EADDRNOTAVAIL,
530
- Errno::EHOSTUNREACH,
531
- TLSError => e
532
- # connect errors, exit gracefully
533
- handle_error(e)
534
- @state = :closed
535
- emit(:close)
536
540
  end
537
541
 
538
542
  def purge_after_closed
@@ -550,6 +554,10 @@ module HTTPX
550
554
  ex.set_backtrace(error.backtrace)
551
555
  error = ex
552
556
  else
557
+ # inactive connections do not contribute to the select loop, therefore
558
+ # they should fail due to such errors.
559
+ return if @state == :inactive
560
+
553
561
  if @timeout
554
562
  @timeout -= error.timeout
555
563
  return unless @timeout <= 0
data/lib/httpx/io/tcp.rb CHANGED
@@ -57,7 +57,9 @@ module HTTPX
57
57
  @io = build_socket
58
58
  end
59
59
  try_connect
60
- rescue Errno::EHOSTUNREACH => e
60
+ rescue Errno::ECONNREFUSED,
61
+ Errno::EADDRNOTAVAIL,
62
+ Errno::EHOSTUNREACH => e
61
63
  raise e if @ip_index <= 0
62
64
 
63
65
  @ip_index -= 1
@@ -81,7 +81,7 @@ module HTTPX
81
81
  super
82
82
  end
83
83
 
84
- def transition(nextstate)
84
+ def handle_transition(nextstate)
85
85
  state = @state
86
86
  super
87
87
  meter_elapsed_time("Connection##{object_id}[#{@origin}]: #{state} -> #{nextstate}") if nextstate == @state
@@ -14,9 +14,13 @@ module HTTPX
14
14
  def call(file, _)
15
15
  return nil if file.eof? # FileMagic returns "application/x-empty" for empty files
16
16
 
17
- FileMagic.open(FileMagic::MAGIC_MIME_TYPE) do |filemagic|
17
+ mime = FileMagic.open(FileMagic::MAGIC_MIME_TYPE) do |filemagic|
18
18
  filemagic.buffer(file.read(MAGIC_NUMBER))
19
19
  end
20
+
21
+ file.rewind
22
+
23
+ mime
20
24
  end
21
25
  elsif defined?(Marcel)
22
26
  def call(file, filename)
@@ -13,7 +13,7 @@ module HTTPX
13
13
 
14
14
  private
15
15
 
16
- def transition(nextstate)
16
+ def handle_transition(nextstate)
17
17
  return super unless @options.proxy && @options.proxy.uri.scheme == "http"
18
18
 
19
19
  case nextstate
@@ -23,7 +23,8 @@ module HTTPX
23
23
  @io.connect
24
24
  return unless @io.connected?
25
25
 
26
- @parser = ConnectProxyParser.new(@write_buffer, @options.merge(max_concurrent_requests: 1))
26
+ @parser = registry(@io.protocol).new(@write_buffer, @options.merge(max_concurrent_requests: 1))
27
+ @parser.extend(ProxyParser)
27
28
  @parser.once(:response, &method(:__http_on_connect))
28
29
  @parser.on(:close) { transition(:closing) }
29
30
  __http_proxy_connect
@@ -36,7 +37,7 @@ module HTTPX
36
37
  @parser.close
37
38
  @parser = nil
38
39
  when :idle
39
- @parser = ProxyParser.new(@write_buffer, @options)
40
+ @parser.callbacks.clear
40
41
  set_parser_callbacks(@parser)
41
42
  end
42
43
  end
@@ -54,7 +55,7 @@ module HTTPX
54
55
  @inflight += 1
55
56
  parser.send(connect_request)
56
57
  else
57
- transition(:connected)
58
+ handle_transition(:connected)
58
59
  end
59
60
  end
60
61
 
@@ -76,9 +77,11 @@ module HTTPX
76
77
  end
77
78
  end
78
79
 
79
- class ProxyParser < Connection::HTTP1
80
- def headline_uri(request)
81
- request.uri.to_s
80
+ module ProxyParser
81
+ def join_headline(request)
82
+ return super if request.verb == :connect
83
+
84
+ "#{request.verb.to_s.upcase} #{request.uri} HTTP/#{@version.join(".")}"
82
85
  end
83
86
 
84
87
  def set_protocol_headers(request)
@@ -91,22 +94,6 @@ module HTTPX
91
94
  end
92
95
  end
93
96
 
94
- class ConnectProxyParser < ProxyParser
95
- attr_reader :pending
96
-
97
- def headline_uri(request)
98
- return super unless request.verb == :connect
99
-
100
- tunnel = request.path
101
- log { "establishing HTTP proxy tunnel to #{tunnel}" }
102
- tunnel
103
- end
104
-
105
- def empty?
106
- @requests.reject { |r| r.verb == :connect }.empty? || @requests.all? { |request| !request.response.nil? }
107
- end
108
- end
109
-
110
97
  class ConnectRequest < Request
111
98
  def initialize(uri, _options)
112
99
  super(:connect, uri, {})
@@ -27,7 +27,7 @@ module HTTPX
27
27
 
28
28
  private
29
29
 
30
- def transition(nextstate)
30
+ def handle_transition(nextstate)
31
31
  return super unless @options.proxy && PROTOCOLS.include?(@options.proxy.uri.scheme)
32
32
 
33
33
  case nextstate
@@ -46,7 +46,7 @@ module HTTPX
46
46
 
47
47
  private
48
48
 
49
- def transition(nextstate)
49
+ def handle_transition(nextstate)
50
50
  return super unless @options.proxy && @options.proxy.uri.scheme == "socks5"
51
51
 
52
52
  case nextstate
@@ -85,7 +85,7 @@ module HTTPX
85
85
  end
86
86
  uris
87
87
  end
88
- options.proxy.merge(uri: @_proxy_uris.first) unless @_proxy_uris.empty?
88
+ { uri: @_proxy_uris.first } unless @_proxy_uris.empty?
89
89
  end
90
90
 
91
91
  def find_connection(request, connections, options)
@@ -109,8 +109,10 @@ module HTTPX
109
109
  return super unless proxy
110
110
 
111
111
  connection = options.connection_class.new("tcp", uri, options)
112
- pool.init_connection(connection, options)
113
- connection
112
+ catch(:coalesced) do
113
+ pool.init_connection(connection, options)
114
+ connection
115
+ end
114
116
  end
115
117
 
116
118
  def fetch_response(request, connections, options)
@@ -181,11 +183,20 @@ module HTTPX
181
183
  super && @options.proxy == options.proxy
182
184
  end
183
185
 
184
- # should not coalesce connections here, as the IP is the IP of the proxy
185
- def coalescable?(*)
186
+ def coalescable?(connection)
186
187
  return super unless @options.proxy
187
188
 
188
- false
189
+ if @io.protocol == "h2" &&
190
+ @origin.scheme == "https" &&
191
+ connection.origin.scheme == "https" &&
192
+ @io.can_verify_peer?
193
+ # in proxied connections, .origin is the proxy ; Given names
194
+ # are stored in .origins, this is what is used.
195
+ origin = URI(connection.origins.first)
196
+ @io.verify_hostname(origin.host)
197
+ else
198
+ @origin == connection.origin
199
+ end
189
200
  end
190
201
 
191
202
  def send(request)
@@ -234,7 +245,7 @@ module HTTPX
234
245
  end
235
246
  end
236
247
 
237
- def transition(nextstate)
248
+ def handle_transition(nextstate)
238
249
  return super unless @options.proxy
239
250
 
240
251
  case nextstate
@@ -29,7 +29,7 @@ module HTTPX
29
29
  attr_writer :pool
30
30
 
31
31
  def initialize(options)
32
- @options = Options.new(options)
32
+ @options = HTTPX::Options.new(options)
33
33
  @resolver_options = DEFAULTS.merge(@options.resolver_options)
34
34
  @_record_types = Hash.new { |types, host| types[host] = @resolver_options[:record_types].dup }
35
35
  @queries = {}
@@ -50,7 +50,7 @@ module HTTPX
50
50
  attr_reader :state
51
51
 
52
52
  def initialize(options)
53
- @options = Options.new(options)
53
+ @options = HTTPX::Options.new(options)
54
54
  @ns_index = 0
55
55
  @resolver_options = DEFAULTS.merge(@options.resolver_options)
56
56
  @nameserver = @resolver_options[:nameserver]
@@ -200,6 +200,7 @@ module HTTPX
200
200
  hostname, connection = @queries.first
201
201
  if @_record_types[hostname].empty?
202
202
  @queries.delete(hostname)
203
+ @timeouts.delete(hostname)
203
204
  @connections.delete(connection)
204
205
  ex = NativeResolveError.new(connection, hostname, e.message)
205
206
  ex.set_backtrace(e.backtrace)
@@ -213,6 +214,7 @@ module HTTPX
213
214
  if @_record_types[hostname].empty?
214
215
  @queries.delete(hostname)
215
216
  @_record_types.delete(hostname)
217
+ @timeouts.delete(hostname)
216
218
  @connections.delete(connection)
217
219
 
218
220
  raise NativeResolveError.new(connection, hostname)
@@ -236,13 +238,18 @@ module HTTPX
236
238
  end
237
239
 
238
240
  if address.key?("alias") # CNAME
239
- if early_resolve(connection, hostname: address["alias"])
241
+ # clean up intermediate queries
242
+ @timeouts.delete(address["name"]) unless connection.origin.host == address["name"]
243
+
244
+ if catch(:coalesced) { early_resolve(connection, hostname: address["alias"]) }
245
+ @timeouts.delete(connection.origin.host)
240
246
  @connections.delete(connection)
241
247
  else
242
248
  resolve(connection, address["alias"])
243
249
  return
244
250
  end
245
251
  else
252
+ @timeouts.delete(connection.origin.host)
246
253
  @connections.delete(connection)
247
254
  Resolver.cached_lookup_set(connection.origin.host, addresses) if @resolver_options[:cache]
248
255
  emit_addresses(connection, addresses.map { |addr| addr["data"] })
@@ -32,7 +32,7 @@ module HTTPX
32
32
  end
33
33
  log { "resolver: answer #{connection.origin.host}: #{addresses.inspect}" }
34
34
  connection.addresses = addresses
35
- catch(:coalesced) { emit(:resolve, connection) }
35
+ emit(:resolve, connection)
36
36
  end
37
37
 
38
38
  def early_resolve(connection, hostname: connection.origin.host)
@@ -15,7 +15,7 @@ module HTTPX
15
15
  attr_reader :state
16
16
 
17
17
  def initialize(options)
18
- @options = Options.new(options)
18
+ @options = HTTPX::Options.new(options)
19
19
  @resolver_options = @options.resolver_options
20
20
  @state = :idle
21
21
  resolv_options = @resolver_options.dup
data/lib/httpx/session.rb CHANGED
@@ -207,7 +207,7 @@ module HTTPX
207
207
 
208
208
  return responses unless request
209
209
 
210
- pool.next_tick until (response = fetch_response(request, connections, request.options))
210
+ catch(:coalesced) { pool.next_tick } until (response = fetch_response(request, connections, request.options))
211
211
 
212
212
  responses << response
213
213
  requests.shift
@@ -309,18 +309,4 @@ module HTTPX
309
309
  # :nocov:
310
310
  end
311
311
  end
312
-
313
- unless ENV.grep(/https?_proxy$/i).empty?
314
- proxy_session = plugin(:proxy)
315
- ::HTTPX.send(:remove_const, :Session)
316
- ::HTTPX.send(:const_set, :Session, proxy_session.class)
317
- end
318
-
319
- # :nocov:
320
- if Session.default_options.debug_level > 2
321
- proxy_session = plugin(:internal_telemetry)
322
- ::HTTPX.send(:remove_const, :Session)
323
- ::HTTPX.send(:const_set, :Session, proxy_session.class)
324
- end
325
- # :nocov:
326
312
  end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HTTPX
4
+ unless ENV.keys.grep(/\Ahttps?_proxy\z/i).empty?
5
+ proxy_session = plugin(:proxy)
6
+ remove_const(:Session)
7
+ const_set(:Session, proxy_session.class)
8
+ remove_const(:Options)
9
+ const_set(:Options, proxy_session.class.default_options.class)
10
+ end
11
+
12
+ # :nocov:
13
+ if Session.default_options.debug_level > 2
14
+ proxy_session = plugin(:internal_telemetry)
15
+ remove_const(:Session)
16
+ const_set(:Session, proxy_session.class)
17
+ end
18
+ # :nocov:
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 = "0.18.6"
4
+ VERSION = "0.18.7"
5
5
  end
data/lib/httpx.rb CHANGED
@@ -64,3 +64,4 @@ module HTTPX
64
64
  end
65
65
 
66
66
  require "httpx/session"
67
+ require "httpx/session_extensions"
@@ -67,8 +67,6 @@ module HTTPX
67
67
 
68
68
  def set_protocol_headers: (Request) -> _Each[[String, String]]
69
69
 
70
- def headline_uri: (Request) -> String
71
-
72
70
  def handle: (Request request) -> void
73
71
 
74
72
  def join_headers: (Request request) -> void
@@ -46,8 +46,6 @@ module HTTPX
46
46
 
47
47
  def send_pending: () -> void
48
48
 
49
- def headline_uri: (Request) -> String
50
-
51
49
  def set_protocol_headers: (Request) -> _Each[[String, String]]
52
50
 
53
51
  def handle: (Request request, HTTP2Next::Stream stream) -> void
@@ -56,6 +54,8 @@ module HTTPX
56
54
 
57
55
  def handle_stream: (HTTP2Next::Stream stream, Request request) -> void
58
56
 
57
+ def join_headline: (Request request) -> String
58
+
59
59
  def join_headers: (HTTP2Next::Stream stream, Request request) -> void
60
60
 
61
61
  def join_trailers: (HTTP2Next::Stream stream, Request request) -> void
data/sig/connection.rbs CHANGED
@@ -22,6 +22,7 @@ module HTTPX
22
22
  BUFFER_SIZE: Integer
23
23
 
24
24
  attr_reader origin: URI::Generic
25
+ attr_reader origins: Array[String]
25
26
  attr_reader state: Symbol
26
27
  attr_reader pending: Array[Request]
27
28
  attr_reader options: Options
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: httpx
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.18.6
4
+ version: 0.18.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tiago Cardoso
@@ -69,6 +69,7 @@ extra_rdoc_files:
69
69
  - doc/release_notes/0_18_4.md
70
70
  - doc/release_notes/0_18_5.md
71
71
  - doc/release_notes/0_18_6.md
72
+ - doc/release_notes/0_18_7.md
72
73
  - doc/release_notes/0_1_0.md
73
74
  - doc/release_notes/0_2_0.md
74
75
  - doc/release_notes/0_2_1.md
@@ -131,6 +132,7 @@ files:
131
132
  - doc/release_notes/0_18_4.md
132
133
  - doc/release_notes/0_18_5.md
133
134
  - doc/release_notes/0_18_6.md
135
+ - doc/release_notes/0_18_7.md
134
136
  - doc/release_notes/0_1_0.md
135
137
  - doc/release_notes/0_2_0.md
136
138
  - doc/release_notes/0_2_1.md
@@ -234,6 +236,7 @@ files:
234
236
  - lib/httpx/selector.rb
235
237
  - lib/httpx/session.rb
236
238
  - lib/httpx/session2.rb
239
+ - lib/httpx/session_extensions.rb
237
240
  - lib/httpx/timers.rb
238
241
  - lib/httpx/transcoder.rb
239
242
  - lib/httpx/transcoder/body.rb