faraday 1.10.3 → 2.13.4

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 (82) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +198 -4
  3. data/LICENSE.md +1 -1
  4. data/README.md +34 -20
  5. data/Rakefile +6 -1
  6. data/examples/client_spec.rb +41 -19
  7. data/examples/client_test.rb +48 -22
  8. data/lib/faraday/adapter/test.rb +62 -13
  9. data/lib/faraday/adapter.rb +6 -10
  10. data/lib/faraday/connection.rb +72 -150
  11. data/lib/faraday/encoders/nested_params_encoder.rb +14 -7
  12. data/lib/faraday/error.rb +62 -9
  13. data/lib/faraday/logging/formatter.rb +29 -16
  14. data/lib/faraday/middleware.rb +43 -2
  15. data/lib/faraday/middleware_registry.rb +17 -63
  16. data/lib/faraday/options/connection_options.rb +7 -6
  17. data/lib/faraday/options/env.rb +85 -62
  18. data/lib/faraday/options/proxy_options.rb +11 -5
  19. data/lib/faraday/options/request_options.rb +7 -6
  20. data/lib/faraday/options/ssl_options.rb +62 -45
  21. data/lib/faraday/options.rb +7 -6
  22. data/lib/faraday/rack_builder.rb +44 -45
  23. data/lib/faraday/request/authorization.rb +33 -41
  24. data/lib/faraday/request/instrumentation.rb +5 -1
  25. data/lib/faraday/request/json.rb +18 -3
  26. data/lib/faraday/request/url_encoded.rb +5 -1
  27. data/lib/faraday/request.rb +15 -30
  28. data/lib/faraday/response/json.rb +25 -5
  29. data/lib/faraday/response/logger.rb +11 -3
  30. data/lib/faraday/response/raise_error.rb +45 -18
  31. data/lib/faraday/response.rb +9 -21
  32. data/lib/faraday/utils/headers.rb +15 -4
  33. data/lib/faraday/utils.rb +11 -7
  34. data/lib/faraday/version.rb +1 -1
  35. data/lib/faraday.rb +10 -45
  36. data/spec/faraday/adapter/test_spec.rb +65 -0
  37. data/spec/faraday/connection_spec.rb +165 -93
  38. data/spec/faraday/error_spec.rb +122 -7
  39. data/spec/faraday/middleware_registry_spec.rb +31 -0
  40. data/spec/faraday/middleware_spec.rb +161 -0
  41. data/spec/faraday/options/env_spec.rb +8 -2
  42. data/spec/faraday/options/options_spec.rb +1 -1
  43. data/spec/faraday/options/proxy_options_spec.rb +35 -0
  44. data/spec/faraday/params_encoders/nested_spec.rb +10 -1
  45. data/spec/faraday/rack_builder_spec.rb +26 -54
  46. data/spec/faraday/request/authorization_spec.rb +50 -28
  47. data/spec/faraday/request/instrumentation_spec.rb +5 -7
  48. data/spec/faraday/request/json_spec.rb +88 -0
  49. data/spec/faraday/request/url_encoded_spec.rb +12 -2
  50. data/spec/faraday/request_spec.rb +5 -15
  51. data/spec/faraday/response/json_spec.rb +93 -6
  52. data/spec/faraday/response/logger_spec.rb +77 -4
  53. data/spec/faraday/response/raise_error_spec.rb +119 -13
  54. data/spec/faraday/response_spec.rb +3 -1
  55. data/spec/faraday/utils/headers_spec.rb +31 -4
  56. data/spec/faraday/utils_spec.rb +65 -1
  57. data/spec/faraday_spec.rb +10 -4
  58. data/spec/spec_helper.rb +5 -6
  59. data/spec/support/fake_safe_buffer.rb +1 -1
  60. data/spec/support/faraday_middleware_subclasses.rb +18 -0
  61. data/spec/support/helper_methods.rb +0 -37
  62. data/spec/support/shared_examples/adapter.rb +2 -2
  63. data/spec/support/shared_examples/request_method.rb +22 -21
  64. metadata +24 -149
  65. data/lib/faraday/adapter/typhoeus.rb +0 -15
  66. data/lib/faraday/autoload.rb +0 -87
  67. data/lib/faraday/dependency_loader.rb +0 -39
  68. data/lib/faraday/deprecate.rb +0 -110
  69. data/lib/faraday/request/basic_authentication.rb +0 -20
  70. data/lib/faraday/request/token_authentication.rb +0 -20
  71. data/spec/faraday/adapter/em_http_spec.rb +0 -49
  72. data/spec/faraday/adapter/em_synchrony_spec.rb +0 -18
  73. data/spec/faraday/adapter/excon_spec.rb +0 -49
  74. data/spec/faraday/adapter/httpclient_spec.rb +0 -73
  75. data/spec/faraday/adapter/net_http_spec.rb +0 -64
  76. data/spec/faraday/adapter/patron_spec.rb +0 -18
  77. data/spec/faraday/adapter/rack_spec.rb +0 -8
  78. data/spec/faraday/adapter/typhoeus_spec.rb +0 -7
  79. data/spec/faraday/composite_read_io_spec.rb +0 -80
  80. data/spec/faraday/deprecate_spec.rb +0 -147
  81. data/spec/faraday/response/middleware_spec.rb +0 -68
  82. data/spec/support/webmock_rack_app.rb +0 -68
