faraday 0.17.1 → 1.1.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 +69 -9
- data/LICENSE.md +1 -1
- data/README.md +17 -347
- data/Rakefile +1 -7
- data/examples/client_spec.rb +65 -0
- data/examples/client_test.rb +79 -0
- data/lib/faraday.rb +94 -176
- data/lib/faraday/adapter.rb +83 -22
- data/lib/faraday/adapter/em_http.rb +143 -100
- data/lib/faraday/adapter/em_http_ssl_patch.rb +24 -18
- data/lib/faraday/adapter/em_synchrony.rb +104 -60
- data/lib/faraday/adapter/em_synchrony/parallel_manager.rb +18 -15
- data/lib/faraday/adapter/excon.rb +98 -56
- data/lib/faraday/adapter/httpclient.rb +83 -59
- data/lib/faraday/adapter/net_http.rb +130 -63
- data/lib/faraday/adapter/net_http_persistent.rb +51 -28
- 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_registry.rb +30 -0
- data/lib/faraday/autoload.rb +47 -36
- data/lib/faraday/connection.rb +312 -182
- data/lib/faraday/dependency_loader.rb +39 -0
- data/lib/faraday/encoders/flat_params_encoder.rb +105 -0
- data/lib/faraday/encoders/nested_params_encoder.rb +176 -0
- data/lib/faraday/error.rb +46 -25
- 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.rb +38 -193
- 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/param_part.rb +53 -0
- data/lib/faraday/parameters.rb +4 -197
- data/lib/faraday/rack_builder.rb +77 -65
- data/lib/faraday/request.rb +86 -44
- 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 +86 -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/response.rb +24 -14
- data/lib/faraday/response/logger.rb +22 -69
- data/lib/faraday/response/raise_error.rb +49 -18
- data/lib/faraday/utils.rb +38 -247
- data/lib/faraday/utils/headers.rb +139 -0
- data/lib/faraday/utils/params_hash.rb +61 -0
- 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/test_spec.rb +260 -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 +42 -0
- data/spec/faraday/params_encoders/nested_spec.rb +142 -0
- data/spec/faraday/rack_builder_spec.rb +345 -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 +302 -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 +120 -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 +60 -16
- 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 -35
- 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 +80 -37
- data/lib/faraday/deprecate.rb +0 -101
- data/lib/faraday/upload_io.rb +0 -67
- data/spec/faraday/deprecate_spec.rb +0 -69
- 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,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,302 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            RSpec.describe Faraday::Request::Multipart do
         | 
| 4 | 
            +
              let(:options) { {} }
         | 
| 5 | 
            +
              let(:conn) do
         | 
| 6 | 
            +
                Faraday.new do |b|
         | 
| 7 | 
            +
                  b.request :multipart, options
         | 
| 8 | 
            +
                  b.request :url_encoded
         | 
| 9 | 
            +
                  b.adapter :test do |stub|
         | 
| 10 | 
            +
                    stub.post('/echo') do |env|
         | 
| 11 | 
            +
                      posted_as = env[:request_headers]['Content-Type']
         | 
| 12 | 
            +
                      expect(env[:body]).to be_a_kind_of(Faraday::CompositeReadIO)
         | 
| 13 | 
            +
                      [200, { 'Content-Type' => posted_as }, env[:body].read]
         | 
| 14 | 
            +
                    end
         | 
| 15 | 
            +
                  end
         | 
| 16 | 
            +
                end
         | 
| 17 | 
            +
              end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
              shared_examples 'a multipart request' do
         | 
| 20 | 
            +
                it 'generates a unique boundary for each request' do
         | 
| 21 | 
            +
                  response1 = conn.post('/echo', payload)
         | 
| 22 | 
            +
                  response2 = conn.post('/echo', payload)
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                  b1 = parse_multipart_boundary(response1.headers['Content-Type'])
         | 
| 25 | 
            +
                  b2 = parse_multipart_boundary(response2.headers['Content-Type'])
         | 
| 26 | 
            +
                  expect(b1).to_not eq(b2)
         | 
| 27 | 
            +
                end
         | 
| 28 | 
            +
              end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
              context 'FilePart: when multipart objects in param' do
         | 
| 31 | 
            +
                let(:payload) do
         | 
