faraday 0.9.1 → 0.16.0

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.
Files changed (98) hide show
  1. checksums.yaml +5 -5
  2. data/LICENSE.md +1 -1
  3. data/README.md +30 -195
  4. data/lib/faraday/adapter/em_http.rb +148 -99
  5. data/lib/faraday/adapter/em_http_ssl_patch.rb +24 -18
  6. data/lib/faraday/adapter/em_synchrony/parallel_manager.rb +18 -15
  7. data/lib/faraday/adapter/em_synchrony.rb +107 -49
  8. data/lib/faraday/adapter/excon.rb +102 -55
  9. data/lib/faraday/adapter/httpclient.rb +80 -36
  10. data/lib/faraday/adapter/net_http.rb +119 -44
  11. data/lib/faraday/adapter/net_http_persistent.rb +68 -27
  12. data/lib/faraday/adapter/patron.rb +76 -34
  13. data/lib/faraday/adapter/rack.rb +28 -12
  14. data/lib/faraday/adapter/test.rb +136 -52
  15. data/lib/faraday/adapter/typhoeus.rb +7 -115
  16. data/lib/faraday/adapter.rb +43 -20
  17. data/lib/faraday/adapter_registry.rb +28 -0
  18. data/lib/faraday/autoload.rb +47 -36
  19. data/lib/faraday/connection.rb +359 -165
  20. data/lib/faraday/dependency_loader.rb +37 -0
  21. data/lib/faraday/encoders/flat_params_encoder.rb +94 -0
  22. data/lib/faraday/encoders/nested_params_encoder.rb +171 -0
  23. data/lib/faraday/error.rb +71 -24
  24. data/lib/faraday/file_part.rb +128 -0
  25. data/lib/faraday/logging/formatter.rb +92 -0
  26. data/lib/faraday/middleware.rb +4 -28
  27. data/lib/faraday/middleware_registry.rb +129 -0
  28. data/lib/faraday/options/connection_options.rb +22 -0
  29. data/lib/faraday/options/env.rb +181 -0
  30. data/lib/faraday/options/proxy_options.rb +28 -0
  31. data/lib/faraday/options/request_options.rb +21 -0
  32. data/lib/faraday/options/ssl_options.rb +59 -0
  33. data/lib/faraday/options.rb +57 -185
  34. data/lib/faraday/param_part.rb +53 -0
  35. data/lib/faraday/parameters.rb +4 -180
  36. data/lib/faraday/rack_builder.rb +74 -38
  37. data/lib/faraday/request/authorization.rb +42 -31
  38. data/lib/faraday/request/basic_authentication.rb +14 -7
  39. data/lib/faraday/request/instrumentation.rb +45 -27
  40. data/lib/faraday/request/multipart.rb +81 -45
  41. data/lib/faraday/request/retry.rb +212 -121
  42. data/lib/faraday/request/token_authentication.rb +15 -10
  43. data/lib/faraday/request/url_encoded.rb +41 -23
  44. data/lib/faraday/request.rb +84 -30
  45. data/lib/faraday/response/logger.rb +22 -48
  46. data/lib/faraday/response/raise_error.rb +36 -14
  47. data/lib/faraday/response.rb +29 -18
  48. data/lib/faraday/utils/headers.rb +139 -0
  49. data/lib/faraday/utils/params_hash.rb +61 -0
  50. data/lib/faraday/utils.rb +28 -216
  51. data/lib/faraday.rb +102 -204
  52. data/spec/external_adapters/faraday_specs_setup.rb +14 -0
  53. metadata +24 -94
  54. data/.document +0 -6
  55. data/CHANGELOG.md +0 -20
  56. data/CONTRIBUTING.md +0 -36
  57. data/Gemfile +0 -25
  58. data/Rakefile +0 -71
  59. data/faraday.gemspec +0 -34
  60. data/lib/faraday/upload_io.rb +0 -67
  61. data/script/cached-bundle +0 -46
  62. data/script/console +0 -7
  63. data/script/generate_certs +0 -42
  64. data/script/package +0 -7
  65. data/script/proxy-server +0 -42
  66. data/script/release +0 -17
  67. data/script/s3-put +0 -71
  68. data/script/server +0 -36
  69. data/script/test +0 -172
  70. data/test/adapters/default_test.rb +0 -14
  71. data/test/adapters/em_http_test.rb +0 -20
  72. data/test/adapters/em_synchrony_test.rb +0 -20
  73. data/test/adapters/excon_test.rb +0 -20
  74. data/test/adapters/httpclient_test.rb +0 -21
  75. data/test/adapters/integration.rb +0 -254
  76. data/test/adapters/logger_test.rb +0 -82
  77. data/test/adapters/net_http_persistent_test.rb +0 -20
  78. data/test/adapters/net_http_test.rb +0 -14
  79. data/test/adapters/patron_test.rb +0 -20
  80. data/test/adapters/rack_test.rb +0 -31
  81. data/test/adapters/test_middleware_test.rb +0 -114
  82. data/test/adapters/typhoeus_test.rb +0 -28
  83. data/test/authentication_middleware_test.rb +0 -65
  84. data/test/composite_read_io_test.rb +0 -111
  85. data/test/connection_test.rb +0 -522
  86. data/test/env_test.rb +0 -218
  87. data/test/helper.rb +0 -81
  88. data/test/live_server.rb +0 -67
  89. data/test/middleware/instrumentation_test.rb +0 -88
  90. data/test/middleware/retry_test.rb +0 -177
  91. data/test/middleware_stack_test.rb +0 -173
  92. data/test/multibyte.txt +0 -1
  93. data/test/options_test.rb +0 -252
  94. data/test/parameters_test.rb +0 -64
  95. data/test/request_middleware_test.rb +0 -142
  96. data/test/response_middleware_test.rb +0 -72
  97. data/test/strawberry.rb +0 -2
  98. data/test/utils_test.rb +0 -58
