faraday 0.7.4 → 1.0.1

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 (119) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +276 -0
  3. data/LICENSE.md +1 -1
  4. data/README.md +40 -153
  5. data/Rakefile +4 -139
  6. data/examples/client_spec.rb +65 -0
  7. data/examples/client_test.rb +79 -0
  8. data/lib/faraday/adapter/em_http.rb +286 -0
  9. data/lib/faraday/adapter/em_http_ssl_patch.rb +62 -0
  10. data/lib/faraday/adapter/em_synchrony/parallel_manager.rb +69 -0
  11. data/lib/faraday/adapter/em_synchrony.rb +120 -36
  12. data/lib/faraday/adapter/excon.rb +108 -12
  13. data/lib/faraday/adapter/httpclient.rb +152 -0
  14. data/lib/faraday/adapter/net_http.rb +187 -43
  15. data/lib/faraday/adapter/net_http_persistent.rb +91 -0
  16. data/lib/faraday/adapter/patron.rb +106 -10
  17. data/lib/faraday/adapter/rack.rb +75 -0
  18. data/lib/faraday/adapter/test.rb +160 -61
  19. data/lib/faraday/adapter/typhoeus.rb +7 -46
  20. data/lib/faraday/adapter.rb +105 -33
  21. data/lib/faraday/adapter_registry.rb +30 -0
  22. data/lib/faraday/autoload.rb +95 -0
  23. data/lib/faraday/connection.rb +525 -157
  24. data/lib/faraday/dependency_loader.rb +37 -0
  25. data/lib/faraday/encoders/flat_params_encoder.rb +98 -0
  26. data/lib/faraday/encoders/nested_params_encoder.rb +171 -0
  27. data/lib/faraday/error.rb +122 -30
  28. data/lib/faraday/file_part.rb +128 -0
  29. data/lib/faraday/logging/formatter.rb +105 -0
  30. data/lib/faraday/middleware.rb +14 -22
  31. data/lib/faraday/middleware_registry.rb +129 -0
  32. data/lib/faraday/options/connection_options.rb +22 -0
  33. data/lib/faraday/options/env.rb +181 -0
  34. data/lib/faraday/options/proxy_options.rb +28 -0
  35. data/lib/faraday/options/request_options.rb +22 -0
  36. data/lib/faraday/options/ssl_options.rb +59 -0
  37. data/lib/faraday/options.rb +222 -0
  38. data/lib/faraday/param_part.rb +53 -0
  39. data/lib/faraday/parameters.rb +5 -0
  40. data/lib/faraday/rack_builder.rb +248 -0
  41. data/lib/faraday/request/authorization.rb +55 -0
  42. data/lib/faraday/request/basic_authentication.rb +20 -0
  43. data/lib/faraday/request/instrumentation.rb +54 -0
  44. data/lib/faraday/request/multipart.rb +84 -48
  45. data/lib/faraday/request/retry.rb +239 -0
  46. data/lib/faraday/request/token_authentication.rb +20 -0
  47. data/lib/faraday/request/url_encoded.rb +46 -27
  48. data/lib/faraday/request.rb +112 -50
  49. data/lib/faraday/response/logger.rb +24 -25
  50. data/lib/faraday/response/raise_error.rb +40 -11
  51. data/lib/faraday/response.rb +44 -35
  52. data/lib/faraday/utils/headers.rb +139 -0
  53. data/lib/faraday/utils/params_hash.rb +61 -0
  54. data/lib/faraday/utils.rb +72 -117
  55. data/lib/faraday.rb +142 -64
  56. data/spec/external_adapters/faraday_specs_setup.rb +14 -0
  57. data/spec/faraday/adapter/em_http_spec.rb +47 -0
  58. data/spec/faraday/adapter/em_synchrony_spec.rb +16 -0
  59. data/spec/faraday/adapter/excon_spec.rb +49 -0
  60. data/spec/faraday/adapter/httpclient_spec.rb +73 -0
  61. data/spec/faraday/adapter/net_http_persistent_spec.rb +57 -0
  62. data/spec/faraday/adapter/net_http_spec.rb +64 -0
  63. data/spec/faraday/adapter/patron_spec.rb +18 -0
  64. data/spec/faraday/adapter/rack_spec.rb +8 -0
  65. data/spec/faraday/adapter/typhoeus_spec.rb +7 -0
  66. data/spec/faraday/adapter_registry_spec.rb +28 -0
  67. data/spec/faraday/adapter_spec.rb +55 -0
  68. data/spec/faraday/composite_read_io_spec.rb +80 -0
  69. data/spec/faraday/connection_spec.rb +691 -0
  70. data/spec/faraday/error_spec.rb +45 -0
  71. data/spec/faraday/middleware_spec.rb +26 -0
  72. data/spec/faraday/options/env_spec.rb +70 -0
  73. data/spec/faraday/options/options_spec.rb +297 -0
  74. data/spec/faraday/options/proxy_options_spec.rb +37 -0
  75. data/spec/faraday/options/request_options_spec.rb +19 -0
  76. data/spec/faraday/params_encoders/flat_spec.rb +34 -0
  77. data/spec/faraday/params_encoders/nested_spec.rb +134 -0
  78. data/spec/faraday/rack_builder_spec.rb +196 -0
  79. data/spec/faraday/request/authorization_spec.rb +88 -0
  80. data/spec/faraday/request/instrumentation_spec.rb +76 -0
  81. data/spec/faraday/request/multipart_spec.rb +274 -0
  82. data/spec/faraday/request/retry_spec.rb +242 -0
  83. data/spec/faraday/request/url_encoded_spec.rb +83 -0
  84. data/spec/faraday/request_spec.rb +109 -0
  85. data/spec/faraday/response/logger_spec.rb +220 -0
  86. data/spec/faraday/response/middleware_spec.rb +68 -0
  87. data/spec/faraday/response/raise_error_spec.rb +106 -0
  88. data/spec/faraday/response_spec.rb +75 -0
  89. data/spec/faraday/utils/headers_spec.rb +82 -0
  90. data/spec/faraday/utils_spec.rb +56 -0
  91. data/spec/faraday_spec.rb +37 -0
  92. data/spec/spec_helper.rb +132 -0
  93. data/spec/support/disabling_stub.rb +14 -0
  94. data/spec/support/fake_safe_buffer.rb +15 -0
  95. data/spec/support/helper_methods.rb +133 -0
  96. data/spec/support/shared_examples/adapter.rb +104 -0
  97. data/spec/support/shared_examples/params_encoder.rb +18 -0
  98. data/spec/support/shared_examples/request_method.rb +234 -0
  99. data/spec/support/streaming_response_checker.rb +35 -0
  100. data/spec/support/webmock_rack_app.rb +68 -0
  101. metadata +126 -126
  102. data/Gemfile +0 -29
  103. data/config.ru +0 -6
  104. data/faraday.gemspec +0 -92
  105. data/lib/faraday/adapter/action_dispatch.rb +0 -29
  106. data/lib/faraday/builder.rb +0 -160
  107. data/lib/faraday/request/json.rb +0 -35
  108. data/lib/faraday/upload_io.rb +0 -23
  109. data/test/adapters/live_test.rb +0 -205
  110. data/test/adapters/logger_test.rb +0 -37
  111. data/test/adapters/net_http_test.rb +0 -33
  112. data/test/adapters/test_middleware_test.rb +0 -70
  113. data/test/connection_test.rb +0 -254
  114. data/test/env_test.rb +0 -158
  115. data/test/helper.rb +0 -41
  116. data/test/live_server.rb +0 -45
  117. data/test/middleware_stack_test.rb +0 -118
  118. data/test/request_middleware_test.rb +0 -116
  119. data/test/response_middleware_test.rb +0 -74
