faraday 1.3.0 → 1.4.3

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: f907e999805c78e8c2e77cb4d6b29a0a6f8fd8fe1a7df9930f02be7ec113a630
4
- data.tar.gz: 1ceac7f2b44d41d11d1ad3f7642daea9198ba3ac2f71a4932cf7d6b7b867f704
3
+ metadata.gz: bc82ee913ef4ffa6468eea5e656c49d4eab0f769321eb63a85fdb4caa9a90143
4
+ data.tar.gz: cccf1ae40f10f353ca2cc5e4e92bb01e4b1d65a9ac3ed50b7e5c5951680ba019
5
5
  SHA512:
6
- metadata.gz: 2eaf54d743ed3d7554fb2be17003e81b08c060582f140b06fb55e8aceb7dfcf2428e898f47f3760eddcdb4ad64aeaacc93916672b9ed10fa4a004d0ea1a20e1a
7
- data.tar.gz: a700f0bc8999e9a0152843bbfbd23001fa23931614ae9573b13ff839e7af01f38ca2adbb01c82e4b387688496b3caff87357e3946d7500595a2f807313e177d7
6
+ metadata.gz: 5360375fef63ad3e1068792458dd8ccc956b603be65e5571b9b6ce73938fc7ba2d220aeb99d4f3b022fa9945a07c8264c6452916e58963883fdd6843ded2d58e
7
+ data.tar.gz: fac76254509856e7e827fecda3c76a070c53cdb1771782ff5d668f14a4b8a4b126be9761d0856fe28eccda4607daf031cb7c8ea4559e245b100db08e95f01a0a
data/CHANGELOG.md CHANGED
@@ -1,5 +1,35 @@
1
1
  # Faraday Changelog
2
2
 