@@ -1,8 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Faraday
2
4
  class Adapter
5
+ # HTTPClient adapter.
3
6
  class HTTPClient < Faraday::Adapter
4
7
  dependency 'httpclient'
5
8
 
9
+ # @return [HTTPClient]
6
10
  def client
7
11
  @client ||= ::HTTPClient.new
8
12
  end
@@ -10,92 +14,132 @@ module Faraday
10
14
  def call(env)
11
15
  super
12
16
 
13
- if req = env[:request]
14
- if proxy = req[:proxy]
17
+ # enable compression
18
+ client.transparent_gzip_decompression = true
19
+
20
+ if (req = env[:request])
21
+ if (proxy = req[:proxy])
15
22
  configure_proxy proxy
16
23
  end
17
24
 
18
- if bind = req[:bind]
25
+ if (bind = req[:bind])
19
26
  configure_socket bind
20
27
  end
21
28
 
22
29
  configure_timeouts req
23
30
  end
24
31
 
25
- if env[:url].scheme == 'https' && ssl = env[:ssl]
32
+ if env[:url].scheme == 'https' && (ssl = env[:ssl])
26
33
  configure_ssl ssl
27
34
  end
28
35
 
29
- # TODO Don't stream yet.
36
+ configure_client
37
+
38
+ # TODO: Don't stream yet.
30
39
  # https://github.com/nahi/httpclient/pull/90
31
40
  env[:body] = env[:body].read if env[:body].respond_to? :read
32
41
 
33
42
  resp = client.request env[:method], env[:url],
34
- :body => env[:body],
35
- :header => env[:request_headers]
43
+ body: env[:body],
44
+ header: env[:request_headers]
36
45
 
37
- save_response env, resp.status, resp.body, resp.headers
46
+ if (req = env[:request]).stream_response?
47
+ warn "Streaming downloads for #{self.class.name} " \
48
+ 'are not yet implemented.'
49
+ req.on_data.call(resp.body, resp.body.bytesize)
50
+ end
51
+ save_response env, resp.status, resp.body, resp.headers, resp.reason
38
52
 
39
53
  @app.call env