@@ -1,246 +1,614 @@
1
- require 'addressable/uri'
2
- require 'set'
3
- require 'base64'
1
+ # frozen_string_literal: true
4
2
 
5
3
  module Faraday
4
+ # Connection objects manage the default properties and the middleware
5
+ # stack for fulfilling an HTTP request.
6
+ #
7
+ # @example
8
+ #
9
+ # conn = Faraday::Connection.new 'http://sushi.com'
10
+ #
11
+ # # GET http://sushi.com/nigiri
12
+ # conn.get 'nigiri'
13
+ # # => #<Faraday::Response>
14
+ #
6
15
  class Connection
7
- include Addressable
8
-
9
- METHODS = Set.new [:get, :post, :put, :delete, :head, :patch, :options]
10
- METHODS_WITH_BODIES = Set.new [:post, :put, :patch, :options]
11
-
12
- attr_accessor :host, :port, :scheme, :params, :headers, :parallel_manager
13
- attr_reader :path_prefix, :builder, :options, :ssl
14
-
15
- # :url
16
- # :params
17
- # :headers
18
- # :request
19
- # :ssl
20
- def initialize(url = nil, options = {})
21
- if url.is_a?(Hash)
22
- options = url
23
- url = options[:url]
24
- end
25
- @headers = Utils::Headers.new
26
- @params = Utils::ParamsHash.new
27
- @options = options[:request] || {}
28
- @ssl = options[:ssl] || {}
29
- @parallel_manager = options[:parallel]
16
+ # A Set of allowed HTTP verbs.
17
+ METHODS = Set.new %i[get post put delete head patch options trace]
30
18
 