| 32 | 
            +
                  {
         | 
| 33 | 
            +
                    a: 1,
         | 
| 34 | 
            +
                    b: {
         | 
| 35 | 
            +
                      c: Faraday::FilePart.new(__FILE__, 'text/x-ruby', nil,
         | 
| 36 | 
            +
                                               'Content-Disposition' => 'form-data; foo=1'),
         | 
| 37 | 
            +
                      d: 2
         | 
| 38 | 
            +
                    }
         | 
| 39 | 
            +
                  }
         | 
| 40 | 
            +
                end
         | 
| 41 | 
            +
                it_behaves_like 'a multipart request'
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                it 'forms a multipart request' do
         | 
| 44 | 
            +
                  response = conn.post('/echo', payload)
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                  boundary = parse_multipart_boundary(response.headers['Content-Type'])
         | 
| 47 | 
            +
                  result = parse_multipart(boundary, response.body)
         | 
| 48 | 
            +
                  expect(result[:errors]).to be_empty
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                  part_a, body_a = result.part('a')
         | 
| 51 | 
            +
                  expect(part_a).to_not be_nil
         | 
| 52 | 
            +
                  expect(part_a.filename).to be_nil
         | 
| 53 | 
            +
                  expect(body_a).to eq('1')
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                  part_bc, body_bc = result.part('b[c]')
         | 
| 56 | 
            +
                  expect(part_bc).to_not be_nil
         | 
| 57 | 
            +
                  expect(part_bc.filename).to eq('multipart_spec.rb')
         | 
| 58 | 
            +
                  expect(part_bc.headers['content-disposition'])
         | 
| 59 | 
            +
                    .to eq(
         | 
| 60 | 
            +
                      'form-data; foo=1; name="b[c]"; filename="multipart_spec.rb"'
         | 
| 61 | 
            +
                    )
         | 
| 62 | 
            +
                  expect(part_bc.headers['content-type']).to eq('text/x-ruby')
         | 
| 63 | 
            +
                  expect(part_bc.headers['content-transfer-encoding']).to eq('binary')
         | 
| 64 | 
            +
                  expect(body_bc).to eq(File.read(__FILE__))
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                  part_bd, body_bd = result.part('b[d]')
         | 
| 67 | 
            +
                  expect(part_bd).to_not be_nil
         | 
| 68 | 
            +
                  expect(part_bd.filename).to be_nil
         | 
| 69 | 
            +
                  expect(body_bd).to eq('2')
         | 
| 70 | 
            +
                end
         | 
| 71 | 
            +
              end
         | 
| 72 | 
            +
             | 
| 73 | 
            +
              context 'FilePart: when providing json and IO content in the same payload' do
         | 
| 74 | 
            +
                let(:io) { StringIO.new('io-content') }
         | 
| 75 | 
            +
                let(:json) do
         | 
| 76 | 
            +
                  {
         | 
| 77 | 
            +
                    b: 1,
         | 
| 78 | 
            +
                    c: 2
         | 
| 79 | 
            +
                  }.to_json
         | 
| 80 | 
            +
                end
         | 
| 81 | 
            +
             | 
| 82 | 
            +
                let(:payload) do
         | 
| 83 | 
            +
                  {
         | 
| 84 | 
            +
                    json: Faraday::ParamPart.new(json, 'application/json'),
         | 
| 85 | 
            +
                    io: Faraday::FilePart.new(io, 'application/pdf')
         | 
| 86 | 
            +
                  }
         | 
| 87 | 
            +
                end
         | 
| 88 | 
            +
             | 
| 89 | 
            +
                it_behaves_like 'a multipart request'
         | 
| 90 | 
            +
             | 
| 91 | 
            +
                it 'forms a multipart request' do
         | 
| 92 | 
            +
                  response = conn.post('/echo', payload)
         | 
| 93 | 
            +
             | 
| 94 | 
            +
                  boundary = parse_multipart_boundary(response.headers['Content-Type'])
         | 
| 95 | 
            +
                  result = parse_multipart(boundary, response.body)
         | 
| 96 | 
            +
                  expect(result[:errors]).to be_empty
         | 
| 97 | 
            +
             | 
| 98 | 
            +
                  part_json, body_json = result.part('json')
         | 
| 99 | 
            +
                  expect(part_json).to_not be_nil
         | 
| 100 | 
            +
                  expect(part_json.mime).to eq('application/json')
         | 
| 101 | 
            +
                  expect(part_json.filename).to be_nil
         | 
| 102 | 
            +
                  expect(body_json).to eq(json)
         | 
| 103 | 
            +
             | 
| 104 | 
            +
                  part_io, body_io = result.part('io')
         | 
| 105 | 
            +
                  expect(part_io).to_not be_nil
         | 
