faraday 0.13.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (92) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +496 -0
  3. data/LICENSE.md +1 -1
  4. data/README.md +28 -328
  5. data/Rakefile +7 -0
  6. data/examples/client_spec.rb +97 -0
  7. data/examples/client_test.rb +118 -0
  8. data/lib/faraday/adapter/test.rb +127 -68
  9. data/lib/faraday/adapter.rb +71 -22
  10. data/lib/faraday/adapter_registry.rb +30 -0
  11. data/lib/faraday/connection.rb +314 -226
  12. data/lib/faraday/encoders/flat_params_encoder.rb +105 -0
  13. data/lib/faraday/encoders/nested_params_encoder.rb +176 -0
  14. data/lib/faraday/error.rb +121 -37
  15. data/lib/faraday/logging/formatter.rb +106 -0
  16. data/lib/faraday/methods.rb +6 -0
  17. data/lib/faraday/middleware.rb +18 -25
  18. data/lib/faraday/middleware_registry.rb +65 -0
  19. data/lib/faraday/options/connection_options.rb +22 -0
  20. data/lib/faraday/options/env.rb +181 -0
  21. data/lib/faraday/options/proxy_options.rb +32 -0
  22. data/lib/faraday/options/request_options.rb +22 -0
  23. data/lib/faraday/options/ssl_options.rb +59 -0
  24. data/lib/faraday/options.rb +41 -195
  25. data/lib/faraday/parameters.rb +4 -196
  26. data/lib/faraday/rack_builder.rb +91 -74
  27. data/lib/faraday/request/authorization.rb +37 -29
  28. data/lib/faraday/request/instrumentation.rb +47 -27
  29. data/lib/faraday/request/json.rb +55 -0
  30. data/lib/faraday/request/url_encoded.rb +45 -23
  31. data/lib/faraday/request.rb +74 -32
  32. data/lib/faraday/response/json.rb +54 -0
  33. data/lib/faraday/response/logger.rb +22 -69
  34. data/lib/faraday/response/raise_error.rb +57 -14
  35. data/lib/faraday/response.rb +26 -33
  36. data/lib/faraday/utils/headers.rb +139 -0
  37. data/lib/faraday/utils/params_hash.rb +61 -0
  38. data/lib/faraday/utils.rb +47 -251
  39. data/lib/faraday/version.rb +5 -0
  40. data/lib/faraday.rb +104 -197
  41. data/spec/external_adapters/faraday_specs_setup.rb +14 -0
  42. data/spec/faraday/adapter/test_spec.rb +377 -0
  43. data/spec/faraday/adapter_registry_spec.rb +28 -0
  44. data/spec/faraday/adapter_spec.rb +55 -0
  45. data/spec/faraday/connection_spec.rb +787 -0
  46. data/spec/faraday/error_spec.rb +60 -0
  47. data/spec/faraday/middleware_spec.rb +52 -0
  48. data/spec/faraday/options/env_spec.rb +70 -0
  49. data/spec/faraday/options/options_spec.rb +297 -0
  50. data/spec/faraday/options/proxy_options_spec.rb +44 -0
  51. data/spec/faraday/options/request_options_spec.rb +19 -0
  52. data/spec/faraday/params_encoders/flat_spec.rb +42 -0
  53. data/spec/faraday/params_encoders/nested_spec.rb +142 -0
  54. data/spec/faraday/rack_builder_spec.rb +302 -0
  55. data/spec/faraday/request/authorization_spec.rb +83 -0
  56. data/spec/faraday/request/instrumentation_spec.rb +74 -0
  57. data/spec/faraday/request/json_spec.rb +111 -0
  58. data/spec/faraday/request/url_encoded_spec.rb +82 -0
  59. data/spec/faraday/request_spec.rb +109 -0
  60. data/spec/faraday/response/json_spec.rb +117 -0
  61. data/spec/faraday/response/logger_spec.rb +220 -0
  62. data/spec/faraday/response/raise_error_spec.rb +172 -0
  63. data/spec/faraday/response_spec.rb +75 -0
  64. data/spec/faraday/utils/headers_spec.rb +82 -0
  65. data/spec/faraday/utils_spec.rb +117 -0
  66. data/spec/faraday_spec.rb +37 -0
  67. data/spec/spec_helper.rb +132 -0
  68. data/spec/support/disabling_stub.rb +14 -0
  69. data/spec/support/fake_safe_buffer.rb +15 -0
  70. data/spec/support/helper_methods.rb +96 -0
  71. data/spec/support/shared_examples/adapter.rb +104 -0
  72. data/spec/support/shared_examples/params_encoder.rb +18 -0
  73. data/spec/support/shared_examples/request_method.rb +249 -0
  74. data/spec/support/streaming_response_checker.rb +35 -0
  75. metadata +71 -34
  76. data/lib/faraday/adapter/em_http.rb +0 -243
  77. data/lib/faraday/adapter/em_http_ssl_patch.rb +0 -56
  78. data/lib/faraday/adapter/em_synchrony/parallel_manager.rb +0 -66
  79. data/lib/faraday/adapter/em_synchrony.rb +0 -106
  80. data/lib/faraday/adapter/excon.rb +0 -80
  81. data/lib/faraday/adapter/httpclient.rb +0 -128
  82. data/lib/faraday/adapter/net_http.rb +0 -135
  83. data/lib/faraday/adapter/net_http_persistent.rb +0 -54
  84. data/lib/faraday/adapter/patron.rb +0 -83
  85. data/lib/faraday/adapter/rack.rb +0 -58
  86. data/lib/faraday/adapter/typhoeus.rb +0 -123
  87. data/lib/faraday/autoload.rb +0 -84
  88. data/lib/faraday/request/basic_authentication.rb +0 -13
  89. data/lib/faraday/request/multipart.rb +0 -68
  90. data/lib/faraday/request/retry.rb +0 -164
  91. data/lib/faraday/request/token_authentication.rb +0 -15
  92. data/lib/faraday/upload_io.rb +0 -67