31
- self.url_prefix = url if url
32
- proxy(options[:proxy])
19
+ # @return [Hash] URI query unencoded key/value pairs.
20
+ attr_reader :params
33
21
 
34
- @params.update options[:params] if options[:params]
35
- @headers.update options[:headers] if options[:headers]
22
+ # @return [Hash] unencoded HTTP header key/value pairs.
23
+ attr_reader :headers
36
24
 
37
- if block_given?
38
- @builder = Builder.create { |b| yield b }
39
- else
40
- @builder = options[:builder] || Builder.new
25
+ # @return [String] a URI with the prefix used for all requests from this
26
+ # Connection. This includes a default host name, scheme, port, and path.
27
+ attr_reader :url_prefix
28
+
29
+ # @return [Faraday::Builder] Builder for this Connection.
30
+ attr_reader :builder
31
+
32
+ # @return [Hash] SSL options.
33
+ attr_reader :ssl
34
+
35
+ # @return [Object] the parallel manager for this Connection.
36
+ attr_reader :parallel_manager
37
+
38
+ # Sets the default parallel manager for this connection.
39
+ attr_writer :default_parallel_manager
40
+
41
+ # @return [Hash] proxy options.
42
+ attr_reader :proxy
43
+
44
+ # Initializes a new Faraday::Connection.
45
+ #
46
+ # @param url [URI, String] URI or String base URL to use as a prefix for all
47
+ # requests (optional).
48
+ # @param options [Hash, Faraday::ConnectionOptions]
49
+ # @option options [URI, String] :url ('http:/') URI or String base URL
50
+ # @option options [Hash<String => String>] :params URI query unencoded
51
+ # key/value pairs.
52
+ # @option options [Hash<String => String>] :headers Hash of unencoded HTTP
53
+ # header key/value pairs.
54
+ # @option options [Hash] :request Hash of request options.
55
+ # @option options [Hash] :ssl Hash of SSL options.
56
+ # @option options [Hash, URI, String] :proxy proxy options, either as a URL
57
+ # or as a Hash
58
+ # @option options [URI, String] :proxy[:uri]
59
+ # @option options [String] :proxy[:user]
60
+ # @option options [String] :proxy[:password]
61
+ # @yield [self] after all setup has been done
62
+ def initialize(url = nil, options = nil)
63
+ options = ConnectionOptions.from(options)
64
+
65
+ if url.is_a?(Hash) || url.is_a?(ConnectionOptions)
66
+ options = options.merge(url)
67
+ url = options.url
41
68
  end
42
- end
43
69
 
44
- def use(klass, *args, &block)
45
- @builder.use(klass, *args, &block)
70
+ @parallel_manager = nil
71
+ @headers = Utils::Headers.new
72
+ @params = Utils::ParamsHash.new
73
+ @options = options.request
74
+ @ssl = options.ssl
75
+ @default_parallel_manager = options.parallel_manager
76
+
77
+ @builder = options.builder || begin
78
+ # pass an empty block to Builder so it doesn't assume default middleware
79
+ options.new_builder(block_given? ? proc { |b| } : nil)
80
+ end
81
+
82
+ self.url_prefix = url || 'http:/'
83
+
84
+ @params.update(options.params) if options.params
85
+ @headers.update(options.headers) if options.headers
86
+
87
+ initialize_proxy(url, options)
88
+
89
+ yield(self) if block_given?
90
+
91
+ @headers[:user_agent] ||= "Faraday v#{VERSION}"
46
92
  end
47
93
 
48
- def request(key, *args, &block)
49
- @builder.request(key, *args, &block)
94
+ def initialize_proxy(url, options)
95
+ @manual_proxy = !!options.proxy
96
+ @proxy =
97
+ if options.proxy
98
+ ProxyOptions.from(options.proxy)
99
+ else
100
+ proxy_from_env(url)
101
+ end
50
102
  end
51
103
 
52
- def response(key, *args, &block)
53
- @builder.response(key, *args, &block)
104
+ # Sets the Hash of URI query unencoded key/value pairs.
105
+ # @param hash [Hash]
106
+ def params=(hash)
107
+ @params.replace hash
54
108
  end
55
109
 
56
- def adapter(key, *args, &block)
57
- @builder.adapter(key, *args, &block)
110
+ # Sets the Hash of unencoded HTTP header key/value pairs.
111
+ # @param hash [Hash]
112
+ def headers=(hash)
113
+ @headers.replace hash
58
114
  end
59
115
 
60
- def build(options = {}, &block)
61
- @builder.build(options, &block)
116
+ extend Forwardable
117
+
118
+ def_delegators :builder, :build, :use, :request, :response, :adapter, :app
119
+
120
+ # Closes the underlying resources and/or connections. In the case of
121
+ # persistent connections, this closes all currently open connections
122
+ # but does not prevent new connections from being made.
123
+ def close
124
+ app.close
62
125
  end