| 106 | 
            +
                  expect(part_io.mime).to eq('application/pdf')
         | 
| 107 | 
            +
                  expect(part_io.filename).to eq('local.path')
         | 
| 108 | 
            +
                  expect(body_io).to eq(io.string)
         | 
| 109 | 
            +
                end
         | 
| 110 | 
            +
              end
         | 
| 111 | 
            +
             | 
| 112 | 
            +
              context 'FilePart: when multipart objects in array param' do
         | 
| 113 | 
            +
                let(:payload) do
         | 
| 114 | 
            +
                  {
         | 
| 115 | 
            +
                    a: 1,
         | 
| 116 | 
            +
                    b: [{
         | 
| 117 | 
            +
                      c: Faraday::FilePart.new(__FILE__, 'text/x-ruby'),
         | 
| 118 | 
            +
                      d: 2
         | 
| 119 | 
            +
                    }]
         | 
| 120 | 
            +
                  }
         | 
| 121 | 
            +
                end
         | 
| 122 | 
            +
             | 
| 123 | 
            +
                it_behaves_like 'a multipart request'
         | 
| 124 | 
            +
             | 
| 125 | 
            +
                it 'forms a multipart request' do
         | 
| 126 | 
            +
                  response = conn.post('/echo', payload)
         | 
| 127 | 
            +
             | 
| 128 | 
            +
                  boundary = parse_multipart_boundary(response.headers['Content-Type'])
         | 
| 129 | 
            +
                  result = parse_multipart(boundary, response.body)
         | 
| 130 | 
            +
                  expect(result[:errors]).to be_empty
         | 
| 131 | 
            +
             | 
| 132 | 
            +
                  part_a, body_a = result.part('a')
         | 
| 133 | 
            +
                  expect(part_a).to_not be_nil
         | 
| 134 | 
            +
                  expect(part_a.filename).to be_nil
         | 
| 135 | 
            +
                  expect(body_a).to eq('1')
         | 
| 136 | 
            +
             | 
| 137 | 
            +
                  part_bc, body_bc = result.part('b[][c]')
         | 
| 138 | 
            +
                  expect(part_bc).to_not be_nil
         | 
| 139 | 
            +
                  expect(part_bc.filename).to eq('multipart_spec.rb')
         | 
| 140 | 
            +
                  expect(part_bc.headers['content-disposition'])
         | 
| 141 | 
            +
                    .to eq(
         | 
| 142 | 
            +
                      'form-data; name="b[][c]"; filename="multipart_spec.rb"'
         | 
| 143 | 
            +
                    )
         | 
| 144 | 
            +
                  expect(part_bc.headers['content-type']).to eq('text/x-ruby')
         | 
| 145 | 
            +
                  expect(part_bc.headers['content-transfer-encoding']).to eq('binary')
         | 
| 146 | 
            +
                  expect(body_bc).to eq(File.read(__FILE__))
         | 
| 147 | 
            +
             | 
| 148 | 
            +
                  part_bd, body_bd = result.part('b[][d]')
         | 
| 149 | 
            +
                  expect(part_bd).to_not be_nil
         | 
| 150 | 
            +
                  expect(part_bd.filename).to be_nil
         | 
| 151 | 
            +
                  expect(body_bd).to eq('2')
         | 
| 152 | 
            +
                end
         | 
| 153 | 
            +
              end
         | 
| 154 | 
            +
             | 
| 155 | 
            +
              context 'UploadIO: when multipart objects in param' do
         | 
| 156 | 
            +
                let(:payload) do
         | 
| 157 | 
            +
                  {
         | 
| 158 | 
            +
                    a: 1,
         | 
| 159 | 
            +
                    b: {
         | 
| 160 | 
            +
                      c: Faraday::UploadIO.new(__FILE__, 'text/x-ruby', nil,
         | 
| 161 | 
            +
                                               'Content-Disposition' => 'form-data; foo=1'),
         | 
| 162 | 
            +
                      d: 2
         | 
| 163 | 
            +
                    }
         | 
| 164 | 
            +
                  }
         | 
| 165 | 
            +
                end
         | 
| 166 | 
            +
                it_behaves_like 'a multipart request'
         | 
| 167 | 
            +
             | 
| 168 | 
            +
                it 'forms a multipart request' do
         | 
| 169 | 
            +
                  response = conn.post('/echo', payload)
         | 
| 170 | 
            +
             | 
| 171 | 
            +
                  boundary = parse_multipart_boundary(response.headers['Content-Type'])
         | 