40
- rescue ::HTTPClient::TimeoutError
41
- raise Faraday::Error::TimeoutError, $!
42
- rescue ::HTTPClient::BadResponseError => err
43
- if err.message.include?('status 407')
44
- raise Faraday::Error::ConnectionFailed, %{407 "Proxy Authentication Required "}
45
- else
46
- raise Faraday::Error::ClientError, $!
54
+ rescue ::HTTPClient::TimeoutError, Errno::ETIMEDOUT
55
+ raise Faraday::TimeoutError, $ERROR_INFO
56
+ rescue ::HTTPClient::BadResponseError => e
57
+ if e.message.include?('status 407')
58
+ raise Faraday::ConnectionFailed,
59
+ %(407 "Proxy Authentication Required ")
47
60
  end
48
- rescue Errno::ECONNREFUSED, EOFError
49
- raise Faraday::Error::ConnectionFailed, $!
50
- rescue => err
51
- if defined?(OpenSSL) && OpenSSL::SSL::SSLError === err
52
- raise Faraday::SSLError, err
53
- else
54
- raise
61
+
62
+ raise Faraday::ClientError, $ERROR_INFO
63
+ rescue Errno::EADDRNOTAVAIL, Errno::ECONNREFUSED, IOError, SocketError
64
+ raise Faraday::ConnectionFailed, $ERROR_INFO
65
+ rescue StandardError => e
66
+ if defined?(OpenSSL) && e.is_a?(OpenSSL::SSL::SSLError)
67
+ raise Faraday::SSLError, e
55
68
  end
69
+
70
+ raise
56
71
  end
57
72
 
73
+ # @param bind [Hash]
58
74
  def configure_socket(bind)
59
75
  client.socket_local.host = bind[:host]
60
76
  client.socket_local.port = bind[:port]
61
77
  end
62
78
 
79
+ # Configure proxy URI and any user credentials.
80
+ #
81
+ # @param proxy [Hash]
63
82
  def configure_proxy(proxy)
64
83
  client.proxy = proxy[:uri]
65
- if proxy[:user] && proxy[:password]
66
- client.set_proxy_auth proxy[:user], proxy[:password]
67
- end
84
+ return unless proxy[:user] && proxy[:password]
85
+
86
+ client.set_proxy_auth(proxy[:user], proxy[:password])
68
87
  end
69
88
 
89
+ # @param ssl [Hash]
70
90
  def configure_ssl(ssl)
71
91
  ssl_config = client.ssl_config
92
+ ssl_config.verify_mode = ssl_verify_mode(ssl)
93
+ ssl_config.cert_store = ssl_cert_store(ssl)
72
94
 
73
95
  ssl_config.add_trust_ca ssl[:ca_file] if ssl[:ca_file]
74
96
  ssl_config.add_trust_ca ssl[:ca_path] if ssl[:ca_path]
75
- ssl_config.cert_store = ssl[:cert_store] if ssl[:cert_store]
76
97
  ssl_config.client_cert = ssl[:client_cert] if ssl[:client_cert]
77
98
  ssl_config.client_key = ssl[:client_key] if ssl[:client_key]
78
99
  ssl_config.verify_depth = ssl[:verify_depth] if ssl[:verify_depth]
79
- ssl_config.verify_mode = ssl_verify_mode(ssl)
80
100
  end
81
101
 
102
+ # @param req [Hash]
82
103
  def configure_timeouts(req)
83
- if req[:timeout]
84
- client.connect_timeout = req[:timeout]
85
- client.receive_timeout = req[:timeout]
86
- client.send_timeout = req[:timeout]
87
- end
104
+ configure_timeout(req) if req[:timeout]
105
+ configure_open_timeout(req) if req[:open_timeout]
106
+ end
107
+
108
+ def configure_timeout(req)
109
+ client.connect_timeout = req[:timeout]
110
+ client.receive_timeout = req[:timeout]
111
+ client.send_timeout = req[:timeout]
112
+ end
113
+
114
+ def configure_open_timeout(req)
115
+ client.connect_timeout = req[:open_timeout]
116
+ client.send_timeout = req[:open_timeout]
117
+ end
118
+
119
+ def configure_client
120
+ @config_block&.call(client)
121
+ end
88
122
 