@@ -11,8 +11,10 @@ RSpec.describe Faraday::Response::RaiseError do
11
11
  stub.get('forbidden') { [403, { 'X-Reason' => 'because' }, 'keep looking'] }
12
12
  stub.get('not-found') { [404, { 'X-Reason' => 'because' }, 'keep looking'] }
13
13
  stub.get('proxy-error') { [407, { 'X-Reason' => 'because' }, 'keep looking'] }
14
+ stub.get('request-timeout') { [408, { 'X-Reason' => 'because' }, 'keep looking'] }
14
15
  stub.get('conflict') { [409, { 'X-Reason' => 'because' }, 'keep looking'] }
15
16
  stub.get('unprocessable-entity') { [422, { 'X-Reason' => 'because' }, 'keep looking'] }
17
+ stub.get('too-many-requests') { [429, { 'X-Reason' => 'because' }, 'keep looking'] }
16
18
  stub.get('4xx') { [499, { 'X-Reason' => 'because' }, 'keep looking'] }
17
19
  stub.get('nil-status') { [nil, { 'X-Reason' => 'nil' }, 'fail'] }
18
20
  stub.get('server-error') { [500, { 'X-Error' => 'bailout' }, 'fail'] }
@@ -26,7 +28,7 @@ RSpec.describe Faraday::Response::RaiseError do
26
28
 
27
29
  it 'raises Faraday::BadRequestError for 400 responses' do
28
30
  expect { conn.get('bad-request') }.to raise_error(Faraday::BadRequestError) do |ex|
29
- expect(ex.message).to eq('the server responded with status 400')
31
+ expect(ex.message).to eq('the server responded with status 400 for GET http:/bad-request')
30
32
  expect(ex.response[:headers]['X-Reason']).to eq('because')
31
33
  expect(ex.response[:status]).to eq(400)
32
34
  expect(ex.response_status).to eq(400)
@@ -37,7 +39,7 @@ RSpec.describe Faraday::Response::RaiseError do
37
39
 
38
40
  it 'raises Faraday::UnauthorizedError for 401 responses' do
39
41
  expect { conn.get('unauthorized') }.to raise_error(Faraday::UnauthorizedError) do |ex|
40
- expect(ex.message).to eq('the server responded with status 401')
42
+ expect(ex.message).to eq('the server responded with status 401 for GET http:/unauthorized')
41
43
  expect(ex.response[:headers]['X-Reason']).to eq('because')
42
44
  expect(ex.response[:status]).to eq(401)
43
45
  expect(ex.response_status).to eq(401)
@@ -48,7 +50,7 @@ RSpec.describe Faraday::Response::RaiseError do
48
50
 
49
51
  it 'raises Faraday::ForbiddenError for 403 responses' do
50
52
  expect { conn.get('forbidden') }.to raise_error(Faraday::ForbiddenError) do |ex|
51
- expect(ex.message).to eq('the server responded with status 403')
53
+ expect(ex.message).to eq('the server responded with status 403 for GET http:/forbidden')
52
54
  expect(ex.response[:headers]['X-Reason']).to eq('because')
53
55
  expect(ex.response[:status]).to eq(403)
54
56
  expect(ex.response_status).to eq(403)
@@ -59,7 +61,7 @@ RSpec.describe Faraday::Response::RaiseError do
59
61
 
60
62
  it 'raises Faraday::ResourceNotFound for 404 responses' do
61
63
  expect { conn.get('not-found') }.to raise_error(Faraday::ResourceNotFound) do |ex|