| 172 | 
            +
                  result = parse_multipart(boundary, response.body)
         | 
| 173 | 
            +
                  expect(result[:errors]).to be_empty
         | 
| 174 | 
            +
             | 
| 175 | 
            +
                  part_a, body_a = result.part('a')
         | 
| 176 | 
            +
                  expect(part_a).to_not be_nil
         | 
| 177 | 
            +
                  expect(part_a.filename).to be_nil
         | 
| 178 | 
            +
                  expect(body_a).to eq('1')
         | 
| 179 | 
            +
             | 
| 180 | 
            +
                  part_bc, body_bc = result.part('b[c]')
         | 
| 181 | 
            +
                  expect(part_bc).to_not be_nil
         | 
| 182 | 
            +
                  expect(part_bc.filename).to eq('multipart_spec.rb')
         | 
| 183 | 
            +
                  expect(part_bc.headers['content-disposition'])
         | 
| 184 | 
            +
                    .to eq(
         | 
| 185 | 
            +
                      'form-data; foo=1; name="b[c]"; filename="multipart_spec.rb"'
         | 
| 186 | 
            +
                    )
         | 
| 187 | 
            +
                  expect(part_bc.headers['content-type']).to eq('text/x-ruby')
         | 
| 188 | 
            +
                  expect(part_bc.headers['content-transfer-encoding']).to eq('binary')
         | 
| 189 | 
            +
                  expect(body_bc).to eq(File.read(__FILE__))
         | 
| 190 | 
            +
             | 
| 191 | 
            +
                  part_bd, body_bd = result.part('b[d]')
         | 
| 192 | 
            +
                  expect(part_bd).to_not be_nil
         | 
| 193 | 
            +
                  expect(part_bd.filename).to be_nil
         | 
| 194 | 
            +
                  expect(body_bd).to eq('2')
         | 
| 195 | 
            +
                end
         | 
| 196 | 
            +
              end
         | 
| 197 | 
            +
             | 
| 198 | 
            +
              context 'UploadIO: when providing json and IO content in the same payload' do
         | 
| 199 | 
            +
                let(:io) { StringIO.new('io-content') }
         | 
| 200 | 
            +
                let(:json) do
         | 
| 201 | 
            +
                  {
         | 
| 202 | 
            +
                    b: 1,
         | 
| 203 | 
            +
                    c: 2
         | 
| 204 | 
            +
                  }.to_json
         | 
| 205 | 
            +
                end
         | 
| 206 | 
            +
             | 
| 207 | 
            +
                let(:payload) do
         | 
| 208 | 
            +
                  {
         | 
| 209 | 
            +
                    json: Faraday::ParamPart.new(json, 'application/json'),
         | 
| 210 | 
            +
                    io: Faraday::UploadIO.new(io, 'application/pdf')
         | 
| 211 | 
            +
                  }
         | 
| 212 | 
            +
                end
         | 
| 213 | 
            +
             | 
| 214 | 
            +
                it_behaves_like 'a multipart request'
         | 
| 215 | 
            +
             | 
| 216 | 
            +
                it 'forms a multipart request' do
         | 
| 217 | 
            +
                  response = conn.post('/echo', payload)
         | 
| 218 | 
            +
             | 
| 219 | 
            +
                  boundary = parse_multipart_boundary(response.headers['Content-Type'])
         | 
| 220 | 
            +
                  result = parse_multipart(boundary, response.body)
         | 
| 221 | 
            +
                  expect(result[:errors]).to be_empty
         | 
| 222 | 
            +
             | 
| 223 | 
            +
                  part_json, body_json = result.part('json')
         | 
| 224 | 
            +
                  expect(part_json).to_not be_nil
         | 
| 225 | 
            +
                  expect(part_json.mime).to eq('application/json')
         | 
| 226 | 
            +
                  expect(part_json.filename).to be_nil
         | 
| 227 | 
            +
                  expect(body_json).to eq(json)
         | 
| 228 | 
            +
             | 
| 229 | 
            +
                  part_io, body_io = result.part('io')
         | 
| 230 | 
            +
                  expect(part_io).to_not be_nil
         | 
| 231 | 
            +
                  expect(part_io.mime).to eq('application/pdf')
         | 
| 232 | 
            +
                  expect(part_io.filename).to eq('local.path')
         | 
| 233 | 
            +
                  expect(body_io).to eq(io.string)
         | 
| 234 | 
            +
                end
         | 
| 235 | 
            +
              end
         | 