63
126
 
64
- # The "rack app" wrapped in middleware. All requests are sent here.
127
+ # @!method get(url = nil, params = nil, headers = nil)
128
+ # Makes a GET HTTP request without a body.
129
+ # @!scope class
65
130
  #
66
- # The builder is responsible for creating the app object. After this,
67
- # the builder gets locked to ensure no further modifications are made
68
- # to the middleware stack.
131
+ # @param url [String] The optional String base URL to use as a prefix for
132
+ # all requests. Can also be the options Hash.
133
+ # @param params [Hash] Hash of URI query unencoded key/value pairs.
134
+ # @param headers [Hash] unencoded HTTP header key/value pairs.
69
135
  #
70
- # Returns an object that responds to `call` and returns a Response.
71
- def app
72
- @app ||= begin
73
- builder.lock!
74
- builder.to_app(lambda { |env|
75
- # the inner app that creates and returns the Response object
76
- response = Response.new
77
- response.finish(env) unless env[:parallel_manager]
78
- env[:response] = response
79
- })
80
- end
81
- end
136
+ # @example
137
+ # conn.get '/items', { page: 1 }, :accept => 'application/json'
138
+ #
139
+ # # ElasticSearch example sending a body with GET.
140
+ # conn.get '/twitter/tweet/_search' do |req|
141
+ # req.headers[:content_type] = 'application/json'
142
+ # req.params[:routing] = 'kimchy'
143
+ # req.body = JSON.generate(query: {...})
144
+ # end
145
+ #
146
+ # @yield [Faraday::Request] for further request customizations
147
+ # @return [Faraday::Response]
82
148
 
83
- def get(url = nil, headers = nil)
84
- block = block_given? ? Proc.new : nil
85
- run_request(:get, url, nil, headers, &block)
86
- end
149
+ # @!method head(url = nil, params = nil, headers = nil)
150
+ # Makes a HEAD HTTP request without a body.
151
+ # @!scope class
152
+ #
153
+ # @param url [String] The optional String base URL to use as a prefix for
154
+ # all requests. Can also be the options Hash.
155
+ # @param params [Hash] Hash of URI query unencoded key/value pairs.
156
+ # @param headers [Hash] unencoded HTTP header key/value pairs.
157
+ #
158
+ # @example
159
+ # conn.head '/items/1'
160
+ #
161
+ # @yield [Faraday::Request] for further request customizations
162
+ # @return [Faraday::Response]
163
+
164
+ # @!method delete(url = nil, params = nil, headers = nil)
165
+ # Makes a DELETE HTTP request without a body.
166
+ # @!scope class
167
+ #
168
+ # @param url [String] The optional String base URL to use as a prefix for
169
+ # all requests. Can also be the options Hash.
170
+ # @param params [Hash] Hash of URI query unencoded key/value pairs.
171
+ # @param headers [Hash] unencoded HTTP header key/value pairs.
172
+ #
173
+ # @example
174
+ # conn.delete '/items/1'
175
+ #
176
+ # @yield [Faraday::Request] for further request customizations
177
+ # @return [Faraday::Response]
87
178
 