89
- if req[:open_timeout]
90
- client.connect_timeout = req[:open_timeout]
91
- client.send_timeout = req[:open_timeout]
123
+ # @param ssl [Hash]
124
+ # @return [OpenSSL::X509::Store]
125
+ def ssl_cert_store(ssl)
126
+ return ssl[:cert_store] if ssl[:cert_store]
127
+
128
+ # Memoize the cert store so that the same one is passed to
129
+ # HTTPClient each time, to avoid resyncing SSL sessions when
130
+ # it's changed
131
+ @ssl_cert_store ||= begin
132
+ # Use the default cert store by default, i.e. system ca certs
133
+ OpenSSL::X509::Store.new.tap(&:set_default_paths)
92
134
  end
93
135
  end
94
136
 
137
+ # @param ssl [Hash]
95
138
  def ssl_verify_mode(ssl)
96
139
  ssl[:verify_mode] || begin
97
140
  if ssl.fetch(:verify, true)
98
- OpenSSL::SSL::VERIFY_PEER | OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT
141
+ OpenSSL::SSL::VERIFY_PEER |
142
+ OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT
99
143
  else
100
144
  OpenSSL::SSL::VERIFY_NONE
101
145
  end
@@ -1,52 +1,66 @@
1
+ # frozen_string_literal: true
2
+
1
3
  begin
2
4
  require 'net/https'
3
5
  rescue LoadError
4
- warn "Warning: no such file to load -- net/https. Make sure openssl is installed if you want ssl support"
6
+ warn 'Warning: no such file to load -- net/https. ' \
7
+ 'Make sure openssl is installed if you want ssl support'
5
8
  require 'net/http'
6
9
  end
7
10
  require 'zlib'
8
11
 
9
12
  module Faraday
10
13
  class Adapter
14
+ # Net::HTTP adapter.
11
15
  class NetHttp < Faraday::Adapter