| 236 | 
            +
             | 
| 237 | 
            +
              context 'UploadIO: when multipart objects in array param' do
         | 
| 238 | 
            +
                let(:payload) do
         | 
| 239 | 
            +
                  {
         | 
| 240 | 
            +
                    a: 1,
         | 
| 241 | 
            +
                    b: [{
         | 
| 242 | 
            +
                      c: Faraday::UploadIO.new(__FILE__, 'text/x-ruby'),
         | 
| 243 | 
            +
                      d: 2
         | 
| 244 | 
            +
                    }]
         | 
| 245 | 
            +
                  }
         | 
| 246 | 
            +
                end
         | 
| 247 | 
            +
             | 
| 248 | 
            +
                it_behaves_like 'a multipart request'
         | 
| 249 | 
            +
             | 
| 250 | 
            +
                it 'forms a multipart request' do
         | 
| 251 | 
            +
                  response = conn.post('/echo', payload)
         | 
| 252 | 
            +
             | 
| 253 | 
            +
                  boundary = parse_multipart_boundary(response.headers['Content-Type'])
         | 
| 254 | 
            +
                  result = parse_multipart(boundary, response.body)
         | 
| 255 | 
            +
                  expect(result[:errors]).to be_empty
         | 
| 256 | 
            +
             | 
| 257 | 
            +
                  part_a, body_a = result.part('a')
         | 
| 258 | 
            +
                  expect(part_a).to_not be_nil
         | 
| 259 | 
            +
                  expect(part_a.filename).to be_nil
         | 
| 260 | 
            +
                  expect(body_a).to eq('1')
         | 
| 261 | 
            +
             | 
| 262 | 
            +
                  part_bc, body_bc = result.part('b[][c]')
         | 
| 263 | 
            +
                  expect(part_bc).to_not be_nil
         | 
| 264 | 
            +
                  expect(part_bc.filename).to eq('multipart_spec.rb')
         | 
| 265 | 
            +
                  expect(part_bc.headers['content-disposition'])
         | 
| 266 | 
            +
                    .to eq(
         | 
| 267 | 
            +
                      'form-data; name="b[][c]"; filename="multipart_spec.rb"'
         | 
| 268 | 
            +
                    )
         | 
| 269 | 
            +
                  expect(part_bc.headers['content-type']).to eq('text/x-ruby')
         | 
| 270 | 
            +
                  expect(part_bc.headers['content-transfer-encoding']).to eq('binary')
         | 
| 271 | 
            +
                  expect(body_bc).to eq(File.read(__FILE__))
         | 
| 272 | 
            +
             | 
| 273 | 
            +
                  part_bd, body_bd = result.part('b[][d]')
         | 
| 274 | 
            +
                  expect(part_bd).to_not be_nil
         | 
| 275 | 
            +
                  expect(part_bd.filename).to be_nil
         | 
| 276 | 
            +
                  expect(body_bd).to eq('2')
         | 
| 277 | 
            +
                end
         | 
| 278 | 
            +
              end
         | 
| 279 | 
            +
             | 
| 280 | 
            +
              context 'when passing flat_encode=true option' do
         | 
| 281 | 
            +
                let(:options) { { flat_encode: true } }
         | 
| 282 | 
            +
                let(:io) { StringIO.new('io-content') }
         | 
| 283 | 
            +
                let(:payload) do
         | 
| 284 | 
            +
                  {
         | 
| 285 | 
            +
                    a: 1,
         | 
| 286 | 
            +
                    b: [
         | 
| 287 | 
            +
                      Faraday::UploadIO.new(io, 'application/pdf'),
         | 
| 288 | 
            +
                      Faraday::UploadIO.new(io, 'application/pdf')
         | 
| 289 | 
            +
                    ]
         | 
| 290 | 
            +
                  }
         | 
| 291 | 
            +
                end
         | 
| 292 | 
            +
             | 
| 293 | 
            +
                it_behaves_like 'a multipart request'
         | 
| 294 | 
            +
             | 
| 295 | 
            +
                it 'encode params using flat encoder' do
         | 
| 296 | 
            +
                  response = conn.post('/echo', payload)
         | 
| 297 | 
            +
             | 
| 298 | 
            +
                  expect(response.body).to include('name="b"')
         | 
| 299 | 
            +
                  expect(response.body).not_to include('name="b[]"')
         | 
| 300 | 
            +
                end
         | 
| 301 | 
            +
              end
         | 
| 302 | 
            +
            end
         |