faraday 1.0.0.pre.rc1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (75) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +276 -0
  3. data/README.md +4 -4
  4. data/Rakefile +7 -0
  5. data/UPGRADING.md +55 -0
  6. data/examples/client_spec.rb +65 -0
  7. data/examples/client_test.rb +79 -0
  8. data/lib/faraday.rb +4 -4
  9. data/lib/faraday/adapter.rb +46 -0
  10. data/lib/faraday/adapter/em_http.rb +5 -6
  11. data/lib/faraday/adapter/em_http_ssl_patch.rb +1 -1
  12. data/lib/faraday/adapter/excon.rb +24 -22
  13. data/lib/faraday/adapter/httpclient.rb +40 -39
  14. data/lib/faraday/adapter/net_http.rb +42 -38
  15. data/lib/faraday/adapter/net_http_persistent.rb +3 -1
  16. data/lib/faraday/adapter/patron.rb +42 -24
  17. data/lib/faraday/adapter/rack.rb +2 -1
  18. data/lib/faraday/connection.rb +10 -22
  19. data/lib/faraday/encoders/flat_params_encoder.rb +7 -3
  20. data/lib/faraday/error.rb +44 -12
  21. data/lib/faraday/{upload_io.rb → file_part.rb} +53 -3
  22. data/lib/faraday/logging/formatter.rb +28 -15
  23. data/lib/faraday/middleware.rb +8 -0
  24. data/lib/faraday/options.rb +1 -1
  25. data/lib/faraday/options/env.rb +1 -1
  26. data/lib/faraday/options/request_options.rb +3 -2
  27. data/lib/faraday/param_part.rb +53 -0
  28. data/lib/faraday/request/multipart.rb +9 -1
  29. data/lib/faraday/response.rb +2 -2
  30. data/lib/faraday/response/raise_error.rb +2 -0
  31. data/spec/faraday/adapter/em_http_spec.rb +47 -0
  32. data/spec/faraday/adapter/em_synchrony_spec.rb +16 -0
  33. data/spec/faraday/adapter/excon_spec.rb +49 -0
  34. data/spec/faraday/adapter/httpclient_spec.rb +73 -0
  35. data/spec/faraday/adapter/net_http_persistent_spec.rb +57 -0
  36. data/spec/faraday/adapter/net_http_spec.rb +64 -0
  37. data/spec/faraday/adapter/patron_spec.rb +18 -0
  38. data/spec/faraday/adapter/rack_spec.rb +8 -0
  39. data/spec/faraday/adapter/typhoeus_spec.rb +7 -0
  40. data/spec/faraday/adapter_registry_spec.rb +28 -0
  41. data/spec/faraday/adapter_spec.rb +55 -0
  42. data/spec/faraday/composite_read_io_spec.rb +80 -0
  43. data/spec/faraday/connection_spec.rb +691 -0
  44. data/spec/faraday/error_spec.rb +45 -0
  45. data/spec/faraday/middleware_spec.rb +26 -0
  46. data/spec/faraday/options/env_spec.rb +70 -0
  47. data/spec/faraday/options/options_spec.rb +297 -0
  48. data/spec/faraday/options/proxy_options_spec.rb +37 -0
  49. data/spec/faraday/options/request_options_spec.rb +19 -0
  50. data/spec/faraday/params_encoders/flat_spec.rb +34 -0
  51. data/spec/faraday/params_encoders/nested_spec.rb +134 -0
  52. data/spec/faraday/rack_builder_spec.rb +196 -0
  53. data/spec/faraday/request/authorization_spec.rb +88 -0
  54. data/spec/faraday/request/instrumentation_spec.rb +76 -0
  55. data/spec/faraday/request/multipart_spec.rb +274 -0
  56. data/spec/faraday/request/retry_spec.rb +242 -0
  57. data/spec/faraday/request/url_encoded_spec.rb +70 -0
  58. data/spec/faraday/request_spec.rb +109 -0
  59. data/spec/faraday/response/logger_spec.rb +220 -0
  60. data/spec/faraday/response/middleware_spec.rb +52 -0
  61. data/spec/faraday/response/raise_error_spec.rb +106 -0
  62. data/spec/faraday/response_spec.rb +75 -0
  63. data/spec/faraday/utils/headers_spec.rb +82 -0
  64. data/spec/faraday/utils_spec.rb +56 -0
  65. data/spec/faraday_spec.rb +37 -0
  66. data/spec/spec_helper.rb +132 -0
  67. data/spec/support/disabling_stub.rb +14 -0
  68. data/spec/support/fake_safe_buffer.rb +15 -0
  69. data/spec/support/helper_methods.rb +133 -0
  70. data/spec/support/shared_examples/adapter.rb +104 -0
  71. data/spec/support/shared_examples/params_encoder.rb +18 -0
  72. data/spec/support/shared_examples/request_method.rb +234 -0
  73. data/spec/support/streaming_response_checker.rb +35 -0
  74. data/spec/support/webmock_rack_app.rb +68 -0
  75. metadata +65 -11
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Faraday::Adapter::Rack do
4
+ features :request_body_on_query_methods, :trace_method,
5
+ :skip_response_body_on_head
6
+
7
+ it_behaves_like 'an adapter', adapter_options: WebmockRackApp.new
8
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Faraday::Adapter::Typhoeus do
4
+ features :request_body_on_query_methods, :parallel, :trace_method
5
+
6
+ it_behaves_like 'an adapter'
7
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Faraday::AdapterRegistry do
4
+ describe '#initialize' do
5
+ subject(:registry) { described_class.new }
6
+
7
+ it { expect { registry.get(:FinFangFoom) }.to raise_error(NameError) }
8
+ it { expect { registry.get('FinFangFoom') }.to raise_error(NameError) }
9
+
10
+ it 'looks up class by string name' do
11
+ expect(registry.get('Faraday::Connection')).to eq(Faraday::Connection)
12
+ end
13
+
14
+ it 'looks up class by symbol name' do
15
+ expect(registry.get(:Faraday)).to eq(Faraday)
16
+ end
17
+
18
+ it 'caches lookups with implicit name' do
19
+ registry.set :symbol
20
+ expect(registry.get('symbol')).to eq(:symbol)
21
+ end
22
+
23
+ it 'caches lookups with explicit name' do
24
+ registry.set 'string', :name
25
+ expect(registry.get(:name)).to eq('string')
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Faraday::Adapter do
4
+ let(:adapter) { Faraday::Adapter.new }
5
+ let(:request) { {} }
6
+
7
+ context '#request_timeout' do
8
+ it 'gets :read timeout' do
9
+ expect(timeout(:read)).to eq(nil)
10
+
11
+ request[:timeout] = 5
12
+ request[:write_timeout] = 1
13
+
14
+ expect(timeout(:read)).to eq(5)
15
+
16
+ request[:read_timeout] = 2
17
+
18
+ expect(timeout(:read)).to eq(2)
19
+ end
20
+
21
+ it 'gets :open timeout' do
22
+ expect(timeout(:open)).to eq(nil)
23
+
24
+ request[:timeout] = 5
25
+ request[:write_timeout] = 1
26
+
27
+ expect(timeout(:open)).to eq(5)
28
+
29
+ request[:open_timeout] = 2
30
+
31
+ expect(timeout(:open)).to eq(2)
32
+ end
33
+
34
+ it 'gets :write timeout' do
35
+ expect(timeout(:write)).to eq(nil)
36
+
37
+ request[:timeout] = 5
38
+ request[:read_timeout] = 1
39
+
40
+ expect(timeout(:write)).to eq(5)
41
+
42
+ request[:write_timeout] = 2
43
+
44
+ expect(timeout(:write)).to eq(2)
45
+ end
46
+
47
+ it 'attempts unknown timeout type' do
48
+ expect { timeout(:unknown) }.to raise_error(ArgumentError)
49
+ end
50
+
51
+ def timeout(type)
52
+ adapter.send(:request_timeout, type, request)
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'stringio'
4
+
5
+ RSpec.describe Faraday::CompositeReadIO do
6
+ Part = Struct.new(:to_io) do
7
+ def length
8
+ to_io.string.length
9
+ end
10
+ end
11
+
12
+ def part(str)
13
+ Part.new StringIO.new(str)
14
+ end
15
+
16
+ def composite_io(*parts)
17
+ Faraday::CompositeReadIO.new(*parts)
18
+ end
19
+
20
+ context 'with empty composite_io' do
21
+ subject { composite_io }
22
+
23
+ it { expect(subject.length).to eq(0) }
24
+ it { expect(subject.read).to eq('') }
25
+ it { expect(subject.read(1)).to be_nil }
26
+ end
27
+
28
+ context 'with empty parts' do
29
+ subject { composite_io(part(''), part('')) }
30
+
31
+ it { expect(subject.length).to eq(0) }
32
+ it { expect(subject.read).to eq('') }
33
+ it { expect(subject.read(1)).to be_nil }
34
+ end
35
+
36
+ context 'with 2 parts' do
37
+ subject { composite_io(part('abcd'), part('1234')) }
38
+
39
+ it { expect(subject.length).to eq(8) }
40
+ it { expect(subject.read).to eq('abcd1234') }
41
+ it 'allows to read in chunks' do
42
+ expect(subject.read(3)).to eq('abc')
43
+ expect(subject.read(3)).to eq('d12')
44
+ expect(subject.read(3)).to eq('34')
45
+ expect(subject.read(3)).to be_nil
46
+ end
47
+ it 'allows to rewind while reading in chunks' do
48
+ expect(subject.read(3)).to eq('abc')
49
+ expect(subject.read(3)).to eq('d12')
50
+ subject.rewind
51
+ expect(subject.read(3)).to eq('abc')
52
+ expect(subject.read(5)).to eq('d1234')
53
+ expect(subject.read(3)).to be_nil
54
+ subject.rewind
55
+ expect(subject.read(2)).to eq('ab')
56
+ end
57
+ end
58
+
59
+ context 'with mix of empty and non-empty parts' do
60
+ subject { composite_io(part(''), part('abcd'), part(''), part('1234'), part('')) }
61
+
62
+ it 'allows to read in chunks' do
63
+ expect(subject.read(6)).to eq('abcd12')
64
+ expect(subject.read(6)).to eq('34')
65
+ expect(subject.read(6)).to be_nil
66
+ end
67
+ end
68
+
69
+ context 'with utf8 multibyte part' do
70
+ subject { composite_io(part("\x86"), part('ファイル')) }
71
+
72
+ it { expect(subject.read).to eq(String.new("\x86\xE3\x83\x95\xE3\x82\xA1\xE3\x82\xA4\xE3\x83\xAB", encoding: 'BINARY')) }
73
+ it 'allows to read in chunks' do
74
+ expect(subject.read(3)).to eq(String.new("\x86\xE3\x83", encoding: 'BINARY'))
75
+ expect(subject.read(3)).to eq(String.new("\x95\xE3\x82", encoding: 'BINARY'))
76
+ expect(subject.read(8)).to eq(String.new("\xA1\xE3\x82\xA4\xE3\x83\xAB", encoding: 'BINARY'))
77
+ expect(subject.read(3)).to be_nil
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,691 @@
1
+ # frozen_string_literal: true
2
+
3
+ shared_examples 'initializer with url' do
4
+ context 'with simple url' do
5
+ let(:address) { 'http://sushi.com' }
6
+
7
+ it { expect(subject.host).to eq('sushi.com') }
8
+ it { expect(subject.port).to eq(80) }
9
+ it { expect(subject.scheme).to eq('http') }
10
+ it { expect(subject.path_prefix).to eq('/') }
11
+ it { expect(subject.params).to eq({}) }
12
+ end
13
+
14
+ context 'with complex url' do
15
+ let(:address) { 'http://sushi.com:815/fish?a=1' }
16
+
17
+ it { expect(subject.port).to eq(815) }
18
+ it { expect(subject.path_prefix).to eq('/fish') }
19
+ it { expect(subject.params).to eq('a' => '1') }
20
+ end
21
+ end
22
+
23
+ shared_examples 'default connection options' do
24
+ after { Faraday.default_connection_options = nil }
25
+
26
+ it 'works with implicit url' do
27
+ conn = Faraday.new 'http://sushi.com/foo'
28
+ expect(conn.options.timeout).to eq(10)
29
+ end
30
+
31
+ it 'works with option url' do
32
+ conn = Faraday.new url: 'http://sushi.com/foo'
33
+ expect(conn.options.timeout).to eq(10)
34
+ end
35
+
36
+ it 'works with instance connection options' do
37
+ conn = Faraday.new 'http://sushi.com/foo', request: { open_timeout: 1 }
38
+ expect(conn.options.timeout).to eq(10)
39
+ expect(conn.options.open_timeout).to eq(1)
40
+ end
41
+
42
+ it 'default connection options persist with an instance overriding' do
43
+ conn = Faraday.new 'http://nigiri.com/bar'
44
+ conn.options.timeout = 1
45
+ expect(Faraday.default_connection_options.request.timeout).to eq(10)
46
+
47
+ other = Faraday.new url: 'https://sushi.com/foo'
48
+ other.options.timeout = 1
49
+
50
+ expect(Faraday.default_connection_options.request.timeout).to eq(10)
51
+ end
52
+
53
+ it 'default connection uses default connection options' do
54
+ expect(Faraday.default_connection.options.timeout).to eq(10)
55
+ end
56
+ end
57
+
58
+ RSpec.describe Faraday::Connection do
59
+ let(:conn) { Faraday::Connection.new(url, options) }
60
+ let(:url) { nil }
61
+ let(:options) { nil }
62
+
63
+ describe '.new' do
64
+ subject { conn }
65
+
66
+ context 'with implicit url param' do
67
+ # Faraday::Connection.new('http://sushi.com')
68
+ let(:url) { address }
69
+
70
+ it_behaves_like 'initializer with url'
71
+ end
72
+
73
+ context 'with explicit url param' do
74
+ # Faraday::Connection.new(url: 'http://sushi.com')
75
+ let(:url) { { url: address } }
76
+
77
+ it_behaves_like 'initializer with url'
78
+ end
79
+
80
+ context 'with custom builder' do
81
+ let(:custom_builder) { Faraday::RackBuilder.new }
82
+ let(:options) { { builder: custom_builder } }
83
+
84
+ it { expect(subject.builder).to eq(custom_builder) }
85
+ end
86
+
87
+ context 'with custom params' do
88
+ let(:options) { { params: { a: 1 } } }
89
+
90
+ it { expect(subject.params).to eq('a' => 1) }
91
+ end
92
+
93
+ context 'with custom params and params in url' do
94
+ let(:url) { 'http://sushi.com/fish?a=1&b=2' }
95
+ let(:options) { { params: { a: 3 } } }
96
+ it { expect(subject.params).to eq('a' => 3, 'b' => '2') }
97
+ end
98
+
99
+ context 'with custom headers' do
100
+ let(:options) { { headers: { user_agent: 'Faraday' } } }
101
+
102
+ it { expect(subject.headers['User-agent']).to eq('Faraday') }
103
+ end
104
+
105
+ context 'with ssl false' do
106
+ let(:options) { { ssl: { verify: false } } }
107
+
108
+ it { expect(subject.ssl.verify?).to be_falsey }
109
+ end
110
+
111
+ context 'with empty block' do
112
+ let(:conn) { Faraday::Connection.new {} }
113
+
114
+ it { expect(conn.builder.handlers.size).to eq(0) }
115
+ end
116
+
117
+ context 'with block' do
118
+ let(:conn) do
119
+ Faraday::Connection.new(params: { 'a' => '1' }) do |faraday|
120
+ faraday.adapter :net_http
121
+ faraday.url_prefix = 'http://sushi.com/omnom'
122
+ end
123
+ end
124
+
125
+ it { expect(conn.builder.handlers.size).to eq(0) }
126
+ it { expect(conn.path_prefix).to eq('/omnom') }
127
+ end
128
+ end
129
+
130
+ describe '#close' do
131
+ it 'can close underlying app' do
132
+ expect(conn.app).to receive(:close)
133
+ conn.close
134
+ end
135
+ end
136
+
137
+ describe 'basic_auth' do
138
+ subject { conn }
139
+
140
+ context 'calling the #basic_auth method' do
141
+ before { subject.basic_auth 'Aladdin', 'open sesame' }
142
+
143
+ it { expect(subject.headers['Authorization']).to eq('Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==') }
144
+ end
145
+
146
+ context 'adding basic auth info to url' do
147
+ let(:url) { 'http://Aladdin:open%20sesame@sushi.com/fish' }
148
+
149
+ it { expect(subject.headers['Authorization']).to eq('Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==') }
150
+ end
151
+ end
152
+
153
+ describe '#token_auth' do
154
+ before { subject.token_auth('abcdef', nonce: 'abc') }
155
+
156
+ it { expect(subject.headers['Authorization']).to eq('Token nonce="abc", token="abcdef"') }
157
+ end
158
+
159
+ describe '#build_exclusive_url' do
160
+ context 'with relative path' do
161
+ subject { conn.build_exclusive_url('sake.html') }
162
+
163
+ it 'uses connection host as default host' do
164
+ conn.host = 'sushi.com'
165
+ expect(subject.host).to eq('sushi.com')
166
+ expect(subject.scheme).to eq('http')
167
+ end
168
+
169
+ it do
170
+ conn.path_prefix = '/fish'
171
+ expect(subject.path).to eq('/fish/sake.html')
172
+ end
173
+
174
+ it do
175
+ conn.path_prefix = '/'
176
+ expect(subject.path).to eq('/sake.html')
177
+ end
178
+
179
+ it do
180
+ conn.path_prefix = 'fish'
181
+ expect(subject.path).to eq('/fish/sake.html')
182
+ end
183
+
184
+ it do
185
+ conn.path_prefix = '/fish/'
186
+ expect(subject.path).to eq('/fish/sake.html')
187
+ end
188
+ end
189
+
190
+ context 'with absolute path' do
191
+ subject { conn.build_exclusive_url('/sake.html') }
192
+
193
+ after { expect(subject.path).to eq('/sake.html') }
194
+
195
+ it { conn.path_prefix = '/fish' }
196
+ it { conn.path_prefix = '/' }
197
+ it { conn.path_prefix = 'fish' }
198
+ it { conn.path_prefix = '/fish/' }
199
+ end
200
+
201
+ context 'with complete url' do
202
+ subject { conn.build_exclusive_url('http://sushi.com/sake.html?a=1') }
203
+
204
+ it { expect(subject.scheme).to eq('http') }
205
+ it { expect(subject.host).to eq('sushi.com') }
206
+ it { expect(subject.port).to eq(80) }
207
+ it { expect(subject.path).to eq('/sake.html') }
208
+ it { expect(subject.query).to eq('a=1') }
209
+ end
210
+
211
+ it 'overrides connection port for absolute url' do
212
+ conn.port = 23
213
+ uri = conn.build_exclusive_url('http://sushi.com')
214
+ expect(uri.port).to eq(80)
215
+ end
216
+
217
+ it 'does not add ending slash given nil url' do
218
+ conn.url_prefix = 'http://sushi.com/nigiri'
219
+ uri = conn.build_exclusive_url
220
+ expect(uri.path).to eq('/nigiri')
221
+ end
222
+
223
+ it 'does not add ending slash given empty url' do
224
+ conn.url_prefix = 'http://sushi.com/nigiri'
225
+ uri = conn.build_exclusive_url('')
226
+ expect(uri.path).to eq('/nigiri')
227
+ end
228
+
229
+ it 'does not use connection params' do
230
+ conn.url_prefix = 'http://sushi.com/nigiri'
231
+ conn.params = { a: 1 }
232
+ expect(conn.build_exclusive_url.to_s).to eq('http://sushi.com/nigiri')
233
+ end
234
+
235
+ it 'allows to provide params argument' do
236
+ conn.url_prefix = 'http://sushi.com/nigiri'
237
+ conn.params = { a: 1 }
238
+ params = Faraday::Utils::ParamsHash.new
239
+ params[:a] = 2
240
+ uri = conn.build_exclusive_url(nil, params)
241
+ expect(uri.to_s).to eq('http://sushi.com/nigiri?a=2')
242
+ end
243
+
244
+ it 'handles uri instances' do
245
+ uri = conn.build_exclusive_url(URI('/sake.html'))
246
+ expect(uri.path).to eq('/sake.html')
247
+ end
248
+
249
+ context 'with url_prefixed connection' do
250
+ let(:url) { 'http://sushi.com/sushi/' }
251
+
252
+ it 'parses url and changes scheme' do
253
+ conn.scheme = 'https'
254
+ uri = conn.build_exclusive_url('sake.html')
255
+ expect(uri.to_s).to eq('https://sushi.com/sushi/sake.html')
256
+ end
257
+
258
+ it 'joins url to base with ending slash' do
259
+ uri = conn.build_exclusive_url('sake.html')
260
+ expect(uri.to_s).to eq('http://sushi.com/sushi/sake.html')
261
+ end
262
+
263
+ it 'used default base with ending slash' do
264
+ uri = conn.build_exclusive_url
265
+ expect(uri.to_s).to eq('http://sushi.com/sushi/')
266
+ end
267
+
268
+ it 'overrides base' do
269
+ uri = conn.build_exclusive_url('/sake/')
270
+ expect(uri.to_s).to eq('http://sushi.com/sake/')
271
+ end
272
+ end
273
+ end
274
+
275
+ describe '#build_url' do
276
+ let(:url) { 'http://sushi.com/nigiri' }
277
+
278
+ it 'uses params' do
279
+ conn.params = { a: 1, b: 1 }
280
+ expect(conn.build_url.to_s).to eq('http://sushi.com/nigiri?a=1&b=1')
281
+ end
282
+
283
+ it 'merges params' do
284
+ conn.params = { a: 1, b: 1 }
285
+ url = conn.build_url(nil, b: 2, c: 3)
286
+ expect(url.to_s).to eq('http://sushi.com/nigiri?a=1&b=2&c=3')
287
+ end
288
+ end
289
+
290
+ describe '#build_request' do
291
+ let(:url) { 'https://asushi.com/sake.html' }
292
+ let(:request) { conn.build_request(:get) }
293
+
294
+ before do
295
+ conn.headers = { 'Authorization' => 'token abc123' }
296
+ request.headers.delete('Authorization')
297
+ end
298
+
299
+ it { expect(conn.headers.keys).to eq(['Authorization']) }
300
+ it { expect(conn.headers.include?('Authorization')).to be_truthy }
301
+ it { expect(request.headers.keys).to be_empty }
302
+ it { expect(request.headers.include?('Authorization')).to be_falsey }
303
+ end
304
+
305
+ describe '#to_env' do
306
+ subject { conn.build_request(:get).to_env(conn).url }
307
+
308
+ let(:url) { 'http://sushi.com/sake.html' }
309
+ let(:options) { { params: @params } }
310
+
311
+ it 'parses url params into query' do
312
+ @params = { 'a[b]' => '1 + 2' }
313
+ expect(subject.query).to eq('a%5Bb%5D=1+%2B+2')
314
+ end
315
+
316
+ it 'escapes per spec' do
317
+ @params = { 'a' => '1+2 foo~bar.-baz' }
318
+ expect(subject.query).to eq('a=1%2B2+foo~bar.-baz')
319
+ end
320
+
321
+ it 'bracketizes nested params in query' do
322
+ @params = { 'a' => { 'b' => 'c' } }
323
+ expect(subject.query).to eq('a%5Bb%5D=c')
324
+ end
325
+
326
+ it 'bracketizes repeated params in query' do
327
+ @params = { 'a' => [1, 2] }
328
+ expect(subject.query).to eq('a%5B%5D=1&a%5B%5D=2')
329
+ end
330
+
331
+ it 'without braketizing repeated params in query' do
332
+ @params = { 'a' => [1, 2] }
333
+ conn.options.params_encoder = Faraday::FlatParamsEncoder
334
+ expect(subject.query).to eq('a=1&a=2')
335
+ end
336
+ end
337
+
338
+ describe 'proxy support' do
339
+ it 'accepts string' do
340
+ with_env 'http_proxy' => 'http://env-proxy.com:80' do
341
+ conn.proxy = 'http://proxy.com'
342
+ expect(conn.proxy.host).to eq('proxy.com')
343
+ end
344
+ end
345
+
346
+ it 'accepts uri' do
347
+ with_env 'http_proxy' => 'http://env-proxy.com:80' do
348
+ conn.proxy = URI.parse('http://proxy.com')
349
+ expect(conn.proxy.host).to eq('proxy.com')
350
+ end
351
+ end
352
+
353
+ it 'accepts hash with string uri' do
354
+ with_env 'http_proxy' => 'http://env-proxy.com:80' do
355
+ conn.proxy = { uri: 'http://proxy.com', user: 'rick' }
356
+ expect(conn.proxy.host).to eq('proxy.com')
357
+ expect(conn.proxy.user).to eq('rick')
358
+ end
359
+ end
360
+
361
+ it 'accepts hash' do
362
+ with_env 'http_proxy' => 'http://env-proxy.com:80' do
363
+ conn.proxy = { uri: URI.parse('http://proxy.com'), user: 'rick' }
364
+ expect(conn.proxy.host).to eq('proxy.com')
365
+ expect(conn.proxy.user).to eq('rick')
366
+ end
367
+ end
368
+
369
+ it 'accepts http env' do
370
+ with_env 'http_proxy' => 'http://env-proxy.com:80' do
371
+ expect(conn.proxy.host).to eq('env-proxy.com')
372
+ end
373
+ end
374
+
375
+ it 'accepts http env with auth' do
376
+ with_env 'http_proxy' => 'http://a%40b:my%20pass@proxy.com:80' do
377
+ expect(conn.proxy.user).to eq('a@b')
378
+ expect(conn.proxy.password).to eq('my pass')
379
+ end
380
+ end
381
+
382
+ it 'accepts env without scheme' do
383
+ with_env 'http_proxy' => 'localhost:8888' do
384
+ uri = conn.proxy[:uri]
385
+ expect(uri.host).to eq('localhost')
386
+ expect(uri.port).to eq(8888)
387
+ end
388
+ end
389
+
390
+ it 'fetches no proxy from nil env' do
391
+ with_env 'http_proxy' => nil do
392
+ expect(conn.proxy).to be_nil
393
+ end
394
+ end
395
+
396
+ it 'fetches no proxy from blank env' do
397
+ with_env 'http_proxy' => '' do
398
+ expect(conn.proxy).to be_nil
399
+ end
400
+ end
401
+
402
+ it 'does not accept uppercase env' do
403
+ with_env 'HTTP_PROXY' => 'http://localhost:8888/' do
404
+ expect(conn.proxy).to be_nil
405
+ end
406
+ end
407
+
408
+ it 'allows when url in no proxy list' do
409
+ with_env 'http_proxy' => 'http://proxy.com', 'no_proxy' => 'example.com' do
410
+ conn = Faraday::Connection.new('http://example.com')
411
+ expect(conn.proxy).to be_nil
412
+ end
413
+ end
414
+
415
+ it 'allows when prefixed url is not in no proxy list' do
416
+ with_env 'http_proxy' => 'http://proxy.com', 'no_proxy' => 'example.com' do
417
+ conn = Faraday::Connection.new('http://prefixedexample.com')
418
+ expect(conn.proxy.host).to eq('proxy.com')
419
+ end
420
+ end
421
+
422
+ it 'allows when subdomain url is in no proxy list' do
423
+ with_env 'http_proxy' => 'http://proxy.com', 'no_proxy' => 'example.com' do
424
+ conn = Faraday::Connection.new('http://subdomain.example.com')
425
+ expect(conn.proxy).to be_nil
426
+ end
427
+ end
428
+
429
+ it 'allows when url not in no proxy list' do
430
+ with_env 'http_proxy' => 'http://proxy.com', 'no_proxy' => 'example2.com' do
431
+ conn = Faraday::Connection.new('http://example.com')
432
+ expect(conn.proxy.host).to eq('proxy.com')
433
+ end
434
+ end
435
+
436
+ it 'allows when ip address is not in no proxy list but url is' do
437
+ with_env 'http_proxy' => 'http://proxy.com', 'no_proxy' => 'localhost' do
438
+ conn = Faraday::Connection.new('http://127.0.0.1')
439
+ expect(conn.proxy).to be_nil
440
+ end
441
+ end
442
+
443
+ it 'allows when url is not in no proxy list but ip address is' do
444
+ with_env 'http_proxy' => 'http://proxy.com', 'no_proxy' => '127.0.0.1' do
445
+ conn = Faraday::Connection.new('http://localhost')
446
+ expect(conn.proxy).to be_nil
447
+ end
448
+ end
449
+
450
+ it 'allows in multi element no proxy list' do
451
+ with_env 'http_proxy' => 'http://proxy.com', 'no_proxy' => 'example0.com,example.com,example1.com' do
452
+ expect(Faraday::Connection.new('http://example0.com').proxy).to be_nil
453
+ expect(Faraday::Connection.new('http://example.com').proxy).to be_nil
454
+ expect(Faraday::Connection.new('http://example1.com').proxy).to be_nil
455
+ expect(Faraday::Connection.new('http://example2.com').proxy.host).to eq('proxy.com')
456
+ end
457
+ end
458
+
459
+ it 'test proxy requires uri' do
460
+ expect { conn.proxy = { uri: :bad_uri, user: 'rick' } }.to raise_error(ArgumentError)
461
+ end
462
+
463
+ it 'uses env http_proxy' do
464
+ with_env 'http_proxy' => 'http://proxy.com' do
465
+ conn = Faraday.new
466
+ expect(conn.instance_variable_get('@manual_proxy')).to be_falsey
467
+ expect(conn.proxy_for_request('http://google.co.uk').host).to eq('proxy.com')
468
+ end
469
+ end
470
+
471
+ it 'uses processes no_proxy before http_proxy' do
472
+ with_env 'http_proxy' => 'http://proxy.com', 'no_proxy' => 'google.co.uk' do
473
+ conn = Faraday.new
474
+ expect(conn.instance_variable_get('@manual_proxy')).to be_falsey
475
+ expect(conn.proxy_for_request('http://google.co.uk')).to be_nil
476
+ end
477
+ end
478
+
479
+ it 'uses env https_proxy' do
480
+ with_env 'https_proxy' => 'https://proxy.com' do
481
+ conn = Faraday.new
482
+ expect(conn.instance_variable_get('@manual_proxy')).to be_falsey
483
+ expect(conn.proxy_for_request('https://google.co.uk').host).to eq('proxy.com')
484
+ end
485
+ end
486
+
487
+ it 'uses processes no_proxy before https_proxy' do
488
+ with_env 'https_proxy' => 'https://proxy.com', 'no_proxy' => 'google.co.uk' do
489
+ conn = Faraday.new
490
+ expect(conn.instance_variable_get('@manual_proxy')).to be_falsey
491
+ expect(conn.proxy_for_request('https://google.co.uk')).to be_nil
492
+ end
493
+ end
494
+
495
+ it 'gives priority to manually set proxy' do
496
+ with_env 'https_proxy' => 'https://proxy.com', 'no_proxy' => 'google.co.uk' do
497
+ conn = Faraday.new
498
+ conn.proxy = 'http://proxy2.com'
499
+
500
+ expect(conn.instance_variable_get('@manual_proxy')).to be_truthy
501
+ expect(conn.proxy_for_request('https://google.co.uk').host).to eq('proxy2.com')
502
+ end
503
+ end
504
+
505
+ it 'ignores env proxy if set that way' do
506
+ with_env_proxy_disabled do
507
+ with_env 'http_proxy' => 'http://duncan.proxy.com:80' do
508
+ expect(conn.proxy).to be_nil
509
+ end
510
+ end
511
+ end
512
+
513
+ context 'performing a request' do
514
+ before { stub_request(:get, 'http://example.com') }
515
+
516
+ it 'dynamically checks proxy' do
517
+ with_env 'http_proxy' => 'http://proxy.com:80' do
518
+ conn = Faraday.new
519
+ expect(conn.proxy.uri.host).to eq('proxy.com')
520
+
521
+ conn.get('http://example.com') do |req|
522
+ expect(req.options.proxy.uri.host).to eq('proxy.com')
523
+ end
524
+ end
525
+
526
+ conn.get('http://example.com')
527
+ expect(conn.instance_variable_get('@temp_proxy')).to be_nil
528
+ end
529
+
530
+ it 'dynamically check no proxy' do
531
+ with_env 'http_proxy' => 'http://proxy.com', 'no_proxy' => 'example.com' do
532
+ conn = Faraday.new
533
+
534
+ expect(conn.proxy.uri.host).to eq('proxy.com')
535
+
536
+ conn.get('http://example.com') do |req|
537
+ expect(req.options.proxy).to be_nil
538
+ end
539
+ end
540
+ end
541
+ end
542
+ end
543
+
544
+ describe '#dup' do
545
+ subject { conn.dup }
546
+
547
+ let(:url) { 'http://sushi.com/foo' }
548
+ let(:options) do
549
+ {
550
+ ssl: { verify: :none },
551
+ headers: { 'content-type' => 'text/plain' },
552
+ params: { 'a' => '1' },
553
+ request: { timeout: 5 }
554
+ }
555
+ end
556
+
557
+ it { expect(subject.build_exclusive_url).to eq(conn.build_exclusive_url) }
558
+ it { expect(subject.headers['content-type']).to eq('text/plain') }
559
+ it { expect(subject.params['a']).to eq('1') }
560
+
561
+ context 'after manual changes' do
562
+ before do
563
+ subject.basic_auth('', '')
564
+ subject.headers['content-length'] = 12
565
+ subject.params['b'] = '2'
566
+ subject.options[:open_timeout] = 10
567
+ end
568
+
569
+ it { expect(subject.builder.handlers.size).to eq(1) }
570
+ it { expect(conn.builder.handlers.size).to eq(1) }
571
+ it { expect(conn.headers.key?('content-length')).to be_falsey }
572
+ it { expect(conn.params.key?('b')).to be_falsey }
573
+ it { expect(subject.options[:timeout]).to eq(5) }
574
+ it { expect(conn.options[:open_timeout]).to be_nil }
575
+ end
576
+ end
577
+
578
+ describe '#respond_to?' do
579
+ it { expect(Faraday.respond_to?(:get)).to be_truthy }
580
+ it { expect(Faraday.respond_to?(:post)).to be_truthy }
581
+ end
582
+
583
+ describe 'default_connection_options' do
584
+ context 'assigning a default value' do
585
+ before do
586
+ Faraday.default_connection_options = nil
587
+ Faraday.default_connection_options.request.timeout = 10
588
+ end
589
+
590
+ it_behaves_like 'default connection options'
591
+ end
592
+
593
+ context 'assigning a hash' do
594
+ before { Faraday.default_connection_options = { request: { timeout: 10 } } }
595
+
596
+ it_behaves_like 'default connection options'
597
+ end
598
+ end
599
+
600
+ describe 'request params' do
601
+ context 'with simple url' do
602
+ let(:url) { 'http://example.com' }
603
+ let!(:stubbed) { stub_request(:get, 'http://example.com?a=a&p=3') }
604
+
605
+ after { expect(stubbed).to have_been_made.once }
606
+
607
+ it 'test_overrides_request_params' do
608
+ conn.get('?p=2&a=a', p: 3)
609
+ end
610
+
611
+ it 'test_overrides_request_params_block' do
612
+ conn.get('?p=1&a=a', p: 2) do |req|
613
+ req.params[:p] = 3
614
+ end
615
+ end
616
+
617
+ it 'test_overrides_request_params_block_url' do
618
+ conn.get(nil, p: 2) do |req|
619
+ req.url('?p=1&a=a', 'p' => 3)
620
+ end
621
+ end
622
+ end
623
+
624
+ context 'with url and extra params' do
625
+ let(:url) { 'http://example.com?a=1&b=2' }
626
+ let(:options) { { params: { c: 3 } } }
627
+
628
+ it 'merges connection and request params' do
629
+ stubbed = stub_request(:get, 'http://example.com?a=1&b=2&c=3&limit=5&page=1')
630
+ conn.get('?page=1', limit: 5)
631
+ expect(stubbed).to have_been_made.once
632
+ end
633
+
634
+ it 'allows to override all params' do
635
+ stubbed = stub_request(:get, 'http://example.com?b=b')
636
+ conn.get('?p=1&a=a', p: 2) do |req|
637
+ expect(req.params[:a]).to eq('a')
638
+ expect(req.params['c']).to eq(3)
639
+ expect(req.params['p']).to eq(2)
640
+ req.params = { b: 'b' }
641
+ expect(req.params['b']).to eq('b')
642
+ end
643
+ expect(stubbed).to have_been_made.once
644
+ end
645
+
646
+ it 'allows to set params_encoder for single request' do
647
+ encoder = Object.new
648
+ def encoder.encode(params)
649
+ params.map { |k, v| "#{k.upcase}-#{v.to_s.upcase}" }.join(',')
650
+ end
651
+ stubbed = stub_request(:get, 'http://example.com/?A-1,B-2,C-3,FEELING-BLUE')
652
+
653
+ conn.get('/', feeling: 'blue') do |req|
654
+ req.options.params_encoder = encoder
655
+ end
656
+ expect(stubbed).to have_been_made.once
657
+ end
658
+ end
659
+
660
+ context 'with default params encoder' do
661
+ let!(:stubbed) { stub_request(:get, 'http://example.com?color%5B%5D=red&color%5B%5D=blue') }
662
+ after { expect(stubbed).to have_been_made.once }
663
+
664
+ it 'supports array params in url' do
665
+ conn.get('http://example.com?color[]=red&color[]=blue')
666
+ end
667
+
668
+ it 'supports array params in params' do
669
+ conn.get('http://example.com', color: %w[red blue])
670
+ end
671
+ end
672
+
673
+ context 'with flat params encoder' do
674
+ let(:options) { { request: { params_encoder: Faraday::FlatParamsEncoder } } }
675
+ let!(:stubbed) { stub_request(:get, 'http://example.com?color=blue') }
676
+ after { expect(stubbed).to have_been_made.once }
677
+
678
+ it 'supports array params in params' do
679
+ conn.get('http://example.com', color: %w[red blue])
680
+ end
681
+
682
+ context 'with array param in url' do
683
+ let(:url) { 'http://example.com?color[]=red&color[]=blue' }
684
+
685
+ it do
686
+ conn.get('/')
687
+ end
688
+ end
689
+ end
690
+ end
691
+ end