faraday 1.0.0 → 2.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +299 -1
- data/LICENSE.md +1 -1
- data/README.md +35 -23
- data/Rakefile +3 -1
- data/examples/client_spec.rb +68 -14
- data/examples/client_test.rb +80 -15
- data/lib/faraday/adapter/test.rb +117 -52
- data/lib/faraday/adapter.rb +6 -20
- data/lib/faraday/adapter_registry.rb +3 -1
- data/lib/faraday/connection.rb +73 -132
- data/lib/faraday/encoders/flat_params_encoder.rb +9 -2
- data/lib/faraday/encoders/nested_params_encoder.rb +20 -8
- data/lib/faraday/error.rb +37 -8
- data/lib/faraday/logging/formatter.rb +28 -15
- data/lib/faraday/methods.rb +6 -0
- data/lib/faraday/middleware.rb +17 -5
- data/lib/faraday/middleware_registry.rb +17 -63
- data/lib/faraday/options/connection_options.rb +7 -6
- data/lib/faraday/options/env.rb +85 -62
- data/lib/faraday/options/proxy_options.rb +11 -3
- data/lib/faraday/options/request_options.rb +7 -6
- data/lib/faraday/options/ssl_options.rb +56 -45
- data/lib/faraday/options.rb +11 -14
- data/lib/faraday/rack_builder.rb +35 -32
- data/lib/faraday/request/authorization.rb +37 -36
- data/lib/faraday/request/instrumentation.rb +5 -1
- data/lib/faraday/request/json.rb +70 -0
- data/lib/faraday/request/url_encoded.rb +8 -2
- data/lib/faraday/request.rb +22 -29
- data/lib/faraday/response/json.rb +73 -0
- data/lib/faraday/response/logger.rb +8 -4
- data/lib/faraday/response/raise_error.rb +41 -3
- data/lib/faraday/response.rb +10 -23
- data/lib/faraday/utils/headers.rb +9 -4
- data/lib/faraday/utils.rb +22 -10
- data/lib/faraday/version.rb +5 -0
- data/lib/faraday.rb +49 -58
- data/spec/faraday/adapter/test_spec.rb +442 -0
- data/spec/faraday/connection_spec.rb +207 -90
- data/spec/faraday/error_spec.rb +45 -5
- data/spec/faraday/middleware_registry_spec.rb +31 -0
- data/spec/faraday/middleware_spec.rb +50 -6
- data/spec/faraday/options/env_spec.rb +8 -2
- data/spec/faraday/options/options_spec.rb +1 -1
- data/spec/faraday/options/proxy_options_spec.rb +15 -0
- data/spec/faraday/params_encoders/flat_spec.rb +8 -0
- data/spec/faraday/params_encoders/nested_spec.rb +16 -0
- data/spec/faraday/rack_builder_spec.rb +171 -50
- data/spec/faraday/request/authorization_spec.rb +54 -24
- data/spec/faraday/request/instrumentation_spec.rb +5 -7
- data/spec/faraday/request/json_spec.rb +199 -0
- data/spec/faraday/request/url_encoded_spec.rb +25 -2
- data/spec/faraday/request_spec.rb +11 -10
- data/spec/faraday/response/json_spec.rb +189 -0
- data/spec/faraday/response/logger_spec.rb +38 -0
- data/spec/faraday/response/raise_error_spec.rb +105 -0
- data/spec/faraday/response_spec.rb +3 -1
- data/spec/faraday/utils/headers_spec.rb +22 -4
- data/spec/faraday/utils_spec.rb +63 -1
- data/spec/faraday_spec.rb +8 -4
- data/spec/spec_helper.rb +6 -5
- data/spec/support/fake_safe_buffer.rb +1 -1
- data/spec/support/helper_methods.rb +0 -37
- data/spec/support/shared_examples/adapter.rb +4 -3
- data/spec/support/shared_examples/request_method.rb +60 -31
- metadata +19 -44
- data/UPGRADING.md +0 -55
- data/lib/faraday/adapter/em_http.rb +0 -285
- data/lib/faraday/adapter/em_http_ssl_patch.rb +0 -62
- data/lib/faraday/adapter/em_synchrony/parallel_manager.rb +0 -69
- data/lib/faraday/adapter/em_synchrony.rb +0 -150
- data/lib/faraday/adapter/excon.rb +0 -124
- data/lib/faraday/adapter/httpclient.rb +0 -151
- data/lib/faraday/adapter/net_http.rb +0 -209
- data/lib/faraday/adapter/net_http_persistent.rb +0 -91
- data/lib/faraday/adapter/patron.rb +0 -132
- data/lib/faraday/adapter/rack.rb +0 -75
- data/lib/faraday/adapter/typhoeus.rb +0 -15
- data/lib/faraday/autoload.rb +0 -95
- data/lib/faraday/dependency_loader.rb +0 -37
- data/lib/faraday/file_part.rb +0 -128
- data/lib/faraday/param_part.rb +0 -53
- data/lib/faraday/request/basic_authentication.rb +0 -20
- data/lib/faraday/request/multipart.rb +0 -99
- data/lib/faraday/request/retry.rb +0 -239
- data/lib/faraday/request/token_authentication.rb +0 -20
- data/spec/faraday/adapter/em_http_spec.rb +0 -47
- data/spec/faraday/adapter/em_synchrony_spec.rb +0 -16
- data/spec/faraday/adapter/excon_spec.rb +0 -49
- data/spec/faraday/adapter/httpclient_spec.rb +0 -73
- data/spec/faraday/adapter/net_http_persistent_spec.rb +0 -57
- data/spec/faraday/adapter/net_http_spec.rb +0 -64
- data/spec/faraday/adapter/patron_spec.rb +0 -18
- data/spec/faraday/adapter/rack_spec.rb +0 -8
- data/spec/faraday/adapter/typhoeus_spec.rb +0 -7
- data/spec/faraday/composite_read_io_spec.rb +0 -80
- data/spec/faraday/request/multipart_spec.rb +0 -274
- data/spec/faraday/request/retry_spec.rb +0 -242
- data/spec/faraday/response/middleware_spec.rb +0 -52
- 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'] }
|
@@ -29,6 +31,9 @@ RSpec.describe Faraday::Response::RaiseError do
|
|
29
31
|
expect(ex.message).to eq('the server responded with status 400')
|
30
32
|
expect(ex.response[:headers]['X-Reason']).to eq('because')
|
31
33
|
expect(ex.response[:status]).to eq(400)
|
34
|
+
expect(ex.response_status).to eq(400)
|
35
|
+
expect(ex.response_body).to eq('keep looking')
|
36
|
+
expect(ex.response_headers['X-Reason']).to eq('because')
|
32
37
|
end
|
33
38
|
end
|
34
39
|
|
@@ -37,6 +42,9 @@ RSpec.describe Faraday::Response::RaiseError do
|
|
37
42
|
expect(ex.message).to eq('the server responded with status 401')
|
38
43
|
expect(ex.response[:headers]['X-Reason']).to eq('because')
|
39
44
|
expect(ex.response[:status]).to eq(401)
|
45
|
+
expect(ex.response_status).to eq(401)
|
46
|
+
expect(ex.response_body).to eq('keep looking')
|
47
|
+
expect(ex.response_headers['X-Reason']).to eq('because')
|
40
48
|
end
|
41
49
|
end
|
42
50
|
|
@@ -45,6 +53,9 @@ RSpec.describe Faraday::Response::RaiseError do
|
|
45
53
|
expect(ex.message).to eq('the server responded with status 403')
|
46
54
|
expect(ex.response[:headers]['X-Reason']).to eq('because')
|
47
55
|
expect(ex.response[:status]).to eq(403)
|
56
|
+
expect(ex.response_status).to eq(403)
|
57
|
+
expect(ex.response_body).to eq('keep looking')
|
58
|
+
expect(ex.response_headers['X-Reason']).to eq('because')
|
48
59
|
end
|
49
60
|
end
|
50
61
|
|
@@ -53,6 +64,9 @@ RSpec.describe Faraday::Response::RaiseError do
|
|
53
64
|
expect(ex.message).to eq('the server responded with status 404')
|
54
65
|
expect(ex.response[:headers]['X-Reason']).to eq('because')
|
55
66
|
expect(ex.response[:status]).to eq(404)
|
67
|
+
expect(ex.response_status).to eq(404)
|
68
|
+
expect(ex.response_body).to eq('keep looking')
|
69
|
+
expect(ex.response_headers['X-Reason']).to eq('because')
|
56
70
|
end
|
57
71
|
end
|
58
72
|
|
@@ -61,6 +75,20 @@ RSpec.describe Faraday::Response::RaiseError do
|
|
61
75
|
expect(ex.message).to eq('407 "Proxy Authentication Required"')
|
62
76
|
expect(ex.response[:headers]['X-Reason']).to eq('because')
|
63
77
|
expect(ex.response[:status]).to eq(407)
|
78
|
+
expect(ex.response_status).to eq(407)
|
79
|
+
expect(ex.response_body).to eq('keep looking')
|
80
|
+
expect(ex.response_headers['X-Reason']).to eq('because')
|
81
|
+
end
|
82
|
+
end
|
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')
|
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')
|
64
92
|
end
|
65
93
|
end
|
66
94
|
|
@@ -69,6 +97,9 @@ RSpec.describe Faraday::Response::RaiseError do
|
|
69
97
|
expect(ex.message).to eq('the server responded with status 409')
|
70
98
|
expect(ex.response[:headers]['X-Reason']).to eq('because')
|
71
99
|
expect(ex.response[:status]).to eq(409)
|
100
|
+
expect(ex.response_status).to eq(409)
|
101
|
+
expect(ex.response_body).to eq('keep looking')
|
102
|
+
expect(ex.response_headers['X-Reason']).to eq('because')
|
72
103
|
end
|
73
104
|
end
|
74
105
|
|
@@ -77,6 +108,20 @@ RSpec.describe Faraday::Response::RaiseError do
|
|
77
108
|
expect(ex.message).to eq('the server responded with status 422')
|
78
109
|
expect(ex.response[:headers]['X-Reason']).to eq('because')
|
79
110
|
expect(ex.response[:status]).to eq(422)
|
111
|
+
expect(ex.response_status).to eq(422)
|
112
|
+
expect(ex.response_body).to eq('keep looking')
|
113
|
+
expect(ex.response_headers['X-Reason']).to eq('because')
|
114
|
+
end
|
115
|
+
end
|
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')
|
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')
|
80
125
|
end
|
81
126
|
end
|
82
127
|
|
@@ -85,6 +130,9 @@ RSpec.describe Faraday::Response::RaiseError do
|
|
85
130
|
expect(ex.message).to eq('http status could not be derived from the server response')
|
86
131
|
expect(ex.response[:headers]['X-Reason']).to eq('nil')
|
87
132
|
expect(ex.response[:status]).to be_nil
|
133
|
+
expect(ex.response_status).to be_nil
|
134
|
+
expect(ex.response_body).to eq('fail')
|
135
|
+
expect(ex.response_headers['X-Reason']).to eq('nil')
|
88
136
|
end
|
89
137
|
end
|
90
138
|
|
@@ -93,6 +141,9 @@ RSpec.describe Faraday::Response::RaiseError do
|
|
93
141
|
expect(ex.message).to eq('the server responded with status 499')
|
94
142
|
expect(ex.response[:headers]['X-Reason']).to eq('because')
|
95
143
|
expect(ex.response[:status]).to eq(499)
|
144
|
+
expect(ex.response_status).to eq(499)
|
145
|
+
expect(ex.response_body).to eq('keep looking')
|
146
|
+
expect(ex.response_headers['X-Reason']).to eq('because')
|
96
147
|
end
|
97
148
|
end
|
98
149
|
|
@@ -101,6 +152,60 @@ RSpec.describe Faraday::Response::RaiseError do
|
|
101
152
|
expect(ex.message).to eq('the server responded with status 500')
|
102
153
|
expect(ex.response[:headers]['X-Error']).to eq('bailout')
|
103
154
|
expect(ex.response[:status]).to eq(500)
|
155
|
+
expect(ex.response_status).to eq(500)
|
156
|
+
expect(ex.response_body).to eq('fail')
|
157
|
+
expect(ex.response_headers['X-Error']).to eq('bailout')
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
describe 'request info' do
|
162
|
+
let(:conn) do
|
163
|
+
Faraday.new do |b|
|
164
|
+
b.response :raise_error, **middleware_options
|
165
|
+
b.adapter :test do |stub|
|
166
|
+
stub.post(url, request_body, request_headers) do
|
167
|
+
[400, { 'X-Reason' => 'because' }, 'keep looking']
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
let(:middleware_options) { {} }
|
173
|
+
let(:request_body) { JSON.generate({ 'item' => 'sth' }) }
|
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}" }
|
178
|
+
|
179
|
+
subject(:perform_request) do
|
180
|
+
conn.post url do |req|
|
181
|
+
req.headers['Authorization'] = 'Basic 123'
|
182
|
+
req.body = request_body
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
it 'returns the request info in the exception' do
|
187
|
+
expect { perform_request }.to raise_error(Faraday::BadRequestError) do |ex|
|
188
|
+
expect(ex.response[:request][:method]).to eq(:post)
|
189
|
+
expect(ex.response[:request][:url]).to eq(URI("http:/#{url}"))
|
190
|
+
expect(ex.response[:request][:url_path]).to eq("/#{url_path}")
|
191
|
+
expect(ex.response[:request][:params]).to eq({ 'full' => 'true' })
|
192
|
+
expect(ex.response[:request][:headers]).to match(a_hash_including(request_headers))
|
193
|
+
expect(ex.response[:request][:body]).to eq(request_body)
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
context 'when the include_request option is set to false' do
|
198
|
+
let(:middleware_options) { { include_request: false } }
|
199
|
+
|
200
|
+
it 'does not include request info in the exception' do
|
201
|
+
expect { perform_request }.to raise_error(Faraday::BadRequestError) do |ex|
|
202
|
+
expect(ex.response.keys).to contain_exactly(
|
203
|
+
:status,
|
204
|
+
:headers,
|
205
|
+
:body
|
206
|
+
)
|
207
|
+
end
|
208
|
+
end
|
104
209
|
end
|
105
210
|
end
|
106
211
|
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
|
@@ -57,26 +57,44 @@ RSpec.describe Faraday::Utils::Headers do
|
|
57
57
|
end
|
58
58
|
|
59
59
|
describe '#parse' do
|
60
|
-
before { subject.parse(headers) }
|
61
|
-
|
62
60
|
context 'when response headers leave http status line out' do
|
63
61
|
let(:headers) { "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n" }
|
64
62
|
|
63
|
+
before { subject.parse(headers) }
|
64
|
+
|
65
65
|
it { expect(subject.keys).to eq(%w[Content-Type]) }
|
66
66
|
it { expect(subject['Content-Type']).to eq('text/html') }
|
67
67
|
it { expect(subject['content-type']).to eq('text/html') }
|
68
68
|
end
|
69
69
|
|
70
70
|
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://
|
71
|
+
let(:headers) { "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nLocation: http://httpbingo.org/\r\n\r\n" }
|
72
72
|
|
73
|
-
|
73
|
+
before { subject.parse(headers) }
|
74
|
+
|
75
|
+
it { expect(subject['location']).to eq('http://httpbingo.org/') }
|
74
76
|
end
|
75
77
|
|
76
78
|
context 'when response headers include a blank line' do
|
77
79
|
let(:headers) { "HTTP/1.1 200 OK\r\n\r\nContent-Type: text/html\r\n\r\n" }
|
78
80
|
|
81
|
+
before { subject.parse(headers) }
|
82
|
+
|
79
83
|
it { expect(subject['content-type']).to eq('text/html') }
|
80
84
|
end
|
85
|
+
|
86
|
+
context 'when response headers include already stored keys' do
|
87
|
+
let(:headers) { "HTTP/1.1 200 OK\r\nX-Numbers: 123\r\n\r\n" }
|
88
|
+
|
89
|
+
before do
|
90
|
+
h = subject
|
91
|
+
h[:x_numbers] = 8
|
92
|
+
h.parse(headers)
|
93
|
+
end
|
94
|
+
|
95
|
+
it do
|
96
|
+
expect(subject[:x_numbers]).to eq('8, 123')
|
97
|
+
end
|
98
|
+
end
|
81
99
|
end
|
82
100
|
end
|
data/spec/faraday/utils_spec.rb
CHANGED
@@ -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
|
-
|
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,66 @@ 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
|
+
}
|
108
|
+
end
|
109
|
+
|
110
|
+
it 'does not overwrite an Options Struct value' do
|
111
|
+
deep_merge = Faraday::Utils.deep_merge!(connection_options, url)
|
112
|
+
|
113
|
+
expect(deep_merge.request.to_h).to eq(request)
|
114
|
+
expect(deep_merge.ssl.to_h).to eq(ssl)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
56
118
|
end
|
data/spec/faraday_spec.rb
CHANGED
@@ -18,10 +18,14 @@ 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
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
21
|
+
expected_message =
|
22
|
+
if RUBY_VERSION >= '3.3'
|
23
|
+
"undefined method `this_method_does_not_exist' for module Faraday"
|
24
|
+
else
|
25
|
+
"undefined method `this_method_does_not_exist' for Faraday:Module"
|
26
|
+
end
|
27
|
+
|
28
|
+
expect { Faraday.this_method_does_not_exist }.to raise_error(NoMethodError, expected_message)
|
25
29
|
end
|
26
30
|
|
27
31
|
it 'proxied methods can be accessed' do
|
data/spec/spec_helper.rb
CHANGED
@@ -29,14 +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
|
-
|
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 }
|
38
|
+
|
39
|
+
# Load all Rspec support files
|
40
|
+
Dir['./spec/support/**/*.rb'].each { |file| require file }
|
40
41
|
|
41
42
|
RSpec.configure do |config|
|
42
43
|
# rspec-expectations config goes here. You can use an alternate
|
@@ -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
|
@@ -33,20 +33,21 @@ shared_examples 'adapter examples' do |**options|
|
|
33
33
|
|
34
34
|
let(:protocol) { ssl_mode? ? 'https' : 'http' }
|
35
35
|
let(:remote) { "#{protocol}://example.com" }
|
36
|
+
let(:stub_remote) { remote }
|
36
37
|
|
37
38
|
let(:conn) do
|
38
39
|
conn_options[:ssl] ||= {}
|
39
|
-
conn_options[:ssl][:ca_file] ||= ENV
|
40
|
+
conn_options[:ssl][:ca_file] ||= ENV.fetch('SSL_FILE', nil)
|
41
|
+
conn_options[:ssl][:verify_hostname] ||= ENV['SSL_VERIFY_HOSTNAME'] == 'yes'
|
40
42
|
|
41
43
|
Faraday.new(remote, conn_options) do |conn|
|
42
|
-
conn.request :multipart
|
43
44
|
conn.request :url_encoded
|
44
45
|
conn.response :raise_error
|
45
46
|
conn.adapter described_class, *adapter_options
|
46
47
|
end
|
47
48
|
end
|
48
49
|
|
49
|
-
let!(:request_stub) { stub_request(http_method,
|
50
|
+
let!(:request_stub) { stub_request(http_method, stub_remote) }
|
50
51
|
|
51
52
|
after do
|
52
53
|
expect(request_stub).to have_been_requested unless request_stub.disabled?
|
@@ -1,5 +1,19 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
shared_examples 'proxy examples' do
|
4
|
+
it 'handles requests with proxy' do
|
5
|
+
res = conn.public_send(http_method, '/')
|
6
|
+
|
7
|
+
expect(res.status).to eq(200)
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'handles proxy failures' do
|
11
|
+
request_stub.to_return(status: 407)
|
12
|
+
|
13
|
+
expect { conn.public_send(http_method, '/') }.to raise_error(Faraday::ProxyAuthError)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
3
17
|
shared_examples 'a request method' do |http_method|
|
4
18
|
let(:query_or_body) { method_with_body?(http_method) ? :body : :query }
|
5
19
|
let(:response) { conn.public_send(http_method, '/') }
|
@@ -13,8 +27,8 @@ shared_examples 'a request method' do |http_method|
|
|
13
27
|
end
|
14
28
|
|
15
29
|
it 'handles headers with multiple values' do
|
16
|
-
request_stub.to_return(headers: { 'Set-Cookie' => '
|
17
|
-
expect(response.headers['set-cookie']).to eq('
|
30
|
+
request_stub.to_return(headers: { 'Set-Cookie' => 'name=value' })
|
31
|
+
expect(response.headers['set-cookie']).to eq('name=value')
|
18
32
|
end
|
19
33
|
|
20
34
|
it 'retrieves the response headers' do
|
@@ -65,7 +79,7 @@ shared_examples 'a request method' do |http_method|
|
|
65
79
|
|
66
80
|
on_feature :request_body_on_query_methods do
|
67
81
|
it 'sends request body' do
|
68
|
-
request_stub.with(
|
82
|
+
request_stub.with({ body: 'test' })
|
69
83
|
res = if query_or_body == :body
|
70
84
|
conn.public_send(http_method, '/', 'test')
|
71
85
|
else
|
@@ -79,7 +93,7 @@ shared_examples 'a request method' do |http_method|
|
|
79
93
|
|
80
94
|
it 'sends url encoded parameters' do
|
81
95
|
payload = { name: 'zack' }
|
82
|
-
request_stub.with(
|
96
|
+
request_stub.with({ query_or_body => payload })
|
83
97
|
res = conn.public_send(http_method, '/', payload)
|
84
98
|
if query_or_body == :query
|
85
99
|
expect(res.env.request_body).to be_nil
|
@@ -90,7 +104,7 @@ shared_examples 'a request method' do |http_method|
|
|
90
104
|
|
91
105
|
it 'sends url encoded nested parameters' do
|
92
106
|
payload = { name: { first: 'zack' } }
|
93
|
-
request_stub.with(
|
107
|
+
request_stub.with({ query_or_body => payload })
|
94
108
|
conn.public_send(http_method, '/', payload)
|
95
109
|
end
|
96
110
|
|
@@ -112,19 +126,6 @@ shared_examples 'a request method' do |http_method|
|
|
112
126
|
expect { conn.public_send(http_method, '/') }.to raise_error(exc)
|
113
127
|
end
|
114
128
|
|
115
|
-
# Can't send files on get, head and delete methods
|
116
|
-
if method_with_body?(http_method)
|
117
|
-
it 'sends files' do
|
118
|
-
payload = { uploaded_file: multipart_file }
|
119
|
-
request_stub.with(headers: { 'Content-Type' => %r{\Amultipart/form-data} }) do |request|
|
120
|
-
# WebMock does not support matching body for multipart/form-data requests yet :(
|
121
|
-
# https://github.com/bblimke/webmock/issues/623
|
122
|
-
request.body =~ /RubyMultipartPost/
|
123
|
-
end
|
124
|
-
conn.public_send(http_method, '/', payload)
|
125
|
-
end
|
126
|
-
end
|
127
|
-
|
128
129
|
on_feature :reason_phrase_parse do
|
129
130
|
it 'parses the reason phrase' do
|
130
131
|
request_stub.to_return(status: [200, 'OK'])
|
@@ -152,12 +153,19 @@ shared_examples 'a request method' do |http_method|
|
|
152
153
|
let(:streamed) { [] }
|
153
154
|
|
154
155
|
context 'when response is empty' do
|
155
|
-
it do
|
156
|
+
it 'handles streaming' do
|
157
|
+
env = nil
|
156
158
|
conn.public_send(http_method, '/') do |req|
|
157
|
-
req.options.on_data = proc
|
159
|
+
req.options.on_data = proc do |chunk, size, block_env|
|
160
|
+
streamed << [chunk, size]
|
161
|
+
env ||= block_env
|
162
|
+
end
|
158
163
|
end
|
159
164
|
|
160
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)
|
161
169
|
end
|
162
170
|
end
|
163
171
|
|
@@ -165,12 +173,19 @@ shared_examples 'a request method' do |http_method|
|
|
165
173
|
before { request_stub.to_return(body: big_string) }
|
166
174
|
|
167
175
|
it 'handles streaming' do
|
176
|
+
env = nil
|
168
177
|
response = conn.public_send(http_method, '/') do |req|
|
169
|
-
req.options.on_data = proc
|
178
|
+
req.options.on_data = proc do |chunk, size, block_env|
|
179
|
+
streamed << [chunk, size]
|
180
|
+
env ||= block_env
|
181
|
+
end
|
170
182
|
end
|
171
183
|
|
172
184
|
expect(response.body).to eq('')
|
173
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)
|
174
189
|
end
|
175
190
|
end
|
176
191
|
end
|
@@ -185,11 +200,11 @@ shared_examples 'a request method' do |http_method|
|
|
185
200
|
@payload2 = { b: '2' }
|
186
201
|
|
187
202
|
request_stub
|
188
|
-
.with(
|
203
|
+
.with({ query_or_body => @payload1 })
|
189
204
|
.to_return(body: @payload1.to_json)
|
190
205
|
|
191
206
|
stub_request(http_method, remote)
|
192
|
-
.with(
|
207
|
+
.with({ query_or_body => @payload2 })
|
193
208
|
.to_return(body: @payload2.to_json)
|
194
209
|
|
195
210
|
conn.in_parallel do
|
@@ -218,17 +233,31 @@ shared_examples 'a request method' do |http_method|
|
|
218
233
|
end
|
219
234
|
end
|
220
235
|
|
221
|
-
|
222
|
-
|
236
|
+
context 'when a proxy is provided as option' do
|
237
|
+
before do
|
238
|
+
conn_options[:proxy] = 'http://env-proxy.com:80'
|
239
|
+
end
|
223
240
|
|
224
|
-
|
225
|
-
expect(res.status).to eq(200)
|
241
|
+
include_examples 'proxy examples'
|
226
242
|
end
|
227
243
|
|
228
|
-
|
229
|
-
|
230
|
-
request_stub.to_return(status: 407)
|
244
|
+
context 'when http_proxy env variable is set' do
|
245
|
+
let(:proxy_url) { 'http://env-proxy.com:80' }
|
231
246
|
|
232
|
-
|
247
|
+
around do |example|
|
248
|
+
with_env 'http_proxy' => proxy_url do
|
249
|
+
example.run
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
include_examples 'proxy examples'
|
254
|
+
|
255
|
+
context 'when the env proxy is ignored' do
|
256
|
+
around do |example|
|
257
|
+
with_env_proxy_disabled(&example)
|
258
|
+
end
|
259
|
+
|
260
|
+
include_examples 'proxy examples'
|
261
|
+
end
|
233
262
|
end
|
234
263
|
end
|