faraday 1.3.0 → 1.4.3

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: 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