3
+ ## [v1.3.0](https://github.com/lostisland/faraday/releases/tag/v1.3.0) (2020-12-31)
4
+
5
+ ### Highlights
6
+ Faraday v1.3.0 is the first release to officially support Ruby 3.0 in the CI pipeline 🎉 🍾!
7
+
8
+ This is also the first release with a previously "included" adapter (Net::HTTP) being isolated into a [separate gem](https://github.com/lostisland/faraday-net_http) 🎊!
9
+ The new adapter is added to Faraday as a dependency for now, so that means full backwards-compatibility, but just to be safe be careful when upgrading!
10
+
11
+ This is a huge step towards are Faraday v2.0 objective of pushing adapters and middleware into separate gems.
12
+ Many thanks to the Faraday Team, @JanDintel and everyone who attended the [ROSS Conf remote event](https://www.rossconf.io/event/remote/)
13
+
14
+ ### Features
15
+
16
+ * Improves consistency with Faraday::Error and Faraday::RaiseError (#1229, @qsona, @iMacTia)
17
+
18
+ ### Fixes
19
+
20
+ * Don't assign to global ::Timer (#1227, @bpo)
21
+
22
+ ### Documentation
23
+
24
+ * CHANGELOG: add releases after 1.0 (#1225, @olleolleolle)
25
+ * Improves retry middleware documentation. (#1228, @iMacTia)
26
+
27
+ ### Misc
28
+
29
+ * Move out Net::HTTP adapter (#1222, @JanDintel, @iMacTia)
30
+ * Adds Ruby 3.0 to CI Matrix (#1226, @iMacTia)
31
+
32
+
3
33
  ## [v1.2.0](https://github.com/lostisland/faraday/releases/tag/v1.2.0) (2020-12-23)
4
34
 
5
35
  ### Features
data/README.md CHANGED
@@ -2,7 +2,6 @@
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/faraday.svg)](https://rubygems.org/gems/faraday)
4
4
  [![GitHub Actions CI](https://github.com/lostisland/faraday/workflows/CI/badge.svg)](https://github.com/lostisland/faraday/actions?query=workflow%3ACI)
5
- [![Maintainability](https://api.codeclimate.com/v1/badges/f869daab091ceef1da73/maintainability)](https://codeclimate.com/github/lostisland/faraday/maintainability)
6
5
  [![Gitter](https://badges.gitter.im/lostisland/faraday.svg)](https://gitter.im/lostisland/faraday?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
7
6
 
8
7
 
@@ -47,7 +46,7 @@ But before you start coding, please read our [Contributing Guide][contributing]
47
46
  [website]: https://lostisland.github.io/faraday
48
47
  [faraday_team]: https://lostisland.github.io/faraday/team
49
48
  [contributing]: https://github.com/lostisland/faraday/blob/master/.github/CONTRIBUTING.md
50
- [apidoc]: http://www.rubydoc.info/gems/faraday
49
+ [apidoc]: https://www.rubydoc.info/github/lostisland/faraday
51
50
  [actions]: https://github.com/lostisland/faraday/actions
52
51
  [jruby]: http://jruby.org/
53
52
  [rubinius]: http://rubini.us/
data/lib/faraday.rb CHANGED
@@ -27,7 +27,11 @@ require 'faraday/error'
27
27
  require 'faraday/file_part'
28
28
  require 'faraday/param_part'
29
29
 
30
+ require 'faraday/em_http'
31
+ require 'faraday/em_synchrony'
32
+ require 'faraday/excon'
30
33
  require 'faraday/net_http'
34
+ require 'faraday/net_http_persistent'
31
35
 
32
36
  # This is the main namespace for Faraday.
33
37
  #
@@ -11,15 +11,8 @@ module Faraday
11
11
 
12
12
  register_middleware File.expand_path('adapter', __dir__),
13
13
  test: [:Test, 'test'],
14
- net_http_persistent: [
15
- :NetHttpPersistent,
16
- 'net_http_persistent'
17
- ],
18
14
  typhoeus: [:Typhoeus, 'typhoeus'],
19
15
  patron: [:Patron, 'patron'],
20
- em_synchrony: [:EMSynchrony, 'em_synchrony'],
21
- em_http: [:EMHttp, 'em_http'],
22
- excon: [:Excon, 'excon'],
23
16
  rack: [:Rack, 'rack'],
24
17
  httpclient: [:HTTPClient, 'httpclient']
25
18
 
@@ -58,12 +58,8 @@ module Faraday
58
58
  class Adapter
59
59
  extend AutoloadHelper
60
60
  autoload_all 'faraday/adapter',
61
- NetHttpPersistent: 'net_http_persistent',
62
- EMSynchrony: 'em_synchrony',
63
- EMHttp: 'em_http',
64
61
  Typhoeus: 'typhoeus',
65
62
  Patron: 'patron',
66
- Excon: 'excon',
67
63
  Test: 'test',
68
64
  Rack: 'rack',
69
65
  HTTPClient: 'httpclient'
@@ -73,6 +73,7 @@ module Faraday
73
73
  @options = options.request
74
74
  @ssl = options.ssl
75
75
  @default_parallel_manager = options.parallel_manager
76
+ @manual_proxy = nil
76
77
 
77
78
  @builder = options.builder || begin
78
79
  # pass an empty block to Builder so it doesn't assume default middleware
@@ -419,6 +420,8 @@ module Faraday
419
420
  basic_auth user, password
420
421
  uri.user = uri.password = nil
421
422
  end
423
+
424
+ @proxy = proxy_from_env(url) unless @manual_proxy
422
425
  end
423
426
 
424
427
  # Sets the path prefix and ensures that it always has a leading
@@ -517,11 +520,11 @@ module Faraday
517
520
  # @return [URI]
518
521
  def build_exclusive_url(url = nil, params = nil, params_encoder = nil)
519
522
  url = nil if url.respond_to?(:empty?) && url.empty?
520
- base = url_prefix
523
+ base = url_prefix.dup
521
524
  if url && base.path && base.path !~ %r{/$}
522
- base = base.dup
523
525
  base.path = "#{base.path}/" # ensure trailing slash
524
526
  end
527
+ url = url && URI.parse(url.to_s).opaque ? url.to_s.gsub(':', '%3A') : url
525
528
  uri = url ? base + url : base
526
529
  if params
527
530
  uri.query = params.to_query(params_encoder || options.params_encoder)
@@ -576,7 +579,11 @@ module Faraday
576
579
  case url
577
580
  when String
578
581
  uri = Utils.URI(url)
579
- uri = URI.parse("#{uri.scheme}://#{uri.hostname}").find_proxy
582
+ uri = if uri.host.nil?
583
+ find_default_proxy
584
+ else
585
+ URI.parse("#{uri.scheme}://#{uri.host}").find_proxy
586
+ end
580
587
  when URI
581
588
  uri = url.find_proxy
582
589
  when nil
@@ -11,6 +11,9 @@ module Faraday
11
11
  def self.from(value)
12
12
  case value
13
13
  when String
14
+ # URIs without a scheme should default to http (like 'example:123').
15
+ # This fixes #1282 and prevents a silent failure in some adapters.
16
+ value = "http://#{value}" unless value.include?('://')
14
17
  value = { uri: Utils.URI(value) }
15
18
  when URI
16
19
  value = { uri: value }
@@ -19,6 +22,7 @@ module Faraday
19
22
  value[:uri] = Utils.URI(uri)
20
23
  end
21
24
  end
25
+
22
26
  super(value)
23
27
  end
24
28
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Faraday
4
- VERSION = '1.3.0'
4
+ VERSION = '1.4.3'
5
5
  end
@@ -18,6 +18,13 @@ shared_examples 'initializer with url' do
18
18
  it { expect(subject.path_prefix).to eq('/fish') }
19
19
  it { expect(subject.params).to eq('a' => '1') }
20
20
  end
21
+
22
+ context 'with IPv6 address' do
23
+ let(:address) { 'http://[::1]:85/' }
24
+
25
+ it { expect(subject.host).to eq('[::1]') }
26
+ it { expect(subject.port).to eq(85) }
27
+ end
21
28
  end
22
29
 
23
30
  shared_examples 'default connection options' do
@@ -246,6 +253,13 @@ RSpec.describe Faraday::Connection do
246
253
  expect(uri.path).to eq('/sake.html')
247
254
  end
248
255
 
256
+ it 'always returns new URI instance' do
257
+ conn.url_prefix = 'http://sushi.com'
258
+ uri1 = conn.build_exclusive_url(nil)
259
+ uri2 = conn.build_exclusive_url(nil)
260
+ expect(uri1).not_to equal(uri2)
261
+ end
262
+
249
263
  context 'with url_prefixed connection' do
250
264
  let(:url) { 'http://sushi.com/sushi/' }
251
265
 
@@ -270,6 +284,29 @@ RSpec.describe Faraday::Connection do
270
284
  expect(uri.to_s).to eq('http://sushi.com/sake/')
271
285
  end
272
286
  end
287
+
288
+ context 'with colon in path' do
289
+ let(:url) { 'http://service.com' }
290
+
291
+ it 'joins url to base when used absolute path' do
292
+ conn = Faraday.new(url: url)
293
+ uri = conn.build_exclusive_url('/service:search?limit=400')
294
+ expect(uri.to_s).to eq('http://service.com/service:search?limit=400')
295
+ end
296
+
297
+ it 'joins url to base when used relative path' do
298
+ conn = Faraday.new(url: url)
299
+ uri = conn.build_exclusive_url('service:search?limit=400')
300
+ expect(uri.to_s).to eq('http://service.com/service%3Asearch?limit=400')
301
+ end
302
+
303
+ it 'joins url to base when used with path prefix' do
304
+ conn = Faraday.new(url: url)
305
+ conn.path_prefix = '/api'
306
+ uri = conn.build_exclusive_url('service:search?limit=400')
307
+ expect(uri.to_s).to eq('http://service.com/api/service%3Asearch?limit=400')
308
+ end
309
+ end
273
310
  end
274
311
 
275
312
  describe '#build_url' do
@@ -412,6 +449,14 @@ RSpec.describe Faraday::Connection do
412
449
  end
413
450
  end
414
451
 
452
+ it 'allows when url in no proxy list with url_prefix' do
453
+ with_env 'http_proxy' => 'http://proxy.com', 'no_proxy' => 'example.com' do
454
+ conn = Faraday::Connection.new
455
+ conn.url_prefix = 'http://example.com'
456
+ expect(conn.proxy).to be_nil
457
+ end
458
+ end
459
+
415
460
  it 'allows when prefixed url is not in no proxy list' do
416
461
  with_env 'http_proxy' => 'http://proxy.com', 'no_proxy' => 'example.com' do
417
462
  conn = Faraday::Connection.new('http://prefixedexample.com')
@@ -14,6 +14,13 @@ RSpec.describe Faraday::ProxyOptions do
14
14
  expect(options.inspect).to match('#<Faraday::ProxyOptions uri=')
15
15
  end
16
16
 
17
+ it 'defaults to http' do
18
+ options = Faraday::ProxyOptions.from 'example.org'
19
+ expect(options.port).to eq(80)
20
+ expect(options.host).to eq('example.org')
21
+ expect(options.scheme).to eq('http')
22
+ end
23
+
17
24
  it 'works with nil' do
18
25
  options = Faraday::ProxyOptions.from nil
19
26
  expect(options).to be_a_kind_of(Faraday::ProxyOptions)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: faraday
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.0
4
+ version: 1.4.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - "@technoweenie"
@@ -10,8 +10,50 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2020-12-31 00:00:00.000000000 Z
13
+ date: 2021-06-24 00:00:00.000000000 Z
14
14
  dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: faraday-em_http
17
+ requirement: !ruby/object:Gem::Requirement
18
+ requirements:
19
+ - - "~>"
20
+ - !ruby/object:Gem::Version
21
+ version: '1.0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ requirements:
26
+ - - "~>"
27
+ - !ruby/object:Gem::Version
28
+ version: '1.0'
29
+ - !ruby/object:Gem::Dependency
30
+ name: faraday-em_synchrony
31
+ requirement: !ruby/object:Gem::Requirement
32
+ requirements:
33
+ - - "~>"
34
+ - !ruby/object:Gem::Version
35
+ version: '1.0'
36
+ type: :runtime
37
+ prerelease: false
38
+ version_requirements: !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - "~>"
41
+ - !ruby/object:Gem::Version
42
+ version: '1.0'
43
+ - !ruby/object:Gem::Dependency
44
+ name: faraday-excon
45
+ requirement: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - "~>"
48
+ - !ruby/object:Gem::Version
49
+ version: '1.1'
50
+ type: :runtime
51
+ prerelease: false
52
+ version_requirements: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - "~>"
55
+ - !ruby/object:Gem::Version
56
+ version: '1.1'
15
57
  - !ruby/object:Gem::Dependency
16
58
  name: faraday-net_http
17
59
  requirement: !ruby/object:Gem::Requirement
@@ -26,6 +68,20 @@ dependencies:
26
68
  - - "~>"
27
69
  - !ruby/object:Gem::Version
28
70
  version: '1.0'
71
+ - !ruby/object:Gem::Dependency
72
+ name: faraday-net_http_persistent
73
+ requirement: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - "~>"
76
+ - !ruby/object:Gem::Version
77
+ version: '1.1'
78
+ type: :runtime
79
+ prerelease: false
80
+ version_requirements: !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - "~>"
83
+ - !ruby/object:Gem::Version
84
+ version: '1.1'
29
85
  - !ruby/object:Gem::Dependency
30
86
  name: multipart-post
31
87
  requirement: !ruby/object:Gem::Requirement
@@ -52,14 +108,14 @@ dependencies:
52
108
  requirements:
53
109
  - - ">="
54
110
  - !ruby/object:Gem::Version
55
- version: '0'
111
+ version: 0.0.4
56
112
  type: :runtime
57
113
  prerelease: false
58
114
  version_requirements: !ruby/object:Gem::Requirement
59
115
  requirements:
60
116
  - - ">="
61
117
  - !ruby/object:Gem::Version
62
- version: '0'
118
+ version: 0.0.4
63
119
  description:
64
120
  email: technoweenie@gmail.com
65
121
  executables: []
@@ -74,13 +130,7 @@ files:
74
130
  - examples/client_test.rb
75
131
  - lib/faraday.rb
76
132
  - lib/faraday/adapter.rb
77
- - lib/faraday/adapter/em_http.rb
78
- - lib/faraday/adapter/em_http_ssl_patch.rb
79
- - lib/faraday/adapter/em_synchrony.rb
80
- - lib/faraday/adapter/em_synchrony/parallel_manager.rb
81
- - lib/faraday/adapter/excon.rb
82
133
  - lib/faraday/adapter/httpclient.rb
83
- - lib/faraday/adapter/net_http_persistent.rb
84
134
  - lib/faraday/adapter/patron.rb
85
135
  - lib/faraday/adapter/rack.rb
86
136
  - lib/faraday/adapter/test.rb
@@ -126,7 +176,6 @@ files:
126
176
  - spec/faraday/adapter/em_synchrony_spec.rb
127
177
  - spec/faraday/adapter/excon_spec.rb
128
178
  - spec/faraday/adapter/httpclient_spec.rb
129
- - spec/faraday/adapter/net_http_persistent_spec.rb
130
179
  - spec/faraday/adapter/net_http_spec.rb
131
180
  - spec/faraday/adapter/patron_spec.rb
132
181
  - spec/faraday/adapter/rack_spec.rb
@@ -172,7 +221,7 @@ licenses:
172
221
  - MIT
173
222
  metadata:
174
223
  homepage_uri: https://lostisland.github.io/faraday
175
- changelog_uri: https://github.com/lostisland/faraday/releases/tag/v1.3.0
224
+ changelog_uri: https://github.com/lostisland/faraday/releases/tag/v1.4.3
176
225
  source_code_uri: https://github.com/lostisland/faraday
177
226
  bug_tracker_uri: https://github.com/lostisland/faraday/issues
178
227
  post_install_message:
@@ -191,7 +240,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
191
240
  - !ruby/object:Gem::Version
192
241
  version: '0'
193
242
  requirements: []
194
- rubygems_version: 3.0.3
243
+ rubygems_version: 3.0.3.1
195
244
  signing_key:
196
245
  specification_version: 4
197
246
  summary: HTTP/REST API client library.
@@ -1,289 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Faraday
4
- class Adapter
5
- # EventMachine adapter. This adapter is useful for either asynchronous
6
- # requests when in an EM reactor loop, or for making parallel requests in
7
- # synchronous code.
8
- class EMHttp < Faraday::Adapter
9
- # Options is a module containing helpers to convert the Faraday env object
10
- # into options hashes for EMHTTP method calls.
11
- module Options
12
- # @return [Hash]
13
- def connection_config(env)
14
- options = {}
15
- configure_proxy(options, env)
16
- configure_timeout(options, env)
17
- configure_socket(options, env)
18
- configure_ssl(options, env)
19
- options
20
- end
21
-
22
- def request_config(env)
23
- options = {
24
- body: read_body(env),
25
- head: env[:request_headers]
26
- # keepalive: true,
27
- # file: 'path/to/file', # stream data off disk
28
- }
29
- configure_compression(options, env)
30
- options
31
- end
32
-
33
- def read_body(env)
34
- body = env[:body]
35
- body.respond_to?(:read) ? body.read : body
36
- end
37
-
38
- # Reads out proxy settings from env into options
39
- def configure_proxy(options, env)
40
- proxy = request_options(env)[:proxy]
41
- return unless proxy
42
-
43
- options[:proxy] = {
44
- host: proxy[:uri].host,
45
- port: proxy[:uri].port,
46
- authorization: [proxy[:user], proxy[:password]]
47
- }
48
- end
49
-
50
- # Reads out host and port settings from env into options
51
- def configure_socket(options, env)
52
- bind = request_options(env)[:bind]
53
- return unless bind
54
-
55
- options[:bind] = {
56
- host: bind[:host],
57
- port: bind[:port]
58
- }
59
- end
60
-
61
- # Reads out SSL certificate settings from env into options
62
- def configure_ssl(options, env)
63
- return unless env[:url].scheme == 'https' && env[:ssl]
64
-
65
- options[:ssl] = {
66
- cert_chain_file: env[:ssl][:ca_file],
67
- verify_peer: env[:ssl].fetch(:verify, true)
68
- }
69
- end
70
-
71
- # Reads out timeout settings from env into options
72
- def configure_timeout(options, env)
73
- req = request_options(env)
74
- options[:inactivity_timeout] = request_timeout(:read, req)
75
- options[:connect_timeout] = request_timeout(:open, req)
76
- end
77
-
78
- # Reads out compression header settings from env into options
79
- def configure_compression(options, env)
80
- return unless (env[:method] == :get) &&
81
- !options[:head].key?('accept-encoding')
82
-
83
- options[:head]['accept-encoding'] = 'gzip, compressed'
84
- end
85
-
86
- def request_options(env)
87
- env[:request]
88
- end
89
- end
90
-
91
- include Options
92
-
93
- dependency do
94
- require 'em-http'
95
-
96
- begin
97
- require 'openssl'
98
- rescue LoadError
99
- warn 'Warning: no such file to load -- openssl. ' \
100
- 'Make sure it is installed if you want HTTPS support'
101
- else
102
- require 'em-http/version'
103
- if EventMachine::HttpRequest::VERSION < '1.1.6'
104
- require 'faraday/adapter/em_http_ssl_patch'
105
- end
106
- end
107
- end
108
-
109
- self.supports_parallel = true
110
-
111
- # @return [Manager]
112
- def self.setup_parallel_manager(_options = nil)
113
- Manager.new
114
- end
115
-
116
- def call(env)
117
- super
118
- perform_request env
119
- @app.call env
120
- end
121
-
122
- def perform_request(env)
123
- if parallel?(env)
124
- manager = env[:parallel_manager]
125
- manager.add do
126
- perform_single_request(env)
127
- .callback { env[:response].finish(env) }
128
- end
129
- elsif EventMachine.reactor_running?
130
- # EM is running: instruct upstream that this is an async request
131
- env[:parallel_manager] = true
132
- perform_single_request(env)
133
- .callback { env[:response].finish(env) }
134
- .errback do
135
- # TODO: no way to communicate the error in async mode
136
- raise NotImplementedError
137
- end
138
- else
139
- error = nil
140
- # start EM, block until request is completed
141
- EventMachine.run do
142
- perform_single_request(env)
143
- .callback { EventMachine.stop }
144
- .errback do |client|
145
- error = error_message(client)
146
- EventMachine.stop
147
- end
148
- end
149
- raise_error(error) if error
150
- end
151
- rescue EventMachine::Connectify::CONNECTError => e
152
- if e.message.include?('Proxy Authentication Required')
153
- raise Faraday::ConnectionFailed,
154
- %(407 "Proxy Authentication Required ")
155
- end
156
-
157
- raise Faraday::ConnectionFailed, e
158
- rescue StandardError => e
159
- if defined?(::OpenSSL::SSL::SSLError) && \
160
- e.is_a?(::OpenSSL::SSL::SSLError)
161
- raise Faraday::SSLError, e
162
- end
163
-
164
- raise
165
- end
166
-
167
- # TODO: reuse the connection to support pipelining
168
- def perform_single_request(env)
169
- req = create_request(env)
170
- req = req.setup_request(env[:method], request_config(env))
171
- req.callback do |client|
172
- if env[:request].stream_response?
173
- warn "Streaming downloads for #{self.class.name} " \
174
- 'are not yet implemented.'
175
- env[:request].on_data.call(
176
- client.response,
177
- client.response.bytesize
178
- )
179
- end
180
- status = client.response_header.status
181
- reason = client.response_header.http_reason
182
- save_response(env, status, client.response, nil, reason) do |headers|
183
- client.response_header.each do |name, value|
184
- headers[name.to_sym] = value
185
- end
186
- end
187
- end
188
- end
189
-
190
- def create_request(env)
191
- EventMachine::HttpRequest.new(
192
- env[:url], connection_config(env).merge(@connection_options)
193
- )
194
- end
195
-
196
- def error_message(client)
197
- client.error || 'request failed'
198
- end
199
-
200
- def raise_error(msg)
201
- error_class = Faraday::ClientError
202
- if timeout_message?(msg)
203
- error_class = Faraday::TimeoutError
204
- msg = 'request timed out'
205
- elsif msg == Errno::ECONNREFUSED
206
- error_class = Faraday::ConnectionFailed
207
- msg = 'connection refused'
208
- elsif msg == 'connection closed by server'
209
- error_class = Faraday::ConnectionFailed
210
- end
211
- raise error_class, msg
212
- end
213
-
214
- def timeout_message?(msg)
215
- msg == Errno::ETIMEDOUT ||
216
- (msg.is_a?(String) && msg.include?('timeout error'))
217
- end
218
-
219
- # @return [Boolean]
220
- def parallel?(env)
221
- !!env[:parallel_manager]
222
- end
223
-
224
- # This parallel manager is designed to start an EventMachine loop
225
- # and block until all registered requests have been completed.
226
- class Manager
227
- # @see reset
228
- def initialize
229
- reset
230
- end
231
-
232
- # Re-initializes instance variables
233
- def reset
234
- @registered_procs = []
235
- @num_registered = 0
236
- @num_succeeded = 0
237
- @errors = []
238
- @running = false
239
- end
240
-
241
- # @return [Boolean]
242
- def running?
243
- @running
244
- end
245
-
246
- def add(&block)
247
- if running?
248
- perform_request(&block)
249
- else
250
- @registered_procs << block
251
- end
252
- @num_registered += 1
253
- end
254
-
255
- def run
256
- if @num_registered.positive?
257
- @running = true
258
- EventMachine.run do
259
- @registered_procs.each do |proc|
260
- perform_request(&proc)
261
- end
262
- end
263
- unless @errors.empty?
264
- raise Faraday::ClientError, @errors.first || 'connection failed'
265
- end
266
- end
267
- ensure
268
- reset
269
- end
270
-
271
- def perform_request
272
- client = yield
273
- client.callback do
274
- @num_succeeded += 1
275
- check_finished
276
- end
277
- client.errback do
278
- @errors << client.error
279
- check_finished
280
- end
281
- end
282
-
283
- def check_finished
284
- EventMachine.stop if @num_succeeded + @errors.size == @num_registered
285
- end
286
- end
287
- end
288
- end
289
- end
@@ -1,62 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'openssl'
4
- require 'em-http'
5
-
6
- # EventMachine patch to make SSL work.
7
- module EmHttpSslPatch
8
- def ssl_verify_peer(cert_string)
9
- begin
10
- @last_seen_cert = OpenSSL::X509::Certificate.new(cert_string)
11
- rescue OpenSSL::X509::CertificateError
12
- return false
13
- end
14
-
15
- unless certificate_store.verify(@last_seen_cert)
16
- raise OpenSSL::SSL::SSLError,
17
- %(unable to verify the server certificate for "#{host}")
18
- end
19
-
20
- begin
21
- certificate_store.add_cert(@last_seen_cert)
22
- rescue OpenSSL::X509::StoreError => e
23
- raise e unless e.message == 'cert already in hash table'
24
- end
25
- true
26
- end
27
-
28
- def ssl_handshake_completed
29
- return true unless verify_peer?
30
-
31
- unless verified_cert_identity?
32
- raise OpenSSL::SSL::SSLError,
33
- %(host "#{host}" does not match the server certificate)
34
- end
35
-
36
- true
37
- end
38
-
39
- def verify_peer?
40
- parent.connopts.tls[:verify_peer]
41
- end
42
-
43
- def verified_cert_identity?
44
- OpenSSL::SSL.verify_certificate_identity(@last_seen_cert, host)
45
- end
46
-
47
- def host
48
- parent.uri.host
49
- end
50
-
51
- def certificate_store
52
- @certificate_store ||= begin
53
- store = OpenSSL::X509::Store.new
54
- store.set_default_paths
55
- ca_file = parent.connopts.tls[:cert_chain_file]
56
- store.add_file(ca_file) if ca_file
57
- store
58
- end
59
- end
60
- end
61
-
62
- EventMachine::HttpStubConnection.include(EmHttpSslPatch)
@@ -1,153 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'uri'
4
-
5
- module Faraday
6
- class Adapter
7
- # EventMachine Synchrony adapter.
8
- class EMSynchrony < Faraday::Adapter
9
- include EMHttp::Options
10
-
11
- dependency do
12
- require 'em-synchrony/em-http'
13
- require 'em-synchrony/em-multi'
14
- require 'fiber'
15
-
16
- require 'faraday/adapter/em_synchrony/parallel_manager'
17
-
18
- if Faraday::Adapter::EMSynchrony.loaded?
19
- begin
20
- require 'openssl'
21
- rescue LoadError
22
- warn 'Warning: no such file to load -- openssl. ' \
23
- 'Make sure it is installed if you want HTTPS support'
24
- else
25
- require 'em-http/version'
26
- if EventMachine::HttpRequest::VERSION < '1.1.6'
27
- require 'faraday/adapter/em_http_ssl_patch'
28
- end
29
- end
30
- end
31
- end
32
-
33
- self.supports_parallel = true
34
-
35
- # @return [ParallelManager]
36
- def self.setup_parallel_manager(_options = nil)
37
- ParallelManager.new
38
- end
39
-
40
- def call(env)
41
- super
42
- request = create_request(env)
43
-
44
- http_method = env[:method].to_s.downcase.to_sym
45
-
46
- if env[:parallel_manager]
47
- # Queue requests for parallel execution.
48
- execute_parallel_request(env, request, http_method)
49
- else
50
- # Execute single request.
51
- execute_single_request(env, request, http_method)
52
- end
53
-
54
- @app.call env
55
- rescue Errno::ECONNREFUSED
56
- raise Faraday::ConnectionFailed, $ERROR_INFO
57
- rescue EventMachine::Connectify::CONNECTError => e
58
- if e.message.include?('Proxy Authentication Required')
59
- raise Faraday::ConnectionFailed,
60
- %(407 "Proxy Authentication Required")
61
- end
62
-
63
- raise Faraday::ConnectionFailed, e
64
- rescue Errno::ETIMEDOUT => e
65
- raise Faraday::TimeoutError, e
66
- rescue RuntimeError => e
67
- if e.message == 'connection closed by server'
68
- raise Faraday::ConnectionFailed, e
69
- end
70
-
71
- raise Faraday::TimeoutError, e if e.message.include?('timeout error')
72
-
73
- raise
74
- rescue StandardError => e
75
- if defined?(OpenSSL) && e.is_a?(OpenSSL::SSL::SSLError)
76
- raise Faraday::SSLError, e
77
- end
78
-
79
- raise
80
- end
81
-
82
- def create_request(env)
83
- EventMachine::HttpRequest.new(
84
- Utils::URI(env[:url].to_s),
85
- connection_config(env).merge(@connection_options)
86
- )
87
- end
88
-
89
- private
90
-
91
- def execute_parallel_request(env, request, http_method)
92
- env[:parallel_manager].add(request, http_method,
93
- request_config(env)) do |resp|
94
- if (req = env[:request]).stream_response?
95
- warn "Streaming downloads for #{self.class.name} " \
96
- 'are not yet implemented.'
97
- req.on_data.call(resp.response, resp.response.bytesize)
98
- end
99
-
100
- save_response(env, resp.response_header.status,
101
- resp.response) do |resp_headers|
102
- resp.response_header.each do |name, value|
103
- resp_headers[name.to_sym] = value
104
- end
105
- end
106
-
107
- # Finalize the response object with values from `env`.
108
- env[:response].finish(env)
109
- end
110
- end
111
-
112
- def execute_single_request(env, request, http_method)
113
- block = -> { request.send(http_method, request_config(env)) }
114
- client = call_block(block)
115
-
116
- raise client.error if client&.error
117
-
118
- if env[:request].stream_response?
119
- warn "Streaming downloads for #{self.class.name} " \
120
- 'are not yet implemented.'
121
- env[:request].on_data.call(
122
- client.response,
123
- client.response.bytesize
124
- )
125
- end
126
- status = client.response_header.status
127
- reason = client.response_header.http_reason
128
- save_response(env, status, client.response, nil, reason) do |headers|
129
- client.response_header.each do |name, value|
130
- headers[name.to_sym] = value
131
- end
132
- end
133
- end
134
-
135
- def call_block(block)
136
- client = nil
137
-
138
- if EM.reactor_running?
139
- client = block.call
140
- else
141
- EM.run do
142
- Fiber.new do
143
- client = block.call
144
- EM.stop
145
- end.resume
146
- end
147
- end
148
-
149
- client
150
- end
151
- end
152
- end
153
- end
@@ -1,69 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Faraday
4
- class Adapter
5
- class EMSynchrony < Faraday::Adapter
6
- # A parallel manager for EMSynchrony.
7
- class ParallelManager
8
- # Add requests to queue.
9
- #
10
- # @param request [EM::HttpRequest]
11
- # @param method [Symbol, String] HTTP method
12
- # @param args [Array] the rest of the positional arguments
13
- def add(request, method, *args, &block)
14
- queue << {
15
- request: request,
16
- method: method,
17
- args: args,
18
- block: block
19
- }
20
- end
21
-
22
- # Run all requests on queue with `EM::Synchrony::Multi`, wrapping
23
- # it in a reactor and fiber if needed.
24
- def run
25
- result = nil
26
- if !EM.reactor_running?
27
- EM.run do
28
- Fiber.new do
29
- result = perform
30
- EM.stop
31
- end.resume
32
- end
33
- else
34
- result = perform
35
- end
36
- result
37
- end
38
-
39
- private
40
-
41
- # The request queue.
42
- def queue
43
- @queue ||= []
44
- end
45
-
46
- # Main `EM::Synchrony::Multi` performer.
47
- def perform
48
- multi = ::EM::Synchrony::Multi.new
49
-
50
- queue.each do |item|
51
- method = "a#{item[:method]}".to_sym
52
-
53
- req = item[:request].send(method, *item[:args])
54
- req.callback(&item[:block])
55
-
56
- req_name = "req_#{multi.requests.size}".to_sym
57
- multi.add(req_name, req)
58
- end
59
-
60
- # Clear the queue, so parallel manager objects can be reused.
61
- @queue = []
62
-
63
- # Block fiber until all requests have returned.
64
- multi.perform
65
- end
66
- end
67
- end
68
- end
69
- end
@@ -1,124 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Faraday
4
- class Adapter
5
- # Excon adapter.
6
- class Excon < Faraday::Adapter
7
- dependency 'excon'
8
-
9
- def build_connection(env)
10
- opts = opts_from_env(env)
11
- ::Excon.new(env[:url].to_s, opts.merge(@connection_options))
12
- end
13
-
14
- def call(env)
15
- super
16
-
17
- req_opts = {
18
- method: env[:method].to_s.upcase,
19
- headers: env[:request_headers],
20
- body: read_body(env)
21
- }
22
-
23
- req = env[:request]
24
- if req&.stream_response?
25
- total = 0
26
- req_opts[:response_block] = lambda do |chunk, _remain, _total|
27
- req.on_data.call(chunk, total += chunk.size)
28
- end
29
- end
30
-
31
- resp = connection(env) { |http| http.request(req_opts) }
32
- save_response(env, resp.status.to_i, resp.body, resp.headers,
33
- resp.reason_phrase)
34
-
35
- @app.call(env)
36
- rescue ::Excon::Errors::SocketError => e
37
- raise Faraday::TimeoutError, e if e.message.match?(/\btimeout\b/)
38
-
39
- raise Faraday::SSLError, e if e.message.match?(/\bcertificate\b/)
40
-
41
- raise Faraday::ConnectionFailed, e
42
- rescue ::Excon::Errors::Timeout => e
43
- raise Faraday::TimeoutError, e
44
- end
45
-
46
- # TODO: support streaming requests
47
- def read_body(env)
48
- env[:body].respond_to?(:read) ? env[:body].read : env[:body]
49
- end
50
-
51
- private
52
-
53
- def opts_from_env(env)
54
- opts = {}
55
- amend_opts_with_ssl!(opts, env[:ssl]) if needs_ssl_settings?(env)
56
-
57
- if (req = env[:request])
58
- amend_opts_with_timeouts!(opts, req)
59
- amend_opts_with_proxy_settings!(opts, req)
60
- end
61
-
62
- opts
63
- end
64
-
65
- def needs_ssl_settings?(env)
66
- env[:url].scheme == 'https' && env[:ssl]
67
- end
68
-
69
- OPTS_KEYS = [
70
- %i[client_cert client_cert],
71
- %i[client_key client_key],
72
- %i[certificate certificate],
73
- %i[private_key private_key],
74
- %i[ssl_ca_path ca_path],
75
- %i[ssl_ca_file ca_file],
76
- %i[ssl_version version],
77
- %i[ssl_min_version min_version],
78
- %i[ssl_max_version max_version]
79
- ].freeze
80
-
81
- def amend_opts_with_ssl!(opts, ssl)
82
- opts[:ssl_verify_peer] = !!ssl.fetch(:verify, true)
83
- # https://github.com/geemus/excon/issues/106
84
- # https://github.com/jruby/jruby-ossl/issues/19
85
- opts[:nonblock] = false
86
-
87
- OPTS_KEYS.each do |(key_in_opts, key_in_ssl)|
88
- next unless ssl[key_in_ssl]
89
-
90
- opts[key_in_opts] = ssl[key_in_ssl]
91
- end
92
- end
93
-
94
- def amend_opts_with_timeouts!(opts, req)
95
- if (sec = request_timeout(:read, req))
96
- opts[:read_timeout] = sec
97
- end
98
-
99
- if (sec = request_timeout(:write, req))
100
- opts[:write_timeout] = sec
101
- end
102
-
103
- return unless (sec = request_timeout(:open, req))
104
-
105
- opts[:connect_timeout] = sec
106
- end
107
-
108
- def amend_opts_with_proxy_settings!(opts, req)
109
- opts[:proxy] = proxy_settings_for_opts(req[:proxy]) if req[:proxy]
110
- end
111
-
112
- def proxy_settings_for_opts(proxy)
113
- {
114
- host: proxy[:uri].host,
115
- hostname: proxy[:uri].hostname,
116
- port: proxy[:uri].port,
117
- scheme: proxy[:uri].scheme,
118
- user: proxy[:user],
119
- password: proxy[:password]
120
- }
121
- end
122
- end
123
- end
124
- end
@@ -1,91 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Faraday
4
- class Adapter
5
- # Net::HTTP::Persistent adapter.
6
- class NetHttpPersistent < NetHttp
7
- dependency 'net/http/persistent'
8
-
9
- private
10
-
11
- def net_http_connection(env)
12
- @cached_connection ||=
13
- if Net::HTTP::Persistent.instance_method(:initialize)
14
- .parameters.first == %i[key name]
15
- options = { name: 'Faraday' }
16
- if @connection_options.key?(:pool_size)
17
- options[:pool_size] = @connection_options[:pool_size]
18
- end
19
- Net::HTTP::Persistent.new(**options)
20
- else
21
- Net::HTTP::Persistent.new('Faraday')
22
- end
23
-
24
- proxy_uri = proxy_uri(env)
25
- if @cached_connection.proxy_uri != proxy_uri
26
- @cached_connection.proxy = proxy_uri
27
- end
28
- @cached_connection
29
- end
30
-
31
- def proxy_uri(env)
32
- proxy_uri = nil
33
- if (proxy = env[:request][:proxy])
34
- proxy_uri = if proxy[:uri].is_a?(::URI::HTTP)
35
- proxy[:uri].dup
36
- else
37
- ::URI.parse(proxy[:uri].to_s)
38
- end
39
- proxy_uri.user = proxy_uri.password = nil
40
- # awful patch for net-http-persistent 2.8
41
- # not unescaping user/password
42
- if proxy[:user]
43
- (class << proxy_uri; self; end).class_eval do
44
- define_method(:user) { proxy[:user] }
45
- define_method(:password) { proxy[:password] }
46
- end
47
- end
48
- end
49
- proxy_uri
50
- end
51
-
52
- def perform_request(http, env)
53
- http.request env[:url], create_request(env)
54
- rescue Errno::ETIMEDOUT, Net::OpenTimeout => e
55
- raise Faraday::TimeoutError, e
56
- rescue Net::HTTP::Persistent::Error => e
57
- raise Faraday::TimeoutError, e if e.message.include? 'Timeout'
58
-
59
- if e.message.include? 'connection refused'
60
- raise Faraday::ConnectionFailed, e
61
- end
62
-
63
- raise
64
- end
65
-
66
- SSL_CONFIGURATIONS = {
67
- certificate: :client_cert,
68
- private_key: :client_key,
69
- ca_file: :ca_file,
70
- ssl_version: :version,
71
- min_version: :min_version,
72
- max_version: :max_version
73
- }.freeze
74
-
75
- def configure_ssl(http, ssl)
76
- return unless ssl
77
-
78
- http_set(http, :verify_mode, ssl_verify_mode(ssl))
79
- http_set(http, :cert_store, ssl_cert_store(ssl))
80
-
81
- SSL_CONFIGURATIONS
82
- .select { |_, key| ssl[key] }
83
- .each { |target, key| http_set(http, target, ssl[key]) }
84
- end
85
-
86
- def http_set(http, attr, value)
87
- http.send("#{attr}=", value) if http.send(attr) != value
88
- end
89
- end
90
- end
91
- end
@@ -1,57 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- RSpec.describe Faraday::Adapter::NetHttpPersistent do
4
- features :request_body_on_query_methods, :reason_phrase_parse, :compression, :trace_method
5
-
6
- it_behaves_like 'an adapter'
7
-
8
- it 'allows to provide adapter specific configs' do
9
- url = URI('https://example.com')
10
-
11
- adapter = described_class.new do |http|
12
- http.idle_timeout = 123
13
- end
14
-
15
- http = adapter.send(:connection, url: url, request: {})
16
- adapter.send(:configure_request, http, {})
17
-
18
- expect(http.idle_timeout).to eq(123)
19
- end
20
-
21
- it 'sets max_retries to 0' do
22
- url = URI('http://example.com')
23
-
24
- adapter = described_class.new
25
-
26
- http = adapter.send(:connection, url: url, request: {})
27
- adapter.send(:configure_request, http, {})
28
-
29
- # `max_retries=` is only present in Ruby 2.5
30
- expect(http.max_retries).to eq(0) if http.respond_to?(:max_retries=)
31
- end
32
-
33
- it 'allows to set pool_size on initialize' do
34
- url = URI('https://example.com')
35
-
36
- adapter = described_class.new(nil, pool_size: 5)
37
-
38
- http = adapter.send(:connection, url: url, request: {})
39
-
40
- # `pool` is only present in net_http_persistent >= 3.0
41
- expect(http.pool.size).to eq(5) if http.respond_to?(:pool)
42
- end
43
-
44
- context 'min_version' do
45
- it 'allows to set min_version in SSL settings' do
46
- url = URI('https://example.com')
47
-
48
- adapter = described_class.new(nil)
49
-
50
- http = adapter.send(:connection, url: url, request: {})
51
- adapter.send(:configure_ssl, http, min_version: :TLS1_2)
52
-
53
- # `min_version` is only present in net_http_persistent >= 3.1 (UNRELEASED)
54
- expect(http.min_version).to eq(:TLS1_2) if http.respond_to?(:min_version)
55
- end
56
- end
57
- end