httpx 0.18.3 → 0.18.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 81b7e14071e0e1978c864c63b0c8146044f255a9b2f7d9d3e917b564fb301ffb
4
- data.tar.gz: 7492d34e586d86e373f62f151ceb6962719fcec156597ff34dae4c10b8347072
3
+ metadata.gz: 4c6e05e5bda153614ac00aac76cdaff0d5d727c58a99fb2297c545aa54b27c0e
4
+ data.tar.gz: 9d682e4136c3e8a3d769e02f96209a72dadbb5722a01d4ec8267cec06b5f1a3a
5
5
  SHA512:
6
- metadata.gz: 5c5930cd68b2ba0f42ea60e6dc5a76ff24b2643f16c300b778e8af62f0f04d8c01baadbf0163e2b925275bbfdf44d8dd73bbf78e06dd2c86b2e54d0eb99cbbdc
7
- data.tar.gz: 20a70c6ffc8895c7b54f84f9832bc8831942048ba37dc83907e0b4fef83484d1f6797a0792b075539cb985a8df65798085f3089e6746d1246e9d27ef0cb63d96
6
+ metadata.gz: f2549dd8e2b9786dfb6916f88bd9979e06fc32806a09ead664f97d67612eb3d4b182e4a48d1b23f037e899ecda50ad36b343e85e97258fd3ecb730f63238417e
7
+ data.tar.gz: f0388924dfba2717069b9cea8463e658d6262c6bfb874185c60baaab1650055fe4a98acd9fa39a6a060f00aa0aa05f1345d627bac138ecf5e17ad4d8aacd92b2
@@ -0,0 +1,14 @@
1
+ # 0.18.4
2
+
3
+ ## Improvements
4
+
5
+ * faraday adapter: added support for `#on_data` callback in order to support [faraday streaming](https://lostisland.github.io/faraday/usage/streaming).
6
+
7
+ * multipart plugin: removed support for file mime type detection using `mime-types`. The reasoning behind it was that `mime-types` uses the filename, which is a very innacurate detection strategy (ex: an mp4 video will be identified as `application/mp4`, instead of the correct `video/mp4`).
8
+ * multipart plugin: supported for file mime type detection using `marcel` and `filemagic` was added. Both use the magic header bytes, which is a more accurate strategy for file type detection.
9
+
10
+ ## Bugfixes
11
+
12
+ * webmock adapter has been reimplemented to work with `httpx` plugins (such as the `:retries` plugin). Some other fixes were applied to make it work better under `vcr` (a common `webmock` extension).
13
+
14
+ * fixed the URI-related bug which was making requests stall under ruby 3.1 (still not officially testinng against it).
@@ -21,6 +21,17 @@ module Faraday
21
21
  end
22
22
  # :nocov:
23
23
 
24
+ unless Faraday::RequestOptions.method_defined?(:stream_response?)
25
+ module RequestOptionsExtensions
26
+ refine Faraday::RequestOptions do
27
+ def stream_response?
28
+ false
29
+ end
30
+ end
31
+ end
32
+ using RequestOptionsExtensions
33
+ end
34
+
24
35
  module RequestMixin
25
36
  using ::HTTPX::HashExtensions
26
37
 
@@ -64,6 +75,27 @@ module Faraday
64
75
 
65
76
  include RequestMixin
66
77
 
78
+ module OnDataPlugin
79
+ module RequestMethods
80
+ attr_writer :response_on_data
81
+
82
+ def response=(response)
83
+ super
84
+ response.body.on_data = @response_on_data
85
+ end
86
+ end
87
+
88
+ module ResponseBodyMethods
89
+ attr_writer :on_data
90
+
91
+ def write(chunk)
92
+ return super unless @on_data
93
+
94
+ @on_data.call(chunk, chunk.bytesize)
95
+ end
96
+ end
97
+ end
98
+
67
99
  class Session < ::HTTPX::Session
68
100
  plugin(:compression)
69
101
  plugin(:persistent)
@@ -137,15 +169,21 @@ module Faraday
137
169
  end
138
170
 
139
171
  def run
140
- requests = @handlers.map { |handler| build_request(handler.env) }
141
172
  env = @handlers.last.env
142
173
 
143
- proxy_options = { uri: env.request.proxy }
144
-
145
174
  session = @session.with(options_from_env(env))
146
- session = session.plugin(:proxy).with(proxy: proxy_options) if env.request.proxy
175
+ session = session.plugin(:proxy).with(proxy: { uri: env.request.proxy }) if env.request.proxy
176
+ session = session.plugin(OnDataPlugin) if env.request.stream_response?
177
+
178
+ requests = @handlers.map { |handler| session.build_request(*build_request(handler.env)) }
179
+
180
+ if env.request.stream_response?
181
+ requests.each do |request|
182
+ request.response_on_data = env.request.on_data
183
+ end
184
+ end
147
185
 
148
- responses = session.request(requests)
186
+ responses = session.request(*requests)
149
187
  Array(responses).each_with_index do |response, index|
150
188
  handler = @handlers[index]
151
189
  handler.on_response.call(response)
@@ -179,11 +217,15 @@ module Faraday
179
217
  return handler
180
218
  end
181
219
 
182
- meth, uri, request_options = build_request(env)
183
-
184
220
  session = @session.with(options_from_env(env))
185
- session = session.plugin(:proxy).with(proxy: proxy_options) if env.request.proxy
186
- response = session.__send__(meth, uri, **request_options)
221
+ session = session.plugin(:proxy).with(proxy: { uri: env.request.proxy }) if env.request.proxy
222
+ session = session.plugin(OnDataPlugin) if env.request.stream_response?
223
+
224
+ request = session.build_request(*build_request(env))
225
+
226
+ request.response_on_data = env.request.on_data if env.request.stream_response?
227
+
228
+ response = session.request(request)
187
229
  response.raise_for_status unless response.is_a?(::HTTPX::Response)
188
230
  save_response(env, response.status, response.body.to_s, response.headers, response.reason) do |response_headers|
189
231
  response_headers.merge!(response.headers)
@@ -16,56 +16,8 @@ module WebMock
16
16
  # Requests are "hijacked" at the session, before they're distributed to a connection.
17
17
  #
18
18
  module Plugin
19
- module InstanceMethods
20
- private
21
-
22
- def send_requests(*requests)
23
- request_signatures = requests.map do |request|
24
- request_signature = _build_webmock_request_signature(request)
25
- WebMock::RequestRegistry.instance.requested_signatures.put(request_signature)
26
- request_signature
27
- end
28
-
29
- responses = request_signatures.map do |request_signature|
30
- WebMock::StubRegistry.instance.response_for_request(request_signature)
31
- end
32
-
33
- real_requests = {}
34
-
35
- requests.each_with_index.each_with_object([request_signatures, responses]) do |(request, idx), (sig_reqs, mock_responses)|
36
- if (webmock_response = mock_responses[idx])
37
- mock_responses[idx] = _build_from_webmock_response(request, webmock_response)
38
- WebMock::CallbackRegistry.invoke_callbacks({ lib: :httpx }, sig_reqs[idx], webmock_response)
39
- log { "mocking #{request.uri} with #{mock_responses[idx].inspect}" }
40
- elsif WebMock.net_connect_allowed?(sig_reqs[idx].uri)
41
- log { "performing #{request.uri}" }
42
- real_requests[request] = idx
43
- else
44
- raise WebMock::NetConnectNotAllowedError, sig_reqs[idx]
45
- end
46
- end
47
-
48
- unless real_requests.empty?
49
- reqs = real_requests.keys
50
- reqs.zip(super(*reqs)).each do |req, res|
51
- idx = real_requests[req]
52
-
53
- if WebMock::CallbackRegistry.any_callbacks?
54
- webmock_response = _build_webmock_response(req, res)
55
- WebMock::CallbackRegistry.invoke_callbacks(
56
- { lib: :httpx, real_request: true }, request_signatures[idx],
57
- webmock_response
58
- )
59
- end
60
-
61
- responses[idx] = res
62
- end
63
- end
64
-
65
- responses
66
- end
67
-
68
- def _build_webmock_request_signature(request)
19
+ class << self
20
+ def build_webmock_request_signature(request)
69
21
  uri = WebMock::Util::URI.heuristic_parse(request.uri)
70
22
  uri.path = uri.normalized_path.gsub("[^:]//", "/")
71
23
 
@@ -77,7 +29,7 @@ module WebMock
77
29
  )