12
- NET_HTTP_EXCEPTIONS = [
13
- EOFError,
16
+ exceptions = [
17
+ IOError,
18
+ Errno::EADDRNOTAVAIL,
14
19
  Errno::ECONNABORTED,
15
20
  Errno::ECONNREFUSED,
16
21
  Errno::ECONNRESET,
17
22
  Errno::EHOSTUNREACH,
18
23
  Errno::EINVAL,
19
24
  Errno::ENETUNREACH,
25
+ Errno::EPIPE,
20
26
  Net::HTTPBadResponse,
21
27
  Net::HTTPHeaderSyntaxError,
22
28
  Net::ProtocolError,
23
29
  SocketError,
24
- Zlib::GzipFile::Error,
30
+ Zlib::GzipFile::Error
25
31
  ]
26
32
 
27
- NET_HTTP_EXCEPTIONS << OpenSSL::SSL::SSLError if defined?(OpenSSL)
28
- NET_HTTP_EXCEPTIONS << Net::OpenTimeout if defined?(Net::OpenTimeout)
33
+ exceptions << OpenSSL::SSL::SSLError if defined?(OpenSSL)
34
+ exceptions << Net::OpenTimeout if defined?(Net::OpenTimeout)
35
+
36
+ NET_HTTP_EXCEPTIONS = exceptions.freeze
37
+
38
+ def initialize(app = nil, opts = {}, &block)
39
+ @ssl_cert_store = nil
40
+ super(app, opts, &block)
41
+ end
29
42
 
30
43
  def call(env)
31
44
  super
32
45
  with_net_http_connection(env) do |http|
33
- configure_ssl(http, env[:ssl]) if env[:url].scheme == 'https' and env[:ssl]
34
-
35
- req = env[:request]
36
- http.read_timeout = http.open_timeout = req[:timeout] if req[:timeout]
37
- http.open_timeout = req[:open_timeout] if req[:open_timeout]
46
+ if (env[:url].scheme == 'https') && env[:ssl]
47
+ configure_ssl(http, env[:ssl])
48
+ end
49
+ configure_request(http, env[:request])
38
50
 
39
51
  begin
40
52
  http_response = perform_request(http, env)
41
- rescue *NET_HTTP_EXCEPTIONS => err
42
- if defined?(OpenSSL) && OpenSSL::SSL::SSLError === err
43
- raise Faraday::SSLError, err
44
- else
45
- raise Error::ConnectionFailed, err
53
+ rescue *NET_HTTP_EXCEPTIONS => e
54
+ if defined?(OpenSSL) && e.is_a?(OpenSSL::SSL::SSLError)
55
+ raise Faraday::SSLError, e
46
56
  end
57
+
58
+ raise Faraday::ConnectionFailed, e
47
59
  end
48
60
 
49
- save_response(env, http_response.code.to_i, http_response.body || '') do |response_headers|
61
+ save_response(env, http_response.code.to_i,
62
+ http_response.body || '', nil,
63
+ http_response.message) do |response_headers|
50
64
  http_response.each_header do |key, value|
51
65
  response_headers[key] = value
52
66
  end
@@ -54,17 +68,19 @@ module Faraday
54
68
  end
55
69
 
56
70
  @app.call env
57
- rescue Timeout::Error => err
58
- raise Faraday::Error::TimeoutError, err
71
+ rescue Timeout::Error, Errno::ETIMEDOUT => e
72
+ raise Faraday::TimeoutError, e
59
73
  end
60
74
 
75
+ private
76
+
61
77
  def create_request(env)
62
78
  request = Net::HTTPGenericRequest.new \
63
- env[:method].to_s.upcase, # request method
64
- !!env[:body], # is there request body
65
- :head != env[:method], # is there response body
66
- env[:url].request_uri, # request uri path
67
- env[:request_headers] # request headers
79
+ env[:method].to_s.upcase, # request method
80
+ !!env[:body], # is there request body
81
+ env[:method] != :head, # is there response body
82
+ env[:url].request_uri, # request uri path
83
+ env[:request_headers] # request headers
68
84
 
69
85
  if env[:body].respond_to?(:read)
70
86
  request.body_stream = env[:body]
@@ -75,9 +91,44 @@ module Faraday
75
91
  end
76
92
 
77
93
  def perform_request(http, env)
78
- if :get == env[:method] and !env[:body]
94
+ if env[:request].stream_response?
95
+ size = 0
96
+ yielded = false
97
+ http_response = request_with_wrapped_block(http, env) do |chunk|
98
+ if chunk.bytesize.positive? || size.positive?
99
+ yielded = true
100
+ size += chunk.bytesize
101
+ env[:request].on_data.call(chunk, size)
102
+ end
103
+ end
104
+ env[:request].on_data.call('', 0) unless yielded
105
+ # Net::HTTP returns something,
106
+ # but it's not meaningful according to the docs.
107
+ http_response.body = nil
108
+ http_response
109
+ else
110
+ request_with_wrapped_block(http, env)
111
+ end
112
+ end
113
+
114
+ def request_with_wrapped_block(http, env, &block)
115
+ if (env[:method] == :get) && !env[:body]
79
116
  # prefer `get` to `request` because the former handles gzip (ruby 1.9)
80
- http.get env[:url].request_uri, env[:request_headers]
117
+ request_via_get_method(http, env, &block)
118
+ else
119
+ request_via_request_method(http, env, &block)
120
+ end
121
+ end
122
+
123
+ def request_via_get_method(http, env, &block)
124
+ http.get env[:url].request_uri, env[:request_headers], &block
125
+ end
126
+
127
+ def request_via_request_method(http, env, &block)
128
+ if block_given?
129
+ http.request create_request(env) do |response|
130
+ response.read_body(&block)
131
+ end
81
132
  else
82
133
  http.request create_request(env)
83
134
  end
@@ -88,32 +139,56 @@ module Faraday
88
139
  end
89
140
 
90
141
  def net_http_connection(env)
91
- if proxy = env[:request][:proxy]
92
- Net::HTTP::Proxy(proxy[:uri].host, proxy[:uri].port, proxy[:user], proxy[:password])
93
- else
94
- Net::HTTP
95
- end.new(env[:url].host, env[:url].port)
142
+ klass = if (proxy = env[:request][:proxy])
143
+ Net::HTTP::Proxy(proxy[:uri].hostname, proxy[:uri].port,
144
+ proxy[:user], proxy[:password])
145
+ else
146
+ Net::HTTP
147
+ end
148
+ port = env[:url].port || (env[:url].scheme == 'https' ? 443 : 80)
149
+ klass.new(env[:url].hostname, port)
96
150
  end
97
151
 
98
152
  def configure_ssl(http, ssl)
99
- http.use_ssl = true
100
- http.verify_mode = ssl_verify_mode(ssl)
101
- http.cert_store = ssl_cert_store(ssl)
102
-
103
- http.cert = ssl[:client_cert] if ssl[:client_cert]
104
- http.key = ssl[:client_key] if ssl[:client_key]
105
- http.ca_file = ssl[:ca_file] if ssl[:ca_file]
106
- http.ca_path = ssl[:ca_path] if ssl[:ca_path]
153
+ http.use_ssl = true
154
+ http.verify_mode = ssl_verify_mode(ssl)
155
+ http.cert_store = ssl_cert_store(ssl)
156
+
157
+ http.cert = ssl[:client_cert] if ssl[:client_cert]
158
+ http.key = ssl[:client_key] if ssl[:client_key]
159
+ http.ca_file = ssl[:ca_file] if ssl[:ca_file]
160
+ http.ca_path = ssl[:ca_path] if ssl[:ca_path]
107
161
  http.verify_depth = ssl[:verify_depth] if ssl[:verify_depth]
108
- http.ssl_version = ssl[:version] if ssl[:version]
162
+ http.ssl_version = ssl[:version] if ssl[:version]
163
+ http.min_version = ssl[:min_version] if ssl[:min_version]
164
+ http.max_version = ssl[:max_version] if ssl[:max_version]
165
+ end
166
+
167
+ def configure_request(http, req)
168
+ if req[:timeout]
169
+ http.read_timeout = req[:timeout]
170
+ http.open_timeout = req[:timeout]
171
+ if http.respond_to?(:write_timeout=)
172
+ http.write_timeout = req[:timeout]
173
+ end
174
+ end
175
+ http.open_timeout = req[:open_timeout] if req[:open_timeout]
176
+ if req[:write_timeout] && http.respond_to?(:write_timeout=)
177
+ http.write_timeout = req[:write_timeout]
178
+ end
179
+ # Only set if Net::Http supports it, since Ruby 2.5.
180
+ http.max_retries = 0 if http.respond_to?(:max_retries=)
181
+
182
+ @config_block&.call(http)
109
183
  end
110
184
 
111
185
  def ssl_cert_store(ssl)
112
186
  return ssl[:cert_store] if ssl[:cert_store]
113
- # Use the default cert store by default, i.e. system ca certs
114
- cert_store = OpenSSL::X509::Store.new
115
- cert_store.set_default_paths
116
- cert_store
187
+
188
+ @ssl_cert_store ||= begin
189
+ # Use the default cert store by default, i.e. system ca certs
190
+ OpenSSL::X509::Store.new.tap(&:set_default_paths)
191
+ end
117
192
  end
118
193
 
119
194
  def ssl_verify_mode(ssl)
@@ -1,47 +1,88 @@
1
- # Rely on autoloading instead of explicit require; helps avoid the "already
2
- # initialized constant" warning on Ruby 1.8.7 when NetHttp is refereced below.
3
- # require 'faraday/adapter/net_http'
1
+ # frozen_string_literal: true
4
2
 
5
3
  module Faraday
6
4
  class Adapter
7
- # Experimental adapter for net-http-persistent
5
+ # Net::HTTP::Persistent adapter.
8
6
  class NetHttpPersistent < NetHttp
9
7
  dependency 'net/http/persistent'
10
8
 
11
- def with_net_http_connection(env)
12
- if proxy = env[:request][:proxy]
13
- proxy_uri = ::URI::HTTP === proxy[:uri] ? proxy[:uri].dup : ::URI.parse(proxy[:uri].to_s)
14
- proxy_uri.user = proxy_uri.password = nil
15
- # awful patch for net-http-persistent 2.8 not unescaping user/password
16
- (class << proxy_uri; self; end).class_eval do
17
- define_method(:user) { proxy[:user] }
18
- define_method(:password) { proxy[:password] }
19
- end if proxy[:user]
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
20
27
  end
28
+ @cached_connection
29
+ end
21
30
 
22
- yield Net::HTTP::Persistent.new 'Faraday', proxy_uri
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
23
50
  end
24
51
 
25
52
  def perform_request(http, env)
26
53
  http.request env[:url], create_request(env)
27
- rescue Net::HTTP::Persistent::Error => error
28
- if error.message.include? 'Timeout'
29
- raise Faraday::Error::TimeoutError, error
30
- elsif error.message.include? 'connection refused'
31
- raise Faraday::Error::ConnectionFailed, error
32
- else
33
- raise
54
+ rescue Errno::ETIMEDOUT => 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
34
61
  end
62
+
63
+ raise
35
64
  end
36
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
+
37
75
  def configure_ssl(http, ssl)
38
- http.verify_mode = ssl_verify_mode(ssl)
39
- http.cert_store = ssl_cert_store(ssl)
76
+ http_set(http, :verify_mode, ssl_verify_mode(ssl))
77
+ http_set(http, :cert_store, ssl_cert_store(ssl))
78
+
79
+ SSL_CONFIGURATIONS
80
+ .select { |_, key| ssl[key] }
81
+ .each { |target, key| http_set(http, target, ssl[key]) }
82
+ end
40
83
 
41
- http.certificate = ssl[:client_cert] if ssl[:client_cert]
42
- http.private_key = ssl[:client_key] if ssl[:client_key]
43
- http.ca_file = ssl[:ca_file] if ssl[:ca_file]
44
- http.ssl_version = ssl[:version] if ssl[:version]
84
+ def http_set(http, attr, value)
85
+ http.send("#{attr}=", value) if http.send(attr) != value
45
86
  end
46
87
  end
47
88
  end
@@ -1,71 +1,113 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Faraday
2
4
  class Adapter
5
+ # Patron adapter.
3
6
  class Patron < Faraday::Adapter
4
7
  dependency 'patron'
5
8
 
6
- def initialize(app, &block)
7
- super(app)
8
- @block = block
9
- end
10
-
11
9
  def call(env)
12
10
  super
13
-
14
11
  # TODO: support streaming requests
15
12
  env[:body] = env[:body].read if env[:body].respond_to? :read
16
13
 
17
- session = @session ||= create_session
14
+ session = ::Patron::Session.new
15
+ @config_block&.call(session)
16
+ if (env[:url].scheme == 'https') && env[:ssl]
17
+ configure_ssl(session, env[:ssl])
18
+ end
18
19
 
19
- if req = env[:request]
20
- session.timeout = session.connect_timeout = req[:timeout] if req[:timeout]
21
- session.connect_timeout = req[:open_timeout] if req[:open_timeout]
20
+ if (req = env[:request])
21
+ if req[:timeout]
22
+ session.timeout = session.connect_timeout = req[:timeout]
23
+ end
24
+ session.connect_timeout = req[:open_timeout] if req[:open_timeout]
22
25
 
23
- if proxy = req[:proxy]
26
+ if (proxy = req[:proxy])
24
27
  proxy_uri = proxy[:uri].dup
25
- proxy_uri.user = proxy[:user] && Utils.escape(proxy[:user]).gsub('+', '%20')
26
- proxy_uri.password = proxy[:password] && Utils.escape(proxy[:password]).gsub('+', '%20')
28
+ proxy_uri.user = proxy[:user] &&
29
+ Utils.escape(proxy[:user]).gsub('+', '%20')
30
+ proxy_uri.password = proxy[:password] &&
31
+ Utils.escape(proxy[:password]).gsub('+', '%20')
27
32
  session.proxy = proxy_uri.to_s
28
33
  end
29
34
  end
30
35
 
31
36
  response = begin
32
37
  data = env[:body] ? env[:body].to_s : nil
33
- session.request(env[:method], env[:url].to_s, env[:request_headers], :data => data)
34
- rescue Errno::ECONNREFUSED, ::Patron::ConnectionFailed
35
- raise Error::ConnectionFailed, $!
38
+ session.request(env[:method], env[:url].to_s,
39
+ env[:request_headers], data: data)
40
+ rescue Errno::ECONNREFUSED, ::Patron::ConnectionFailed
41
+ raise Faraday::ConnectionFailed, $ERROR_INFO
42
+ end
43
+
44
+ if (req = env[:request]).stream_response?
45
+ warn "Streaming downloads for #{self.class.name} " \
46
+ 'are not yet implemented.'
47
+ req.on_data.call(response.body, response.body.bytesize)
36
48
  end
49
+ # Remove the "HTTP/1.1 200", leaving just the reason phrase
50
+ reason_phrase = response.status_line.gsub(/^.* \d{3} /, '')
37
51
 
38
- save_response(env, response.status, response.body, response.headers)
52
+ save_response(env, response.status, response.body,
53
+ response.headers, reason_phrase)
39
54
 
40
55
  @app.call env
41
- rescue ::Patron::TimeoutError => err
42
- if err.message == "Connection time-out"
43
- raise Faraday::Error::ConnectionFailed, err
44
- else
45
- raise Faraday::Error::TimeoutError, err
56
+ rescue ::Patron::TimeoutError => e
57
+ if connection_timed_out_message?(e.message)
58
+ raise Faraday::ConnectionFailed, e
46
59
  end
47
- rescue ::Patron::Error => err
48
- if err.message.include?("code 407")
49
- raise Error::ConnectionFailed, %{407 "Proxy Authentication Required "}
50
- else
51
- raise Error::ConnectionFailed, err
60
+
61
+ raise Faraday::TimeoutError, e
62
+ rescue ::Patron::Error => e
63
+ if e.message.include?('code 407')
64
+ raise Faraday::ConnectionFailed,
65
+ %(407 "Proxy Authentication Required ")
52
66
  end
67
+
68
+ raise Faraday::ConnectionFailed, e
53
69
  end
54
70
 
55
71
  if loaded? && defined?(::Patron::Request::VALID_ACTIONS)
56
72
  # HAX: helps but doesn't work completely
57
73
  # https://github.com/toland/patron/issues/34
58
74
  ::Patron::Request::VALID_ACTIONS.tap do |actions|
59
- actions << :patch unless actions.include? :patch
60
- actions << :options unless actions.include? :options
75
+ if actions[0].is_a?(Symbol)
76
+ actions << :patch unless actions.include? :patch
77
+ actions << :options unless actions.include? :options
78
+ else
79
+ # Patron 0.4.20 and up
80
+ actions << 'PATCH' unless actions.include? 'PATCH'
81
+ actions << 'OPTIONS' unless actions.include? 'OPTIONS'
82
+ end
61
83
  end
62
84
  end
63
85
 
64
- def create_session
65
- session = ::Patron::Session.new
66
- session.insecure = true
67
- @block.call(session) if @block
68
- session
86
+ def configure_ssl(session, ssl)
87
+ if ssl.fetch(:verify, true)
88
+ session.cacert = ssl[:ca_file]
89
+ else
90
+ session.insecure = true
91
+ end
92
+ end
93
+
94
+ private
95
+
96
+ CURL_TIMEOUT_MESSAGES = [
97
+ 'Connection time-out',
98
+ 'Connection timed out',
99
+ 'Timed out before name resolve',
100
+ 'server connect has timed out',
101
+ 'Resolving timed out',
102
+ 'name lookup timed out',
103
+ 'timed out before SSL',
104
+ 'connect() timed out'
105
+ ].freeze
106
+
107
+ def connection_timed_out_message?(message)
108
+ CURL_TIMEOUT_MESSAGES.any? do |curl_message|
109
+ message.include?(curl_message)
110
+ end
69
111
  end
70
112
  end
71
113
  end