62
- expect(ex.message).to eq('the server responded with status 404')
64
+ expect(ex.message).to eq('the server responded with status 404 for GET http:/not-found')
63
65
  expect(ex.response[:headers]['X-Reason']).to eq('because')
64
66
  expect(ex.response[:status]).to eq(404)
65
67
  expect(ex.response_status).to eq(404)
@@ -79,9 +81,20 @@ RSpec.describe Faraday::Response::RaiseError do
79
81
  end
80
82
  end
81
83
 
84
+ it 'raises Faraday::RequestTimeoutError for 408 responses' do
85
+ expect { conn.get('request-timeout') }.to raise_error(Faraday::RequestTimeoutError) do |ex|
86
+ expect(ex.message).to eq('the server responded with status 408 for GET http:/request-timeout')
87
+ expect(ex.response[:headers]['X-Reason']).to eq('because')
88
+ expect(ex.response[:status]).to eq(408)
89
+ expect(ex.response_status).to eq(408)
90
+ expect(ex.response_body).to eq('keep looking')
91
+ expect(ex.response_headers['X-Reason']).to eq('because')
92
+ end
93
+ end
94
+
82
95
  it 'raises Faraday::ConflictError for 409 responses' do
83
96
  expect { conn.get('conflict') }.to raise_error(Faraday::ConflictError) do |ex|
84
- expect(ex.message).to eq('the server responded with status 409')
97
+ expect(ex.message).to eq('the server responded with status 409 for GET http:/conflict')
85
98
  expect(ex.response[:headers]['X-Reason']).to eq('because')
86
99
  expect(ex.response[:status]).to eq(409)
87
100
  expect(ex.response_status).to eq(409)
@@ -92,7 +105,7 @@ RSpec.describe Faraday::Response::RaiseError do
92
105
 
93
106
  it 'raises Faraday::UnprocessableEntityError for 422 responses' do
94
107
  expect { conn.get('unprocessable-entity') }.to raise_error(Faraday::UnprocessableEntityError) do |ex|
95
- expect(ex.message).to eq('the server responded with status 422')
108
+ expect(ex.message).to eq('the server responded with status 422 for GET http:/unprocessable-entity')
96
109
  expect(ex.response[:headers]['X-Reason']).to eq('because')
97
110
  expect(ex.response[:status]).to eq(422)
98
111
  expect(ex.response_status).to eq(422)
@@ -101,6 +114,17 @@ RSpec.describe Faraday::Response::RaiseError do
101
114
  end
102
115
  end
103
116
 
117
+ it 'raises Faraday::TooManyRequestsError for 429 responses' do
118
+ expect { conn.get('too-many-requests') }.to raise_error(Faraday::TooManyRequestsError) do |ex|
119
+ expect(ex.message).to eq('the server responded with status 429 for GET http:/too-many-requests')
120
+ expect(ex.response[:headers]['X-Reason']).to eq('because')
121
+ expect(ex.response[:status]).to eq(429)
122
+ expect(ex.response_status).to eq(429)
123
+ expect(ex.response_body).to eq('keep looking')
124
+ expect(ex.response_headers['X-Reason']).to eq('because')
125
+ end
126
+ end
127
+
104
128
  it 'raises Faraday::NilStatusError for nil status in response' do
105
129
  expect { conn.get('nil-status') }.to raise_error(Faraday::NilStatusError) do |ex|
106
130
  expect(ex.message).to eq('http status could not be derived from the server response')
@@ -114,7 +138,7 @@ RSpec.describe Faraday::Response::RaiseError do
114
138
 
115
139
  it 'raises Faraday::ClientError for other 4xx responses' do
116
140
  expect { conn.get('4xx') }.to raise_error(Faraday::ClientError) do |ex|
117
- expect(ex.message).to eq('the server responded with status 499')
141
+ expect(ex.message).to eq('the server responded with status 499 for GET http:/4xx')
118
142
  expect(ex.response[:headers]['X-Reason']).to eq('because')
119
143
  expect(ex.response[:status]).to eq(499)
120
144
  expect(ex.response_status).to eq(499)
@@ -125,7 +149,7 @@ RSpec.describe Faraday::Response::RaiseError do
125
149
 
126
150
  it 'raises Faraday::ServerError for 500 responses' do
127
151
  expect { conn.get('server-error') }.to raise_error(Faraday::ServerError) do |ex|
128
- expect(ex.message).to eq('the server responded with status 500')
152
+ expect(ex.message).to eq('the server responded with status 500 for GET http:/server-error')
129
153
  expect(ex.response[:headers]['X-Error']).to eq('bailout')
