faraday 0.17.6 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +52 -8
- data/LICENSE.md +1 -1
- data/README.md +18 -358
- data/Rakefile +1 -7
- data/examples/client_spec.rb +65 -0
- data/examples/client_test.rb +79 -0
- data/lib/faraday/adapter/em_http.rb +142 -99
- data/lib/faraday/adapter/em_http_ssl_patch.rb +24 -18
- data/lib/faraday/adapter/em_synchrony/parallel_manager.rb +18 -15
- data/lib/faraday/adapter/em_synchrony.rb +104 -60
- data/lib/faraday/adapter/excon.rb +98 -56
- data/lib/faraday/adapter/httpclient.rb +83 -59
- data/lib/faraday/adapter/net_http.rb +129 -63
- data/lib/faraday/adapter/net_http_persistent.rb +50 -27
- data/lib/faraday/adapter/patron.rb +80 -43
- data/lib/faraday/adapter/rack.rb +30 -13
- data/lib/faraday/adapter/test.rb +86 -53
- data/lib/faraday/adapter/typhoeus.rb +4 -1
- data/lib/faraday/adapter.rb +82 -22
- data/lib/faraday/adapter_registry.rb +30 -0
- data/lib/faraday/autoload.rb +47 -36
- data/lib/faraday/connection.rb +312 -182
- data/lib/faraday/dependency_loader.rb +37 -0
- data/lib/faraday/encoders/flat_params_encoder.rb +98 -0
- data/lib/faraday/encoders/nested_params_encoder.rb +171 -0
- data/lib/faraday/error.rb +9 -35
- data/lib/faraday/file_part.rb +128 -0
- data/lib/faraday/logging/formatter.rb +105 -0
- data/lib/faraday/middleware.rb +12 -28
- data/lib/faraday/middleware_registry.rb +129 -0
- data/lib/faraday/options/connection_options.rb +22 -0
- data/lib/faraday/options/env.rb +181 -0
- data/lib/faraday/options/proxy_options.rb +28 -0
- data/lib/faraday/options/request_options.rb +22 -0
- data/lib/faraday/options/ssl_options.rb +59 -0
- data/lib/faraday/options.rb +32 -183
- data/lib/faraday/param_part.rb +53 -0
- data/lib/faraday/parameters.rb +4 -197
- data/lib/faraday/rack_builder.rb +66 -55
- data/lib/faraday/request/authorization.rb +44 -30
- data/lib/faraday/request/basic_authentication.rb +14 -7
- data/lib/faraday/request/instrumentation.rb +45 -27
- data/lib/faraday/request/multipart.rb +79 -48
- data/lib/faraday/request/retry.rb +197 -171
- data/lib/faraday/request/token_authentication.rb +15 -10
- data/lib/faraday/request/url_encoded.rb +43 -23
- data/lib/faraday/request.rb +68 -38
- data/lib/faraday/response/logger.rb +22 -69
- data/lib/faraday/response/raise_error.rb +38 -18
- data/lib/faraday/response.rb +24 -14
- data/lib/faraday/utils/headers.rb +139 -0
- data/lib/faraday/utils/params_hash.rb +61 -0
- data/lib/faraday/utils.rb +36 -245
- data/lib/faraday.rb +94 -175
- data/spec/external_adapters/faraday_specs_setup.rb +14 -0
- data/spec/faraday/adapter/em_http_spec.rb +47 -0
- data/spec/faraday/adapter/em_synchrony_spec.rb +16 -0
- data/spec/faraday/adapter/excon_spec.rb +49 -0
- data/spec/faraday/adapter/httpclient_spec.rb +73 -0
- data/spec/faraday/adapter/net_http_persistent_spec.rb +57 -0
- data/spec/faraday/adapter/net_http_spec.rb +64 -0
- data/spec/faraday/adapter/patron_spec.rb +18 -0
- data/spec/faraday/adapter/rack_spec.rb +8 -0
- data/spec/faraday/adapter/typhoeus_spec.rb +7 -0
- data/spec/faraday/adapter_registry_spec.rb +28 -0
- data/spec/faraday/adapter_spec.rb +55 -0
- data/spec/faraday/composite_read_io_spec.rb +80 -0
- data/spec/faraday/connection_spec.rb +691 -0
- data/spec/faraday/error_spec.rb +0 -57
- data/spec/faraday/middleware_spec.rb +26 -0
- data/spec/faraday/options/env_spec.rb +70 -0
- data/spec/faraday/options/options_spec.rb +297 -0
- data/spec/faraday/options/proxy_options_spec.rb +37 -0
- data/spec/faraday/options/request_options_spec.rb +19 -0
- data/spec/faraday/params_encoders/flat_spec.rb +34 -0
- data/spec/faraday/params_encoders/nested_spec.rb +134 -0
- data/spec/faraday/rack_builder_spec.rb +196 -0
- data/spec/faraday/request/authorization_spec.rb +88 -0
- data/spec/faraday/request/instrumentation_spec.rb +76 -0
- data/spec/faraday/request/multipart_spec.rb +274 -0
- data/spec/faraday/request/retry_spec.rb +242 -0
- data/spec/faraday/request/url_encoded_spec.rb +83 -0
- data/spec/faraday/request_spec.rb +109 -0
- data/spec/faraday/response/logger_spec.rb +220 -0
- data/spec/faraday/response/middleware_spec.rb +68 -0
- data/spec/faraday/response/raise_error_spec.rb +15 -15
- data/spec/faraday/response_spec.rb +75 -0
- data/spec/faraday/utils/headers_spec.rb +82 -0
- data/spec/faraday/utils_spec.rb +56 -0
- data/spec/faraday_spec.rb +37 -0
- data/spec/spec_helper.rb +63 -36
- data/spec/support/disabling_stub.rb +14 -0
- data/spec/support/fake_safe_buffer.rb +15 -0
- data/spec/support/helper_methods.rb +133 -0
- data/spec/support/shared_examples/adapter.rb +104 -0
- data/spec/support/shared_examples/params_encoder.rb +18 -0
- data/spec/support/shared_examples/request_method.rb +234 -0
- data/spec/support/streaming_response_checker.rb +35 -0
- data/spec/support/webmock_rack_app.rb +68 -0
- metadata +66 -38
- data/lib/faraday/deprecate.rb +0 -109
- data/lib/faraday/upload_io.rb +0 -77
- data/spec/faraday/deprecate_spec.rb +0 -147
- data/test/adapters/default_test.rb +0 -14
- data/test/adapters/em_http_test.rb +0 -30
- data/test/adapters/em_synchrony_test.rb +0 -32
- data/test/adapters/excon_test.rb +0 -30
- data/test/adapters/httpclient_test.rb +0 -34
- data/test/adapters/integration.rb +0 -263
- data/test/adapters/logger_test.rb +0 -136
- data/test/adapters/net_http_persistent_test.rb +0 -114
- data/test/adapters/net_http_test.rb +0 -79
- data/test/adapters/patron_test.rb +0 -40
- data/test/adapters/rack_test.rb +0 -38
- data/test/adapters/test_middleware_test.rb +0 -157
- data/test/adapters/typhoeus_test.rb +0 -38
- data/test/authentication_middleware_test.rb +0 -65
- data/test/composite_read_io_test.rb +0 -109
- data/test/connection_test.rb +0 -738
- data/test/env_test.rb +0 -268
- data/test/helper.rb +0 -75
- data/test/live_server.rb +0 -67
- data/test/middleware/instrumentation_test.rb +0 -88
- data/test/middleware/retry_test.rb +0 -282
- data/test/middleware_stack_test.rb +0 -260
- data/test/multibyte.txt +0 -1
- data/test/options_test.rb +0 -333
- data/test/parameters_test.rb +0 -157
- data/test/request_middleware_test.rb +0 -126
- data/test/response_middleware_test.rb +0 -72
- data/test/strawberry.rb +0 -2
- data/test/utils_test.rb +0 -98
@@ -0,0 +1,196 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
RSpec.describe Faraday::RackBuilder do
|
4
|
+
# mock handler classes
|
5
|
+
(Handler = Struct.new(:app)).class_eval do
|
6
|
+
def call(env)
|
7
|
+
env[:request_headers]['X-Middleware'] ||= ''
|
8
|
+
env[:request_headers]['X-Middleware'] += ":#{self.class.name.split('::').last}"
|
9
|
+
app.call(env)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class Apple < Handler
|
14
|
+
end
|
15
|
+
class Orange < Handler
|
16
|
+
end
|
17
|
+
class Banana < Handler
|
18
|
+
end
|
19
|
+
|
20
|
+
class Broken < Faraday::Middleware
|
21
|
+
dependency 'zomg/i_dont/exist'
|
22
|
+
end
|
23
|
+
|
24
|
+
subject { conn.builder }
|
25
|
+
|
26
|
+
context 'with default stack' do
|
27
|
+
let(:conn) { Faraday::Connection.new }
|
28
|
+
|
29
|
+
it { expect(subject[0]).to eq(Faraday::Request.lookup_middleware(:url_encoded)) }
|
30
|
+
it { expect(subject.adapter).to eq(Faraday::Adapter.lookup_middleware(Faraday.default_adapter)) }
|
31
|
+
end
|
32
|
+
|
33
|
+
context 'with custom empty block' do
|
34
|
+
let(:conn) { Faraday::Connection.new {} }
|
35
|
+
|
36
|
+
it { expect(subject[0]).to be_nil }
|
37
|
+
it { expect(subject.adapter).to eq(Faraday::Adapter.lookup_middleware(Faraday.default_adapter)) }
|
38
|
+
end
|
39
|
+
|
40
|
+
context 'with custom adapter only' do
|
41
|
+
let(:conn) do
|
42
|
+
Faraday::Connection.new do |builder|
|
43
|
+
builder.adapter :test do |stub|
|
44
|
+
stub.get('/') { |_| [200, {}, ''] }
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
it { expect(subject[0]).to be_nil }
|
50
|
+
it { expect(subject.adapter).to eq(Faraday::Adapter.lookup_middleware(:test)) }
|
51
|
+
end
|
52
|
+
|
53
|
+
context 'with custom handler and adapter' do
|
54
|
+
let(:conn) do
|
55
|
+
Faraday::Connection.new do |builder|
|
56
|
+
builder.use Apple
|
57
|
+
builder.adapter :test do |stub|
|
58
|
+
stub.get('/') { |_| [200, {}, ''] }
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'locks the stack after making a request' do
|
64
|
+
expect(subject.locked?).to be_falsey
|
65
|
+
conn.get('/')
|
66
|
+
expect(subject.locked?).to be_truthy
|
67
|
+
expect { subject.use(Orange) }.to raise_error(Faraday::RackBuilder::StackLocked)
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'dup stack is unlocked' do
|
71
|
+
expect(subject.locked?).to be_falsey
|
72
|
+
subject.lock!
|
73
|
+
expect(subject.locked?).to be_truthy
|
74
|
+
dup = subject.dup
|
75
|
+
expect(dup).to eq(subject)
|
76
|
+
expect(dup.locked?).to be_falsey
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'allows to compare handlers' do
|
80
|
+
expect(subject.handlers.first).to eq(Faraday::RackBuilder::Handler.new(Apple))
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
context 'when having a single handler' do
|
85
|
+
let(:conn) { Faraday::Connection.new {} }
|
86
|
+
|
87
|
+
before { subject.use(Apple) }
|
88
|
+
|
89
|
+
it { expect(subject.handlers).to eq([Apple]) }
|
90
|
+
|
91
|
+
it 'allows rebuilding' do
|
92
|
+
subject.build do |builder|
|
93
|
+
builder.use(Orange)
|
94
|
+
end
|
95
|
+
expect(subject.handlers).to eq([Orange])
|
96
|
+
end
|
97
|
+
|
98
|
+
it 'allows use' do
|
99
|
+
subject.use(Orange)
|
100
|
+
expect(subject.handlers).to eq([Apple, Orange])
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'allows insert_before' do
|
104
|
+
subject.insert_before(Apple, Orange)
|
105
|
+
expect(subject.handlers).to eq([Orange, Apple])
|
106
|
+
end
|
107
|
+
|
108
|
+
it 'allows insert_after' do
|
109
|
+
subject.insert_after(Apple, Orange)
|
110
|
+
expect(subject.handlers).to eq([Apple, Orange])
|
111
|
+
end
|
112
|
+
|
113
|
+
it 'raises an error trying to use an unregistered symbol' do
|
114
|
+
expect { subject.use(:apple) }.to raise_error(Faraday::Error) do |err|
|
115
|
+
expect(err.message).to eq(':apple is not registered on Faraday::Middleware')
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
context 'with custom registered middleware' do
|
121
|
+
let(:conn) { Faraday::Connection.new {} }
|
122
|
+
|
123
|
+
after { Faraday::Middleware.unregister_middleware(:apple) }
|
124
|
+
|
125
|
+
it 'allows to register with constant' do
|
126
|
+
Faraday::Middleware.register_middleware(apple: Apple)
|
127
|
+
subject.use(:apple)
|
128
|
+
expect(subject.handlers).to eq([Apple])
|
129
|
+
end
|
130
|
+
|
131
|
+
it 'allows to register with symbol' do
|
132
|
+
Faraday::Middleware.register_middleware(apple: :Apple)
|
133
|
+
subject.use(:apple)
|
134
|
+
expect(subject.handlers).to eq([Apple])
|
135
|
+
end
|
136
|
+
|
137
|
+
it 'allows to register with string' do
|
138
|
+
Faraday::Middleware.register_middleware(apple: 'Apple')
|
139
|
+
subject.use(:apple)
|
140
|
+
expect(subject.handlers).to eq([Apple])
|
141
|
+
end
|
142
|
+
|
143
|
+
it 'allows to register with Proc' do
|
144
|
+
Faraday::Middleware.register_middleware(apple: -> { Apple })
|
145
|
+
subject.use(:apple)
|
146
|
+
expect(subject.handlers).to eq([Apple])
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
context 'when having two handlers' do
|
151
|
+
let(:conn) { Faraday::Connection.new {} }
|
152
|
+
|
153
|
+
before do
|
154
|
+
subject.use(Apple)
|
155
|
+
subject.use(Orange)
|
156
|
+
end
|
157
|
+
|
158
|
+
it 'allows insert_before' do
|
159
|
+
subject.insert_before(Orange, Banana)
|
160
|
+
expect(subject.handlers).to eq([Apple, Banana, Orange])
|
161
|
+
end
|
162
|
+
|
163
|
+
it 'allows insert_after' do
|
164
|
+
subject.insert_after(Apple, Banana)
|
165
|
+
expect(subject.handlers).to eq([Apple, Banana, Orange])
|
166
|
+
end
|
167
|
+
|
168
|
+
it 'allows to swap handlers' do
|
169
|
+
subject.swap(Apple, Banana)
|
170
|
+
expect(subject.handlers).to eq([Banana, Orange])
|
171
|
+
end
|
172
|
+
|
173
|
+
it 'allows to delete a handler' do
|
174
|
+
subject.delete(Apple)
|
175
|
+
expect(subject.handlers).to eq([Orange])
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
context 'when having a handler with broken dependency' do
|
180
|
+
let(:conn) do
|
181
|
+
Faraday::Connection.new do |builder|
|
182
|
+
builder.adapter :test do |stub|
|
183
|
+
stub.get('/') { |_| [200, {}, ''] }
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
before { subject.use(Broken) }
|
189
|
+
|
190
|
+
it 'raises an error while making a request' do
|
191
|
+
expect { conn.get('/') }.to raise_error(RuntimeError) do |err|
|
192
|
+
expect(err.message).to match(%r{missing dependency for Broken: .+ -- zomg/i_dont/exist})
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
RSpec.describe Faraday::Request::Authorization do
|
4
|
+
let(:conn) do
|
5
|
+
Faraday.new do |b|
|
6
|
+
b.request auth_type, *auth_config
|
7
|
+
b.adapter :test do |stub|
|
8
|
+
stub.get('/auth-echo') do |env|
|
9
|
+
[200, {}, env[:request_headers]['Authorization']]
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
shared_examples 'does not interfere with existing authentication' do
|
16
|
+
context 'and request already has an authentication header' do
|
17
|
+
let(:response) { conn.get('/auth-echo', nil, authorization: 'Token token="bar"') }
|
18
|
+
|
19
|
+
it 'does not interfere with existing authorization' do
|
20
|
+
expect(response.body).to eq('Token token="bar"')
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
let(:response) { conn.get('/auth-echo') }
|
26
|
+
|
27
|
+
describe 'basic_auth' do
|
28
|
+
let(:auth_type) { :basic_auth }
|
29
|
+
|
30
|
+
context 'when passed correct params' do
|
31
|
+
let(:auth_config) { %w[aladdin opensesame] }
|
32
|
+
|
33
|
+
it { expect(response.body).to eq('Basic YWxhZGRpbjpvcGVuc2VzYW1l') }
|
34
|
+
|
35
|
+
include_examples 'does not interfere with existing authentication'
|
36
|
+
end
|
37
|
+
|
38
|
+
context 'when passed very long values' do
|
39
|
+
let(:auth_config) { ['A' * 255, ''] }
|
40
|
+
|
41
|
+
it { expect(response.body).to eq("Basic #{'QUFB' * 85}Og==") }
|
42
|
+
|
43
|
+
include_examples 'does not interfere with existing authentication'
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe 'token_auth' do
|
48
|
+
let(:auth_type) { :token_auth }
|
49
|
+
|
50
|
+
context 'when passed correct params' do
|
51
|
+
let(:auth_config) { 'quux' }
|
52
|
+
|
53
|
+
it { expect(response.body).to eq('Token token="quux"') }
|
54
|
+
|
55
|
+
include_examples 'does not interfere with existing authentication'
|
56
|
+
end
|
57
|
+
|
58
|
+
context 'when other values are provided' do
|
59
|
+
let(:auth_config) { ['baz', foo: 42] }
|
60
|
+
|
61
|
+
it { expect(response.body).to match(/^Token /) }
|
62
|
+
it { expect(response.body).to match(/token="baz"/) }
|
63
|
+
it { expect(response.body).to match(/foo="42"/) }
|
64
|
+
|
65
|
+
include_examples 'does not interfere with existing authentication'
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
describe 'authorization' do
|
70
|
+
let(:auth_type) { :authorization }
|
71
|
+
|
72
|
+
context 'when passed two strings' do
|
73
|
+
let(:auth_config) { ['custom', 'abc def'] }
|
74
|
+
|
75
|
+
it { expect(response.body).to eq('custom abc def') }
|
76
|
+
|
77
|
+
include_examples 'does not interfere with existing authentication'
|
78
|
+
end
|
79
|
+
|
80
|
+
context 'when passed a string and a hash' do
|
81
|
+
let(:auth_config) { ['baz', foo: 42] }
|
82
|
+
|
83
|
+
it { expect(response.body).to eq('baz foo="42"') }
|
84
|
+
|
85
|
+
include_examples 'does not interfere with existing authentication'
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
RSpec.describe Faraday::Request::Instrumentation do
|
4
|
+
class FakeInstrumenter
|
5
|
+
attr_reader :instrumentations
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@instrumentations = []
|
9
|
+
end
|
10
|
+
|
11
|
+
def instrument(name, env)
|
12
|
+
@instrumentations << [name, env]
|
13
|
+
yield
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
let(:config) { {} }
|
18
|
+
let(:options) { Faraday::Request::Instrumentation::Options.from config }
|
19
|
+
let(:instrumenter) { FakeInstrumenter.new }
|
20
|
+
let(:conn) do
|
21
|
+
Faraday.new do |f|
|
22
|
+
f.request :instrumentation, config.merge(instrumenter: instrumenter)
|
23
|
+
f.adapter :test do |stub|
|
24
|
+
stub.get '/' do
|
25
|
+
[200, {}, 'ok']
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
it { expect(options.name).to eq('request.faraday') }
|
32
|
+
it 'defaults to ActiveSupport::Notifications' do
|
33
|
+
begin
|
34
|
+
res = options.instrumenter
|
35
|
+
rescue NameError => e
|
36
|
+
expect(e.to_s).to match('ActiveSupport')
|
37
|
+
else
|
38
|
+
expect(res).to eq(ActiveSupport::Notifications)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'instruments with default name' do
|
43
|
+
expect(instrumenter.instrumentations.size).to eq(0)
|
44
|
+
|
45
|
+
res = conn.get '/'
|
46
|
+
expect(res.body).to eq('ok')
|
47
|
+
expect(instrumenter.instrumentations.size).to eq(1)
|
48
|
+
|
49
|
+
name, env = instrumenter.instrumentations.first
|
50
|
+
expect(name).to eq('request.faraday')
|
51
|
+
expect(env[:url].path).to eq('/')
|
52
|
+
end
|
53
|
+
|
54
|
+
context 'with custom name' do
|
55
|
+
let(:config) { { name: 'custom' } }
|
56
|
+
|
57
|
+
it { expect(options.name).to eq('custom') }
|
58
|
+
it 'instruments with custom name' do
|
59
|
+
expect(instrumenter.instrumentations.size).to eq(0)
|
60
|
+
|
61
|
+
res = conn.get '/'
|
62
|
+
expect(res.body).to eq('ok')
|
63
|
+
expect(instrumenter.instrumentations.size).to eq(1)
|
64
|
+
|
65
|
+
name, env = instrumenter.instrumentations.first
|
66
|
+
expect(name).to eq('custom')
|
67
|
+
expect(env[:url].path).to eq('/')
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
context 'with custom instrumenter' do
|
72
|
+
let(:config) { { instrumenter: :custom } }
|
73
|
+
|
74
|
+
it { expect(options.instrumenter).to eq(:custom) }
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,274 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
RSpec.describe Faraday::Request::Multipart do
|
4
|
+
let(:conn) do
|
5
|
+
Faraday.new do |b|
|
6
|
+
b.request :multipart
|
7
|
+
b.request :url_encoded
|
8
|
+
b.adapter :test do |stub|
|
9
|
+
stub.post('/echo') do |env|
|
10
|
+
posted_as = env[:request_headers]['Content-Type']
|
11
|
+
expect(env[:body]).to be_a_kind_of(Faraday::CompositeReadIO)
|
12
|
+
[200, { 'Content-Type' => posted_as }, env[:body].read]
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
shared_examples 'a multipart request' do
|
19
|
+
it 'generates a unique boundary for each request' do
|
20
|
+
response1 = conn.post('/echo', payload)
|
21
|
+
response2 = conn.post('/echo', payload)
|
22
|
+
|
23
|
+
b1 = parse_multipart_boundary(response1.headers['Content-Type'])
|
24
|
+
b2 = parse_multipart_boundary(response2.headers['Content-Type'])
|
25
|
+
expect(b1).to_not eq(b2)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
context 'FilePart: when multipart objects in param' do
|
30
|
+
let(:payload) do
|
31
|
+
{
|
32
|
+
a: 1,
|
33
|
+
b: {
|
34
|
+
c: Faraday::FilePart.new(__FILE__, 'text/x-ruby', nil,
|
35
|
+
'Content-Disposition' => 'form-data; foo=1'),
|
36
|
+
d: 2
|
37
|
+
}
|
38
|
+
}
|
39
|
+
end
|
40
|
+
it_behaves_like 'a multipart request'
|
41
|
+
|
42
|
+
it 'forms a multipart request' do
|
43
|
+
response = conn.post('/echo', payload)
|
44
|
+
|
45
|
+
boundary = parse_multipart_boundary(response.headers['Content-Type'])
|
46
|
+
result = parse_multipart(boundary, response.body)
|
47
|
+
expect(result[:errors]).to be_empty
|
48
|
+
|
49
|
+
part_a, body_a = result.part('a')
|
50
|
+
expect(part_a).to_not be_nil
|
51
|
+
expect(part_a.filename).to be_nil
|
52
|
+
expect(body_a).to eq('1')
|
53
|
+
|
54
|
+
part_bc, body_bc = result.part('b[c]')
|
55
|
+
expect(part_bc).to_not be_nil
|
56
|
+
expect(part_bc.filename).to eq('multipart_spec.rb')
|
57
|
+
expect(part_bc.headers['content-disposition']).to eq(
|
58
|
+
'form-data; foo=1; name="b[c]"; filename="multipart_spec.rb"'
|
59
|
+
)
|
60
|
+
expect(part_bc.headers['content-type']).to eq('text/x-ruby')
|
61
|
+
expect(part_bc.headers['content-transfer-encoding']).to eq('binary')
|
62
|
+
expect(body_bc).to eq(File.read(__FILE__))
|
63
|
+
|
64
|
+
part_bd, body_bd = result.part('b[d]')
|
65
|
+
expect(part_bd).to_not be_nil
|
66
|
+
expect(part_bd.filename).to be_nil
|
67
|
+
expect(body_bd).to eq('2')
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
context 'FilePart: when providing json and IO content in the same payload' do
|
72
|
+
let(:io) { StringIO.new('io-content') }
|
73
|
+
let(:json) do
|
74
|
+
{
|
75
|
+
b: 1,
|
76
|
+
c: 2
|
77
|
+
}.to_json
|
78
|
+
end
|
79
|
+
|
80
|
+
let(:payload) do
|
81
|
+
{
|
82
|
+
json: Faraday::ParamPart.new(json, 'application/json'),
|
83
|
+
io: Faraday::FilePart.new(io, 'application/pdf')
|
84
|
+
}
|
85
|
+
end
|
86
|
+
|
87
|
+
it_behaves_like 'a multipart request'
|
88
|
+
|
89
|
+
it 'forms a multipart request' do
|
90
|
+
response = conn.post('/echo', payload)
|
91
|
+
|
92
|
+
boundary = parse_multipart_boundary(response.headers['Content-Type'])
|
93
|
+
result = parse_multipart(boundary, response.body)
|
94
|
+
expect(result[:errors]).to be_empty
|
95
|
+
|
96
|
+
part_json, body_json = result.part('json')
|
97
|
+
expect(part_json).to_not be_nil
|
98
|
+
expect(part_json.mime).to eq('application/json')
|
99
|
+
expect(part_json.filename).to be_nil
|
100
|
+
expect(body_json).to eq(json)
|
101
|
+
|
102
|
+
part_io, body_io = result.part('io')
|
103
|
+
expect(part_io).to_not be_nil
|
104
|
+
expect(part_io.mime).to eq('application/pdf')
|
105
|
+
expect(part_io.filename).to eq('local.path')
|
106
|
+
expect(body_io).to eq(io.string)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
context 'FilePart: when multipart objects in array param' do
|
111
|
+
let(:payload) do
|
112
|
+
{
|
113
|
+
a: 1,
|
114
|
+
b: [{
|
115
|
+
c: Faraday::FilePart.new(__FILE__, 'text/x-ruby'),
|
116
|
+
d: 2
|
117
|
+
}]
|
118
|
+
}
|
119
|
+
end
|
120
|
+
|
121
|
+
it_behaves_like 'a multipart request'
|
122
|
+
|
123
|
+
it 'forms a multipart request' do
|
124
|
+
response = conn.post('/echo', payload)
|
125
|
+
|
126
|
+
boundary = parse_multipart_boundary(response.headers['Content-Type'])
|
127
|
+
result = parse_multipart(boundary, response.body)
|
128
|
+
expect(result[:errors]).to be_empty
|
129
|
+
|
130
|
+
part_a, body_a = result.part('a')
|
131
|
+
expect(part_a).to_not be_nil
|
132
|
+
expect(part_a.filename).to be_nil
|
133
|
+
expect(body_a).to eq('1')
|
134
|
+
|
135
|
+
part_bc, body_bc = result.part('b[][c]')
|
136
|
+
expect(part_bc).to_not be_nil
|
137
|
+
expect(part_bc.filename).to eq('multipart_spec.rb')
|
138
|
+
expect(part_bc.headers['content-disposition']).to eq(
|
139
|
+
'form-data; name="b[][c]"; filename="multipart_spec.rb"'
|
140
|
+
)
|
141
|
+
expect(part_bc.headers['content-type']).to eq('text/x-ruby')
|
142
|
+
expect(part_bc.headers['content-transfer-encoding']).to eq('binary')
|
143
|
+
expect(body_bc).to eq(File.read(__FILE__))
|
144
|
+
|
145
|
+
part_bd, body_bd = result.part('b[][d]')
|
146
|
+
expect(part_bd).to_not be_nil
|
147
|
+
expect(part_bd.filename).to be_nil
|
148
|
+
expect(body_bd).to eq('2')
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
context 'UploadIO: when multipart objects in param' do
|
153
|
+
let(:payload) do
|
154
|
+
{
|
155
|
+
a: 1,
|
156
|
+
b: {
|
157
|
+
c: Faraday::UploadIO.new(__FILE__, 'text/x-ruby', nil,
|
158
|
+
'Content-Disposition' => 'form-data; foo=1'),
|
159
|
+
d: 2
|
160
|
+
}
|
161
|
+
}
|
162
|
+
end
|
163
|
+
it_behaves_like 'a multipart request'
|
164
|
+
|
165
|
+
it 'forms a multipart request' do
|
166
|
+
response = conn.post('/echo', payload)
|
167
|
+
|
168
|
+
boundary = parse_multipart_boundary(response.headers['Content-Type'])
|
169
|
+
result = parse_multipart(boundary, response.body)
|
170
|
+
expect(result[:errors]).to be_empty
|
171
|
+
|
172
|
+
part_a, body_a = result.part('a')
|
173
|
+
expect(part_a).to_not be_nil
|
174
|
+
expect(part_a.filename).to be_nil
|
175
|
+
expect(body_a).to eq('1')
|
176
|
+
|
177
|
+
part_bc, body_bc = result.part('b[c]')
|
178
|
+
expect(part_bc).to_not be_nil
|
179
|
+
expect(part_bc.filename).to eq('multipart_spec.rb')
|
180
|
+
expect(part_bc.headers['content-disposition']).to eq(
|
181
|
+
'form-data; foo=1; name="b[c]"; filename="multipart_spec.rb"'
|
182
|
+
)
|
183
|
+
expect(part_bc.headers['content-type']).to eq('text/x-ruby')
|
184
|
+
expect(part_bc.headers['content-transfer-encoding']).to eq('binary')
|
185
|
+
expect(body_bc).to eq(File.read(__FILE__))
|
186
|
+
|
187
|
+
part_bd, body_bd = result.part('b[d]')
|
188
|
+
expect(part_bd).to_not be_nil
|
189
|
+
expect(part_bd.filename).to be_nil
|
190
|
+
expect(body_bd).to eq('2')
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
context 'UploadIO: when providing json and IO content in the same payload' do
|
195
|
+
let(:io) { StringIO.new('io-content') }
|
196
|
+
let(:json) do
|
197
|
+
{
|
198
|
+
b: 1,
|
199
|
+
c: 2
|
200
|
+
}.to_json
|
201
|
+
end
|
202
|
+
|
203
|
+
let(:payload) do
|
204
|
+
{
|
205
|
+
json: Faraday::ParamPart.new(json, 'application/json'),
|
206
|
+
io: Faraday::UploadIO.new(io, 'application/pdf')
|
207
|
+
}
|
208
|
+
end
|
209
|
+
|
210
|
+
it_behaves_like 'a multipart request'
|
211
|
+
|
212
|
+
it 'forms a multipart request' do
|
213
|
+
response = conn.post('/echo', payload)
|
214
|
+
|
215
|
+
boundary = parse_multipart_boundary(response.headers['Content-Type'])
|
216
|
+
result = parse_multipart(boundary, response.body)
|
217
|
+
expect(result[:errors]).to be_empty
|
218
|
+
|
219
|
+
part_json, body_json = result.part('json')
|
220
|
+
expect(part_json).to_not be_nil
|
221
|
+
expect(part_json.mime).to eq('application/json')
|
222
|
+
expect(part_json.filename).to be_nil
|
223
|
+
expect(body_json).to eq(json)
|
224
|
+
|
225
|
+
part_io, body_io = result.part('io')
|
226
|
+
expect(part_io).to_not be_nil
|
227
|
+
expect(part_io.mime).to eq('application/pdf')
|
228
|
+
expect(part_io.filename).to eq('local.path')
|
229
|
+
expect(body_io).to eq(io.string)
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
context 'UploadIO: when multipart objects in array param' do
|
234
|
+
let(:payload) do
|
235
|
+
{
|
236
|
+
a: 1,
|
237
|
+
b: [{
|
238
|
+
c: Faraday::UploadIO.new(__FILE__, 'text/x-ruby'),
|
239
|
+
d: 2
|
240
|
+
}]
|
241
|
+
}
|
242
|
+
end
|
243
|
+
|
244
|
+
it_behaves_like 'a multipart request'
|
245
|
+
|
246
|
+
it 'forms a multipart request' do
|
247
|
+
response = conn.post('/echo', payload)
|
248
|
+
|
249
|
+
boundary = parse_multipart_boundary(response.headers['Content-Type'])
|
250
|
+
result = parse_multipart(boundary, response.body)
|
251
|
+
expect(result[:errors]).to be_empty
|
252
|
+
|
253
|
+
part_a, body_a = result.part('a')
|
254
|
+
expect(part_a).to_not be_nil
|
255
|
+
expect(part_a.filename).to be_nil
|
256
|
+
expect(body_a).to eq('1')
|
257
|
+
|
258
|
+
part_bc, body_bc = result.part('b[][c]')
|
259
|
+
expect(part_bc).to_not be_nil
|
260
|
+
expect(part_bc.filename).to eq('multipart_spec.rb')
|
261
|
+
expect(part_bc.headers['content-disposition']).to eq(
|
262
|
+
'form-data; name="b[][c]"; filename="multipart_spec.rb"'
|
263
|
+
)
|
264
|
+
expect(part_bc.headers['content-type']).to eq('text/x-ruby')
|
265
|
+
expect(part_bc.headers['content-transfer-encoding']).to eq('binary')
|
266
|
+
expect(body_bc).to eq(File.read(__FILE__))
|
267
|
+
|
268
|
+
part_bd, body_bd = result.part('b[][d]')
|
269
|
+
expect(part_bd).to_not be_nil
|
270
|
+
expect(part_bd.filename).to be_nil
|
271
|
+
expect(body_bd).to eq('2')
|
272
|
+
end
|
273
|
+
end
|
274
|
+
end
|