@@ -0,0 +1,787 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CustomEncoder
4
+ def encode(params)
5
+ params.map { |k, v| "#{k.upcase}-#{v.to_s.upcase}" }.join(',')
6
+ end
7
+
8
+ def decode(params)
9
+ params.split(',').map { |pair| pair.split('-') }.to_h
10
+ end
11
+ end
12
+
13
+ shared_examples 'initializer with url' do
14
+ context 'with simple url' do
15
+ let(:address) { 'http://sushi.com' }
16
+
17
+ it { expect(subject.host).to eq('sushi.com') }
18
+ it { expect(subject.port).to eq(80) }
19
+ it { expect(subject.scheme).to eq('http') }
20
+ it { expect(subject.path_prefix).to eq('/') }
21
+ it { expect(subject.params).to eq({}) }
22
+ end
23
+
24
+ context 'with complex url' do
25
+ let(:address) { 'http://sushi.com:815/fish?a=1' }
26
+
27
+ it { expect(subject.port).to eq(815) }
28
+ it { expect(subject.path_prefix).to eq('/fish') }
29
+ it { expect(subject.params).to eq('a' => '1') }
30
+ end
31
+
32
+ context 'with IPv6 address' do
33
+ let(:address) { 'http://[::1]:85/' }
34
+
35
+ it { expect(subject.host).to eq('[::1]') }
36
+ it { expect(subject.port).to eq(85) }
37
+ end
38
+ end
39
+
40
+ shared_examples 'default connection options' do
41
+ after { Faraday.default_connection_options = nil }
42
+
43
+ it 'works with implicit url' do
44
+ conn = Faraday.new 'http://sushi.com/foo'
45
+ expect(conn.options.timeout).to eq(10)
46
+ end
47
+
48
+ it 'works with option url' do
49
+ conn = Faraday.new url: 'http://sushi.com/foo'
50
+ expect(conn.options.timeout).to eq(10)
51
+ end
52
+
53
+ it 'works with instance connection options' do
54
+ conn = Faraday.new 'http://sushi.com/foo', request: { open_timeout: 1 }
55
+ expect(conn.options.timeout).to eq(10)
56
+ expect(conn.options.open_timeout).to eq(1)
57
+ end
58
+
59
+ it 'default connection options persist with an instance overriding' do
60
+ conn = Faraday.new 'http://nigiri.com/bar'
61
+ conn.options.timeout = 1
62
+ expect(Faraday.default_connection_options.request.timeout).to eq(10)
63
+
64
+ other = Faraday.new url: 'https://sushi.com/foo'
65
+ other.options.timeout = 1
66
+
67
+ expect(Faraday.default_connection_options.request.timeout).to eq(10)
68
+ end
69
+
70
+ it 'default connection uses default connection options' do
71
+ expect(Faraday.default_connection.options.timeout).to eq(10)
72
+ end
73
+ end
74
+
75
+ RSpec.describe Faraday::Connection do
76
+ let(:conn) { Faraday::Connection.new(url, options) }
77
+ let(:url) { nil }
78
+ let(:options) { nil }
79
+
80
+ describe '.new' do
81
+ subject { conn }
82
+
83
+ context 'with implicit url param' do
84
+ # Faraday::Connection.new('http://sushi.com')
85
+ let(:url) { address }
86
+
87
+ it_behaves_like 'initializer with url'
88
+ end
89
+
90
+ context 'with explicit url param' do
91
+ # Faraday::Connection.new(url: 'http://sushi.com')
92
+ let(:url) { { url: address } }
93
+
94
+ it_behaves_like 'initializer with url'
95
+ end
96
+
97
+ context 'with custom builder' do
98
+ let(:custom_builder) { Faraday::RackBuilder.new }
99
+ let(:options) { { builder: custom_builder } }
100
+
101
+ it { expect(subject.builder).to eq(custom_builder) }
102
+ end
103
+
104
+ context 'with custom params' do
105
+ let(:options) { { params: { a: 1 } } }
106
+
107
+ it { expect(subject.params).to eq('a' => 1) }
108
+ end
109
+
110
+ context 'with custom params and params in url' do
111
+ let(:url) { 'http://sushi.com/fish?a=1&b=2' }
112
+ let(:options) { { params: { a: 3 } } }
113
+ it { expect(subject.params).to eq('a' => 3, 'b' => '2') }
114
+ end
115
+
116
+ context 'with basic_auth in url' do
117
+ let(:url) { 'http://Aladdin:open%20sesame@sushi.com/fish' }
118
+
119
+ it { expect(subject.headers['Authorization']).to eq('Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==') }
120
+ end
121
+
122
+ context 'with custom headers' do
123
+ let(:options) { { headers: { user_agent: 'Faraday' } } }
124
+
125
+ it { expect(subject.headers['User-agent']).to eq('Faraday') }
126
+ end
127
+
128
+ context 'with ssl false' do
129
+ let(:options) { { ssl: { verify: false } } }
130
+
131
+ it { expect(subject.ssl.verify?).to be_falsey }
132
+ end
133
+
134
+ context 'with empty block' do
135
+ let(:conn) { Faraday::Connection.new {} }
136
+
137
+ it { expect(conn.builder.handlers.size).to eq(0) }
138
+ end
139
+
140
+ context 'with block' do
141
+ let(:conn) do
142
+ Faraday::Connection.new(params: { 'a' => '1' }) do |faraday|
143
+ faraday.adapter :test
144
+ faraday.url_prefix = 'http://sushi.com/omnom'
145
+ end
146
+ end
147
+
148
+ it { expect(conn.builder.handlers.size).to eq(0) }
149
+ it { expect(conn.path_prefix).to eq('/omnom') }
150
+ end
151
+ end
152
+
153
+ describe '#close' do
154
+ before { Faraday.default_adapter = :test }
155
+ after { Faraday.default_adapter = nil }
156
+
157
+ it 'can close underlying app' do
158
+ expect(conn.app).to receive(:close)
159
+ conn.close
160
+ end
161
+ end
162
+
163
+ describe '#build_exclusive_url' do
164
+ context 'with relative path' do
165
+ subject { conn.build_exclusive_url('sake.html') }
166
+
167
+ it 'uses connection host as default host' do
168
+ conn.host = 'sushi.com'
169
+ expect(subject.host).to eq('sushi.com')
170
+ expect(subject.scheme).to eq('http')
171
+ end
172
+
173
+ it do
174
+ conn.path_prefix = '/fish'
175
+ expect(subject.path).to eq('/fish/sake.html')
176
+ end
177
+
178
+ it do
179
+ conn.path_prefix = '/'
180
+ expect(subject.path).to eq('/sake.html')
181
+ end
182
+
183
+ it do
184
+ conn.path_prefix = 'fish'
185
+ expect(subject.path).to eq('/fish/sake.html')
186
+ end
187
+
188
+ it do
189
+ conn.path_prefix = '/fish/'
190
+ expect(subject.path).to eq('/fish/sake.html')
191
+ end
192
+ end
193
+
194
+ context 'with absolute path' do
195
+ subject { conn.build_exclusive_url('/sake.html') }
196
+
197
+ after { expect(subject.path).to eq('/sake.html') }
198
+
199
+ it { conn.path_prefix = '/fish' }
200
+ it { conn.path_prefix = '/' }
201
+ it { conn.path_prefix = 'fish' }
202
+ it { conn.path_prefix = '/fish/' }
203
+ end
204
+
205
+ context 'with complete url' do
206
+ subject { conn.build_exclusive_url('http://sushi.com/sake.html?a=1') }
207
+
208
+ it { expect(subject.scheme).to eq('http') }
209
+ it { expect(subject.host).to eq('sushi.com') }
210
+ it { expect(subject.port).to eq(80) }
211
+ it { expect(subject.path).to eq('/sake.html') }
212
+ it { expect(subject.query).to eq('a=1') }
213
+ end
214
+
215
+ it 'overrides connection port for absolute url' do
216
+ conn.port = 23
217
+ uri = conn.build_exclusive_url('http://sushi.com')
218
+ expect(uri.port).to eq(80)
219
+ end
220
+
221
+ it 'does not add ending slash given nil url' do
222
+ conn.url_prefix = 'http://sushi.com/nigiri'
223
+ uri = conn.build_exclusive_url
224
+ expect(uri.path).to eq('/nigiri')
225
+ end
226
+
227
+ it 'does not add ending slash given empty url' do
228
+ conn.url_prefix = 'http://sushi.com/nigiri'
229
+ uri = conn.build_exclusive_url('')
230
+ expect(uri.path).to eq('/nigiri')
231
+ end
232
+
233
+ it 'does not use connection params' do
234
+ conn.url_prefix = 'http://sushi.com/nigiri'
235
+ conn.params = { a: 1 }
236
+ expect(conn.build_exclusive_url.to_s).to eq('http://sushi.com/nigiri')
237
+ end
238
+
239
+ it 'allows to provide params argument' do
240
+ conn.url_prefix = 'http://sushi.com/nigiri'
241
+ conn.params = { a: 1 }
242
+ params = Faraday::Utils::ParamsHash.new
243
+ params[:a] = 2
244
+ uri = conn.build_exclusive_url(nil, params)
245
+ expect(uri.to_s).to eq('http://sushi.com/nigiri?a=2')
246
+ end
247
+
248
+ it 'handles uri instances' do
249
+ uri = conn.build_exclusive_url(URI('/sake.html'))
250
+ expect(uri.path).to eq('/sake.html')
251
+ end
252
+
253
+ it 'always returns new URI instance' do
254
+ conn.url_prefix = 'http://sushi.com'
255
+ uri1 = conn.build_exclusive_url(nil)
256
+ uri2 = conn.build_exclusive_url(nil)
257
+ expect(uri1).not_to equal(uri2)
258
+ end
259
+
260
+ context 'with url_prefixed connection' do
261
+ let(:url) { 'http://sushi.com/sushi/' }
262
+
263
+ it 'parses url and changes scheme' do
264
+ conn.scheme = 'https'
265
+ uri = conn.build_exclusive_url('sake.html')
266
+ expect(uri.to_s).to eq('https://sushi.com/sushi/sake.html')
267
+ end
268
+
269
+ it 'joins url to base with ending slash' do
270
+ uri = conn.build_exclusive_url('sake.html')
271
+ expect(uri.to_s).to eq('http://sushi.com/sushi/sake.html')
272
+ end
273
+
274
+ it 'used default base with ending slash' do
275
+ uri = conn.build_exclusive_url
276
+ expect(uri.to_s).to eq('http://sushi.com/sushi/')
277
+ end
278
+
279
+ it 'overrides base' do
280
+ uri = conn.build_exclusive_url('/sake/')
281
+ expect(uri.to_s).to eq('http://sushi.com/sake/')
282
+ end
283
+ end
284
+
285
+ context 'with colon in path' do
286
+ let(:url) { 'http://service.com' }
287
+
288
+ it 'joins url to base when used absolute path' do
289
+ conn = Faraday.new(url: url)
290
+ uri = conn.build_exclusive_url('/service:search?limit=400')
291
+ expect(uri.to_s).to eq('http://service.com/service:search?limit=400')
292
+ end
293
+
294
+ it 'joins url to base when used relative path' do
295
+ conn = Faraday.new(url: url)
296
+ uri = conn.build_exclusive_url('service:search?limit=400')
297
+ expect(uri.to_s).to eq('http://service.com/service%3Asearch?limit=400')
298
+ end
299
+
300
+ it 'joins url to base when used with path prefix' do
301
+ conn = Faraday.new(url: url)
302
+ conn.path_prefix = '/api'
303
+ uri = conn.build_exclusive_url('service:search?limit=400')
304
+ expect(uri.to_s).to eq('http://service.com/api/service%3Asearch?limit=400')
305
+ end
306
+ end
307
+ end
308
+
309
+ describe '#build_url' do
310
+ let(:url) { 'http://sushi.com/nigiri' }
311
+
312
+ it 'uses params' do
313
+ conn.params = { a: 1, b: 1 }
314
+ expect(conn.build_url.to_s).to eq('http://sushi.com/nigiri?a=1&b=1')
315
+ end
316
+
317
+ it 'merges params' do
318
+ conn.params = { a: 1, b: 1 }
319
+ url = conn.build_url(nil, b: 2, c: 3)
320
+ expect(url.to_s).to eq('http://sushi.com/nigiri?a=1&b=2&c=3')
321
+ end
322
+ end
323
+
324
+ describe '#build_request' do
325
+ let(:url) { 'https://asushi.com/sake.html' }
326
+ let(:request) { conn.build_request(:get) }
327
+
328
+ before do
329
+ conn.headers = { 'Authorization' => 'token abc123' }
330
+ request.headers.delete('Authorization')
331
+ end
332
+
333
+ it { expect(conn.headers.keys).to eq(['Authorization']) }
334
+ it { expect(conn.headers.include?('Authorization')).to be_truthy }
335
+ it { expect(request.headers.keys).to be_empty }
336
+ it { expect(request.headers.include?('Authorization')).to be_falsey }
337
+ end
338
+
339
+ describe '#to_env' do
340
+ subject { conn.build_request(:get).to_env(conn).url }
341
+
342
+ let(:url) { 'http://sushi.com/sake.html' }
343
+ let(:options) { { params: @params } }
344
+
345
+ it 'parses url params into query' do
346
+ @params = { 'a[b]' => '1 + 2' }
347
+ expect(subject.query).to eq('a%5Bb%5D=1+%2B+2')
348
+ end
349
+
350
+ it 'escapes per spec' do
351
+ @params = { 'a' => '1+2 foo~bar.-baz' }
352
+ expect(subject.query).to eq('a=1%2B2+foo~bar.-baz')
353
+ end
354
+
355
+ it 'bracketizes nested params in query' do
356
+ @params = { 'a' => { 'b' => 'c' } }
357
+ expect(subject.query).to eq('a%5Bb%5D=c')
358
+ end
359
+
360
+ it 'bracketizes repeated params in query' do
361
+ @params = { 'a' => [1, 2] }
362
+ expect(subject.query).to eq('a%5B%5D=1&a%5B%5D=2')
363
+ end
364
+
365
+ it 'without braketizing repeated params in query' do
366
+ @params = { 'a' => [1, 2] }
367
+ conn.options.params_encoder = Faraday::FlatParamsEncoder
368
+ expect(subject.query).to eq('a=1&a=2')
369
+ end
370
+ end
371
+
372
+ describe 'proxy support' do
373
+ it 'accepts string' do
374
+ with_env 'http_proxy' => 'http://env-proxy.com:80' do
375
+ conn.proxy = 'http://proxy.com'
376
+ expect(conn.proxy.host).to eq('proxy.com')
377
+ end
378
+ end
379
+
380
+ it 'accepts uri' do
381
+ with_env 'http_proxy' => 'http://env-proxy.com:80' do
382
+ conn.proxy = URI.parse('http://proxy.com')
383
+ expect(conn.proxy.host).to eq('proxy.com')
384
+ end
385
+ end
386
+
387
+ it 'accepts hash with string uri' do
388
+ with_env 'http_proxy' => 'http://env-proxy.com:80' do
389
+ conn.proxy = { uri: 'http://proxy.com', user: 'rick' }
390
+ expect(conn.proxy.host).to eq('proxy.com')
391
+ expect(conn.proxy.user).to eq('rick')
392
+ end
393
+ end
394
+
395
+ it 'accepts hash' do
396
+ with_env 'http_proxy' => 'http://env-proxy.com:80' do
397
+ conn.proxy = { uri: URI.parse('http://proxy.com'), user: 'rick' }
398
+ expect(conn.proxy.host).to eq('proxy.com')
399
+ expect(conn.proxy.user).to eq('rick')
400
+ end
401
+ end
402
+
403
+ it 'accepts http env' do
404
+ with_env 'http_proxy' => 'http://env-proxy.com:80' do
405
+ expect(conn.proxy.host).to eq('env-proxy.com')
406
+ end
407
+ end
408
+
409
+ it 'accepts http env with auth' do
410
+ with_env 'http_proxy' => 'http://a%40b:my%20pass@proxy.com:80' do
411
+ expect(conn.proxy.user).to eq('a@b')
412
+ expect(conn.proxy.password).to eq('my pass')
413
+ end
414
+ end
415
+
416
+ it 'accepts env without scheme' do
417
+ with_env 'http_proxy' => 'localhost:8888' do
418
+ uri = conn.proxy[:uri]
419
+ expect(uri.host).to eq('localhost')
420
+ expect(uri.port).to eq(8888)
421
+ end
422
+ end
423
+
424
+ it 'fetches no proxy from nil env' do
425
+ with_env 'http_proxy' => nil do
426
+ expect(conn.proxy).to be_nil
427
+ end
428
+ end
429
+
430
+ it 'fetches no proxy from blank env' do
431
+ with_env 'http_proxy' => '' do
432
+ expect(conn.proxy).to be_nil
433
+ end
434
+ end
435
+
436
+ it 'does not accept uppercase env' do
437
+ with_env 'HTTP_PROXY' => 'http://localhost:8888/' do
438
+ expect(conn.proxy).to be_nil
439
+ end
440
+ end
441
+
442
+ it 'allows when url in no proxy list' do
443
+ with_env 'http_proxy' => 'http://proxy.com', 'no_proxy' => 'example.com' do
444
+ conn = Faraday::Connection.new('http://example.com')
445
+ expect(conn.proxy).to be_nil
446
+ end
447
+ end
448
+
449
+ it 'allows when url in no proxy list with url_prefix' do
450
+ with_env 'http_proxy' => 'http://proxy.com', 'no_proxy' => 'example.com' do
451
+ conn = Faraday::Connection.new
452
+ conn.url_prefix = 'http://example.com'
453
+ expect(conn.proxy).to be_nil
454
+ end
455
+ end
456
+
457
+ it 'allows when prefixed url is not in no proxy list' do
458
+ with_env 'http_proxy' => 'http://proxy.com', 'no_proxy' => 'example.com' do
459
+ conn = Faraday::Connection.new('http://prefixedexample.com')
460
+ expect(conn.proxy.host).to eq('proxy.com')
461
+ end
462
+ end
463
+
464
+ it 'allows when subdomain url is in no proxy list' do
465
+ with_env 'http_proxy' => 'http://proxy.com', 'no_proxy' => 'example.com' do
466
+ conn = Faraday::Connection.new('http://subdomain.example.com')
467
+ expect(conn.proxy).to be_nil
468
+ end
469
+ end
470
+
471
+ it 'allows when url not in no proxy list' do
472
+ with_env 'http_proxy' => 'http://proxy.com', 'no_proxy' => 'example2.com' do
473
+ conn = Faraday::Connection.new('http://example.com')
474
+ expect(conn.proxy.host).to eq('proxy.com')
475
+ end
476
+ end
477
+
478
+ it 'allows when ip address is not in no proxy list but url is' do
479
+ with_env 'http_proxy' => 'http://proxy.com', 'no_proxy' => 'localhost' do
480
+ conn = Faraday::Connection.new('http://127.0.0.1')
481
+ expect(conn.proxy).to be_nil
482
+ end
483
+ end
484
+
485
+ it 'allows when url is not in no proxy list but ip address is' do
486
+ with_env 'http_proxy' => 'http://proxy.com', 'no_proxy' => '127.0.0.1' do
487
+ conn = Faraday::Connection.new('http://localhost')
488
+ expect(conn.proxy).to be_nil
489
+ end
490
+ end
491
+
492
+ it 'allows in multi element no proxy list' do
493
+ with_env 'http_proxy' => 'http://proxy.com', 'no_proxy' => 'example0.com,example.com,example1.com' do
494
+ expect(Faraday::Connection.new('http://example0.com').proxy).to be_nil
495
+ expect(Faraday::Connection.new('http://example.com').proxy).to be_nil
496
+ expect(Faraday::Connection.new('http://example1.com').proxy).to be_nil
497
+ expect(Faraday::Connection.new('http://example2.com').proxy.host).to eq('proxy.com')
498
+ end
499
+ end
500
+
501
+ it 'test proxy requires uri' do
502
+ expect { conn.proxy = { uri: :bad_uri, user: 'rick' } }.to raise_error(ArgumentError)
503
+ end
504
+
505
+ it 'uses env http_proxy' do
506
+ with_env 'http_proxy' => 'http://proxy.com' do
507
+ conn = Faraday.new
508
+ expect(conn.instance_variable_get('@manual_proxy')).to be_falsey
509
+ expect(conn.proxy_for_request('http://google.co.uk').host).to eq('proxy.com')
510
+ end
511
+ end
512
+
513
+ it 'uses processes no_proxy before http_proxy' do
514
+ with_env 'http_proxy' => 'http://proxy.com', 'no_proxy' => 'google.co.uk' do
515
+ conn = Faraday.new
516
+ expect(conn.instance_variable_get('@manual_proxy')).to be_falsey
517
+ expect(conn.proxy_for_request('http://google.co.uk')).to be_nil
518
+ end
519
+ end
520
+
521
+ it 'uses env https_proxy' do
522
+ with_env 'https_proxy' => 'https://proxy.com' do
523
+ conn = Faraday.new
524
+ expect(conn.instance_variable_get('@manual_proxy')).to be_falsey
525
+ expect(conn.proxy_for_request('https://google.co.uk').host).to eq('proxy.com')
526
+ end
527
+ end
528
+
529
+ it 'uses processes no_proxy before https_proxy' do
530
+ with_env 'https_proxy' => 'https://proxy.com', 'no_proxy' => 'google.co.uk' do
531
+ conn = Faraday.new
532
+ expect(conn.instance_variable_get('@manual_proxy')).to be_falsey
533
+ expect(conn.proxy_for_request('https://google.co.uk')).to be_nil
534
+ end
535
+ end
536
+
537
+ it 'gives priority to manually set proxy' do
538
+ with_env 'https_proxy' => 'https://proxy.com', 'no_proxy' => 'google.co.uk' do
539
+ conn = Faraday.new
540
+ conn.proxy = 'http://proxy2.com'
541
+
542
+ expect(conn.instance_variable_get('@manual_proxy')).to be_truthy
543
+ expect(conn.proxy_for_request('https://google.co.uk').host).to eq('proxy2.com')
544
+ end
545
+ end
546
+
547
+ it 'ignores env proxy if set that way' do
548
+ with_env_proxy_disabled do
549
+ with_env 'http_proxy' => 'http://duncan.proxy.com:80' do
550
+ expect(conn.proxy).to be_nil
551
+ end
552
+ end
553
+ end
554
+
555
+ context 'performing a request' do
556
+ let(:url) { 'http://example.com' }
557
+ let(:conn) do
558
+ Faraday.new do |f|
559
+ f.adapter :test do |stubs|
560
+ stubs.get(url) do
561
+ [200, {}, 'ok']
562
+ end
563
+ end
564
+ end
565
+ end
566
+
567
+ it 'dynamically checks proxy' do
568
+ with_env 'http_proxy' => 'http://proxy.com:80' do
569
+ expect(conn.proxy.uri.host).to eq('proxy.com')
570
+
571
+ conn.get(url) do |req|
572
+ expect(req.options.proxy.uri.host).to eq('proxy.com')
573
+ end
574
+ end
575
+
576
+ conn.get(url)
577
+ expect(conn.instance_variable_get('@temp_proxy')).to be_nil
578
+ end
579
+
580
+ it 'dynamically check no proxy' do
581
+ with_env 'http_proxy' => 'http://proxy.com', 'no_proxy' => 'example.com' do
582
+ expect(conn.proxy.uri.host).to eq('proxy.com')
583
+
584
+ conn.get('http://example.com') do |req|
585
+ expect(req.options.proxy).to be_nil
586
+ end
587
+ end
588
+ end
589
+ end
590
+ end
591
+
592
+ describe '#dup' do
593
+ subject { conn.dup }
594
+
595
+ let(:url) { 'http://sushi.com/foo' }
596
+ let(:options) do
597
+ {
598
+ ssl: { verify: :none },
599
+ headers: { 'content-type' => 'text/plain' },
600
+ params: { 'a' => '1' },
601
+ request: { timeout: 5 }
602
+ }
603
+ end
604
+
605
+ it { expect(subject.build_exclusive_url).to eq(conn.build_exclusive_url) }
606
+ it { expect(subject.headers['content-type']).to eq('text/plain') }
607
+ it { expect(subject.params['a']).to eq('1') }
608
+
609
+ context 'after manual changes' do
610
+ before do
611
+ subject.headers['content-length'] = 12
612
+ subject.params['b'] = '2'
613
+ subject.options[:open_timeout] = 10
614
+ end
615
+
616
+ it { expect(subject.builder.handlers.size).to eq(1) }
617
+ it { expect(conn.builder.handlers.size).to eq(1) }
618
+ it { expect(conn.headers.key?('content-length')).to be_falsey }
619
+ it { expect(conn.params.key?('b')).to be_falsey }
620
+ it { expect(subject.options[:timeout]).to eq(5) }
621
+ it { expect(conn.options[:open_timeout]).to be_nil }
622
+ end
623
+ end
624
+
625
+ describe '#respond_to?' do
626
+ it { expect(Faraday.respond_to?(:get)).to be_truthy }
627
+ it { expect(Faraday.respond_to?(:post)).to be_truthy }
628
+ end
629
+
630
+ describe 'default_connection_options' do
631
+ context 'assigning a default value' do
632
+ before do
633
+ Faraday.default_connection_options = nil
634
+ Faraday.default_connection_options.request.timeout = 10
635
+ end
636
+
637
+ it_behaves_like 'default connection options'
638
+ end
639
+
640
+ context 'assigning a hash' do
641
+ before { Faraday.default_connection_options = { request: { timeout: 10 } } }
642
+
643
+ it_behaves_like 'default connection options'
644
+ end
645
+
646
+ context 'preserving a user_agent assigned via default_conncetion_options' do
647
+ around do |example|
648
+ old = Faraday.default_connection_options
649
+ Faraday.default_connection_options = { headers: { user_agent: 'My Agent 1.2' } }
650
+ example.run
651
+ Faraday.default_connection_options = old
652
+ end
653
+
654
+ context 'when url is a Hash' do
655
+ let(:conn) { Faraday.new(url: 'http://example.co', headers: { 'CustomHeader' => 'CustomValue' }) }
656
+
657
+ it { expect(conn.headers).to eq('CustomHeader' => 'CustomValue', 'User-Agent' => 'My Agent 1.2') }
658
+ end
659
+
660
+ context 'when url is a String' do
661
+ let(:conn) { Faraday.new('http://example.co', headers: { 'CustomHeader' => 'CustomValue' }) }
662
+
663
+ it { expect(conn.headers).to eq('CustomHeader' => 'CustomValue', 'User-Agent' => 'My Agent 1.2') }
664
+ end
665
+ end
666
+ end
667
+
668
+ describe 'request params' do
669
+ context 'with simple url' do
670
+ let(:url) { 'http://example.com' }
671
+ let(:stubs) { Faraday::Adapter::Test::Stubs.new }
672
+
673
+ before do
674
+ conn.adapter(:test, stubs)
675
+ stubs.get('http://example.com?a=a&p=3') do
676
+ [200, {}, 'ok']
677
+ end
678
+ end
679
+
680
+ after { stubs.verify_stubbed_calls }
681
+
682
+ it 'test_overrides_request_params' do
683
+ conn.get('?p=2&a=a', p: 3)
684
+ end
685
+
686
+ it 'test_overrides_request_params_block' do
687
+ conn.get('?p=1&a=a', p: 2) do |req|
688
+ req.params[:p] = 3
689
+ end
690
+ end
691
+
692
+ it 'test_overrides_request_params_block_url' do
693
+ conn.get(nil, p: 2) do |req|
694
+ req.url('?p=1&a=a', 'p' => 3)
695
+ end
696
+ end
697
+ end
698
+
699
+ context 'with url and extra params' do
700
+ let(:url) { 'http://example.com?a=1&b=2' }
701
+ let(:options) { { params: { c: 3 } } }
702
+ let(:stubs) { Faraday::Adapter::Test::Stubs.new }
703
+
704
+ before do
705
+ conn.adapter(:test, stubs)
706
+ end
707
+
708
+ it 'merges connection and request params' do
709
+ expected = 'http://example.com?a=1&b=2&c=3&limit=5&page=1'
710
+ stubs.get(expected) { [200, {}, 'ok'] }
711
+ conn.get('?page=1', limit: 5)
712
+ stubs.verify_stubbed_calls
713
+ end
714
+
715
+ it 'allows to override all params' do
716
+ expected = 'http://example.com?b=b'
717
+ stubs.get(expected) { [200, {}, 'ok'] }
718
+ conn.get('?p=1&a=a', p: 2) do |req|
719
+ expect(req.params[:a]).to eq('a')
720
+ expect(req.params['c']).to eq(3)
721
+ expect(req.params['p']).to eq(2)
722
+ req.params = { b: 'b' }
723
+ expect(req.params['b']).to eq('b')
724
+ end
725
+ stubs.verify_stubbed_calls
726
+ end
727
+
728
+ it 'allows to set params_encoder for single request' do
729
+ encoder = CustomEncoder.new
730
+ expected = 'http://example.com/?A-1,B-2,C-3,FEELING-BLUE'
731
+ stubs.get(expected) { [200, {}, 'ok'] }
732
+
733
+ conn.get('/', a: 1, b: 2, c: 3, feeling: 'blue') do |req|
734
+ req.options.params_encoder = encoder
735
+ end
736
+ stubs.verify_stubbed_calls
737
+ end
738
+ end
739
+
740
+ context 'with default params encoder' do
741
+ let(:stubs) { Faraday::Adapter::Test::Stubs.new }
742
+
743
+ before do
744
+ conn.adapter(:test, stubs)
745
+ stubs.get('http://example.com?color%5B%5D=blue&color%5B%5D=red') do
746
+ [200, {}, 'ok']
747
+ end
748
+ end
749
+
750
+ after { stubs.verify_stubbed_calls }
751
+
752
+ it 'supports array params in url' do
753
+ conn.get('http://example.com?color[]=blue&color[]=red')
754
+ end
755
+
756
+ it 'supports array params in params' do
757
+ conn.get('http://example.com', color: %w[blue red])
758
+ end
759
+ end
760
+
761
+ context 'with flat params encoder' do
762
+ let(:options) { { request: { params_encoder: Faraday::FlatParamsEncoder } } }
763
+ let(:stubs) { Faraday::Adapter::Test::Stubs.new }
764
+
765
+ before do
766
+ conn.adapter(:test, stubs)
767
+ stubs.get('http://example.com?color=blue&color=red') do
768
+ [200, {}, 'ok']
769
+ end
770
+ end
771
+
772
+ after { stubs.verify_stubbed_calls }
773
+
774
+ it 'supports array params in params' do
775
+ conn.get('http://example.com', color: %w[blue red])
776
+ end
777
+
778
+ context 'with array param in url' do
779
+ let(:url) { 'http://example.com?color[]=blue&color[]=red' }
780
+
781
+ it do
782
+ conn.get('/')
783
+ end
784
+ end
785
+ end
786
+ end
787
+ end