130
154
  expect(ex.response[:status]).to eq(500)
131
155
  expect(ex.response_status).to eq(500)
@@ -137,21 +161,24 @@ RSpec.describe Faraday::Response::RaiseError do
137
161
  describe 'request info' do
138
162
  let(:conn) do
139
163
  Faraday.new do |b|
140
- b.response :raise_error
164
+ b.response :raise_error, **middleware_options
141
165
  b.adapter :test do |stub|
142
- stub.post('request?full=true', request_body, request_headers) do
166
+ stub.post(url, request_body, request_headers) do
143
167
  [400, { 'X-Reason' => 'because' }, 'keep looking']
144
168
  end
145
169
  end
146
170
  end
147
171
  end
172
+ let(:middleware_options) { {} }
148
173
  let(:request_body) { JSON.generate({ 'item' => 'sth' }) }
149
174
  let(:request_headers) { { 'Authorization' => 'Basic 123' } }
175
+ let(:url_path) { 'request' }
176
+ let(:query_params) { 'full=true' }
177
+ let(:url) { "#{url_path}?#{query_params}" }
150
178
 
151
179
  subject(:perform_request) do
152
- conn.post 'request' do |req|
180
+ conn.post url do |req|
153
181
  req.headers['Authorization'] = 'Basic 123'
154
- req.params[:full] = true
155
182
  req.body = request_body
156
183
  end
157
184
  end
@@ -159,11 +186,90 @@ RSpec.describe Faraday::Response::RaiseError do
159
186
  it 'returns the request info in the exception' do
160
187
  expect { perform_request }.to raise_error(Faraday::BadRequestError) do |ex|
161
188
  expect(ex.response[:request][:method]).to eq(:post)
162
- expect(ex.response[:request][:url_path]).to eq('/request')
189
+ expect(ex.response[:request][:url]).to eq(URI("http:/#{url}"))
190
+ expect(ex.response[:request][:url_path]).to eq("/#{url_path}")
163
191
  expect(ex.response[:request][:params]).to eq({ 'full' => 'true' })
164
192
  expect(ex.response[:request][:headers]).to match(a_hash_including(request_headers))
165
193
  expect(ex.response[:request][:body]).to eq(request_body)
166
194
  end
167
195
  end
196
+
197
+ describe 'DEFAULT_OPTION: include_request' do
198
+ before(:each) do
199
+ Faraday::Response::RaiseError.instance_variable_set(:@default_options, nil)
200
+ Faraday::Middleware.instance_variable_set(:@default_options, nil)
201
+ end
202
+
203
+ after(:all) do
204
+ Faraday::Response::RaiseError.instance_variable_set(:@default_options, nil)
205
+ Faraday::Middleware.instance_variable_set(:@default_options, nil)
206
+ end
207
+
208
+ context 'when RaiseError DEFAULT_OPTION (include_request: true) is used' do
209
+ it 'includes request info in the exception' do
210
+ expect { perform_request }.to raise_error(Faraday::BadRequestError) do |ex|
211
+ expect(ex.response.keys).to contain_exactly(
212
+ :status,
213
+ :headers,
214
+ :body,
215
+ :request
216
+ )
217
+ end
218
+ end
219
+ end
220
+
221
+ context 'when application sets default_options `include_request: false`' do
222
+ before(:each) do
223
+ Faraday::Response::RaiseError.default_options = { include_request: false }
224
+ end
225
+
226
+ context 'and when include_request option is omitted' do
227
+ it 'does not include request info in the exception' do
228
+ expect { perform_request }.to raise_error(Faraday::BadRequestError) do |ex|
229
+ expect(ex.response.keys).to contain_exactly(
230
+ :status,
231
+ :headers,
232
+ :body
233
+ )
234
+ end
235
+ end
236
+ end
237
+
238
+ context 'and when include_request option is explicitly set for instance' do
239
+ let(:middleware_options) { { include_request: true } }
240
+
241
+ it 'includes request info in the exception' do
242
+ expect { perform_request }.to raise_error(Faraday::BadRequestError) do |ex|
243
+ expect(ex.response.keys).to contain_exactly(
244
+ :status,
245
+ :headers,
246
+ :body,
247
+ :request
248
+ )
249
+ end
250
+ end
251
+ end
252
+ end
253
+ end
254
+ end
255
+
256
+ describe 'allowing certain status codes' do
257
+ let(:conn) do
258
+ Faraday.new do |b|
259
+ b.response :raise_error, allowed_statuses: [404]
260
+ b.adapter :test do |stub|
261
+ stub.get('bad-request') { [400, { 'X-Reason' => 'because' }, 'keep looking'] }
262
+ stub.get('not-found') { [404, { 'X-Reason' => 'because' }, 'keep looking'] }
263
+ end
264
+ end
265
+ end
266
+
267
+ it 'raises an error for status codes that are not explicitly allowed' do
268
+ expect { conn.get('bad-request') }.to raise_error(Faraday::BadRequestError)
269
+ end
270
+
271
+ it 'does not raise an error for allowed status codes' do
272
+ expect { conn.get('not-found') }.not_to raise_error
273
+ end
168
274
  end