78
30
  end
79
31
 
80
- def _build_webmock_response(_request, response)
32
+ def build_webmock_response(_request, response)
81
33
  webmock_response = WebMock::Response.new
82
34
  webmock_response.status = [response.status, HTTP_REASONS[response.status]]
83
35
  webmock_response.body = response.body.to_s
@@ -85,10 +37,10 @@ module WebMock
85
37
  webmock_response
86
38
  end
87
39
 
88
- def _build_from_webmock_response(request, webmock_response)
89
- return _build_error_response(request, HTTPX::TimeoutError.new(1, "Timed out")) if webmock_response.should_timeout
40
+ def build_from_webmock_response(request, webmock_response)
41
+ return build_error_response(request, HTTPX::TimeoutError.new(1, "Timed out")) if webmock_response.should_timeout
90
42
 
91
- return _build_error_response(request, webmock_response.exception) if webmock_response.exception
43
+ return build_error_response(request, webmock_response.exception) if webmock_response.exception
92
44
 
93
45
  response = request.options.response_class.new(request,
94
46
  webmock_response.status[0],
@@ -98,10 +50,70 @@ module WebMock
98
50
  response
99
51
  end
100
52
 
101
- def _build_error_response(request, exception)
53
+ def build_error_response(request, exception)
102
54
  HTTPX::ErrorResponse.new(request, exception, request.options)
103
55
  end
104
56
  end
57
+
58
+ module InstanceMethods
59
+ def build_connection(*)
60
+ connection = super
61
+ connection.once(:unmock_connection) do
62
+ pool.__send__(:resolve_connection, connection)
63
+ pool.__send__(:unregister_connection, connection) unless connection.addresses
64
+ end
65
+ connection
66
+ end
67
+ end
68
+
69
+ module ConnectionMethods
70
+ def initialize(*)
71
+ super
72
+ @mocked = true
73
+ end
74
+
75
+ def open?
76
+ return true if @mocked
77
+
78
+ super
79
+ end
80
+
81
+ def interests
82
+ return if @mocked
83
+
84
+ super
85
+ end
86
+
87
+ def send(request)
88
+ request_signature = Plugin.build_webmock_request_signature(request)
89
+ WebMock::RequestRegistry.instance.requested_signatures.put(request_signature)
90
+
91
+ if (mock_response = WebMock::StubRegistry.instance.response_for_request(request_signature))
92
+ response = Plugin.build_from_webmock_response(request, mock_response)
93
+ WebMock::CallbackRegistry.invoke_callbacks({ lib: :httpx }, request_signature, mock_response)
94
+ log { "mocking #{request.uri} with #{mock_response.inspect}" }
95
+ request.response = response
96
+ request.emit(:response, response)
97
+ elsif WebMock.net_connect_allowed?(request_signature.uri)
98
+ if WebMock::CallbackRegistry.any_callbacks?
99
+ request.on(:response) do |resp|
100
+ unless resp.is_a?(HTTPX::ErrorResponse)
101
+ webmock_response = Plugin.build_webmock_response(request, resp)
102
+ WebMock::CallbackRegistry.invoke_callbacks(
103
+ { lib: :httpx, real_request: true }, request_signature,
104
+ webmock_response
105
+ )
106
+ end
107
+ end
108
+ end
109
+ @mocked = false
110
+ emit(:unmock_connection, self)
111
+ super
112
+ else
113
+ raise WebMock::NetConnectNotAllowedError, request_signature
114
+ end
115
+ end
116
+ end
105
117
  end
106
118
 
107
119
  class HttpxAdapter < HttpLibAdapter
@@ -109,12 +121,12 @@ module WebMock
109
121
 
110
122
  class << self
111
123
  def enable!
112
- @original_session = ::HTTPX::Session
124
+ @original_session = HTTPX::Session
113
125
 
114
- webmock_session = ::HTTPX.plugin(Plugin)
126
+ webmock_session = HTTPX.plugin(Plugin)
115
127
 
116
- ::HTTPX.send(:remove_const, :Session)
117
- ::HTTPX.send(:const_set, :Session, webmock_session.class)
128
+ HTTPX.send(:remove_const, :Session)
129
+ HTTPX.send(:const_set, :Session, webmock_session.class)
118
130
  end
119
131
 
120
132
  def disable!
data/lib/httpx/altsvc.rb CHANGED
@@ -70,7 +70,7 @@ module HTTPX
70
70
 
71
71
  scanner = StringScanner.new(altsvc)
72
72
  until scanner.eos?
73
- alt_origin = scanner.scan(/[^=]+=("[^"]+"|[^;,]+)/)
73
+ alt_service = scanner.scan(/[^=]+=("[^"]+"|[^;,]+)/)
74
74
 
75
75
  alt_params = []
76
76
  loop do
@@ -80,29 +80,45 @@ module HTTPX
80
80
  break if scanner.eos? || scanner.scan(/ *, */)
81
81
  end
82
82
  alt_params = Hash[alt_params.map { |field| field.split("=") }]
83
- yield(parse_altsvc_origin(alt_origin), alt_params)
83
+
84
+ alt_proto, alt_authority = alt_service.split("=")
85
+ alt_origin = parse_altsvc_origin(alt_proto, alt_authority)
86
+ return unless alt_origin
87
+
88
+ yield(alt_origin, alt_params.merge("proto" => alt_proto))
89
+ end
90
+ end
91
+
92
+ def parse_altsvc_scheme(alt_proto)
93
+ case alt_proto
94
+ when "h2c"
95
+ "http"
96
+ when "h2"
97
+ "https"
84
98
  end
85
99
  end
86
100
 
87
101
  # :nocov:
88
102
  if RUBY_VERSION < "2.2"
89
- def parse_altsvc_origin(alt_origin)
90
- alt_proto, alt_origin = alt_origin.split("=")
103
+ def parse_altsvc_origin(alt_proto, alt_origin)
104
+ alt_scheme = parse_altsvc_scheme(alt_proto) or return
105
+
91
106
  alt_origin = alt_origin[1..-2] if alt_origin.start_with?("\"") && alt_origin.end_with?("\"")
92
107
  if alt_origin.start_with?(":")
93
- alt_origin = "#{alt_proto}://dummy#{alt_origin}"
108
+ alt_origin = "#{alt_scheme}://dummy#{alt_origin}"
94
109
  uri = URI.parse(alt_origin)
95
110
  uri.host = nil
96
111
  uri
97
112
  else
98
- URI.parse("#{alt_proto}://#{alt_origin}")
113
+ URI.parse("#{alt_scheme}://#{alt_origin}")
99
114
  end
100
115
  end
101
116
  else
102
- def parse_altsvc_origin(alt_origin)
103
- alt_proto, alt_origin = alt_origin.split("=")
117
+ def parse_altsvc_origin(alt_proto, alt_origin)
118
+ alt_scheme = parse_altsvc_scheme(alt_proto) or return
104
119
  alt_origin = alt_origin[1..-2] if alt_origin.start_with?("\"") && alt_origin.end_with?("\"")
105
- URI.parse("#{alt_proto}://#{alt_origin}")
120
+
121
+ URI.parse("#{alt_scheme}://#{alt_origin}")
106
122
  end
107
123
  end
108
124
  # :nocov:
data/lib/httpx/io/udp.rb CHANGED
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "socket"
4
3
  require "ipaddr"
5
4
 
6
5
  module HTTPX
@@ -8,11 +8,19 @@ module HTTPX
8
8
  DEFAULT_MIMETYPE = "application/octet-stream"
9
9
 
10
10
  # inspired by https://github.com/shrinerb/shrine/blob/master/lib/shrine/plugins/determine_mime_type.rb
11
- if defined?(MIME::Types)
11
+ if defined?(FileMagic)
12
+ def call(file, _)
13
+ return nil if file.eof? # FileMagic returns "application/x-empty" for empty files
14
+
15
+ FileMagic.open(FileMagic::MAGIC_MIME_TYPE) do |filemagic|
16
+ filemagic.buffer(file.read(MAGIC_NUMBER))
17
+ end
18
+ end
19
+ elsif defined?(Marcel)
20
+ def call(file, filename)
21
+ return nil if file.eof? # marcel returns "application/octet-stream" for empty files
12
22
 
13
- def call(_file, filename)
14
- mime = MIME::Types.of(filename).first
15
- mime.content_type if mime
23
+ Marcel::MimeType.for(file, name: filename)
16
24
  end
17
25
 
18
26
  elsif defined?(MimeMagic)
data/lib/httpx/session.rb CHANGED
@@ -163,7 +163,7 @@ module HTTPX
163
163
  case uri.scheme
164
164
  when "http"
165
165
  "tcp"
166
- when "https", "h2"
166
+ when "https"
167
167
  "ssl"
168
168
  else
169
169
  raise UnsupportedSchemeError, "#{uri}: #{uri.scheme}: unsupported URI scheme"
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.3"
4
+ VERSION = "0.18.4"
5
5
  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: 0.18.3
4
+ version: 0.18.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tiago Cardoso
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-12-14 00:00:00.000000000 Z
11
+ date: 2021-12-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: http-2-next
@@ -66,6 +66,7 @@ extra_rdoc_files:
66
66
  - doc/release_notes/0_18_1.md
67
67
  - doc/release_notes/0_18_2.md
68
68
  - doc/release_notes/0_18_3.md
69
+ - doc/release_notes/0_18_4.md
69
70
  - doc/release_notes/0_1_0.md
70
71
  - doc/release_notes/0_2_0.md
71
72
  - doc/release_notes/0_2_1.md
@@ -125,6 +126,7 @@ files:
125
126
  - doc/release_notes/0_18_1.md
126
127
  - doc/release_notes/0_18_2.md
127
128
  - doc/release_notes/0_18_3.md
129
+ - doc/release_notes/0_18_4.md
128
130
  - doc/release_notes/0_1_0.md
129
131
  - doc/release_notes/0_2_0.md
130
132
  - doc/release_notes/0_2_1.md
@@ -322,7 +324,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
322
324
  - !ruby/object:Gem::Version
323
325
  version: '0'
324
326
  requirements: []
325
- rubygems_version: 3.2.22
327
+ rubygems_version: 3.3.3
326
328
  signing_key:
327
329
  specification_version: 4
328
330
  summary: HTTPX, to the future, and beyond