httpx 0.18.5 → 0.19.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +5 -1
  3. data/doc/release_notes/0_18_5.md +2 -2
  4. data/doc/release_notes/0_18_6.md +5 -0
  5. data/doc/release_notes/0_18_7.md +5 -0
  6. data/doc/release_notes/0_19_0.md +39 -0
  7. data/doc/release_notes/0_19_1.md +5 -0
  8. data/lib/httpx/adapters/faraday.rb +7 -3
  9. data/lib/httpx/connection/http1.rb +5 -5
  10. data/lib/httpx/connection/http2.rb +1 -5
  11. data/lib/httpx/connection.rb +22 -10
  12. data/lib/httpx/extensions.rb +16 -0
  13. data/lib/httpx/headers.rb +0 -2
  14. data/lib/httpx/io/tcp.rb +27 -6
  15. data/lib/httpx/options.rb +44 -11
  16. data/lib/httpx/plugins/cookies.rb +5 -7
  17. data/lib/httpx/plugins/internal_telemetry.rb +1 -1
  18. data/lib/httpx/plugins/multipart/mime_type_detector.rb +7 -1
  19. data/lib/httpx/plugins/proxy/http.rb +10 -23
  20. data/lib/httpx/plugins/proxy/socks4.rb +1 -1
  21. data/lib/httpx/plugins/proxy/socks5.rb +1 -1
  22. data/lib/httpx/plugins/proxy.rb +20 -12
  23. data/lib/httpx/plugins/retries.rb +1 -1
  24. data/lib/httpx/pool.rb +40 -20
  25. data/lib/httpx/resolver/https.rb +32 -42
  26. data/lib/httpx/resolver/multi.rb +79 -0
  27. data/lib/httpx/resolver/native.rb +28 -36
  28. data/lib/httpx/resolver/resolver.rb +92 -0
  29. data/lib/httpx/resolver/system.rb +175 -19
  30. data/lib/httpx/resolver.rb +37 -11
  31. data/lib/httpx/response.rb +4 -2
  32. data/lib/httpx/session.rb +1 -15
  33. data/lib/httpx/session_extensions.rb +26 -0
  34. data/lib/httpx/timers.rb +1 -1
  35. data/lib/httpx/transcoder/chunker.rb +0 -1
  36. data/lib/httpx/version.rb +1 -1
  37. data/lib/httpx.rb +3 -0
  38. data/sig/connection/http1.rbs +0 -2
  39. data/sig/connection/http2.rbs +2 -2
  40. data/sig/connection.rbs +1 -0
  41. data/sig/errors.rbs +8 -0
  42. data/sig/headers.rbs +0 -2
  43. data/sig/httpx.rbs +4 -0
  44. data/sig/options.rbs +10 -7
  45. data/sig/parser/http1.rbs +14 -5
  46. data/sig/pool.rbs +17 -9
  47. data/sig/registry.rbs +3 -0
  48. data/sig/request.rbs +11 -0
  49. data/sig/resolver/https.rbs +15 -27
  50. data/sig/resolver/multi.rbs +7 -0
  51. data/sig/resolver/native.rbs +3 -12
  52. data/sig/resolver/resolver.rbs +36 -0
  53. data/sig/resolver/system.rbs +3 -9
  54. data/sig/resolver.rbs +12 -10
  55. data/sig/response.rbs +15 -5
  56. data/sig/selector.rbs +3 -3
  57. data/sig/timers.rbs +5 -2
  58. data/sig/transcoder/chunker.rbs +16 -5
  59. data/sig/transcoder/json.rbs +5 -0
  60. data/sig/transcoder.rbs +3 -1
  61. metadata +15 -4
  62. data/lib/httpx/resolver/resolver_mixin.rb +0 -75
  63. data/sig/resolver/resolver_mixin.rbs +0 -26
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 843e16ae32c900167850fe3a864446af2cd6b6a9d3ca6368f64b999964a1a5b2
4
- data.tar.gz: 533464195c08ab7e0ba2e5aa7871fdf8b59e59643122ca48ce5e7cf8a6ebb351
3
+ metadata.gz: 56e51d7951de9e5964eb1106d8829504e1d0fbac2ca7906b64f6139467159403
4
+ data.tar.gz: 6b7726c50101f8356cf04628ee9ff32cbd6babd3bf98f426d768ae52d835f4a0
5
5
  SHA512:
6
- metadata.gz: 6e170cf6a47bce0a1eeeea25c53870615958f77e2fe9eb9b4176783b860bc26c8d45712e0ad758ba2481bd2712a21bb14d10a32fb5f17871a9421413edf162ac
7
- data.tar.gz: ae5a0bc77a54cf66528dfd89c7127b5ce64730be6fc0fe8be6d79966dfa36e2e04b348f39e1e91d3b457e34c35f8b3a33e18bc656f7823874ffbaedc72e3b8ad
6
+ metadata.gz: d29f216b4176e5085d7495d3ac015eb4b9f7e76f4dd064ae1f822890cc44d1718ec14fe9a3935515af6c7882762ebd875a0e15983d5707a2c9f68d069bfd084c
7
+ data.tar.gz: 30537730f11c0bfffd43882abd430c8a006a2a863b7ef247286f085440781d1b44e048d031df2c3af95fffc9d5ce04fcaad6bd0d05d039fc77023a0dc95a7508
data/README.md CHANGED
@@ -140,11 +140,15 @@ In order to use HTTP/2 under JRuby, [check this link](https://gitlab.com/honeyry
140
140
  * Doesn't work with ruby 2.4.0 for Windows (see [#36](https://gitlab.com/honeyryderchuck/httpx/issues/36)).
141
141
  * Using `total_timeout` along with the `:persistent` plugin [does not work as you might expect](https://gitlab.com/honeyryderchuck/httpx/-/wikis/Timeouts#total_timeout).
142
142
 
143
+ ## Versioning Policy
144
+
145
+ Although 0.x software, `httpx` is considered API-stable and production-ready, i.e. current API or options may be subject to deprecation and emit log warnings, but can only effectively be removed in a major version change.
146
+
143
147
  ## Contributing
144
148
 
145
149
  * Discuss your contribution in an issue
146
150
  * Fork it
147
151
  * Make your changes, add some tests
148
- * Ensure all tests pass (`bundle exec rake test`)
152
+ * Ensure all tests pass (`docker-compose -f docker-compose.yml -f docker-compose-ruby-{RUBY_VERSION}.yml run httpx bundle exec rake test`)
149
153
  * Open a Merge Request (that's Pull Request in Github-ish)
150
154
  * Wait for feedback
@@ -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 missing constant in `filemagic` integration.
@@ -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.
@@ -0,0 +1,39 @@
1
+ # 0.19.0
2
+
3
+ ## Features
4
+
5
+ ### Happy Eyeballs v2
6
+
7
+ When the system supports dual-stack networking, `httpx` implements the Happy Eyeballs v2 algorithm (RFC 8305) to resolve hostnames to both IPv6 and IPv4 addresses while privileging IPv6 connectivity. This is implemented by `httpx` both for the `:native` as well as the `:https` (DoH) resolver (which do not perform address sorting, thereby being "DNS-based load-balancing" friendly), and "outsourced" to `getaddrinfo` when using the `:system` resolver.
8
+
9
+ IPv6 connectivity will also be privileged for `/etc/hosts` local DNS (i.e. `localhost` connections will connec to `::1`).
10
+
11
+ A new option, `:ip_families`, will also be available (`[Socket::AF_INET6, Socket::AF_INET]` in dual-stack systems). If you'd like to i.e. force IPv4 connectivity, you can do use it (`client = HTTPX.with(ip_families: [Socket::AF_INET])`).
12
+
13
+ ## Improvements
14
+
15
+ ### DNS: :system resolver uses getaddrinfo (instead of the resolver lib)
16
+
17
+ The `:system` resolver switched to using the `getaddinfo` system function to perform DNS requests. Not only is this call **not** blocking the session event loop anymore (unlike pre-0.19.0 `:system` resolver), it adds a lot of functionality that the stdlib `resolv` library just doesn't support at the moment (such as SRV records).
18
+
19
+ ### HTTP/2 proxy support
20
+
21
+ The `:proxy` plugin handles "prior-knowledge" HTTP/2 proxies.
22
+
23
+ ```ruby
24
+ HTTPX.plugin(:proxy, fallback_protocol: "h2").with_proxy(uri: "http://http2-proxy:3128").get(...
25
+ ```
26
+
27
+ Connection coalescing has also been enabled for proxied connections (also `CONNECT`-tunneled connections).
28
+
29
+ ### curl-to-httpx
30
+
31
+ widget in [project website](https://honeyryderchuck.gitlab.io/httpx/) to turn curl commands into the equivalent `httpx` code.
32
+
33
+ ## Bugfixes
34
+
35
+ * faraday adapter now supports passing session options.
36
+ * proxy: several fixes which enabled env-var (`HTTP(S)_PROXY`) defined proxy support.
37
+ * proxy: fixed graceful recovery from proxy tcp connect errors.
38
+ * several fixes around CNAMEs timeouts with the native resolver.
39
+ * https resolver is now closed when wrapping session closes (it was left open).
@@ -0,0 +1,5 @@
1
+ # 0.19.1
2
+
3
+ ## Bugfixes
4
+
5
+ Fixing a DNS dual-stack case where one the resolvers may have finished way before the previous one and will therefore return no timeout.
@@ -81,6 +81,9 @@ module Faraday
81
81
 
82
82
  def response=(response)
83
83
  super
84
+
85
+ return if response.is_a?(::HTTPX::ErrorResponse)
86
+
84
87
  response.body.on_data = @response_on_data
85
88
  end
86
89
  end
@@ -136,7 +139,7 @@ module Faraday
136
139
 
137
140
  def on_response(&blk)
138
141
  if blk
139
- @on_response = lambda do |response|
142
+ @on_response = ->(response) do
140
143
  blk.call(response)
141
144
  end
142
145
  self
@@ -200,9 +203,9 @@ module Faraday
200
203
  end
201
204
  end
202
205
 
203
- def initialize(app)
206
+ def initialize(app, options = {})
204
207
  super(app)
205
- @session = Session.new
208
+ @session = Session.new(options)
206
209
  end
207
210
 
208
211
  def call(env)
@@ -210,6 +213,7 @@ module Faraday
210
213
  if parallel?(env)
211
214
  handler = env[:parallel_manager].enqueue(env)
212
215
  handler.on_response do |response|
216
+ response.raise_for_status
213
217
  save_response(env, response.status, response.body.to_s, response.headers, response.reason) do |response_headers|
214
218
  response_headers.merge!(response.headers)
215
219
  end
@@ -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 :io, :origin, :origins, :state, :pending, :options
48
48
 
49
49
  attr_writer :timers
50
50
 
@@ -78,7 +78,11 @@ module HTTPX
78
78
  # this is a semi-private method, to be used by the resolver
79
79
  # to initiate the io object.
80
80
  def addresses=(addrs)
81
- @io ||= IO.registry(@type).new(@origin, addrs, @options) # rubocop:disable Naming/MemoizedInstanceVariableName
81
+ if @io
82
+ @io.add_addresses(addrs)
83
+ else
84
+ @io = IO.registry(@type).new(@origin, addrs, @options)
85
+ end
82
86
  end
83
87
 
84
88
  def addresses
@@ -489,6 +493,18 @@ module HTTPX
489
493
  end
490
494
 
491
495
  def transition(nextstate)
496
+ handle_transition(nextstate)
497
+ rescue Errno::ECONNREFUSED,
498
+ Errno::EADDRNOTAVAIL,
499
+ Errno::EHOSTUNREACH,
500
+ TLSError => e
501
+ # connect errors, exit gracefully
502
+ handle_error(e)
503
+ @state = :closed
504
+ emit(:close)
505
+ end
506
+
507
+ def handle_transition(nextstate)
492
508
  case nextstate
493
509
  when :idle
494
510
  @timeout = @current_timeout = @options.timeout[:connect_timeout]
@@ -525,14 +541,6 @@ module HTTPX
525
541
  emit(:activate)
526
542
  end
527
543
  @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
544
  end
537
545
 
538
546
  def purge_after_closed
@@ -550,6 +558,10 @@ module HTTPX
550
558
  ex.set_backtrace(error.backtrace)
551
559
  error = ex
552
560
  else
561
+ # inactive connections do not contribute to the select loop, therefore
562
+ # they should fail due to such errors.
563
+ return if @state == :inactive
564
+
553
565
  if @timeout
554
566
  @timeout -= error.timeout
555
567
  return unless @timeout <= 0
@@ -54,6 +54,22 @@ module HTTPX
54
54
  Numeric.__send__(:include, NegMethods)
55
55
  end
56
56
 
57
+ module StringExtensions
58
+ refine String do
59
+ def delete_suffix!(suffix)
60
+ suffix = Backports.coerce_to_str(suffix)
61
+ chomp! if frozen?
62
+ len = suffix.length
63
+ if len > 0 && index(suffix, -len)
64
+ self[-len..-1] = ''
65
+ self
66
+ else
67
+ nil
68
+ end
69
+ end unless String.method_defined?(:delete_suffix!)
70
+ end
71
+ end
72
+
57
73
  module HashExtensions
58
74
  refine Hash do
59
75
  def compact
data/lib/httpx/headers.rb CHANGED
@@ -2,8 +2,6 @@
2
2
 
3
3
  module HTTPX
4
4
  class Headers
5
- EMPTY = [].freeze
6
-
7
5
  class << self
8
6
  def new(headers = nil)
9
7
  return headers if headers.is_a?(self)
data/lib/httpx/io/tcp.rb CHANGED
@@ -15,6 +15,7 @@ module HTTPX
15
15
 
16
16
  def initialize(origin, addresses, options)
17
17
  @state = :idle
18
+ @addresses = []
18
19
  @hostname = origin.host
19
20
  @options = Options.new(options)
20
21
  @fallback_protocol = @options.fallback_protocol
@@ -30,15 +31,29 @@ module HTTPX
30
31
  raise Error, "Given IO objects do not match the request authority" unless @io
31
32
 
32
33
  _, _, _, @ip = @io.addr
33
- @addresses ||= [@ip]
34
- @ip_index = @addresses.size - 1
34
+ @addresses << @ip
35
35
  @keep_open = true
36
36
  @state = :connected
37
37
  else
38
- @addresses = addresses.map { |addr| addr.is_a?(IPAddr) ? addr : IPAddr.new(addr) }
38
+ add_addresses(addresses)
39
39
  end
40
40
  @ip_index = @addresses.size - 1
41
- @io ||= build_socket
41
+ # @io ||= build_socket
42
+ end
43
+
44
+ def add_addresses(addrs)
45
+ return if addrs.empty?
46
+
47
+ addrs = addrs.map { |addr| addr.is_a?(IPAddr) ? addr : IPAddr.new(addr) }
48
+
49
+ ip_index = @ip_index || (@addresses.size - 1)
50
+ if addrs.first.ipv6?
51
+ # should be the next in line
52
+ @addresses = [*@addresses[0, ip_index], *addrs, *@addresses[ip_index..-1]]
53
+ else
54
+ @addresses.unshift(*addrs)
55
+ @ip_index += addrs.size if @ip_index
56
+ end
42
57
  end
43
58
 
44
59
  def to_io
@@ -52,20 +67,26 @@ module HTTPX
52
67
  def connect
53
68
  return unless closed?
54
69
 
55
- if @io.closed?
70
+ if !@io || @io.closed?
56
71
  transition(:idle)
57
72
  @io = build_socket
58
73
  end
59
74
  try_connect
60
- rescue Errno::EHOSTUNREACH => e
75
+ rescue Errno::ECONNREFUSED,
76
+ Errno::EADDRNOTAVAIL,
77
+ Errno::EHOSTUNREACH => e
61
78
  raise e if @ip_index <= 0
62
79
 
80
+ log { "failed connecting to #{@ip} (#{e.message}), trying next..." }
63
81
  @ip_index -= 1
82
+ @io = build_socket
64
83
  retry
65
84
  rescue Errno::ETIMEDOUT => e
66
85
  raise ConnectTimeoutError.new(@options.timeout[:connect_timeout], e.message) if @ip_index <= 0
67
86
 
87
+ log { "failed connecting to #{@ip} (#{e.message}), trying next..." }
68
88
  @ip_index -= 1
89
+ @io = build_socket
69
90
  retry
70
91
  end
71
92
 
data/lib/httpx/options.rb CHANGED
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "socket"
4
+
3
5
  module HTTPX
4
6
  class Options
5
7
  WINDOW_SIZE = 1 << 14 # 16K
@@ -9,6 +11,18 @@ module HTTPX
9
11
  KEEP_ALIVE_TIMEOUT = 20
10
12
  SETTINGS_TIMEOUT = 10
11
13
 
14
+ # https://github.com/ruby/resolv/blob/095f1c003f6073730500f02acbdbc55f83d70987/lib/resolv.rb#L408
15
+ ip_address_families = begin
16
+ list = Socket.ip_address_list
17
+ if list.any? { |a| a.ipv6? && !a.ipv6_loopback? && !a.ipv6_linklocal? }
18
+ [Socket::AF_INET6, Socket::AF_INET]
19
+ else
20
+ [Socket::AF_INET]
21
+ end
22
+ rescue NotImplementedError
23
+ [Socket::AF_INET]
24
+ end
25
+
12
26
  DEFAULT_OPTIONS = {
13
27
  :debug => ENV.key?("HTTPX_DEBUG") ? $stderr : nil,
14
28
  :debug_level => (ENV["HTTPX_DEBUG"] || 1).to_i,
@@ -37,6 +51,7 @@ module HTTPX
37
51
  :persistent => false,
38
52
  :resolver_class => (ENV["HTTPX_RESOLVER"] || :native).to_sym,
39
53
  :resolver_options => { cache: true },
54
+ :ip_families => ip_address_families,
40
55
  }.freeze
41
56
 
42
57
  begin
@@ -110,20 +125,18 @@ module HTTPX
110
125
  end
111
126
 
112
127
  def initialize(options = {})
113
- defaults = DEFAULT_OPTIONS.merge(options)
114
- defaults.each do |k, v|
115
- next if v.nil?
116
-
117
- begin
118
- value = __send__(:"option_#{k}", v)
119
- instance_variable_set(:"@#{k}", value)
120
- rescue NoMethodError
121
- raise Error, "unknown option: #{k}"
122
- end
123
- end
128
+ __initialize__(options)
124
129
  freeze
125
130
  end
126
131
 
132
+ def freeze
133
+ super
134
+ @origin.freeze
135
+ @timeout.freeze
136
+ @headers.freeze
137
+ @addresses.freeze
138
+ end
139
+
127
140
  def option_origin(value)
128
141
  URI(value)
129
142
  end
@@ -174,6 +187,10 @@ module HTTPX
174
187
  Array(value)
175
188
  end
176
189
 
190
+ def option_ip_families(value)
191
+ Array(value)
192
+ end
193
+
177
194
  %i[
178
195
  params form json body ssl http2_settings
179
196
  request_class response_class headers_class request_body_class
@@ -249,5 +266,21 @@ module HTTPX
249
266
  end
250
267
  end
251
268
  end
269
+
270
+ private
271
+
272
+ def __initialize__(options = {})
273
+ defaults = DEFAULT_OPTIONS.merge(options)
274
+ defaults.each do |k, v|
275
+ next if v.nil?
276
+
277
+ begin
278
+ value = __send__(:"option_#{k}", v)
279
+ instance_variable_set(:"@#{k}", value)
280
+ rescue NoMethodError
281
+ raise Error, "unknown option: #{k}"
282
+ end
283
+ end
284
+ end
252
285
  end
253
286
  end
@@ -18,12 +18,6 @@ module HTTPX
18
18
  require "httpx/plugins/cookies/set_cookie_parser"
19
19
  end
20
20
 
21
- module OptionsMethods
22
- def option_cookies(value)
23
- value.is_a?(Jar) ? value : Jar.new(value)
24
- end
25
- end
26
-
27
21
  module InstanceMethods
28
22
  extend Forwardable
29
23
 
@@ -77,7 +71,7 @@ module HTTPX
77
71
  end
78
72
 
79
73
  module OptionsMethods
80
- def initialize(*)
74
+ def __initialize__(*)
81
75
  super
82
76
 
83
77
  return unless @headers.key?("cookie")
@@ -89,6 +83,10 @@ module HTTPX
89
83
  end
90
84
  end
91
85
  end
86
+
87
+ def option_cookies(value)
88
+ value.is_a?(Jar) ? value : Jar.new(value)
89
+ end
92
90
  end
93
91
  end
94
92
  register_plugin :cookies, Cookies
@@ -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
@@ -9,12 +9,18 @@ module HTTPX
9
9
 
10
10
  # inspired by https://github.com/shrinerb/shrine/blob/master/lib/shrine/plugins/determine_mime_type.rb
11
11
  if defined?(FileMagic)
12
+ MAGIC_NUMBER = 256 * 1024
13
+
12
14
  def call(file, _)
13
15
  return nil if file.eof? # FileMagic returns "application/x-empty" for empty files
14
16
 
15
- FileMagic.open(FileMagic::MAGIC_MIME_TYPE) do |filemagic|
17
+ mime = FileMagic.open(FileMagic::MAGIC_MIME_TYPE) do |filemagic|
16
18
  filemagic.buffer(file.read(MAGIC_NUMBER))
17
19
  end
20
+
21
+ file.rewind
22
+
23
+ mime
18
24
  end
19
25
  elsif defined?(Marcel)
20
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