169
275
  end
@@ -4,7 +4,7 @@ RSpec.describe Faraday::Response do
4
4
  subject { Faraday::Response.new(env) }
5
5
 
6
6
  let(:env) do
7
- Faraday::Env.from(status: 404, body: 'yikes',
7
+ Faraday::Env.from(status: 404, body: 'yikes', url: Faraday::Utils.URI('https://lostisland.github.io/faraday'),
8
8
  response_headers: { 'Content-Type' => 'text/plain' })
9
9
  end
10
10
 
@@ -30,6 +30,7 @@ RSpec.describe Faraday::Response do
30
30
  it { expect(hash[:status]).to eq(subject.status) }
31
31
  it { expect(hash[:response_headers]).to eq(subject.headers) }
32
32
  it { expect(hash[:body]).to eq(subject.body) }
33
+ it { expect(hash[:url]).to eq(subject.env.url) }
33
34
  end
34
35
 
35
36
  describe 'marshal serialization support' do
@@ -45,6 +46,7 @@ RSpec.describe Faraday::Response do
45
46
  it { expect(loaded.env[:body]).to eq(env[:body]) }
46
47
  it { expect(loaded.env[:response_headers]).to eq(env[:response_headers]) }
47
48
  it { expect(loaded.env[:status]).to eq(env[:status]) }
49
+ it { expect(loaded.env[:url]).to eq(env[:url]) }
48
50
  end
49
51
 
50
52
  describe '#on_complete' do
@@ -56,27 +56,54 @@ RSpec.describe Faraday::Utils::Headers do
56
56
  it { expect(subject.delete('content-type')).to be_nil }
57
57
  end
58
58
 
59
- describe '#parse' do
60
- before { subject.parse(headers) }
59
+ describe '#dig' do
60
+ before { subject['Content-Type'] = 'application/json' }
61
+
62
+ it { expect(subject&.dig('Content-Type')).to eq('application/json') }
63
+ it { expect(subject&.dig('CONTENT-TYPE')).to eq('application/json') }
64
+ it { expect(subject&.dig(:content_type)).to eq('application/json') }
65
+ it { expect(subject&.dig('invalid')).to be_nil }
66
+ end
61
67
 
68
+ describe '#parse' do
62
69
  context 'when response headers leave http status line out' do
63
70
  let(:headers) { "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n" }
64
71
 
72
+ before { subject.parse(headers) }
73
+
65
74
  it { expect(subject.keys).to eq(%w[Content-Type]) }
66
75
  it { expect(subject['Content-Type']).to eq('text/html') }
67
76
  it { expect(subject['content-type']).to eq('text/html') }
68
77
  end
69
78
 
70
79
  context 'when response headers values include a colon' do
71
- let(:headers) { "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nLocation: http://sushi.com/\r\n\r\n" }
80
+ let(:headers) { "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nLocation: http://httpbingo.org/\r\n\r\n" }
72
81
 
73
- it { expect(subject['location']).to eq('http://sushi.com/') }
82
+ before { subject.parse(headers) }
83
+
84
+ it { expect(subject['location']).to eq('http://httpbingo.org/') }
74
85
  end
75
86
 
76
87
  context 'when response headers include a blank line' do
77
88
  let(:headers) { "HTTP/1.1 200 OK\r\n\r\nContent-Type: text/html\r\n\r\n" }
78
89
 
90
+ before { subject.parse(headers) }
91
+
79
92
  it { expect(subject['content-type']).to eq('text/html') }
80
93
  end
94
+
95
+ context 'when response headers include already stored keys' do
96
+ let(:headers) { "HTTP/1.1 200 OK\r\nX-Numbers: 123\r\n\r\n" }
97
+
98
+ before do
99
+ h = subject
100
+ h[:x_numbers] = 8
101
+ h.parse(headers)
102
+ end
103
+
104
+ it do
105
+ expect(subject[:x_numbers]).to eq('8, 123')
106
+ end
107
+ end
81
108
  end
82
109
  end
@@ -4,7 +4,7 @@ RSpec.describe Faraday::Utils do
4
4
  describe 'headers parsing' do
5
5
  let(:multi_response_headers) do
6
6
  "HTTP/1.x 500 OK\r\nContent-Type: text/html; charset=UTF-8\r\n" \
7
- "HTTP/1.x 200 OK\r\nContent-Type: application/json; charset=UTF-8\r\n\r\n"
7
+ "HTTP/1.x 200 OK\r\nContent-Type: application/json; charset=UTF-8\r\n\r\n"
8
8
  end
9
9
 
10
10
  it 'parse headers for aggregated responses' do
@@ -53,4 +53,68 @@ RSpec.describe Faraday::Utils do
53
53
  expect(headers).not_to have_key('authorization')
54
54
  end
55
55
  end
56
+
57
+ describe '.deep_merge!' do
58
+ let(:connection_options) { Faraday::ConnectionOptions.new }
59
+ let(:url) do
60
+ {
61
+ url: 'http://example.com/abc',
62
+ headers: { 'Mime-Version' => '1.0' },
63
+ request: { oauth: { consumer_key: 'anonymous' } },
64
+ ssl: { version: '2' }
65
+ }
66
+ end
67
+
68
+ it 'recursively merges the headers' do
69
+ connection_options.headers = { user_agent: 'My Agent 1.0' }
70
+ deep_merge = Faraday::Utils.deep_merge!(connection_options, url)
71
+
72
+ expect(deep_merge.headers).to eq('Mime-Version' => '1.0', user_agent: 'My Agent 1.0')
73
+ end
74
+
75
+ context 'when a target hash has an Options Struct value' do
76
+ let(:request) do
77
+ {
78
+ params_encoder: nil,
79
+ proxy: nil,
80
+ bind: nil,
81
+ timeout: nil,
82
+ open_timeout: nil,
83
+ read_timeout: nil,
84
+ write_timeout: nil,
85
+ boundary: nil,
86
+ oauth: { consumer_key: 'anonymous' },
87
+ context: nil,
88
+ on_data: nil
89
+ }
90
+ end
91
+ let(:ssl) do
92
+ {
93
+ verify: nil,
94
+ ca_file: nil,
95
+ ca_path: nil,
96
+ verify_mode: nil,
97
+ cert_store: nil,
98
+ client_cert: nil,
99
+ client_key: nil,
100
+ certificate: nil,
101
+ private_key: nil,
102
+ verify_depth: nil,
103
+ version: '2',
104
+ min_version: nil,
105
+ max_version: nil,
106
+ verify_hostname: nil,
107
+ hostname: nil,
108
+ ciphers: nil
109
+ }
110
+ end
111
+
112
+ it 'does not overwrite an Options Struct value' do
113
+ deep_merge = Faraday::Utils.deep_merge!(connection_options, url)
114
+
115
+ expect(deep_merge.request.to_h).to eq(request)
116
+ expect(deep_merge.ssl.to_h).to eq(ssl)
117
+ end
118
+ end
119
+ end
56
120
  end
data/spec/faraday_spec.rb CHANGED
@@ -18,10 +18,16 @@ RSpec.describe Faraday do
18
18
  end
19
19
 
20
20
  it 'uses method_missing on Faraday if there is no proxyable method' do
21
- expect { Faraday.this_method_does_not_exist }.to raise_error(
22
- NoMethodError,
23
- "undefined method `this_method_does_not_exist' for Faraday:Module"
24
- )
21
+ expected_message =
22
+ if RUBY_VERSION >= '3.4'
23
+ "undefined method 'this_method_does_not_exist' for module Faraday"
24
+ elsif RUBY_VERSION >= '3.3'
25
+ "undefined method `this_method_does_not_exist' for module Faraday"
26
+ else
27
+ "undefined method `this_method_does_not_exist' for Faraday:Module"
28
+ end
29
+
30
+ expect { Faraday.this_method_does_not_exist }.to raise_error(NoMethodError, expected_message)
25
31
  end
26
32
 
27
33
  it 'proxied methods can be accessed' do
data/spec/spec_helper.rb CHANGED
@@ -29,16 +29,15 @@ SimpleCov.start do
29
29
  minimum_coverage_by_file 26
30
30
  end
31
31
 
32
- # Ensure all /lib files are loaded
33
- # so they will be included in the test coverage report.
34
- Dir['./lib/**/*.rb'].sort.each { |file| require file }
35
-
36
32
  require 'faraday'
37
33
  require 'pry'
38
34
 
39
- Dir['./spec/support/**/*.rb'].sort.each { |f| require f }
35
+ # Ensure all /lib files are loaded
36
+ # so they will be included in the test coverage report.
37
+ Dir['./lib/**/*.rb'].each { |file| require file }
40
38
 
41
- Faraday::Deprecate.skip = false
39
+ # Load all Rspec support files
40
+ Dir['./spec/support/**/*.rb'].each { |file| require file }
42
41
 
43
42
  RSpec.configure do |config|
44
43
  # rspec-expectations config goes here. You can use an alternate
@@ -8,7 +8,7 @@ FakeSafeBuffer = Struct.new(:string) do
8
8
 
9
9
  def gsub(regex)
10
10
  string.gsub(regex) do
11
- match, = $&, '' =~ /a/
11
+ match, = Regexp.last_match(0), '' =~ /a/ # rubocop:disable Performance/StringInclude
12
12
  yield(match)
13
13
  end
14
14
  end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FaradayMiddlewareSubclasses
4
+ class SubclassNoOptions < Faraday::Middleware
5
+ end
6
+
7
+ class SubclassOneOption < Faraday::Middleware
8
+ DEFAULT_OPTIONS = { some_other_option: false }.freeze
9
+ end
10
+
11
+ class SubclassTwoOptions < Faraday::Middleware
12
+ DEFAULT_OPTIONS = { some_option: true, some_other_option: false }.freeze
13
+ end
14
+ end
15
+
16
+ Faraday::Response.register_middleware(no_options: FaradayMiddlewareSubclasses::SubclassNoOptions)
17
+ Faraday::Response.register_middleware(one_option: FaradayMiddlewareSubclasses::SubclassOneOption)
18
+ Faraday::Response.register_middleware(two_options: FaradayMiddlewareSubclasses::SubclassTwoOptions)
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'multipart_parser/reader'
4
-
5
3
  module Faraday
6
4
  module HelperMethods
7
5
  def self.included(base)
@@ -86,41 +84,6 @@ module Faraday
86
84
  end
87
85
  end
88
86
 
89
- def multipart_file
90
- Faraday::FilePart.new(__FILE__, 'text/x-ruby')
91
- end
92
-
93
- # parse boundary out of a Content-Type header like:
94
- # Content-Type: multipart/form-data; boundary=gc0p4Jq0M2Yt08jU534c0p
95
- def parse_multipart_boundary(ctype)
96
- MultipartParser::Reader.extract_boundary_value(ctype)
97
- end
98
-
99
- # parse a multipart MIME message, returning a hash of any multipart errors
100
- def parse_multipart(boundary, body)
101
- reader = MultipartParser::Reader.new(boundary)
102
- result = { errors: [], parts: [] }
103
- def result.part(name)
104
- hash = self[:parts].detect { |h| h[:part].name == name }
105
- [hash[:part], hash[:body].join]
106
- end
107
-
108
- reader.on_part do |part|
109
- result[:parts] << thispart = {
110
- part: part,
111
- body: []
112
- }
113
- part.on_data do |chunk|
114
- thispart[:body] << chunk
115
- end
116
- end
117
- reader.on_error do |msg|
118
- result[:errors] << msg
119
- end
120
- reader.write(body)
121
- result
122
- end
123
-
124
87
  def method_with_body?(method)
125
88
  self.class.method_with_body?(method)
126
89
  end
@@ -37,10 +37,10 @@ shared_examples 'adapter examples' do |**options|
37
37
 
38
38
  let(:conn) do
39
39
  conn_options[:ssl] ||= {}
40
- conn_options[:ssl][:ca_file] ||= ENV['SSL_FILE']
40
+ conn_options[:ssl][:ca_file] ||= ENV.fetch('SSL_FILE', nil)
41
+ conn_options[:ssl][:verify_hostname] ||= ENV['SSL_VERIFY_HOSTNAME'] == 'yes'
41
42
 
42
43
  Faraday.new(remote, conn_options) do |conn|
43
- conn.request :multipart
44
44
  conn.request :url_encoded
45
45
  conn.response :raise_error
46
46
  conn.adapter described_class, *adapter_options
@@ -79,7 +79,7 @@ shared_examples 'a request method' do |http_method|
79
79
 
80
80
  on_feature :request_body_on_query_methods do
81
81
  it 'sends request body' do
82
- request_stub.with(Hash[:body, 'test'])
82
+ request_stub.with({ body: 'test' })
83
83
  res = if query_or_body == :body
84
84
  conn.public_send(http_method, '/', 'test')
85
85
  else
@@ -93,7 +93,7 @@ shared_examples 'a request method' do |http_method|
93
93
 
94
94
  it 'sends url encoded parameters' do
95
95
  payload = { name: 'zack' }
96
- request_stub.with(Hash[query_or_body, payload])
96
+ request_stub.with({ query_or_body => payload })
97
97
  res = conn.public_send(http_method, '/', payload)
98
98
  if query_or_body == :query
99
99
  expect(res.env.request_body).to be_nil
@@ -104,7 +104,7 @@ shared_examples 'a request method' do |http_method|
104
104
 
105
105
  it 'sends url encoded nested parameters' do
106
106
  payload = { name: { first: 'zack' } }
107
- request_stub.with(Hash[query_or_body, payload])
107
+ request_stub.with({ query_or_body => payload })
108
108
  conn.public_send(http_method, '/', payload)
109
109
  end
110
110
 
@@ -126,19 +126,6 @@ shared_examples 'a request method' do |http_method|
126
126
  expect { conn.public_send(http_method, '/') }.to raise_error(exc)
127
127
  end
128
128
 
129
- # Can't send files on get, head and delete methods
130
- if method_with_body?(http_method)
131
- it 'sends files' do
132
- payload = { uploaded_file: multipart_file }
133
- request_stub.with(headers: { 'Content-Type' => %r{\Amultipart/form-data} }) do |request|
134
- # WebMock does not support matching body for multipart/form-data requests yet :(
135
- # https://github.com/bblimke/webmock/issues/623
136
- request.body.include?('RubyMultipartPost')
137
- end
138
- conn.public_send(http_method, '/', payload)
139
- end
140
- end
141
-
142
129
  on_feature :reason_phrase_parse do
143
130
  it 'parses the reason phrase' do
144
131
  request_stub.to_return(status: [200, 'OK'])
@@ -166,12 +153,19 @@ shared_examples 'a request method' do |http_method|
166
153
  let(:streamed) { [] }
167
154
 
168
155
  context 'when response is empty' do
169
- it do
156
+ it 'handles streaming' do
157
+ env = nil
170
158
  conn.public_send(http_method, '/') do |req|
171
- req.options.on_data = proc { |*args| streamed << args }
159
+ req.options.on_data = proc do |chunk, size, block_env|
160
+ streamed << [chunk, size]
161
+ env ||= block_env
162
+ end
172
163
  end
173
164
 
174
165
  expect(streamed).to eq([['', 0]])
166
+ # TODO: enable this after updating all existing adapters to the new streaming API
167
+ # expect(env).to be_a(Faraday::Env)
168
+ # expect(env.status).to eq(200)
175
169
  end
176
170
  end
177
171
 
@@ -179,12 +173,19 @@ shared_examples 'a request method' do |http_method|
179
173
  before { request_stub.to_return(body: big_string) }
180
174
 
181
175
  it 'handles streaming' do
176
+ env = nil
182
177
  response = conn.public_send(http_method, '/') do |req|
183
- req.options.on_data = proc { |*args| streamed << args }
178
+ req.options.on_data = proc do |chunk, size, block_env|
179
+ streamed << [chunk, size]
180
+ env ||= block_env
181
+ end
184
182
  end
185
183
 
186
184
  expect(response.body).to eq('')
187
185
  check_streaming_response(streamed, chunk_size: 16 * 1024)
186
+ # TODO: enable this after updating all existing adapters to the new streaming API
187
+ # expect(env).to be_a(Faraday::Env)
188
+ # expect(env.status).to eq(200)
188
189
  end
189
190
  end
190
191
  end
@@ -199,11 +200,11 @@ shared_examples 'a request method' do |http_method|
199
200
  @payload2 = { b: '2' }
200
201
 
201
202
  request_stub
202
- .with(Hash[query_or_body, @payload1])
203
+ .with({ query_or_body => @payload1 })
203
204
  .to_return(body: @payload1.to_json)
204
205
 
205
206
  stub_request(http_method, remote)
206
- .with(Hash[query_or_body, @payload2])
207
+ .with({ query_or_body => @payload2 })
207
208
  .to_return(body: @payload2.to_json)
208
209
 
209
210
  conn.in_parallel do