88
- def post(url = nil, body = nil, headers = nil)
89
- block = block_given? ? Proc.new : nil
90
- run_request(:post, url, body, headers, &block)
179
+ # @!method trace(url = nil, params = nil, headers = nil)
180
+ # Makes a TRACE HTTP request without a body.
181
+ # @!scope class
182
+ #
183
+ # @param url [String] The optional String base URL to use as a prefix for
184
+ # all requests. Can also be the options Hash.
185
+ # @param params [Hash] Hash of URI query unencoded key/value pairs.
186
+ # @param headers [Hash] unencoded HTTP header key/value pairs.
187
+ #
188
+ # @example
189
+ # conn.connect '/items/1'
190
+ #
191
+ # @yield [Faraday::Request] for further request customizations
192
+ # @return [Faraday::Response]
193
+
194
+ # @!visibility private
195
+ METHODS_WITH_QUERY.each do |method|
196
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
197
+ def #{method}(url = nil, params = nil, headers = nil)
198
+ run_request(:#{method}, url, nil, headers) do |request|
199
+ request.params.update(params) if params
200
+ yield request if block_given?
201
+ end
202
+ end
203
+ RUBY
91
204
  end
92
205
 
93
- def put(url = nil, body = nil, headers = nil)
94
- block = block_given? ? Proc.new : nil
95
- run_request(:put, url, body, headers, &block)
206
+ # @overload options()
207
+ # Returns current Connection options.
208
+ #
209
+ # @overload options(url, params = nil, headers = nil)
210
+ # Makes an OPTIONS HTTP request to the given URL.
211
+ # @param url [String] String base URL to sue as a prefix for all requests.
212
+ # @param params [Hash] Hash of URI query unencoded key/value pairs.
213
+ # @param headers [Hash] unencoded HTTP header key/value pairs.
214
+ #
215
+ # @example
216
+ # conn.options '/items/1'
217
+ #
218
+ # @yield [Faraday::Request] for further request customizations
219
+ # @return [Faraday::Response]
220
+ def options(*args)
221
+ return @options if args.size.zero?
222
+
223
+ url, params, headers = *args
224
+ run_request(:options, url, nil, headers) do |request|
225
+ request.params.update(params) if params
226
+ yield request if block_given?
227
+ end
96
228
  end
97
229
 
98
- def patch(url = nil, body = nil, headers = nil)
99
- block = block_given? ? Proc.new : nil
100
- run_request(:patch, url, body, headers, &block)
230
+ # @!method post(url = nil, body = nil, headers = nil)
231
+ # Makes a POST HTTP request with a body.
232
+ # @!scope class
233
+ #
234
+ # @param url [String] The optional String base URL to use as a prefix for
235
+ # all requests. Can also be the options Hash.
236
+ # @param body [String] body for the request.
237
+ # @param headers [Hash] unencoded HTTP header key/value pairs.
238
+ #
239
+ # @example
240
+ # conn.post '/items', data, content_type: 'application/json'
241
+ #
242
+ # # Simple ElasticSearch indexing sample.
243
+ # conn.post '/twitter/tweet' do |req|
244
+ # req.headers[:content_type] = 'application/json'
245
+ # req.params[:routing] = 'kimchy'
246
+ # req.body = JSON.generate(user: 'kimchy', ...)
247
+ # end
248
+ #
249
+ # @yield [Faraday::Request] for further request customizations
250
+ # @return [Faraday::Response]
251
+
252
+ # @!method put(url = nil, body = nil, headers = nil)
253
+ # Makes a PUT HTTP request with a body.
254
+ # @!scope class
255
+ #
256
+ # @param url [String] The optional String base URL to use as a prefix for
257
+ # all requests. Can also be the options Hash.
258
+ # @param body [String] body for the request.
259
+ # @param headers [Hash] unencoded HTTP header key/value pairs.
260
+ #
261
+ # @example
262
+ # # TODO: Make it a PUT example
263
+ # conn.post '/items', data, content_type: 'application/json'
264
+ #
265
+ # # Simple ElasticSearch indexing sample.
266
+ # conn.post '/twitter/tweet' do |req|
267
+ # req.headers[:content_type] = 'application/json'
268
+ # req.params[:routing] = 'kimchy'
269
+ # req.body = JSON.generate(user: 'kimchy', ...)
270
+ # end
271
+ #
272
+ # @yield [Faraday::Request] for further request customizations
273
+ # @return [Faraday::Response]
274
+
275
+ # @!visibility private
276
+ METHODS_WITH_BODY.each do |method|
277
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
278
+ def #{method}(url = nil, body = nil, headers = nil, &block)
279
+ run_request(:#{method}, url, body, headers, &block)
280
+ end
281
+ RUBY
101
282
  end
102
283
 
103
- def head(url = nil, headers = nil)
104
- block = block_given? ? Proc.new : nil
105
- run_request(:head, url, nil, headers, &block)
284
+ # Sets up the Authorization header with these credentials, encoded
285
+ # with base64.
286
+ #
287
+ # @param login [String] The authentication login.
288
+ # @param pass [String] The authentication password.
289
+ #
290
+ # @example
291
+ #
292
+ # conn.basic_auth 'Aladdin', 'open sesame'
293
+ # conn.headers['Authorization']
294
+ # # => "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=="
295
+ #
296
+ # @return [void]
297
+ def basic_auth(login, pass)
298
+ set_authorization_header(:basic_auth, login, pass)
106
299
  end
107
300
 
108
- def delete(url = nil, headers = nil)
109
- block = block_given? ? Proc.new : nil
110
- run_request(:delete, url, nil, headers, &block)
301
+ # Sets up the Authorization header with the given token.
302
+ #
303
+ # @param token [String]
304
+ # @param options [Hash] extra token options.
305
+ #
306
+ # @example
307
+ #
308
+ # conn.token_auth 'abcdef', foo: 'bar'
309
+ # conn.headers['Authorization']
310
+ # # => "Token token=\"abcdef\",
311
+ # foo=\"bar\""
312
+ #
313
+ # @return [void]
314
+ def token_auth(token, options = nil)
315
+ set_authorization_header(:token_auth, token, options)
111
316
  end
112
317
 
113
- def basic_auth(login, pass)
114
- auth = Base64.encode64("#{login}:#{pass}")
115
- auth.gsub!("\n", "")
116
- @headers['authorization'] = "Basic #{auth}"
318
+ # Sets up a custom Authorization header.
319
+ #
320
+ # @param type [String] authorization type
321
+ # @param token [String, Hash] token. A String value is taken literally, and
322
+ # a Hash is encoded into comma-separated key/value pairs.
323
+ #
324
+ # @example
325
+ #
326
+ # conn.authorization :Bearer, 'mF_9.B5f-4.1JqM'
327
+ # conn.headers['Authorization']
328
+ # # => "Bearer mF_9.B5f-4.1JqM"
329
+ #
330
+ # conn.authorization :Token, token: 'abcdef', foo: 'bar'
331
+ # conn.headers['Authorization']
332
+ # # => "Token token=\"abcdef\",
333
+ # foo=\"bar\""
334
+ #
335
+ # @return [void]
336
+ def authorization(type, token)
337
+ set_authorization_header(:authorization, type, token)
117
338
  end
118
339
 
119
- def token_auth(token, options = {})
120
- values = ["token=#{token.to_s.inspect}"]
121
- options.each do |key, value|
122
- values << "#{key}=#{value.to_s.inspect}"
340
+ # Check if the adapter is parallel-capable.
341
+ #
342
+ # @yield if the adapter isn't parallel-capable, or if no adapter is set yet.
343
+ #
344
+ # @return [Object, nil] a parallel manager or nil if yielded
345
+ # @api private
346
+ def default_parallel_manager
347
+ @default_parallel_manager ||= begin
348
+ adapter = @builder.adapter.klass if @builder.adapter
349
+
350
+ if support_parallel?(adapter)
351
+ adapter.setup_parallel_manager
352
+ elsif block_given?
353
+ yield
354
+ end
123
355
  end
124
- # 21 = "Authorization: Token ".size
125
- comma = ",\n#{' ' * 21}"
126
- @headers['authorization'] = "Token #{values * comma}"
127
356
  end
128
357
 
358
+ # Determine if this Faraday::Connection can make parallel requests.
359
+ #
360
+ # @return [Boolean]
129
361
  def in_parallel?
130
362
  !!@parallel_manager
131
363
  end
132
364
 
133
- def in_parallel(manager)
134
- @parallel_manager = manager
365
+ # Sets up the parallel manager to make a set of requests.
366
+ #
367
+ # @param manager [Object] The parallel manager that this Connection's
368
+ # Adapter uses.
369
+ #
370
+ # @yield a block to execute multiple requests.
371
+ # @return [void]
372
+ def in_parallel(manager = nil)
373
+ @parallel_manager = manager || default_parallel_manager do
374
+ warn 'Warning: `in_parallel` called but no parallel-capable adapter ' \
375
+ 'on Faraday stack'
376
+ warn caller[2, 10].join("\n")
377
+ nil
378
+ end
135
379
  yield
136
- @parallel_manager && @parallel_manager.run
380
+ @parallel_manager&.run
137
381
  ensure
138
382
  @parallel_manager = nil
139
383
  end
140
384
 
141
- def proxy(arg = nil)
142
- return @proxy if arg.nil?
143
-
144
- @proxy =
145
- case arg
146
- when String then {:uri => proxy_arg_to_uri(arg)}
147
- when URI then {:uri => arg}
148
- when Hash
149
- if arg[:uri] = proxy_arg_to_uri(arg[:uri])
150
- arg
151
- else
152
- raise ArgumentError, "no :uri option."
153
- end
154
- end
385
+ # Sets the Hash proxy options.
386
+ #
387
+ # @param new_value [Object]
388
+ def proxy=(new_value)
389
+ @manual_proxy = true
390
+ @proxy = new_value ? ProxyOptions.from(new_value) : nil
155
391
  end
156
392
 
157
- # Parses the giving url with Addressable::URI and stores the individual
158
- # components in this connection. These components serve as defaults for
393
+ def_delegators :url_prefix, :scheme, :scheme=, :host, :host=, :port, :port=
394
+ def_delegator :url_prefix, :path, :path_prefix
395
+
396
+ # Parses the given URL with URI and stores the individual
397
+ # components in this connection. These components serve as defaults for
159
398
  # requests made by this connection.
160
399
  #
400
+ # @param url [String, URI]
401
+ # @param encoder [Object]
402
+ #
403
+ # @example
404
+ #
161
405
  # conn = Faraday::Connection.new { ... }
162
406
  # conn.url_prefix = "https://sushi.com/api"
163
407
  # conn.scheme # => https
164
408
  # conn.path_prefix # => "/api"
165
409
  #
166
410
  # conn.get("nigiri?page=2") # accesses https://sushi.com/api/nigiri
167
- #
168
- def url_prefix=(url)
169
- uri = URI.parse(url)
170
- self.scheme = uri.scheme
171
- self.host = uri.host
172
- self.port = uri.port
411
+ def url_prefix=(url, encoder = nil)
412
+ uri = @url_prefix = Utils.URI(url)
173
413
  self.path_prefix = uri.path
174
414
 
175
- @params.merge_query(uri.query)
176
- basic_auth(uri.user, uri.password) if uri.user && uri.password
415
+ params.merge_query(uri.query, encoder)
416
+ uri.query = nil
177
417
 
178
- uri
418
+ with_uri_credentials(uri) do |user, password|
419
+ basic_auth user, password
420
+ uri.user = uri.password = nil
421
+ end
179
422
  end
180
423
 
181
- # Ensures that the path prefix always has a leading / and no trailing /
424
+ # Sets the path prefix and ensures that it always has a leading
425
+ # slash.
426
+ #
427
+ # @param value [String]
428
+ #
429
+ # @return [String] the new path prefix
182
430
  def path_prefix=(value)
183
- if value
184
- value.chomp! "/"
185
- value.replace "/#{value}" if value !~ /^\//
186
- end
187
- @path_prefix = value
431
+ url_prefix.path = if value
432
+ value = '/' + value unless value[0, 1] == '/'
433
+ value
434
+ end
188
435
  end
189
436
 
437
+ # Takes a relative url for a request and combines it with the defaults
438
+ # set on the connection instance.
439
+ #
440
+ # @param url [String]
441
+ # @param extra_params [Hash]
442
+ #
443
+ # @example
444
+ # conn = Faraday::Connection.new { ... }
445
+ # conn.url_prefix = "https://sushi.com/api?token=abc"
446
+ # conn.scheme # => https
447
+ # conn.path_prefix # => "/api"
448
+ #
449
+ # conn.build_url("nigiri?page=2")
450
+ # # => https://sushi.com/api/nigiri?token=abc&page=2
451
+ #
452
+ # conn.build_url("nigiri", page: 2)
453
+ # # => https://sushi.com/api/nigiri?token=abc&page=2
454
+ #
455
+ def build_url(url = nil, extra_params = nil)
456
+ uri = build_exclusive_url(url)
457
+
458
+ query_values = params.dup.merge_query(uri.query, options.params_encoder)
459
+ query_values.update(extra_params) if extra_params
460
+ uri.query =
461
+ if query_values.empty?
462
+ nil
463
+ else
464
+ query_values.to_query(options.params_encoder)
465
+ end
466
+
467
+ uri
468
+ end
469
+
470
+ # Builds and runs the Faraday::Request.
471
+ #
472
+ # @param method [Symbol] HTTP method.
473
+ # @param url [String, URI] String or URI to access.
474
+ # @param body [Object] The request body that will eventually be converted to
475
+ # a string.
476
+ # @param headers [Hash] unencoded HTTP header key/value pairs.
477
+ #
478
+ # @return [Faraday::Response]
190
479
  def run_request(method, url, body, headers)
191
- if !METHODS.include?(method)
480
+ unless METHODS.include?(method)
192
481
  raise ArgumentError, "unknown http method: #{method}"
193
482
  end
194
483
 
195
- request = Request.create(method) do |req|
484
+ request = build_request(method) do |req|
485
+ req.options.proxy = proxy_for_request(url)
196
486
  req.url(url) if url
197
487
  req.headers.update(headers) if headers
198
488
  req.body = body if body
199
- yield req if block_given?
489
+ yield(req) if block_given?
200
490
  end
201
491
 
202
- env = request.to_env(self)
203
- self.app.call(env)
492
+ builder.build_response(self, request)
204
493
  end
205
494
 
206
- # Takes a relative url for a request and combines it with the defaults
207
- # set on the connection instance.
495
+ # Creates and configures the request object.
208
496
  #
209
- # conn = Faraday::Connection.new { ... }
210
- # conn.url_prefix = "https://sushi.com/api?token=abc"
211
- # conn.scheme # => https
212
- # conn.path_prefix # => "/api"
213
- #
214
- # conn.build_url("nigiri?page=2") # => https://sushi.com/api/nigiri?token=abc&page=2
215
- # conn.build_url("nigiri", :page => 2) # => https://sushi.com/api/nigiri?token=abc&page=2
497
+ # @param method [Symbol]
216
498
  #
217
- def build_url(url, extra_params = nil)
218
- uri = URI.parse(url.to_s)
219
- if @path_prefix && uri.path !~ /^\//
220
- new_path = @path_prefix.size > 1 ? @path_prefix.dup : ''
221
- new_path << "/#{uri.path}" unless uri.path.empty?
222
- uri.path = new_path
499
+ # @yield [Faraday::Request] if block given
500
+ # @return [Faraday::Request]
501
+ def build_request(method)
502
+ Request.create(method) do |req|
503
+ req.params = params.dup
504
+ req.headers = headers.dup
505
+ req.options = options.dup
506
+ yield(req) if block_given?
223
507
  end
224
- uri.host ||= @host
225
- uri.port ||= @port
226
- uri.scheme ||= @scheme
227
-
228
- params = @params.dup.merge_query(uri.query)
229
- params.update extra_params if extra_params
230
- uri.query = params.empty? ? nil : params.to_query
508
+ end
231
509
 
510
+ # Build an absolute URL based on url_prefix.
511
+ #
512
+ # @param url [String, URI]
513
+ # @param params [Faraday::Utils::ParamsHash] A Faraday::Utils::ParamsHash to
514
+ # replace the query values
515
+ # of the resulting url (default: nil).
516
+ #
517
+ # @return [URI]
518
+ def build_exclusive_url(url = nil, params = nil, params_encoder = nil)
519
+ url = nil if url.respond_to?(:empty?) && url.empty?
520
+ base = url_prefix
521
+ if url && base.path && base.path !~ %r{/$}
522
+ base = base.dup
523
+ base.path = base.path + '/' # ensure trailing slash
524
+ end
525
+ uri = url ? base + url : base
526
+ if params
527
+ uri.query = params.to_query(params_encoder || options.params_encoder)
528
+ end
529
+ # rubocop:disable Style/SafeNavigation
530
+ uri.query = nil if uri.query && uri.query.empty?
531
+ # rubocop:enable Style/SafeNavigation
232
532
  uri
233
533
  end
234
534
 
535
+ # Creates a duplicate of this Faraday::Connection.
536
+ #
537
+ # @api private
538
+ #
539
+ # @return [Faraday::Connection]
235
540
  def dup
236
- self.class.new(build_url(''), :headers => headers.dup, :params => params.dup, :builder => builder.dup)
541
+ self.class.new(build_exclusive_url,
542
+ headers: headers.dup,
543
+ params: params.dup,
544
+ builder: builder.dup,
545
+ ssl: ssl.dup,
546
+ request: options.dup)
547
+ end
548
+
549
+ # Yields username and password extracted from a URI if they both exist.
550
+ #
551
+ # @param uri [URI]
552
+ # @yield [username, password] any username and password
553
+ # @yieldparam username [String] any username from URI
554
+ # @yieldparam password [String] any password from URI
555
+ # @return [void]
556
+ # @api private
557
+ def with_uri_credentials(uri)
558
+ return unless uri.user && uri.password
559
+
560
+ yield(Utils.unescape(uri.user), Utils.unescape(uri.password))
237
561
  end
238
562
 
239
- def proxy_arg_to_uri(arg)
240
- case arg
241
- when String then URI.parse(arg)
242
- when URI then arg
563
+ def set_authorization_header(header_type, *args)
564
+ header = Faraday::Request
565
+ .lookup_middleware(header_type)
566
+ .header(*args)
567
+
568
+ headers[Faraday::Request::Authorization::KEY] = header
569
+ end
570
+
571
+ def proxy_from_env(url)
572
+ return if Faraday.ignore_env_proxy
573
+
574
+ uri = nil
575
+ if URI.parse('').respond_to?(:find_proxy)
576
+ case url
577
+ when String
578
+ uri = Utils.URI(url)
579
+ uri = URI.parse("#{uri.scheme}://#{uri.hostname}").find_proxy
580
+ when URI
581
+ uri = url.find_proxy
582
+ when nil
583
+ uri = find_default_proxy
584
+ end
585
+ else
586
+ warn 'no_proxy is unsupported' if ENV['no_proxy'] || ENV['NO_PROXY']
587
+ uri = find_default_proxy
243
588
  end
589
+ ProxyOptions.from(uri) if uri
590
+ end
591
+
592
+ def find_default_proxy
593
+ uri = ENV['http_proxy']
594
+ return unless uri && !uri.empty?
595
+
596
+ uri = 'http://' + uri if uri !~ /^http/i
597
+ uri
598
+ end
599
+
600
+ def proxy_for_request(url)
601
+ return proxy if @manual_proxy
602
+
603
+ if url && Utils.URI(url).absolute?
604
+ proxy_from_env(url)
605
+ else
606
+ proxy
607
+ end
608
+ end
609
+
610
+ def support_parallel?(adapter)
611
+ adapter&.respond_to?(:supports_parallel?) && adapter&.supports_parallel?
244
612
  end
245
613
  end
246
614
  end